3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "serverenvironment.h"
21 #include "content_sao.h"
25 #include "nodemetadata.h"
30 #include "remoteplayer.h"
31 #include "scripting_server.h"
33 #include "util/serialize.h"
34 #include "util/basic_macros.h"
35 #include "util/pointedthing.h"
36 #include "threading/mutex_auto_lock.h"
38 #include "gameparams.h"
39 #include "database-dummy.h"
40 #include "database-files.h"
41 #include "database-sqlite3.h"
43 #include "database-postgresql.h"
46 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
48 // A number that is much smaller than the timeout for particle spawners should/could ever be
49 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
55 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
58 // Initialize timer to random value to spread processing
59 float itv = abm->getTriggerInterval();
60 itv = MYMAX(0.001, itv); // No less than 1ms
61 int minval = MYMAX(-0.51*itv, -60); // Clamp to
62 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
63 timer = myrand_range(minval, maxval);
70 void LBMContentMapping::deleteContents()
72 for (auto &it : lbm_list) {
77 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
79 // Add the lbm_def to the LBMContentMapping.
80 // Unknown names get added to the global NameIdMapping.
81 INodeDefManager *nodedef = gamedef->ndef();
83 lbm_list.push_back(lbm_def);
85 for (const std::string &nodeTrigger: lbm_def->trigger_contents) {
86 std::set<content_t> c_ids;
87 bool found = nodedef->getIds(nodeTrigger, c_ids);
89 content_t c_id = gamedef->allocateUnknownNodeId(nodeTrigger);
90 if (c_id == CONTENT_IGNORE) {
91 // Seems it can't be allocated.
92 warningstream << "Could not internalize node name \"" << nodeTrigger
93 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
99 for (content_t c_id : c_ids) {
100 map[c_id].push_back(lbm_def);
105 const std::vector<LoadingBlockModifierDef *> *
106 LBMContentMapping::lookup(content_t c) const
108 lbm_map::const_iterator it = map.find(c);
111 // This first dereferences the iterator, returning
112 // a std::vector<LoadingBlockModifierDef *>
113 // reference, then we convert it to a pointer.
114 return &(it->second);
117 LBMManager::~LBMManager()
119 for (auto &m_lbm_def : m_lbm_defs) {
120 delete m_lbm_def.second;
123 for (auto &it : m_lbm_lookup) {
124 (it.second).deleteContents();
128 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
130 // Precondition, in query mode the map isn't used anymore
131 FATAL_ERROR_IF(m_query_mode == true,
132 "attempted to modify LBMManager in query mode");
134 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
135 throw ModError("Error adding LBM \"" + lbm_def->name +
136 "\": Does not follow naming conventions: "
137 "Only characters [a-z0-9_:] are allowed.");
140 m_lbm_defs[lbm_def->name] = lbm_def;
143 void LBMManager::loadIntroductionTimes(const std::string ×,
144 IGameDef *gamedef, u32 now)
149 // Storing it in a map first instead of
150 // handling the stuff directly in the loop
151 // removes all duplicate entries.
152 // TODO make this std::unordered_map
153 std::map<std::string, u32> introduction_times;
156 The introduction times string consists of name~time entries,
157 with each entry terminated by a semicolon. The time is decimal.
162 while ((idx_new = times.find(";", idx)) != std::string::npos) {
163 std::string entry = times.substr(idx, idx_new - idx);
164 std::vector<std::string> components = str_split(entry, '~');
165 if (components.size() != 2)
166 throw SerializationError("Introduction times entry \""
167 + entry + "\" requires exactly one '~'!");
168 const std::string &name = components[0];
169 u32 time = from_string<u32>(components[1]);
170 introduction_times[name] = time;
174 // Put stuff from introduction_times into m_lbm_lookup
175 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
176 it != introduction_times.end(); ++it) {
177 const std::string &name = it->first;
178 u32 time = it->second;
180 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
181 m_lbm_defs.find(name);
182 if (def_it == m_lbm_defs.end()) {
183 // This seems to be an LBM entry for
184 // an LBM we haven't loaded. Discard it.
187 LoadingBlockModifierDef *lbm_def = def_it->second;
188 if (lbm_def->run_at_every_load) {
189 // This seems to be an LBM entry for
190 // an LBM that runs at every load.
191 // Don't add it just yet.
195 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
197 // Erase the entry so that we know later
198 // what elements didn't get put into m_lbm_lookup
199 m_lbm_defs.erase(name);
202 // Now also add the elements from m_lbm_defs to m_lbm_lookup
203 // that weren't added in the previous step.
204 // They are introduced first time to this world,
205 // or are run at every load (introducement time hardcoded to U32_MAX).
207 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
208 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
210 for (auto &m_lbm_def : m_lbm_defs) {
211 if (m_lbm_def.second->run_at_every_load) {
212 lbms_running_always.addLBM(m_lbm_def.second, gamedef);
214 lbms_we_introduce_now.addLBM(m_lbm_def.second, gamedef);
218 // Clear the list, so that we don't delete remaining elements
219 // twice in the destructor
223 std::string LBMManager::createIntroductionTimesString()
225 // Precondition, we must be in query mode
226 FATAL_ERROR_IF(m_query_mode == false,
227 "attempted to query on non fully set up LBMManager");
229 std::ostringstream oss;
230 for (const auto &it : m_lbm_lookup) {
232 const std::vector<LoadingBlockModifierDef *> &lbm_list = it.second.lbm_list;
233 for (const auto &lbm_def : lbm_list) {
234 // Don't add if the LBM runs at every load,
235 // then introducement time is hardcoded
236 // and doesn't need to be stored
237 if (lbm_def->run_at_every_load)
239 oss << lbm_def->name << "~" << time << ";";
245 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
247 // Precondition, we need m_lbm_lookup to be initialized
248 FATAL_ERROR_IF(m_query_mode == false,
249 "attempted to query on non fully set up LBMManager");
250 v3s16 pos_of_block = block->getPosRelative();
254 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
255 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
256 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
257 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
259 n = block->getNodeNoEx(pos);
261 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
262 iit != m_lbm_lookup.end(); ++iit) {
263 const std::vector<LoadingBlockModifierDef *> *lbm_list =
264 iit->second.lookup(c);
267 for (auto lbmdef : *lbm_list) {
268 lbmdef->trigger(env, pos + pos_of_block, n);
278 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
281 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
282 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
283 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
286 if (p.getDistanceFrom(p0) <= r) {
293 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
295 std::set<v3s16> &blocks_removed,
296 std::set<v3s16> &blocks_added)
301 std::set<v3s16> newlist = m_forceloaded_list;
302 for(std::vector<v3s16>::iterator i = active_positions.begin();
303 i != active_positions.end(); ++i)
305 fillRadiusBlock(*i, radius, newlist);
309 Find out which blocks on the old list are not on the new list
311 // Go through old list
312 for(std::set<v3s16>::iterator i = m_list.begin();
313 i != m_list.end(); ++i)
316 // If not on new list, it's been removed
317 if(newlist.find(p) == newlist.end())
318 blocks_removed.insert(p);
322 Find out which blocks on the new list are not on the old list
324 // Go through new list
325 for(std::set<v3s16>::iterator i = newlist.begin();
326 i != newlist.end(); ++i)
329 // If not on old list, it's been added
330 if(m_list.find(p) == m_list.end())
331 blocks_added.insert(p);
338 for(std::set<v3s16>::iterator i = newlist.begin();
339 i != newlist.end(); ++i)
350 ServerEnvironment::ServerEnvironment(ServerMap *map,
351 ServerScripting *scriptIface, Server *server,
352 const std::string &path_world):
355 m_script(scriptIface),
357 m_path_world(path_world)
359 // Determine which database backend to use
360 std::string conf_path = path_world + DIR_DELIM + "world.mt";
362 bool succeeded = conf.readConfigFile(conf_path.c_str());
363 if (!succeeded || !conf.exists("player_backend")) {
364 // fall back to files
365 conf.set("player_backend", "files");
366 warningstream << "/!\\ You are using old player file backend. "
367 << "This backend is deprecated and will be removed in next release /!\\"
368 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
369 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
371 if (!conf.updateConfigFile(conf_path.c_str())) {
372 errorstream << "ServerEnvironment::ServerEnvironment(): "
373 << "Failed to update world.mt!" << std::endl;
377 std::string name = "";
378 conf.getNoEx("player_backend", name);
379 m_player_database = openPlayerDatabase(name, path_world, conf);
382 ServerEnvironment::~ServerEnvironment()
384 // Clear active block list.
385 // This makes the next one delete all active objects.
386 m_active_blocks.clear();
388 // Convert all objects to static and delete the active objects
389 deactivateFarObjects(true);
394 // Delete ActiveBlockModifiers
395 for (std::vector<ABMWithState>::iterator
396 i = m_abms.begin(); i != m_abms.end(); ++i){
400 // Deallocate players
401 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
402 i != m_players.end(); ++i) {
406 delete m_player_database;
409 Map & ServerEnvironment::getMap()
414 ServerMap & ServerEnvironment::getServerMap()
419 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
421 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
422 i != m_players.end(); ++i) {
423 RemotePlayer *player = *i;
424 if (player->peer_id == peer_id)
430 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
432 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
433 i != m_players.end(); ++i) {
434 RemotePlayer *player = *i;
435 if (strcmp(player->getName(), name) == 0)
441 void ServerEnvironment::addPlayer(RemotePlayer *player)
443 DSTACK(FUNCTION_NAME);
445 Check that peer_ids are unique.
446 Also check that names are unique.
447 Exception: there can be multiple players with peer_id=0
449 // If peer id is non-zero, it has to be unique.
450 if (player->peer_id != 0)
451 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
452 // Name has to be unique.
453 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
455 m_players.push_back(player);
458 void ServerEnvironment::removePlayer(RemotePlayer *player)
460 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
461 it != m_players.end(); ++it) {
462 if ((*it) == player) {
470 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
472 return m_player_database->removePlayer(name);
475 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
477 float distance = pos1.getDistanceFrom(pos2);
479 //calculate normalized direction vector
480 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
481 (pos2.Y - pos1.Y)/distance,
482 (pos2.Z - pos1.Z)/distance);
484 //find out if there's a node on path between pos1 and pos2
485 for (float i = 1; i < distance; i += stepsize) {
486 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
487 normalized_vector.Y * i,
488 normalized_vector.Z * i) +pos1,BS);
490 MapNode n = getMap().getNodeNoEx(pos);
492 if(n.param0 != CONTENT_AIR) {
502 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
503 const std::string &str_reason, bool reconnect)
505 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
506 it != m_players.end(); ++it) {
507 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
508 m_server->DenyAccessVerCompliant(player->peer_id,
509 player->protocol_version, reason, str_reason, reconnect);
513 void ServerEnvironment::saveLoadedPlayers()
515 std::string players_path = m_path_world + DIR_DELIM + "players";
516 fs::CreateDir(players_path);
518 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
519 it != m_players.end();
521 if ((*it)->checkModified() ||
522 ((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) {
524 m_player_database->savePlayer(*it);
525 } catch (DatabaseException &e) {
526 errorstream << "Failed to save player " << (*it)->getName() << " exception: "
527 << e.what() << std::endl;
534 void ServerEnvironment::savePlayer(RemotePlayer *player)
537 m_player_database->savePlayer(player);
538 } catch (DatabaseException &e) {
539 errorstream << "Failed to save player " << player->getName() << " exception: "
540 << e.what() << std::endl;
545 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
546 u16 peer_id, bool is_singleplayer)
548 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
549 // Create player if it doesn't exist
550 if (!m_player_database->loadPlayer(player, playersao)) {
552 // Set player position
553 infostream << "Server: Finding spawn place for player \""
554 << player->getName() << "\"" << std::endl;
555 playersao->setBasePosition(m_server->findSpawnPos());
557 // Make sure the player is saved
558 player->setModified(true);
560 // If the player exists, ensure that they respawn inside legal bounds
561 // This fixes an assert crash when the player can't be added
562 // to the environment
563 ServerMap &map = getServerMap();
564 if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) {
565 actionstream << "Respawn position for player \""
566 << player->getName() << "\" outside limits, resetting" << std::endl;
567 playersao->setBasePosition(m_server->findSpawnPos());
571 // Add player to environment
574 /* Clean up old HUD elements from previous sessions */
577 /* Add object to environment */
578 addActiveObject(playersao);
583 void ServerEnvironment::saveMeta()
585 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
587 // Open file and serialize
588 std::ostringstream ss(std::ios_base::binary);
591 args.setU64("game_time", m_game_time);
592 args.setU64("time_of_day", getTimeOfDay());
593 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
594 args.setU64("lbm_introduction_times_version", 1);
595 args.set("lbm_introduction_times",
596 m_lbm_mgr.createIntroductionTimesString());
597 args.setU64("day_count", m_day_count);
601 if(!fs::safeWriteToFile(path, ss.str()))
603 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
605 throw SerializationError("Couldn't save env meta");
609 void ServerEnvironment::loadMeta()
611 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
613 // Open file and deserialize
614 std::ifstream is(path.c_str(), std::ios_base::binary);
616 infostream << "ServerEnvironment::loadMeta(): Failed to open "
617 << path << std::endl;
618 throw SerializationError("Couldn't load env meta");
623 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
624 throw SerializationError("ServerEnvironment::loadMeta(): "
625 "EnvArgsEnd not found!");
629 m_game_time = args.getU64("game_time");
630 } catch (SettingNotFoundException &e) {
631 // Getting this is crucial, otherwise timestamps are useless
632 throw SerializationError("Couldn't load env meta game_time");
635 setTimeOfDay(args.exists("time_of_day") ?
636 // set day to early morning by default
637 args.getU64("time_of_day") : 5250);
639 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
640 // If missing, do as if clearObjects was never called
641 args.getU64("last_clear_objects_time") : 0;
643 std::string lbm_introduction_times = "";
645 u64 ver = args.getU64("lbm_introduction_times_version");
647 lbm_introduction_times = args.get("lbm_introduction_times");
649 infostream << "ServerEnvironment::loadMeta(): Non-supported"
650 << " introduction time version " << ver << std::endl;
652 } catch (SettingNotFoundException &e) {
653 // No problem, this is expected. Just continue with an empty string
655 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
657 m_day_count = args.exists("day_count") ?
658 args.getU64("day_count") : 0;
661 void ServerEnvironment::loadDefaultMeta()
663 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
668 ActiveBlockModifier *abm;
670 std::set<content_t> required_neighbors;
676 ServerEnvironment *m_env;
677 std::vector<std::vector<ActiveABM> *> m_aabms;
679 ABMHandler(std::vector<ABMWithState> &abms,
680 float dtime_s, ServerEnvironment *env,
686 INodeDefManager *ndef = env->getGameDef()->ndef();
687 for(std::vector<ABMWithState>::iterator
688 i = abms.begin(); i != abms.end(); ++i) {
689 ActiveBlockModifier *abm = i->abm;
690 float trigger_interval = abm->getTriggerInterval();
691 if(trigger_interval < 0.001)
692 trigger_interval = 0.001;
693 float actual_interval = dtime_s;
696 if(i->timer < trigger_interval)
698 i->timer -= trigger_interval;
699 actual_interval = trigger_interval;
701 float chance = abm->getTriggerChance();
706 if (abm->getSimpleCatchUp()) {
707 float intervals = actual_interval / trigger_interval;
710 aabm.chance = chance / intervals;
714 aabm.chance = chance;
718 const std::set<std::string> &required_neighbors_s =
719 abm->getRequiredNeighbors();
720 for (std::set<std::string>::iterator rn = required_neighbors_s.begin();
721 rn != required_neighbors_s.end(); ++rn) {
722 ndef->getIds(*rn, aabm.required_neighbors);
726 const std::set<std::string> &contents_s = abm->getTriggerContents();
727 for (std::set<std::string>::iterator cs = contents_s.begin();
728 cs != contents_s.end(); ++cs) {
729 std::set<content_t> ids;
730 ndef->getIds(*cs, ids);
731 for (std::set<content_t>::const_iterator k = ids.begin();
732 k != ids.end(); ++k) {
734 if (c >= m_aabms.size())
735 m_aabms.resize(c + 256, NULL);
737 m_aabms[c] = new std::vector<ActiveABM>;
738 m_aabms[c]->push_back(aabm);
746 for (size_t i = 0; i < m_aabms.size(); i++)
750 // Find out how many objects the given block and its neighbours contain.
751 // Returns the number of objects in the block, and also in 'wider' the
752 // number of objects in the block and all its neighbours. The latter
753 // may an estimate if any neighbours are unloaded.
754 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
757 u32 wider_unknown_count = 0;
758 for(s16 x=-1; x<=1; x++)
759 for(s16 y=-1; y<=1; y++)
760 for(s16 z=-1; z<=1; z++)
762 MapBlock *block2 = map->getBlockNoCreateNoEx(
763 block->getPos() + v3s16(x,y,z));
765 wider_unknown_count++;
768 wider += block2->m_static_objects.m_active.size()
769 + block2->m_static_objects.m_stored.size();
772 u32 active_object_count = block->m_static_objects.m_active.size();
773 u32 wider_known_count = 3*3*3 - wider_unknown_count;
774 wider += wider_unknown_count * wider / wider_known_count;
775 return active_object_count;
778 void apply(MapBlock *block)
780 if(m_aabms.empty() || block->isDummy())
783 ServerMap *map = &m_env->getServerMap();
785 u32 active_object_count_wider;
786 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
787 m_env->m_added_objects = 0;
790 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
791 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
792 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
794 const MapNode &n = block->getNodeUnsafe(p0);
795 content_t c = n.getContent();
797 if (c >= m_aabms.size() || !m_aabms[c])
800 v3s16 p = p0 + block->getPosRelative();
801 for(std::vector<ActiveABM>::iterator
802 i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
803 if(myrand() % i->chance != 0)
807 if(!i->required_neighbors.empty())
810 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
811 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
812 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
817 if (block->isValidPosition(p1)) {
818 // if the neighbor is found on the same map block
819 // get it straight from there
820 const MapNode &n = block->getNodeUnsafe(p1);
823 // otherwise consult the map
824 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
827 std::set<content_t>::const_iterator k;
828 k = i->required_neighbors.find(c);
829 if(k != i->required_neighbors.end()){
833 // No required neighbor found
838 // Call all the trigger variations
839 i->abm->trigger(m_env, p, n);
840 i->abm->trigger(m_env, p, n,
841 active_object_count, active_object_count_wider);
843 // Count surrounding objects again if the abms added any
844 if(m_env->m_added_objects > 0) {
845 active_object_count = countObjects(block, map, active_object_count_wider);
846 m_env->m_added_objects = 0;
853 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
855 // Reset usage timer immediately, otherwise a block that becomes active
856 // again at around the same time as it would normally be unloaded will
857 // get unloaded incorrectly. (I think this still leaves a small possibility
858 // of a race condition between this and server::AsyncRunStep, which only
859 // some kind of synchronisation will fix, but it at least reduces the window
860 // of opportunity for it to break from seconds to nanoseconds)
861 block->resetUsageTimer();
863 // Get time difference
865 u32 stamp = block->getTimestamp();
866 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
867 dtime_s = m_game_time - stamp;
868 dtime_s += additional_dtime;
870 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
871 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
873 // Remove stored static objects if clearObjects was called since block's timestamp
874 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
875 block->m_static_objects.m_stored.clear();
876 // do not set changed flag to avoid unnecessary mapblock writes
879 // Set current time as timestamp
880 block->setTimestampNoChangedFlag(m_game_time);
882 /*infostream<<"ServerEnvironment::activateBlock(): block is "
883 <<dtime_s<<" seconds old."<<std::endl;*/
885 // Activate stored objects
886 activateObjects(block, dtime_s);
888 /* Handle LoadingBlockModifiers */
889 m_lbm_mgr.applyLBMs(this, block, stamp);
892 std::vector<NodeTimer> elapsed_timers =
893 block->m_node_timers.step((float)dtime_s);
894 if (!elapsed_timers.empty()) {
896 for (std::vector<NodeTimer>::iterator
897 i = elapsed_timers.begin();
898 i != elapsed_timers.end(); ++i){
899 n = block->getNodeNoEx(i->position);
900 v3s16 p = i->position + block->getPosRelative();
901 if (m_script->node_on_timer(p, n, i->elapsed))
902 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
906 /* Handle ActiveBlockModifiers */
907 ABMHandler abmhandler(m_abms, dtime_s, this, false);
908 abmhandler.apply(block);
911 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
913 m_abms.push_back(ABMWithState(abm));
916 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
918 m_lbm_mgr.addLBMDef(lbm);
921 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
923 INodeDefManager *ndef = m_server->ndef();
924 MapNode n_old = m_map->getNodeNoEx(p);
927 if (ndef->get(n_old).has_on_destruct)
928 m_script->node_on_destruct(p, n_old);
931 if (!m_map->addNodeWithEvent(p, n))
934 // Update active VoxelManipulator if a mapgen thread
935 m_map->updateVManip(p);
937 // Call post-destructor
938 if (ndef->get(n_old).has_after_destruct)
939 m_script->node_after_destruct(p, n_old);
942 if (ndef->get(n).has_on_construct)
943 m_script->node_on_construct(p, n);
948 bool ServerEnvironment::removeNode(v3s16 p)
950 INodeDefManager *ndef = m_server->ndef();
951 MapNode n_old = m_map->getNodeNoEx(p);
954 if (ndef->get(n_old).has_on_destruct)
955 m_script->node_on_destruct(p, n_old);
958 // This is slightly optimized compared to addNodeWithEvent(air)
959 if (!m_map->removeNodeWithEvent(p))
962 // Update active VoxelManipulator if a mapgen thread
963 m_map->updateVManip(p);
965 // Call post-destructor
966 if (ndef->get(n_old).has_after_destruct)
967 m_script->node_after_destruct(p, n_old);
969 // Air doesn't require constructor
973 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
975 if (!m_map->addNodeWithEvent(p, n, false))
978 // Update active VoxelManipulator if a mapgen thread
979 m_map->updateVManip(p);
984 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
987 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
988 i != m_active_objects.end(); ++i) {
989 ServerActiveObject* obj = i->second;
991 v3f objectpos = obj->getBasePosition();
992 if (objectpos.getDistanceFrom(pos) > radius)
994 objects.push_back(id);
998 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1000 infostream << "ServerEnvironment::clearObjects(): "
1001 << "Removing all active objects" << std::endl;
1002 std::vector<u16> objects_to_remove;
1003 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1004 i != m_active_objects.end(); ++i) {
1005 ServerActiveObject* obj = i->second;
1006 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1009 // Delete static object if block is loaded
1010 if (obj->m_static_exists) {
1011 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1013 block->m_static_objects.remove(id);
1014 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1015 MOD_REASON_CLEAR_ALL_OBJECTS);
1016 obj->m_static_exists = false;
1019 // If known by some client, don't delete immediately
1020 if (obj->m_known_by_count > 0) {
1021 obj->m_pending_deactivation = true;
1022 obj->m_removed = true;
1026 // Tell the object about removal
1027 obj->removingFromEnvironment();
1028 // Deregister in scripting api
1029 m_script->removeObjectReference(obj);
1031 // Delete active object
1032 if (obj->environmentDeletes())
1034 // Id to be removed from m_active_objects
1035 objects_to_remove.push_back(id);
1038 // Remove references from m_active_objects
1039 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1040 i != objects_to_remove.end(); ++i) {
1041 m_active_objects.erase(*i);
1044 // Get list of loaded blocks
1045 std::vector<v3s16> loaded_blocks;
1046 infostream << "ServerEnvironment::clearObjects(): "
1047 << "Listing all loaded blocks" << std::endl;
1048 m_map->listAllLoadedBlocks(loaded_blocks);
1049 infostream << "ServerEnvironment::clearObjects(): "
1050 << "Done listing all loaded blocks: "
1051 << loaded_blocks.size()<<std::endl;
1053 // Get list of loadable blocks
1054 std::vector<v3s16> loadable_blocks;
1055 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1056 infostream << "ServerEnvironment::clearObjects(): "
1057 << "Listing all loadable blocks" << std::endl;
1058 m_map->listAllLoadableBlocks(loadable_blocks);
1059 infostream << "ServerEnvironment::clearObjects(): "
1060 << "Done listing all loadable blocks: "
1061 << loadable_blocks.size() << std::endl;
1063 loadable_blocks = loaded_blocks;
1066 infostream << "ServerEnvironment::clearObjects(): "
1067 << "Now clearing objects in " << loadable_blocks.size()
1068 << " blocks" << std::endl;
1070 // Grab a reference on each loaded block to avoid unloading it
1071 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1072 i != loaded_blocks.end(); ++i) {
1074 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1075 assert(block != NULL);
1079 // Remove objects in all loadable blocks
1080 u32 unload_interval = U32_MAX;
1081 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1082 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1083 unload_interval = MYMAX(unload_interval, 1);
1085 u32 report_interval = loadable_blocks.size() / 10;
1086 u32 num_blocks_checked = 0;
1087 u32 num_blocks_cleared = 0;
1088 u32 num_objs_cleared = 0;
1089 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1090 i != loadable_blocks.end(); ++i) {
1092 MapBlock *block = m_map->emergeBlock(p, false);
1094 errorstream << "ServerEnvironment::clearObjects(): "
1095 << "Failed to emerge block " << PP(p) << std::endl;
1098 u32 num_stored = block->m_static_objects.m_stored.size();
1099 u32 num_active = block->m_static_objects.m_active.size();
1100 if (num_stored != 0 || num_active != 0) {
1101 block->m_static_objects.m_stored.clear();
1102 block->m_static_objects.m_active.clear();
1103 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1104 MOD_REASON_CLEAR_ALL_OBJECTS);
1105 num_objs_cleared += num_stored + num_active;
1106 num_blocks_cleared++;
1108 num_blocks_checked++;
1110 if (report_interval != 0 &&
1111 num_blocks_checked % report_interval == 0) {
1112 float percent = 100.0 * (float)num_blocks_checked /
1113 loadable_blocks.size();
1114 infostream << "ServerEnvironment::clearObjects(): "
1115 << "Cleared " << num_objs_cleared << " objects"
1116 << " in " << num_blocks_cleared << " blocks ("
1117 << percent << "%)" << std::endl;
1119 if (num_blocks_checked % unload_interval == 0) {
1120 m_map->unloadUnreferencedBlocks();
1123 m_map->unloadUnreferencedBlocks();
1125 // Drop references that were added above
1126 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1127 i != loaded_blocks.end(); ++i) {
1129 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1134 m_last_clear_objects_time = m_game_time;
1136 infostream << "ServerEnvironment::clearObjects(): "
1137 << "Finished: Cleared " << num_objs_cleared << " objects"
1138 << " in " << num_blocks_cleared << " blocks" << std::endl;
1141 void ServerEnvironment::step(float dtime)
1143 DSTACK(FUNCTION_NAME);
1145 //TimeTaker timer("ServerEnv step");
1147 /* Step time of day */
1148 stepTimeOfDay(dtime);
1151 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1152 // really matter that much.
1153 static thread_local const float server_step =
1154 g_settings->getFloat("dedicated_server_step");
1155 m_recommended_send_interval = server_step;
1161 m_game_time_fraction_counter += dtime;
1162 u32 inc_i = (u32)m_game_time_fraction_counter;
1163 m_game_time += inc_i;
1164 m_game_time_fraction_counter -= (float)inc_i;
1171 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1172 for (RemotePlayer *player : m_players) {
1173 // Ignore disconnected players
1174 if (player->peer_id == 0)
1178 player->move(dtime, this, 100 * BS);
1183 Manage active block list
1185 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1186 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1188 Get player block positions
1190 std::vector<v3s16> players_blockpos;
1191 for (RemotePlayer *player: m_players) {
1192 // Ignore disconnected players
1193 if (player->peer_id == 0)
1196 PlayerSAO *playersao = player->getPlayerSAO();
1199 players_blockpos.push_back(
1200 getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS)));
1204 Update list of active blocks, collecting changes
1206 static thread_local const s16 active_block_range =
1207 g_settings->getS16("active_block_range");
1208 std::set<v3s16> blocks_removed;
1209 std::set<v3s16> blocks_added;
1210 m_active_blocks.update(players_blockpos, active_block_range,
1211 blocks_removed, blocks_added);
1214 Handle removed blocks
1217 // Convert active objects that are no more in active blocks to static
1218 deactivateFarObjects(false);
1220 for (const v3s16 &p: blocks_removed) {
1221 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1225 // Set current time as timestamp (and let it set ChangedFlag)
1226 block->setTimestamp(m_game_time);
1233 for (const v3s16 &p: blocks_added) {
1234 MapBlock *block = m_map->getBlockOrEmerge(p);
1236 m_active_blocks.m_list.erase(p);
1240 activateBlock(block);
1245 Mess around in active blocks
1247 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1248 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1250 float dtime = m_cache_nodetimer_interval;
1252 for (const v3s16 &p: m_active_blocks.m_list) {
1253 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1257 // Reset block usage timer
1258 block->resetUsageTimer();
1260 // Set current time as timestamp
1261 block->setTimestampNoChangedFlag(m_game_time);
1262 // If time has changed much from the one on disk,
1263 // set block to be saved when it is unloaded
1264 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1265 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1266 MOD_REASON_BLOCK_EXPIRED);
1269 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1270 if (!elapsed_timers.empty()) {
1273 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1274 n = block->getNodeNoEx(elapsed_timer.position);
1275 p2 = elapsed_timer.position + block->getPosRelative();
1276 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1277 block->setNodeTimer(NodeTimer(
1278 elapsed_timer.timeout, 0, elapsed_timer.position));
1285 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1287 if (m_active_block_interval_overload_skip > 0) {
1288 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1289 m_active_block_interval_overload_skip--;
1292 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1293 TimeTaker timer("modify in active blocks per interval");
1295 // Initialize handling of ActiveBlockModifiers
1296 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1298 for (const v3s16 &p : m_active_blocks.m_list) {
1299 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1303 // Set current time as timestamp
1304 block->setTimestampNoChangedFlag(m_game_time);
1306 /* Handle ActiveBlockModifiers */
1307 abmhandler.apply(block);
1310 u32 time_ms = timer.stop(true);
1311 u32 max_time_ms = 200;
1312 if (time_ms > max_time_ms) {
1313 warningstream<<"active block modifiers took "
1314 <<time_ms<<"ms (longer than "
1315 <<max_time_ms<<"ms)"<<std::endl;
1316 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1321 Step script environment (run global on_step())
1323 m_script->environment_Step(dtime);
1329 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1330 //TimeTaker timer("Step active objects");
1332 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1334 // This helps the objects to send data at the same time
1335 bool send_recommended = false;
1336 m_send_recommended_timer += dtime;
1337 if(m_send_recommended_timer > getSendRecommendedInterval())
1339 m_send_recommended_timer -= getSendRecommendedInterval();
1340 send_recommended = true;
1343 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1344 i != m_active_objects.end(); ++i) {
1345 ServerActiveObject* obj = i->second;
1346 // Don't step if is to be removed or stored statically
1347 if(obj->m_removed || obj->m_pending_deactivation)
1350 obj->step(dtime, send_recommended);
1351 // Read messages from object
1352 while(!obj->m_messages_out.empty())
1354 m_active_object_messages.push(
1355 obj->m_messages_out.front());
1356 obj->m_messages_out.pop();
1362 Manage active objects
1364 if (m_object_management_interval.step(dtime, 0.5)) {
1365 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1367 Remove objects that satisfy (m_removed && m_known_by_count==0)
1369 removeRemovedObjects();
1373 Manage particle spawner expiration
1375 if (m_particle_management_interval.step(dtime, 1.0)) {
1376 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1377 i != m_particle_spawners.end(); ) {
1378 //non expiring spawners
1379 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1385 if (i->second <= 0.f)
1386 m_particle_spawners.erase(i++);
1393 u32 ServerEnvironment::addParticleSpawner(float exptime)
1395 // Timers with lifetime 0 do not expire
1396 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1399 for (;;) { // look for unused particlespawner id
1401 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1402 if (f == m_particle_spawners.end()) {
1403 m_particle_spawners[id] = time;
1410 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1412 u32 id = addParticleSpawner(exptime);
1413 m_particle_spawner_attachments[id] = attached_id;
1414 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1415 obj->attachParticleSpawner(id);
1420 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1422 m_particle_spawners.erase(id);
1423 const auto &it = m_particle_spawner_attachments.find(id);
1424 if (it != m_particle_spawner_attachments.end()) {
1425 u16 obj_id = it->second;
1426 ServerActiveObject *sao = getActiveObject(obj_id);
1427 if (sao != NULL && remove_from_object) {
1428 sao->detachParticleSpawner(id);
1430 m_particle_spawner_attachments.erase(id);
1434 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1436 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1437 return (n != m_active_objects.end() ? n->second : NULL);
1440 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1445 return objects.find(id) == objects.end();
1448 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1450 //try to reuse id's as late as possible
1451 static u16 last_used_id = 0;
1452 u16 startid = last_used_id;
1456 if(isFreeServerActiveObjectId(last_used_id, objects))
1457 return last_used_id;
1459 if(last_used_id == startid)
1464 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1466 assert(object); // Pre-condition
1468 u16 id = addActiveObjectRaw(object, true, 0);
1473 Finds out what new objects have been added to
1474 inside a radius around a position
1476 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1478 std::set<u16> ¤t_objects,
1479 std::queue<u16> &added_objects)
1481 f32 radius_f = radius * BS;
1482 f32 player_radius_f = player_radius * BS;
1484 if (player_radius_f < 0)
1485 player_radius_f = 0;
1487 Go through the object list,
1488 - discard m_removed objects,
1489 - discard objects that are too far away,
1490 - discard objects that are found in current_objects.
1491 - add remaining objects to added_objects
1493 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1494 i != m_active_objects.end(); ++i) {
1498 ServerActiveObject *object = i->second;
1502 // Discard if removed or deactivating
1503 if(object->m_removed || object->m_pending_deactivation)
1506 f32 distance_f = object->getBasePosition().
1507 getDistanceFrom(playersao->getBasePosition());
1508 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1509 // Discard if too far
1510 if (distance_f > player_radius_f && player_radius_f != 0)
1512 } else if (distance_f > radius_f)
1515 // Discard if already on current_objects
1516 std::set<u16>::iterator n;
1517 n = current_objects.find(id);
1518 if(n != current_objects.end())
1520 // Add to added_objects
1521 added_objects.push(id);
1526 Finds out what objects have been removed from
1527 inside a radius around a position
1529 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1531 std::set<u16> ¤t_objects,
1532 std::queue<u16> &removed_objects)
1534 f32 radius_f = radius * BS;
1535 f32 player_radius_f = player_radius * BS;
1537 if (player_radius_f < 0)
1538 player_radius_f = 0;
1540 Go through current_objects; object is removed if:
1541 - object is not found in m_active_objects (this is actually an
1542 error condition; objects should be set m_removed=true and removed
1543 only after all clients have been informed about removal), or
1544 - object has m_removed=true, or
1545 - object is too far away
1547 for(std::set<u16>::iterator
1548 i = current_objects.begin();
1549 i != current_objects.end(); ++i)
1552 ServerActiveObject *object = getActiveObject(id);
1554 if (object == NULL) {
1555 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1556 << " object in current_objects is NULL" << std::endl;
1557 removed_objects.push(id);
1561 if (object->m_removed || object->m_pending_deactivation) {
1562 removed_objects.push(id);
1566 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1567 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1568 if (distance_f <= player_radius_f || player_radius_f == 0)
1570 } else if (distance_f <= radius_f)
1573 // Object is no longer visible
1574 removed_objects.push(id);
1578 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1579 v3s16 blockpos, bool static_exists, v3s16 static_block)
1581 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1585 for (std::map<u16, StaticObject>::iterator
1586 so_it = block->m_static_objects.m_active.begin();
1587 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1588 // Get the ServerActiveObject counterpart to this StaticObject
1589 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it->first);
1590 if (ao_it == m_active_objects.end()) {
1591 // If this ever happens, there must be some kind of nasty bug.
1592 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1593 "Object from MapBlock::m_static_objects::m_active not found "
1594 "in m_active_objects";
1598 ServerActiveObject *sao = ao_it->second;
1599 sao->m_static_exists = static_exists;
1600 sao->m_static_block = static_block;
1604 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1606 if(m_active_object_messages.empty())
1607 return ActiveObjectMessage(0);
1609 ActiveObjectMessage message = m_active_object_messages.front();
1610 m_active_object_messages.pop();
1614 void ServerEnvironment::getSelectedActiveObjects(
1615 const core::line3d<f32> &shootline_on_map,
1616 std::vector<PointedThing> &objects)
1618 std::vector<u16> objectIds;
1619 getObjectsInsideRadius(objectIds, shootline_on_map.start,
1620 shootline_on_map.getLength() + 10.0f);
1621 const v3f line_vector = shootline_on_map.getVector();
1623 for (u32 i = 0; i < objectIds.size(); i++) {
1624 ServerActiveObject* obj = getActiveObject(objectIds[i]);
1626 aabb3f selection_box;
1627 if (!obj->getSelectionBox(&selection_box))
1630 v3f pos = obj->getBasePosition();
1632 aabb3f offsetted_box(selection_box.MinEdge + pos,
1633 selection_box.MaxEdge + pos);
1635 v3f current_intersection;
1636 v3s16 current_normal;
1637 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1638 ¤t_intersection, ¤t_normal)) {
1639 objects.push_back(PointedThing(
1640 (s16) objectIds[i], current_intersection, current_normal,
1641 (current_intersection - shootline_on_map.start).getLengthSQ()));
1647 ************ Private methods *************
1650 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1651 bool set_changed, u32 dtime_s)
1653 assert(object); // Pre-condition
1654 if(object->getId() == 0){
1655 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1658 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1659 <<"no free ids available"<<std::endl;
1660 if(object->environmentDeletes())
1664 object->setId(new_id);
1667 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1668 <<"supplied with id "<<object->getId()<<std::endl;
1671 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1672 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1673 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1674 if(object->environmentDeletes())
1679 if (objectpos_over_limit(object->getBasePosition())) {
1680 v3f p = object->getBasePosition();
1681 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1682 << "object position (" << p.X << "," << p.Y << "," << p.Z
1683 << ") outside maximum range" << std::endl;
1684 if (object->environmentDeletes())
1689 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1690 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1692 m_active_objects[object->getId()] = object;
1694 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1695 <<"Added id="<<object->getId()<<"; there are now "
1696 <<m_active_objects.size()<<" active objects."
1699 // Register reference in scripting api (must be done before post-init)
1700 m_script->addObjectReference(object);
1701 // Post-initialize object
1702 object->addedToEnvironment(dtime_s);
1704 // Add static data to block
1705 if(object->isStaticAllowed())
1707 // Add static object to active static list of the block
1708 v3f objectpos = object->getBasePosition();
1709 std::string staticdata = "";
1710 object->getStaticData(&staticdata);
1711 StaticObject s_obj(object->getType(), objectpos, staticdata);
1712 // Add to the block where the object is located in
1713 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1714 MapBlock *block = m_map->emergeBlock(blockpos);
1716 block->m_static_objects.m_active[object->getId()] = s_obj;
1717 object->m_static_exists = true;
1718 object->m_static_block = blockpos;
1721 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1722 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1724 v3s16 p = floatToInt(objectpos, BS);
1725 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1726 <<"could not emerge block for storing id="<<object->getId()
1727 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1731 return object->getId();
1735 Remove objects that satisfy (m_removed && m_known_by_count==0)
1737 void ServerEnvironment::removeRemovedObjects()
1739 std::vector<u16> objects_to_remove;
1740 for(ServerActiveObjectMap::iterator i = m_active_objects.begin();
1741 i != m_active_objects.end(); ++i) {
1743 ServerActiveObject* obj = i->second;
1744 // This shouldn't happen but check it
1747 infostream<<"NULL object found in ServerEnvironment"
1748 <<" while finding removed objects. id="<<id<<std::endl;
1749 // Id to be removed from m_active_objects
1750 objects_to_remove.push_back(id);
1755 We will delete objects that are marked as removed or thatare
1756 waiting for deletion after deactivation
1758 if (!obj->m_removed && !obj->m_pending_deactivation)
1762 Delete static data from block if is marked as removed
1764 if(obj->m_static_exists && obj->m_removed)
1766 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1768 block->m_static_objects.remove(id);
1769 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1770 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1771 obj->m_static_exists = false;
1773 infostream<<"Failed to emerge block from which an object to "
1774 <<"be removed was loaded from. id="<<id<<std::endl;
1778 // If m_known_by_count > 0, don't actually remove. On some future
1779 // invocation this will be 0, which is when removal will continue.
1780 if(obj->m_known_by_count > 0)
1784 Move static data from active to stored if not marked as removed
1786 if(obj->m_static_exists && !obj->m_removed){
1787 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1789 std::map<u16, StaticObject>::iterator i =
1790 block->m_static_objects.m_active.find(id);
1791 if(i != block->m_static_objects.m_active.end()){
1792 block->m_static_objects.m_stored.push_back(i->second);
1793 block->m_static_objects.m_active.erase(id);
1794 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1795 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1798 infostream<<"Failed to emerge block from which an object to "
1799 <<"be deactivated was loaded from. id="<<id<<std::endl;
1803 // Tell the object about removal
1804 obj->removingFromEnvironment();
1805 // Deregister in scripting api
1806 m_script->removeObjectReference(obj);
1809 if(obj->environmentDeletes())
1812 // Id to be removed from m_active_objects
1813 objects_to_remove.push_back(id);
1815 // Remove references from m_active_objects
1816 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1817 i != objects_to_remove.end(); ++i) {
1818 m_active_objects.erase(*i);
1822 static void print_hexdump(std::ostream &o, const std::string &data)
1824 const int linelength = 16;
1825 for(int l=0; ; l++){
1826 int i0 = linelength * l;
1827 bool at_end = false;
1828 int thislinelength = linelength;
1829 if(i0 + thislinelength > (int)data.size()){
1830 thislinelength = data.size() - i0;
1833 for(int di=0; di<linelength; di++){
1836 if(di<thislinelength)
1837 snprintf(buf, 4, "%.2x ", data[i]);
1839 snprintf(buf, 4, " ");
1843 for(int di=0; di<thislinelength; di++){
1857 Convert stored objects from blocks near the players to active.
1859 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1864 // Ignore if no stored objects (to not set changed flag)
1865 if(block->m_static_objects.m_stored.empty())
1868 verbosestream<<"ServerEnvironment::activateObjects(): "
1869 <<"activating objects of block "<<PP(block->getPos())
1870 <<" ("<<block->m_static_objects.m_stored.size()
1871 <<" objects)"<<std::endl;
1872 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1874 errorstream<<"suspiciously large amount of objects detected: "
1875 <<block->m_static_objects.m_stored.size()<<" in "
1876 <<PP(block->getPos())
1877 <<"; removing all of them."<<std::endl;
1878 // Clear stored list
1879 block->m_static_objects.m_stored.clear();
1880 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1881 MOD_REASON_TOO_MANY_OBJECTS);
1885 // Activate stored objects
1886 std::vector<StaticObject> new_stored;
1887 for (std::vector<StaticObject>::iterator
1888 i = block->m_static_objects.m_stored.begin();
1889 i != block->m_static_objects.m_stored.end(); ++i) {
1890 StaticObject &s_obj = *i;
1892 // Create an active object from the data
1893 ServerActiveObject *obj = ServerActiveObject::create
1894 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1895 // If couldn't create object, store static data back.
1897 errorstream<<"ServerEnvironment::activateObjects(): "
1898 <<"failed to create active object from static object "
1899 <<"in block "<<PP(s_obj.pos/BS)
1900 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1901 print_hexdump(verbosestream, s_obj.data);
1903 new_stored.push_back(s_obj);
1906 verbosestream<<"ServerEnvironment::activateObjects(): "
1907 <<"activated static object pos="<<PP(s_obj.pos/BS)
1908 <<" type="<<(int)s_obj.type<<std::endl;
1909 // This will also add the object to the active static list
1910 addActiveObjectRaw(obj, false, dtime_s);
1912 // Clear stored list
1913 block->m_static_objects.m_stored.clear();
1914 // Add leftover failed stuff to stored list
1915 for(std::vector<StaticObject>::iterator
1916 i = new_stored.begin();
1917 i != new_stored.end(); ++i) {
1918 StaticObject &s_obj = *i;
1919 block->m_static_objects.m_stored.push_back(s_obj);
1922 // Turn the active counterparts of activated objects not pending for
1924 for(std::map<u16, StaticObject>::iterator
1925 i = block->m_static_objects.m_active.begin();
1926 i != block->m_static_objects.m_active.end(); ++i)
1929 ServerActiveObject *object = getActiveObject(id);
1931 object->m_pending_deactivation = false;
1935 Note: Block hasn't really been modified here.
1936 The objects have just been activated and moved from the stored
1937 static list to the active static list.
1938 As such, the block is essentially the same.
1939 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1940 Otherwise there would be a huge amount of unnecessary I/O.
1945 Convert objects that are not standing inside active blocks to static.
1947 If m_known_by_count != 0, active object is not deleted, but static
1948 data is still updated.
1950 If force_delete is set, active object is deleted nevertheless. It
1951 shall only be set so in the destructor of the environment.
1953 If block wasn't generated (not in memory or on disk),
1955 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1957 std::vector<u16> objects_to_remove;
1958 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1959 i != m_active_objects.end(); ++i) {
1960 // force_delete might be overriden per object
1961 bool force_delete = _force_delete;
1963 ServerActiveObject* obj = i->second;
1966 // Do not deactivate if static data creation not allowed
1967 if(!force_delete && !obj->isStaticAllowed())
1970 // If pending deactivation, let removeRemovedObjects() do it
1971 if(!force_delete && obj->m_pending_deactivation)
1975 v3f objectpos = obj->getBasePosition();
1977 // The block in which the object resides in
1978 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1980 // If object's static data is stored in a deactivated block and object
1981 // is actually located in an active block, re-save to the block in
1982 // which the object is actually located in.
1984 obj->m_static_exists &&
1985 !m_active_blocks.contains(obj->m_static_block) &&
1986 m_active_blocks.contains(blockpos_o))
1988 v3s16 old_static_block = obj->m_static_block;
1990 // Save to block where object is located
1991 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1993 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1994 <<"Could not save object id="<<id
1995 <<" to it's current block "<<PP(blockpos_o)
1999 std::string staticdata_new = "";
2000 obj->getStaticData(&staticdata_new);
2001 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2002 block->m_static_objects.insert(id, s_obj);
2003 obj->m_static_block = blockpos_o;
2004 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2005 MOD_REASON_STATIC_DATA_ADDED);
2007 // Delete from block where object was located
2008 block = m_map->emergeBlock(old_static_block, false);
2010 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2011 <<"Could not delete object id="<<id
2012 <<" from it's previous block "<<PP(old_static_block)
2016 block->m_static_objects.remove(id);
2017 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2018 MOD_REASON_STATIC_DATA_REMOVED);
2022 // If block is active, don't remove
2023 if(!force_delete && m_active_blocks.contains(blockpos_o))
2026 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2027 <<"deactivating object id="<<id<<" on inactive block "
2028 <<PP(blockpos_o)<<std::endl;
2030 // If known by some client, don't immediately delete.
2031 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2034 Update the static data
2037 if(obj->isStaticAllowed())
2039 // Create new static object
2040 std::string staticdata_new = "";
2041 obj->getStaticData(&staticdata_new);
2042 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2044 bool stays_in_same_block = false;
2045 bool data_changed = true;
2047 if (obj->m_static_exists) {
2048 if (obj->m_static_block == blockpos_o)
2049 stays_in_same_block = true;
2051 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2054 std::map<u16, StaticObject>::iterator n =
2055 block->m_static_objects.m_active.find(id);
2056 if (n != block->m_static_objects.m_active.end()) {
2057 StaticObject static_old = n->second;
2059 float save_movem = obj->getMinimumSavedMovement();
2061 if (static_old.data == staticdata_new &&
2062 (static_old.pos - objectpos).getLength() < save_movem)
2063 data_changed = false;
2065 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2066 <<"id="<<id<<" m_static_exists=true but "
2067 <<"static data doesn't actually exist in "
2068 <<PP(obj->m_static_block)<<std::endl;
2073 bool shall_be_written = (!stays_in_same_block || data_changed);
2075 // Delete old static object
2076 if(obj->m_static_exists)
2078 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2081 block->m_static_objects.remove(id);
2082 obj->m_static_exists = false;
2083 // Only mark block as modified if data changed considerably
2084 if(shall_be_written)
2085 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2086 MOD_REASON_STATIC_DATA_CHANGED);
2090 // Add to the block where the object is located in
2091 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2092 // Get or generate the block
2093 MapBlock *block = NULL;
2095 block = m_map->emergeBlock(blockpos);
2096 } catch(InvalidPositionException &e){
2097 // Handled via NULL pointer
2098 // NOTE: emergeBlock's failure is usually determined by it
2099 // actually returning NULL
2104 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2105 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2106 << " statically but block " << PP(blockpos)
2107 << " already contains "
2108 << block->m_static_objects.m_stored.size()
2110 << " Forcing delete." << std::endl;
2111 force_delete = true;
2113 // If static counterpart already exists in target block,
2115 // This shouldn't happen because the object is removed from
2116 // the previous block before this according to
2117 // obj->m_static_block, but happens rarely for some unknown
2118 // reason. Unsuccessful attempts have been made to find
2120 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2121 warningstream<<"ServerEnv: Performing hack #83274"
2123 block->m_static_objects.remove(id);
2125 // Store static data
2126 u16 store_id = pending_delete ? id : 0;
2127 block->m_static_objects.insert(store_id, s_obj);
2129 // Only mark block as modified if data changed considerably
2130 if(shall_be_written)
2131 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2132 MOD_REASON_STATIC_DATA_CHANGED);
2134 obj->m_static_exists = true;
2135 obj->m_static_block = block->getPos();
2140 v3s16 p = floatToInt(objectpos, BS);
2141 errorstream<<"ServerEnv: Could not find or generate "
2142 <<"a block for storing id="<<obj->getId()
2143 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2150 If known by some client, set pending deactivation.
2151 Otherwise delete it immediately.
2154 if(pending_delete && !force_delete)
2156 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2157 <<"object id="<<id<<" is known by clients"
2158 <<"; not deleting yet"<<std::endl;
2160 obj->m_pending_deactivation = true;
2164 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2165 <<"object id="<<id<<" is not known by clients"
2166 <<"; deleting"<<std::endl;
2168 // Tell the object about removal
2169 obj->removingFromEnvironment();
2170 // Deregister in scripting api
2171 m_script->removeObjectReference(obj);
2173 // Delete active object
2174 if(obj->environmentDeletes())
2176 // Id to be removed from m_active_objects
2177 objects_to_remove.push_back(id);
2180 // Remove references from m_active_objects
2181 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2182 i != objects_to_remove.end(); ++i) {
2183 m_active_objects.erase(*i);
2187 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2188 const std::string &savedir, const Settings &conf)
2191 if (name == "sqlite3")
2192 return new PlayerDatabaseSQLite3(savedir);
2193 else if (name == "dummy")
2194 return new Database_Dummy();
2196 else if (name == "postgresql") {
2197 std::string connect_string = "";
2198 conf.getNoEx("pgsql_player_connection", connect_string);
2199 return new PlayerDatabasePostgreSQL(connect_string);
2202 else if (name == "files")
2203 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2205 throw BaseException(std::string("Database backend ") + name + " not supported.");
2208 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2209 const Settings &cmd_args)
2211 std::string migrate_to = cmd_args.get("migrate-players");
2213 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2214 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2215 errorstream << "Cannot read world.mt!" << std::endl;
2219 if (!world_mt.exists("player_backend")) {
2220 errorstream << "Please specify your current backend in world.mt:"
2222 << " player_backend = {files|sqlite3|postgresql}"
2227 std::string backend = world_mt.get("player_backend");
2228 if (backend == migrate_to) {
2229 errorstream << "Cannot migrate: new backend is same"
2230 << " as the old one" << std::endl;
2234 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2237 if (backend == "files") {
2238 // Create backup directory
2239 fs::CreateDir(players_backup_path);
2243 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2244 game_params.world_path, world_mt);
2245 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2246 game_params.world_path, world_mt);
2248 std::vector<std::string> player_list;
2249 srcdb->listPlayers(player_list);
2250 for (std::vector<std::string>::const_iterator it = player_list.begin();
2251 it != player_list.end(); ++it) {
2252 actionstream << "Migrating player " << it->c_str() << std::endl;
2253 RemotePlayer player(it->c_str(), NULL);
2254 PlayerSAO playerSAO(NULL, &player, 15000, false);
2256 srcdb->loadPlayer(&player, &playerSAO);
2258 playerSAO.finalize(&player, std::set<std::string>());
2259 player.setPlayerSAO(&playerSAO);
2261 dstdb->savePlayer(&player);
2263 // For files source, move player files to backup dir
2264 if (backend == "files") {
2266 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2267 players_backup_path + DIR_DELIM + (*it));
2271 actionstream << "Successfully migrated " << player_list.size() << " players"
2273 world_mt.set("player_backend", migrate_to);
2274 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2275 errorstream << "Failed to update world.mt!" << std::endl;
2277 actionstream << "world.mt updated" << std::endl;
2279 // When migration is finished from file backend, remove players directory if empty
2280 if (backend == "files") {
2281 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2288 } catch (BaseException &e) {
2289 errorstream << "An error occured during migration: " << e.what() << std::endl;