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.
21 #include "serverenvironment.h"
26 #include "nodemetadata.h"
32 #include "remoteplayer.h"
33 #include "scripting_server.h"
35 #include "util/serialize.h"
36 #include "util/basic_macros.h"
37 #include "util/pointedthing.h"
38 #include "threading/mutex_auto_lock.h"
40 #include "gameparams.h"
41 #include "database/database-dummy.h"
42 #include "database/database-files.h"
43 #include "database/database-sqlite3.h"
45 #include "database/database-postgresql.h"
48 #include "database/database-leveldb.h"
50 #include "server/luaentity_sao.h"
51 #include "server/player_sao.h"
53 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
55 // A number that is much smaller than the timeout for particle spawners should/could ever be
56 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
62 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
65 // Initialize timer to random value to spread processing
66 float itv = abm->getTriggerInterval();
67 itv = MYMAX(0.001, itv); // No less than 1ms
68 int minval = MYMAX(-0.51*itv, -60); // Clamp to
69 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
70 timer = myrand_range(minval, maxval);
77 void LBMContentMapping::deleteContents()
79 for (auto &it : lbm_list) {
84 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
86 // Add the lbm_def to the LBMContentMapping.
87 // Unknown names get added to the global NameIdMapping.
88 const NodeDefManager *nodedef = gamedef->ndef();
90 lbm_list.push_back(lbm_def);
92 for (const std::string &nodeTrigger: lbm_def->trigger_contents) {
93 std::vector<content_t> c_ids;
94 bool found = nodedef->getIds(nodeTrigger, c_ids);
96 content_t c_id = gamedef->allocateUnknownNodeId(nodeTrigger);
97 if (c_id == CONTENT_IGNORE) {
98 // Seems it can't be allocated.
99 warningstream << "Could not internalize node name \"" << nodeTrigger
100 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
103 c_ids.push_back(c_id);
106 for (content_t c_id : c_ids) {
107 map[c_id].push_back(lbm_def);
112 const std::vector<LoadingBlockModifierDef *> *
113 LBMContentMapping::lookup(content_t c) const
115 lbm_map::const_iterator it = map.find(c);
118 // This first dereferences the iterator, returning
119 // a std::vector<LoadingBlockModifierDef *>
120 // reference, then we convert it to a pointer.
121 return &(it->second);
124 LBMManager::~LBMManager()
126 for (auto &m_lbm_def : m_lbm_defs) {
127 delete m_lbm_def.second;
130 for (auto &it : m_lbm_lookup) {
131 (it.second).deleteContents();
135 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
137 // Precondition, in query mode the map isn't used anymore
138 FATAL_ERROR_IF(m_query_mode,
139 "attempted to modify LBMManager in query mode");
141 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
142 throw ModError("Error adding LBM \"" + lbm_def->name +
143 "\": Does not follow naming conventions: "
144 "Only characters [a-z0-9_:] are allowed.");
147 m_lbm_defs[lbm_def->name] = lbm_def;
150 void LBMManager::loadIntroductionTimes(const std::string ×,
151 IGameDef *gamedef, u32 now)
156 // Storing it in a map first instead of
157 // handling the stuff directly in the loop
158 // removes all duplicate entries.
159 // TODO make this std::unordered_map
160 std::map<std::string, u32> introduction_times;
163 The introduction times string consists of name~time entries,
164 with each entry terminated by a semicolon. The time is decimal.
169 while ((idx_new = times.find(';', idx)) != std::string::npos) {
170 std::string entry = times.substr(idx, idx_new - idx);
171 std::vector<std::string> components = str_split(entry, '~');
172 if (components.size() != 2)
173 throw SerializationError("Introduction times entry \""
174 + entry + "\" requires exactly one '~'!");
175 const std::string &name = components[0];
176 u32 time = from_string<u32>(components[1]);
177 introduction_times[name] = time;
181 // Put stuff from introduction_times into m_lbm_lookup
182 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
183 it != introduction_times.end(); ++it) {
184 const std::string &name = it->first;
185 u32 time = it->second;
187 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
188 m_lbm_defs.find(name);
189 if (def_it == m_lbm_defs.end()) {
190 // This seems to be an LBM entry for
191 // an LBM we haven't loaded. Discard it.
194 LoadingBlockModifierDef *lbm_def = def_it->second;
195 if (lbm_def->run_at_every_load) {
196 // This seems to be an LBM entry for
197 // an LBM that runs at every load.
198 // Don't add it just yet.
202 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
204 // Erase the entry so that we know later
205 // what elements didn't get put into m_lbm_lookup
206 m_lbm_defs.erase(name);
209 // Now also add the elements from m_lbm_defs to m_lbm_lookup
210 // that weren't added in the previous step.
211 // They are introduced first time to this world,
212 // or are run at every load (introducement time hardcoded to U32_MAX).
214 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
215 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
217 for (auto &m_lbm_def : m_lbm_defs) {
218 if (m_lbm_def.second->run_at_every_load) {
219 lbms_running_always.addLBM(m_lbm_def.second, gamedef);
221 lbms_we_introduce_now.addLBM(m_lbm_def.second, gamedef);
225 // Clear the list, so that we don't delete remaining elements
226 // twice in the destructor
230 std::string LBMManager::createIntroductionTimesString()
232 // Precondition, we must be in query mode
233 FATAL_ERROR_IF(!m_query_mode,
234 "attempted to query on non fully set up LBMManager");
236 std::ostringstream oss;
237 for (const auto &it : m_lbm_lookup) {
239 const std::vector<LoadingBlockModifierDef *> &lbm_list = it.second.lbm_list;
240 for (const auto &lbm_def : lbm_list) {
241 // Don't add if the LBM runs at every load,
242 // then introducement time is hardcoded
243 // and doesn't need to be stored
244 if (lbm_def->run_at_every_load)
246 oss << lbm_def->name << "~" << time << ";";
252 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
254 // Precondition, we need m_lbm_lookup to be initialized
255 FATAL_ERROR_IF(!m_query_mode,
256 "attempted to query on non fully set up LBMManager");
257 v3s16 pos_of_block = block->getPosRelative();
261 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
262 for (; it != m_lbm_lookup.end(); ++it) {
263 // Cache previous version to speedup lookup which has a very high performance
264 // penalty on each call
265 content_t previous_c{};
266 std::vector<LoadingBlockModifierDef *> *lbm_list = nullptr;
268 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
269 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
270 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) {
271 n = block->getNodeNoEx(pos);
274 // If content_t are not matching perform an LBM lookup
275 if (previous_c != c) {
276 lbm_list = (std::vector<LoadingBlockModifierDef *> *)
277 it->second.lookup(c);
283 for (auto lbmdef : *lbm_list) {
284 lbmdef->trigger(env, pos + pos_of_block, n);
294 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
297 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
298 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
299 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
302 if (p.getDistanceFrom(p0) <= r) {
309 void fillViewConeBlock(v3s16 p0,
311 const v3f camera_pos,
312 const v3f camera_dir,
313 const float camera_fov,
314 std::set<v3s16> &list)
317 const s16 r_nodes = r * BS * MAP_BLOCKSIZE;
318 for (p.X = p0.X - r; p.X <= p0.X+r; p.X++)
319 for (p.Y = p0.Y - r; p.Y <= p0.Y+r; p.Y++)
320 for (p.Z = p0.Z - r; p.Z <= p0.Z+r; p.Z++) {
321 if (isBlockInSight(p, camera_pos, camera_dir, camera_fov, r_nodes)) {
327 void ActiveBlockList::update(std::vector<PlayerSAO*> &active_players,
328 s16 active_block_range,
329 s16 active_object_range,
330 std::set<v3s16> &blocks_removed,
331 std::set<v3s16> &blocks_added)
336 std::set<v3s16> newlist = m_forceloaded_list;
337 m_abm_list = m_forceloaded_list;
338 for (const PlayerSAO *playersao : active_players) {
339 v3s16 pos = getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS));
340 fillRadiusBlock(pos, active_block_range, m_abm_list);
341 fillRadiusBlock(pos, active_block_range, newlist);
343 s16 player_ao_range = std::min(active_object_range, playersao->getWantedRange());
344 // only do this if this would add blocks
345 if (player_ao_range > active_block_range) {
346 v3f camera_dir = v3f(0,0,1);
347 camera_dir.rotateYZBy(playersao->getLookPitch());
348 camera_dir.rotateXZBy(playersao->getRotation().Y);
349 fillViewConeBlock(pos,
351 playersao->getEyePosition(),
359 Find out which blocks on the old list are not on the new list
361 // Go through old list
362 for (v3s16 p : m_list) {
363 // If not on new list, it's been removed
364 if (newlist.find(p) == newlist.end())
365 blocks_removed.insert(p);
369 Find out which blocks on the new list are not on the old list
371 // Go through new list
372 for (v3s16 p : newlist) {
373 // If not on old list, it's been added
374 if(m_list.find(p) == m_list.end())
375 blocks_added.insert(p);
382 for (v3s16 p : newlist) {
391 // Random device to seed pseudo random generators.
392 static std::random_device seed;
394 ServerEnvironment::ServerEnvironment(ServerMap *map,
395 ServerScripting *scriptIface, Server *server,
396 const std::string &path_world):
399 m_script(scriptIface),
401 m_path_world(path_world),
404 // Determine which database backend to use
405 std::string conf_path = path_world + DIR_DELIM + "world.mt";
408 std::string player_backend_name = "sqlite3";
409 std::string auth_backend_name = "sqlite3";
411 bool succeeded = conf.readConfigFile(conf_path.c_str());
413 // If we open world.mt read the backend configurations.
415 // Read those values before setting defaults
416 bool player_backend_exists = conf.exists("player_backend");
417 bool auth_backend_exists = conf.exists("auth_backend");
419 // player backend is not set, assume it's legacy file backend.
420 if (!player_backend_exists) {
421 // fall back to files
422 conf.set("player_backend", "files");
423 player_backend_name = "files";
425 if (!conf.updateConfigFile(conf_path.c_str())) {
426 errorstream << "ServerEnvironment::ServerEnvironment(): "
427 << "Failed to update world.mt!" << std::endl;
430 conf.getNoEx("player_backend", player_backend_name);
433 // auth backend is not set, assume it's legacy file backend.
434 if (!auth_backend_exists) {
435 conf.set("auth_backend", "files");
436 auth_backend_name = "files";
438 if (!conf.updateConfigFile(conf_path.c_str())) {
439 errorstream << "ServerEnvironment::ServerEnvironment(): "
440 << "Failed to update world.mt!" << std::endl;
443 conf.getNoEx("auth_backend", auth_backend_name);
447 if (player_backend_name == "files") {
448 warningstream << "/!\\ You are using old player file backend. "
449 << "This backend is deprecated and will be removed in a future release /!\\"
450 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
451 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
454 if (auth_backend_name == "files") {
455 warningstream << "/!\\ You are using old auth file backend. "
456 << "This backend is deprecated and will be removed in a future release /!\\"
457 << std::endl << "Switching to SQLite3 is advised, "
458 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
461 m_player_database = openPlayerDatabase(player_backend_name, path_world, conf);
462 m_auth_database = openAuthDatabase(auth_backend_name, path_world, conf);
465 ServerEnvironment::~ServerEnvironment()
467 // Clear active block list.
468 // This makes the next one delete all active objects.
469 m_active_blocks.clear();
471 // Convert all objects to static and delete the active objects
472 deactivateFarObjects(true);
477 // Delete ActiveBlockModifiers
478 for (ABMWithState &m_abm : m_abms) {
482 // Deallocate players
483 for (RemotePlayer *m_player : m_players) {
487 delete m_player_database;
488 delete m_auth_database;
491 Map & ServerEnvironment::getMap()
496 ServerMap & ServerEnvironment::getServerMap()
501 RemotePlayer *ServerEnvironment::getPlayer(const session_t peer_id)
503 for (RemotePlayer *player : m_players) {
504 if (player->getPeerId() == peer_id)
510 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
512 for (RemotePlayer *player : m_players) {
513 if (strcmp(player->getName(), name) == 0)
519 void ServerEnvironment::addPlayer(RemotePlayer *player)
522 Check that peer_ids are unique.
523 Also check that names are unique.
524 Exception: there can be multiple players with peer_id=0
526 // If peer id is non-zero, it has to be unique.
527 if (player->getPeerId() != PEER_ID_INEXISTENT)
528 FATAL_ERROR_IF(getPlayer(player->getPeerId()) != NULL, "Peer id not unique");
529 // Name has to be unique.
530 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
532 m_players.push_back(player);
535 void ServerEnvironment::removePlayer(RemotePlayer *player)
537 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
538 it != m_players.end(); ++it) {
539 if ((*it) == player) {
547 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
549 return m_player_database->removePlayer(name);
552 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
553 const std::string &str_reason, bool reconnect)
555 for (RemotePlayer *player : m_players) {
556 m_server->DenyAccessVerCompliant(player->getPeerId(),
557 player->protocol_version, reason, str_reason, reconnect);
561 void ServerEnvironment::saveLoadedPlayers(bool force)
563 for (RemotePlayer *player : m_players) {
564 if (force || player->checkModified() || (player->getPlayerSAO() &&
565 player->getPlayerSAO()->getMeta().isModified())) {
567 m_player_database->savePlayer(player);
568 } catch (DatabaseException &e) {
569 errorstream << "Failed to save player " << player->getName() << " exception: "
570 << e.what() << std::endl;
577 void ServerEnvironment::savePlayer(RemotePlayer *player)
580 m_player_database->savePlayer(player);
581 } catch (DatabaseException &e) {
582 errorstream << "Failed to save player " << player->getName() << " exception: "
583 << e.what() << std::endl;
588 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
589 session_t peer_id, bool is_singleplayer)
591 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
592 // Create player if it doesn't exist
593 if (!m_player_database->loadPlayer(player, playersao)) {
595 // Set player position
596 infostream << "Server: Finding spawn place for player \""
597 << player->getName() << "\"" << std::endl;
598 playersao->setBasePosition(m_server->findSpawnPos());
600 // Make sure the player is saved
601 player->setModified(true);
603 // If the player exists, ensure that they respawn inside legal bounds
604 // This fixes an assert crash when the player can't be added
605 // to the environment
606 if (objectpos_over_limit(playersao->getBasePosition())) {
607 actionstream << "Respawn position for player \""
608 << player->getName() << "\" outside limits, resetting" << std::endl;
609 playersao->setBasePosition(m_server->findSpawnPos());
613 // Add player to environment
616 /* Clean up old HUD elements from previous sessions */
619 /* Add object to environment */
620 addActiveObject(playersao);
625 void ServerEnvironment::saveMeta()
630 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
632 // Open file and serialize
633 std::ostringstream ss(std::ios_base::binary);
636 args.setU64("game_time", m_game_time);
637 args.setU64("time_of_day", getTimeOfDay());
638 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
639 args.setU64("lbm_introduction_times_version", 1);
640 args.set("lbm_introduction_times",
641 m_lbm_mgr.createIntroductionTimesString());
642 args.setU64("day_count", m_day_count);
646 if(!fs::safeWriteToFile(path, ss.str()))
648 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
650 throw SerializationError("Couldn't save env meta");
654 void ServerEnvironment::loadMeta()
656 SANITY_CHECK(!m_meta_loaded);
657 m_meta_loaded = true;
659 // If file doesn't exist, load default environment metadata
660 if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
661 infostream << "ServerEnvironment: Loading default environment metadata"
667 infostream << "ServerEnvironment: Loading environment metadata" << std::endl;
669 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
671 // Open file and deserialize
672 std::ifstream is(path.c_str(), std::ios_base::binary);
674 infostream << "ServerEnvironment::loadMeta(): Failed to open "
675 << path << std::endl;
676 throw SerializationError("Couldn't load env meta");
681 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
682 throw SerializationError("ServerEnvironment::loadMeta(): "
683 "EnvArgsEnd not found!");
687 m_game_time = args.getU64("game_time");
688 } catch (SettingNotFoundException &e) {
689 // Getting this is crucial, otherwise timestamps are useless
690 throw SerializationError("Couldn't load env meta game_time");
693 setTimeOfDay(args.exists("time_of_day") ?
694 // set day to early morning by default
695 args.getU64("time_of_day") : 5250);
697 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
698 // If missing, do as if clearObjects was never called
699 args.getU64("last_clear_objects_time") : 0;
701 std::string lbm_introduction_times;
703 u64 ver = args.getU64("lbm_introduction_times_version");
705 lbm_introduction_times = args.get("lbm_introduction_times");
707 infostream << "ServerEnvironment::loadMeta(): Non-supported"
708 << " introduction time version " << ver << std::endl;
710 } catch (SettingNotFoundException &e) {
711 // No problem, this is expected. Just continue with an empty string
713 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
715 m_day_count = args.exists("day_count") ?
716 args.getU64("day_count") : 0;
720 * called if env_meta.txt doesn't exist (e.g. new world)
722 void ServerEnvironment::loadDefaultMeta()
724 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
729 ActiveBlockModifier *abm;
731 std::vector<content_t> required_neighbors;
732 bool check_required_neighbors; // false if required_neighbors is known to be empty
738 ServerEnvironment *m_env;
739 std::vector<std::vector<ActiveABM> *> m_aabms;
741 ABMHandler(std::vector<ABMWithState> &abms,
742 float dtime_s, ServerEnvironment *env,
748 const NodeDefManager *ndef = env->getGameDef()->ndef();
749 for (ABMWithState &abmws : abms) {
750 ActiveBlockModifier *abm = abmws.abm;
751 float trigger_interval = abm->getTriggerInterval();
752 if(trigger_interval < 0.001)
753 trigger_interval = 0.001;
754 float actual_interval = dtime_s;
756 abmws.timer += dtime_s;
757 if(abmws.timer < trigger_interval)
759 abmws.timer -= trigger_interval;
760 actual_interval = trigger_interval;
762 float chance = abm->getTriggerChance();
767 if (abm->getSimpleCatchUp()) {
768 float intervals = actual_interval / trigger_interval;
771 aabm.chance = chance / intervals;
775 aabm.chance = chance;
779 const std::vector<std::string> &required_neighbors_s =
780 abm->getRequiredNeighbors();
781 for (const std::string &required_neighbor_s : required_neighbors_s) {
782 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
784 aabm.check_required_neighbors = !required_neighbors_s.empty();
787 const std::vector<std::string> &contents_s = abm->getTriggerContents();
788 for (const std::string &content_s : contents_s) {
789 std::vector<content_t> ids;
790 ndef->getIds(content_s, ids);
791 for (content_t c : ids) {
792 if (c >= m_aabms.size())
793 m_aabms.resize(c + 256, NULL);
795 m_aabms[c] = new std::vector<ActiveABM>;
796 m_aabms[c]->push_back(aabm);
804 for (auto &aabms : m_aabms)
808 // Find out how many objects the given block and its neighbours contain.
809 // Returns the number of objects in the block, and also in 'wider' the
810 // number of objects in the block and all its neighbours. The latter
811 // may an estimate if any neighbours are unloaded.
812 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
815 u32 wider_unknown_count = 0;
816 for(s16 x=-1; x<=1; x++)
817 for(s16 y=-1; y<=1; y++)
818 for(s16 z=-1; z<=1; z++)
820 MapBlock *block2 = map->getBlockNoCreateNoEx(
821 block->getPos() + v3s16(x,y,z));
823 wider_unknown_count++;
826 wider += block2->m_static_objects.m_active.size()
827 + block2->m_static_objects.m_stored.size();
830 u32 active_object_count = block->m_static_objects.m_active.size();
831 u32 wider_known_count = 3*3*3 - wider_unknown_count;
832 wider += wider_unknown_count * wider / wider_known_count;
833 return active_object_count;
836 void apply(MapBlock *block, int &blocks_scanned, int &abms_run, int &blocks_cached)
838 if(m_aabms.empty() || block->isDummy())
841 // Check the content type cache first
842 // to see whether there are any ABMs
843 // to be run at all for this block.
844 if (block->contents_cached) {
846 bool run_abms = false;
847 for (content_t c : block->contents) {
848 if (c < m_aabms.size() && m_aabms[c]) {
857 block->contents.clear();
861 ServerMap *map = &m_env->getServerMap();
863 u32 active_object_count_wider;
864 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
865 m_env->m_added_objects = 0;
868 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
869 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
870 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
872 const MapNode &n = block->getNodeUnsafe(p0);
873 content_t c = n.getContent();
874 // Cache content types as we go
875 if (!block->contents_cached && !block->do_not_cache_contents) {
876 block->contents.insert(c);
877 if (block->contents.size() > 64) {
878 // Too many different nodes... don't try to cache
879 block->do_not_cache_contents = true;
880 block->contents.clear();
884 if (c >= m_aabms.size() || !m_aabms[c])
887 v3s16 p = p0 + block->getPosRelative();
888 for (ActiveABM &aabm : *m_aabms[c]) {
889 if (myrand() % aabm.chance != 0)
893 if (aabm.check_required_neighbors) {
895 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
896 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
897 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
902 if (block->isValidPosition(p1)) {
903 // if the neighbor is found on the same map block
904 // get it straight from there
905 const MapNode &n = block->getNodeUnsafe(p1);
908 // otherwise consult the map
909 MapNode n = map->getNode(p1 + block->getPosRelative());
912 if (CONTAINS(aabm.required_neighbors, c))
915 // No required neighbor found
921 // Call all the trigger variations
922 aabm.abm->trigger(m_env, p, n);
923 aabm.abm->trigger(m_env, p, n,
924 active_object_count, active_object_count_wider);
926 // Count surrounding objects again if the abms added any
927 if(m_env->m_added_objects > 0) {
928 active_object_count = countObjects(block, map, active_object_count_wider);
929 m_env->m_added_objects = 0;
933 block->contents_cached = !block->do_not_cache_contents;
937 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
939 // Reset usage timer immediately, otherwise a block that becomes active
940 // again at around the same time as it would normally be unloaded will
941 // get unloaded incorrectly. (I think this still leaves a small possibility
942 // of a race condition between this and server::AsyncRunStep, which only
943 // some kind of synchronisation will fix, but it at least reduces the window
944 // of opportunity for it to break from seconds to nanoseconds)
945 block->resetUsageTimer();
947 // Get time difference
949 u32 stamp = block->getTimestamp();
950 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
951 dtime_s = m_game_time - stamp;
952 dtime_s += additional_dtime;
954 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
955 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
957 // Remove stored static objects if clearObjects was called since block's timestamp
958 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
959 block->m_static_objects.m_stored.clear();
960 // do not set changed flag to avoid unnecessary mapblock writes
963 // Set current time as timestamp
964 block->setTimestampNoChangedFlag(m_game_time);
966 /*infostream<<"ServerEnvironment::activateBlock(): block is "
967 <<dtime_s<<" seconds old."<<std::endl;*/
969 // Activate stored objects
970 activateObjects(block, dtime_s);
972 /* Handle LoadingBlockModifiers */
973 m_lbm_mgr.applyLBMs(this, block, stamp);
976 std::vector<NodeTimer> elapsed_timers =
977 block->m_node_timers.step((float)dtime_s);
978 if (!elapsed_timers.empty()) {
980 for (const NodeTimer &elapsed_timer : elapsed_timers) {
981 n = block->getNodeNoEx(elapsed_timer.position);
982 v3s16 p = elapsed_timer.position + block->getPosRelative();
983 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
984 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
985 elapsed_timer.position));
990 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
992 m_abms.emplace_back(abm);
995 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
997 m_lbm_mgr.addLBMDef(lbm);
1000 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1002 const NodeDefManager *ndef = m_server->ndef();
1003 MapNode n_old = m_map->getNode(p);
1005 const ContentFeatures &cf_old = ndef->get(n_old);
1008 if (cf_old.has_on_destruct)
1009 m_script->node_on_destruct(p, n_old);
1012 if (!m_map->addNodeWithEvent(p, n))
1015 // Update active VoxelManipulator if a mapgen thread
1016 m_map->updateVManip(p);
1018 // Call post-destructor
1019 if (cf_old.has_after_destruct)
1020 m_script->node_after_destruct(p, n_old);
1022 // Retrieve node content features
1023 // if new node is same as old, reuse old definition to prevent a lookup
1024 const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
1027 if (cf_new.has_on_construct)
1028 m_script->node_on_construct(p, n);
1033 bool ServerEnvironment::removeNode(v3s16 p)
1035 const NodeDefManager *ndef = m_server->ndef();
1036 MapNode n_old = m_map->getNode(p);
1039 if (ndef->get(n_old).has_on_destruct)
1040 m_script->node_on_destruct(p, n_old);
1043 // This is slightly optimized compared to addNodeWithEvent(air)
1044 if (!m_map->removeNodeWithEvent(p))
1047 // Update active VoxelManipulator if a mapgen thread
1048 m_map->updateVManip(p);
1050 // Call post-destructor
1051 if (ndef->get(n_old).has_after_destruct)
1052 m_script->node_after_destruct(p, n_old);
1054 // Air doesn't require constructor
1058 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1060 if (!m_map->addNodeWithEvent(p, n, false))
1063 // Update active VoxelManipulator if a mapgen thread
1064 m_map->updateVManip(p);
1069 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1071 infostream << "ServerEnvironment::clearObjects(): "
1072 << "Removing all active objects" << std::endl;
1073 auto cb_removal = [this] (ServerActiveObject *obj, u16 id) {
1074 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1077 // Delete static object if block is loaded
1078 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1080 // If known by some client, don't delete immediately
1081 if (obj->m_known_by_count > 0) {
1082 obj->m_pending_removal = true;
1086 // Tell the object about removal
1087 obj->removingFromEnvironment();
1088 // Deregister in scripting api
1089 m_script->removeObjectReference(obj);
1091 // Delete active object
1092 if (obj->environmentDeletes())
1098 m_ao_manager.clear(cb_removal);
1100 // Get list of loaded blocks
1101 std::vector<v3s16> loaded_blocks;
1102 infostream << "ServerEnvironment::clearObjects(): "
1103 << "Listing all loaded blocks" << std::endl;
1104 m_map->listAllLoadedBlocks(loaded_blocks);
1105 infostream << "ServerEnvironment::clearObjects(): "
1106 << "Done listing all loaded blocks: "
1107 << loaded_blocks.size()<<std::endl;
1109 // Get list of loadable blocks
1110 std::vector<v3s16> loadable_blocks;
1111 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1112 infostream << "ServerEnvironment::clearObjects(): "
1113 << "Listing all loadable blocks" << std::endl;
1114 m_map->listAllLoadableBlocks(loadable_blocks);
1115 infostream << "ServerEnvironment::clearObjects(): "
1116 << "Done listing all loadable blocks: "
1117 << loadable_blocks.size() << std::endl;
1119 loadable_blocks = loaded_blocks;
1122 actionstream << "ServerEnvironment::clearObjects(): "
1123 << "Now clearing objects in " << loadable_blocks.size()
1124 << " blocks" << std::endl;
1126 // Grab a reference on each loaded block to avoid unloading it
1127 for (v3s16 p : loaded_blocks) {
1128 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1129 assert(block != NULL);
1133 // Remove objects in all loadable blocks
1134 u32 unload_interval = U32_MAX;
1135 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1136 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1137 unload_interval = MYMAX(unload_interval, 1);
1139 u32 report_interval = loadable_blocks.size() / 10;
1140 u32 num_blocks_checked = 0;
1141 u32 num_blocks_cleared = 0;
1142 u32 num_objs_cleared = 0;
1143 for (auto i = loadable_blocks.begin();
1144 i != loadable_blocks.end(); ++i) {
1146 MapBlock *block = m_map->emergeBlock(p, false);
1148 errorstream << "ServerEnvironment::clearObjects(): "
1149 << "Failed to emerge block " << PP(p) << std::endl;
1152 u32 num_stored = block->m_static_objects.m_stored.size();
1153 u32 num_active = block->m_static_objects.m_active.size();
1154 if (num_stored != 0 || num_active != 0) {
1155 block->m_static_objects.m_stored.clear();
1156 block->m_static_objects.m_active.clear();
1157 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1158 MOD_REASON_CLEAR_ALL_OBJECTS);
1159 num_objs_cleared += num_stored + num_active;
1160 num_blocks_cleared++;
1162 num_blocks_checked++;
1164 if (report_interval != 0 &&
1165 num_blocks_checked % report_interval == 0) {
1166 float percent = 100.0 * (float)num_blocks_checked /
1167 loadable_blocks.size();
1168 actionstream << "ServerEnvironment::clearObjects(): "
1169 << "Cleared " << num_objs_cleared << " objects"
1170 << " in " << num_blocks_cleared << " blocks ("
1171 << percent << "%)" << std::endl;
1173 if (num_blocks_checked % unload_interval == 0) {
1174 m_map->unloadUnreferencedBlocks();
1177 m_map->unloadUnreferencedBlocks();
1179 // Drop references that were added above
1180 for (v3s16 p : loaded_blocks) {
1181 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1186 m_last_clear_objects_time = m_game_time;
1188 actionstream << "ServerEnvironment::clearObjects(): "
1189 << "Finished: Cleared " << num_objs_cleared << " objects"
1190 << " in " << num_blocks_cleared << " blocks" << std::endl;
1193 void ServerEnvironment::step(float dtime)
1195 ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
1196 /* Step time of day */
1197 stepTimeOfDay(dtime);
1200 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1201 // really matter that much.
1202 static thread_local const float server_step =
1203 g_settings->getFloat("dedicated_server_step");
1204 m_recommended_send_interval = server_step;
1210 m_game_time_fraction_counter += dtime;
1211 u32 inc_i = (u32)m_game_time_fraction_counter;
1212 m_game_time += inc_i;
1213 m_game_time_fraction_counter -= (float)inc_i;
1220 ScopeProfiler sp(g_profiler, "ServerEnv: move players", SPT_AVG);
1221 for (RemotePlayer *player : m_players) {
1222 // Ignore disconnected players
1223 if (player->getPeerId() == PEER_ID_INEXISTENT)
1227 player->move(dtime, this, 100 * BS);
1231 if (m_database_check_interval.step(dtime, 10.0f)) {
1232 m_auth_database->pingDatabase();
1233 m_player_database->pingDatabase();
1234 m_map->pingDatabase();
1237 Manage active block list
1239 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1240 ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
1242 Get player block positions
1244 std::vector<PlayerSAO*> players;
1245 for (RemotePlayer *player: m_players) {
1246 // Ignore disconnected players
1247 if (player->getPeerId() == PEER_ID_INEXISTENT)
1250 PlayerSAO *playersao = player->getPlayerSAO();
1253 players.push_back(playersao);
1257 Update list of active blocks, collecting changes
1259 // use active_object_send_range_blocks since that is max distance
1260 // for active objects sent the client anyway
1261 static thread_local const s16 active_object_range =
1262 g_settings->getS16("active_object_send_range_blocks");
1263 static thread_local const s16 active_block_range =
1264 g_settings->getS16("active_block_range");
1265 std::set<v3s16> blocks_removed;
1266 std::set<v3s16> blocks_added;
1267 m_active_blocks.update(players, active_block_range, active_object_range,
1268 blocks_removed, blocks_added);
1271 Handle removed blocks
1274 // Convert active objects that are no more in active blocks to static
1275 deactivateFarObjects(false);
1277 for (const v3s16 &p: blocks_removed) {
1278 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1282 // Set current time as timestamp (and let it set ChangedFlag)
1283 block->setTimestamp(m_game_time);
1290 for (const v3s16 &p: blocks_added) {
1291 MapBlock *block = m_map->getBlockOrEmerge(p);
1293 m_active_blocks.m_list.erase(p);
1294 m_active_blocks.m_abm_list.erase(p);
1298 activateBlock(block);
1303 Mess around in active blocks
1305 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1306 ScopeProfiler sp(g_profiler, "ServerEnv: Run node timers", SPT_AVG);
1308 float dtime = m_cache_nodetimer_interval;
1310 for (const v3s16 &p: m_active_blocks.m_list) {
1311 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1315 // Reset block usage timer
1316 block->resetUsageTimer();
1318 // Set current time as timestamp
1319 block->setTimestampNoChangedFlag(m_game_time);
1320 // If time has changed much from the one on disk,
1321 // set block to be saved when it is unloaded
1322 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1323 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1324 MOD_REASON_BLOCK_EXPIRED);
1327 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1328 if (!elapsed_timers.empty()) {
1331 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1332 n = block->getNodeNoEx(elapsed_timer.position);
1333 p2 = elapsed_timer.position + block->getPosRelative();
1334 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1335 block->setNodeTimer(NodeTimer(
1336 elapsed_timer.timeout, 0, elapsed_timer.position));
1343 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) {
1344 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1345 TimeTaker timer("modify in active blocks per interval");
1347 // Initialize handling of ActiveBlockModifiers
1348 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1350 int blocks_scanned = 0;
1352 int blocks_cached = 0;
1354 std::vector<v3s16> output(m_active_blocks.m_abm_list.size());
1356 // Shuffle the active blocks so that each block gets an equal chance
1357 // of having its ABMs run.
1358 std::copy(m_active_blocks.m_abm_list.begin(), m_active_blocks.m_abm_list.end(), output.begin());
1359 std::shuffle(output.begin(), output.end(), m_rgen);
1362 // The time budget for ABMs is 20%.
1363 u32 max_time_ms = m_cache_abm_interval * 1000 / 5;
1364 for (const v3s16 &p : output) {
1365 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1371 // Set current time as timestamp
1372 block->setTimestampNoChangedFlag(m_game_time);
1374 /* Handle ActiveBlockModifiers */
1375 abmhandler.apply(block, blocks_scanned, abms_run, blocks_cached);
1377 u32 time_ms = timer.getTimerTime();
1379 if (time_ms > max_time_ms) {
1380 warningstream << "active block modifiers took "
1381 << time_ms << "ms (processed " << i << " of "
1382 << output.size() << " active blocks)" << std::endl;
1386 g_profiler->avg("ServerEnv: active blocks", m_active_blocks.m_abm_list.size());
1387 g_profiler->avg("ServerEnv: active blocks cached", blocks_cached);
1388 g_profiler->avg("ServerEnv: active blocks scanned for ABMs", blocks_scanned);
1389 g_profiler->avg("ServerEnv: ABMs run", abms_run);
1395 Step script environment (run global on_step())
1397 m_script->environment_Step(dtime);
1403 ScopeProfiler sp(g_profiler, "ServerEnv: Run SAO::step()", SPT_AVG);
1405 // This helps the objects to send data at the same time
1406 bool send_recommended = false;
1407 m_send_recommended_timer += dtime;
1408 if (m_send_recommended_timer > getSendRecommendedInterval()) {
1409 m_send_recommended_timer -= getSendRecommendedInterval();
1410 send_recommended = true;
1413 auto cb_state = [this, dtime, send_recommended] (ServerActiveObject *obj) {
1418 obj->step(dtime, send_recommended);
1419 // Read messages from object
1420 obj->dumpAOMessagesToQueue(m_active_object_messages);
1422 m_ao_manager.step(dtime, cb_state);
1426 Manage active objects
1428 if (m_object_management_interval.step(dtime, 0.5)) {
1429 removeRemovedObjects();
1433 Manage particle spawner expiration
1435 if (m_particle_management_interval.step(dtime, 1.0)) {
1436 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1437 i != m_particle_spawners.end(); ) {
1438 //non expiring spawners
1439 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1445 if (i->second <= 0.f)
1446 m_particle_spawners.erase(i++);
1452 // Send outdated player inventories
1453 for (RemotePlayer *player : m_players) {
1454 if (player->getPeerId() == PEER_ID_INEXISTENT)
1457 PlayerSAO *sao = player->getPlayerSAO();
1458 if (sao && player->inventory.checkModified())
1459 m_server->SendInventory(sao, true);
1462 // Send outdated detached inventories
1463 m_server->sendDetachedInventories(PEER_ID_INEXISTENT, true);
1466 u32 ServerEnvironment::addParticleSpawner(float exptime)
1468 // Timers with lifetime 0 do not expire
1469 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1472 for (;;) { // look for unused particlespawner id
1474 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1475 if (f == m_particle_spawners.end()) {
1476 m_particle_spawners[id] = time;
1483 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1485 u32 id = addParticleSpawner(exptime);
1486 m_particle_spawner_attachments[id] = attached_id;
1487 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1488 obj->attachParticleSpawner(id);
1493 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1495 m_particle_spawners.erase(id);
1496 const auto &it = m_particle_spawner_attachments.find(id);
1497 if (it != m_particle_spawner_attachments.end()) {
1498 u16 obj_id = it->second;
1499 ServerActiveObject *sao = getActiveObject(obj_id);
1500 if (sao != NULL && remove_from_object) {
1501 sao->detachParticleSpawner(id);
1503 m_particle_spawner_attachments.erase(id);
1507 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1509 assert(object); // Pre-condition
1511 u16 id = addActiveObjectRaw(object, true, 0);
1516 Finds out what new objects have been added to
1517 inside a radius around a position
1519 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1521 std::set<u16> ¤t_objects,
1522 std::queue<u16> &added_objects)
1524 f32 radius_f = radius * BS;
1525 f32 player_radius_f = player_radius * BS;
1527 if (player_radius_f < 0.0f)
1528 player_radius_f = 0.0f;
1530 m_ao_manager.getAddedActiveObjectsAroundPos(playersao->getBasePosition(), radius_f,
1531 player_radius_f, current_objects, added_objects);
1535 Finds out what objects have been removed from
1536 inside a radius around a position
1538 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1540 std::set<u16> ¤t_objects,
1541 std::queue<u16> &removed_objects)
1543 f32 radius_f = radius * BS;
1544 f32 player_radius_f = player_radius * BS;
1546 if (player_radius_f < 0)
1547 player_radius_f = 0;
1549 Go through current_objects; object is removed if:
1550 - object is not found in m_active_objects (this is actually an
1551 error condition; objects should be removed only after all clients
1552 have been informed about removal), or
1553 - object is to be removed or deactivated, or
1554 - object is too far away
1556 for (u16 id : current_objects) {
1557 ServerActiveObject *object = getActiveObject(id);
1559 if (object == NULL) {
1560 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1561 << " object in current_objects is NULL" << std::endl;
1562 removed_objects.push(id);
1566 if (object->isGone()) {
1567 removed_objects.push(id);
1571 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1572 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1573 if (distance_f <= player_radius_f || player_radius_f == 0)
1575 } else if (distance_f <= radius_f)
1578 // Object is no longer visible
1579 removed_objects.push(id);
1583 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1584 v3s16 blockpos, bool static_exists, v3s16 static_block)
1586 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1590 for (auto &so_it : block->m_static_objects.m_active) {
1591 // Get the ServerActiveObject counterpart to this StaticObject
1592 ServerActiveObject *sao = m_ao_manager.getActiveObject(so_it.first);
1594 // If this ever happens, there must be some kind of nasty bug.
1595 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1596 "Object from MapBlock::m_static_objects::m_active not found "
1597 "in m_active_objects";
1601 sao->m_static_exists = static_exists;
1602 sao->m_static_block = static_block;
1606 bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest)
1608 if(m_active_object_messages.empty())
1611 *dest = std::move(m_active_object_messages.front());
1612 m_active_object_messages.pop();
1616 void ServerEnvironment::getSelectedActiveObjects(
1617 const core::line3d<f32> &shootline_on_map,
1618 std::vector<PointedThing> &objects)
1620 std::vector<ServerActiveObject *> objs;
1621 getObjectsInsideRadius(objs, shootline_on_map.start,
1622 shootline_on_map.getLength() + 10.0f, nullptr);
1623 const v3f line_vector = shootline_on_map.getVector();
1625 for (auto obj : objs) {
1628 aabb3f selection_box;
1629 if (!obj->getSelectionBox(&selection_box))
1632 v3f pos = obj->getBasePosition();
1634 aabb3f offsetted_box(selection_box.MinEdge + pos,
1635 selection_box.MaxEdge + pos);
1637 v3f current_intersection;
1638 v3s16 current_normal;
1639 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1640 ¤t_intersection, ¤t_normal)) {
1641 objects.emplace_back(
1642 (s16) obj->getId(), current_intersection, current_normal,
1643 (current_intersection - shootline_on_map.start).getLengthSQ());
1649 ************ Private methods *************
1652 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1653 bool set_changed, u32 dtime_s)
1655 if (!m_ao_manager.registerObject(object)) {
1659 // Register reference in scripting api (must be done before post-init)
1660 m_script->addObjectReference(object);
1661 // Post-initialize object
1662 object->addedToEnvironment(dtime_s);
1664 // Add static data to block
1665 if (object->isStaticAllowed()) {
1666 // Add static object to active static list of the block
1667 v3f objectpos = object->getBasePosition();
1668 StaticObject s_obj(object, objectpos);
1669 // Add to the block where the object is located in
1670 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1671 MapBlock *block = m_map->emergeBlock(blockpos);
1673 block->m_static_objects.m_active[object->getId()] = s_obj;
1674 object->m_static_exists = true;
1675 object->m_static_block = blockpos;
1678 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1679 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1681 v3s16 p = floatToInt(objectpos, BS);
1682 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1683 <<"could not emerge block for storing id="<<object->getId()
1684 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1688 return object->getId();
1692 Remove objects that satisfy (isGone() && m_known_by_count==0)
1694 void ServerEnvironment::removeRemovedObjects()
1696 ScopeProfiler sp(g_profiler, "ServerEnvironment::removeRemovedObjects()", SPT_AVG);
1698 auto clear_cb = [this] (ServerActiveObject *obj, u16 id) {
1699 // This shouldn't happen but check it
1701 errorstream << "ServerEnvironment::removeRemovedObjects(): "
1702 << "NULL object found. id=" << id << std::endl;
1707 We will handle objects marked for removal or deactivation
1713 Delete static data from block if removed
1715 if (obj->m_pending_removal)
1716 deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1718 // If still known by clients, don't actually remove. On some future
1719 // invocation this will be 0, which is when removal will continue.
1720 if(obj->m_known_by_count > 0)
1724 Move static data from active to stored if deactivated
1726 if (!obj->m_pending_removal && obj->m_static_exists) {
1727 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1729 const auto i = block->m_static_objects.m_active.find(id);
1730 if (i != block->m_static_objects.m_active.end()) {
1731 block->m_static_objects.m_stored.push_back(i->second);
1732 block->m_static_objects.m_active.erase(id);
1733 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1734 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1736 warningstream << "ServerEnvironment::removeRemovedObjects(): "
1737 << "id=" << id << " m_static_exists=true but "
1738 << "static data doesn't actually exist in "
1739 << PP(obj->m_static_block) << std::endl;
1742 infostream << "Failed to emerge block from which an object to "
1743 << "be deactivated was loaded from. id=" << id << std::endl;
1747 // Tell the object about removal
1748 obj->removingFromEnvironment();
1749 // Deregister in scripting api
1750 m_script->removeObjectReference(obj);
1753 if (obj->environmentDeletes())
1759 m_ao_manager.clear(clear_cb);
1762 static void print_hexdump(std::ostream &o, const std::string &data)
1764 const int linelength = 16;
1765 for(int l=0; ; l++){
1766 int i0 = linelength * l;
1767 bool at_end = false;
1768 int thislinelength = linelength;
1769 if(i0 + thislinelength > (int)data.size()){
1770 thislinelength = data.size() - i0;
1773 for(int di=0; di<linelength; di++){
1776 if(di<thislinelength)
1777 porting::mt_snprintf(buf, sizeof(buf), "%.2x ", data[i]);
1779 porting::mt_snprintf(buf, sizeof(buf), " ");
1783 for(int di=0; di<thislinelength; di++){
1796 ServerActiveObject* ServerEnvironment::createSAO(ActiveObjectType type, v3f pos,
1797 const std::string &data)
1800 case ACTIVEOBJECT_TYPE_LUAENTITY:
1801 return new LuaEntitySAO(this, pos, data);
1803 warningstream << "ServerActiveObject: No factory for type=" << type << std::endl;
1809 Convert stored objects from blocks near the players to active.
1811 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1816 // Ignore if no stored objects (to not set changed flag)
1817 if(block->m_static_objects.m_stored.empty())
1820 verbosestream<<"ServerEnvironment::activateObjects(): "
1821 <<"activating objects of block "<<PP(block->getPos())
1822 <<" ("<<block->m_static_objects.m_stored.size()
1823 <<" objects)"<<std::endl;
1824 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1826 errorstream<<"suspiciously large amount of objects detected: "
1827 <<block->m_static_objects.m_stored.size()<<" in "
1828 <<PP(block->getPos())
1829 <<"; removing all of them."<<std::endl;
1830 // Clear stored list
1831 block->m_static_objects.m_stored.clear();
1832 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1833 MOD_REASON_TOO_MANY_OBJECTS);
1837 // Activate stored objects
1838 std::vector<StaticObject> new_stored;
1839 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1840 // Create an active object from the data
1841 ServerActiveObject *obj = createSAO((ActiveObjectType) s_obj.type, s_obj.pos,
1843 // If couldn't create object, store static data back.
1845 errorstream<<"ServerEnvironment::activateObjects(): "
1846 <<"failed to create active object from static object "
1847 <<"in block "<<PP(s_obj.pos/BS)
1848 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1849 print_hexdump(verbosestream, s_obj.data);
1851 new_stored.push_back(s_obj);
1854 verbosestream<<"ServerEnvironment::activateObjects(): "
1855 <<"activated static object pos="<<PP(s_obj.pos/BS)
1856 <<" type="<<(int)s_obj.type<<std::endl;
1857 // This will also add the object to the active static list
1858 addActiveObjectRaw(obj, false, dtime_s);
1861 // Clear stored list
1862 block->m_static_objects.m_stored.clear();
1863 // Add leftover failed stuff to stored list
1864 for (const StaticObject &s_obj : new_stored) {
1865 block->m_static_objects.m_stored.push_back(s_obj);
1869 Note: Block hasn't really been modified here.
1870 The objects have just been activated and moved from the stored
1871 static list to the active static list.
1872 As such, the block is essentially the same.
1873 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1874 Otherwise there would be a huge amount of unnecessary I/O.
1879 Convert objects that are not standing inside active blocks to static.
1881 If m_known_by_count != 0, active object is not deleted, but static
1882 data is still updated.
1884 If force_delete is set, active object is deleted nevertheless. It
1885 shall only be set so in the destructor of the environment.
1887 If block wasn't generated (not in memory or on disk),
1889 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1891 auto cb_deactivate = [this, _force_delete] (ServerActiveObject *obj, u16 id) {
1892 // force_delete might be overriden per object
1893 bool force_delete = _force_delete;
1895 // Do not deactivate if static data creation not allowed
1896 if (!force_delete && !obj->isStaticAllowed())
1899 // removeRemovedObjects() is responsible for these
1900 if (!force_delete && obj->isGone())
1903 const v3f &objectpos = obj->getBasePosition();
1905 // The block in which the object resides in
1906 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1908 // If object's static data is stored in a deactivated block and object
1909 // is actually located in an active block, re-save to the block in
1910 // which the object is actually located in.
1911 if (!force_delete && obj->m_static_exists &&
1912 !m_active_blocks.contains(obj->m_static_block) &&
1913 m_active_blocks.contains(blockpos_o)) {
1914 // Delete from block where object was located
1915 deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
1917 StaticObject s_obj(obj, objectpos);
1918 // Save to block where object is located
1919 saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
1924 // If block is still active, don't remove
1925 if (!force_delete && m_active_blocks.contains(blockpos_o))
1928 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
1929 << "deactivating object id=" << id << " on inactive block "
1930 << PP(blockpos_o) << std::endl;
1932 // If known by some client, don't immediately delete.
1933 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1936 Update the static data
1938 if (obj->isStaticAllowed()) {
1939 // Create new static object
1940 StaticObject s_obj(obj, objectpos);
1942 bool stays_in_same_block = false;
1943 bool data_changed = true;
1945 // Check if static data has changed considerably
1946 if (obj->m_static_exists) {
1947 if (obj->m_static_block == blockpos_o)
1948 stays_in_same_block = true;
1950 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1953 const auto n = block->m_static_objects.m_active.find(id);
1954 if (n != block->m_static_objects.m_active.end()) {
1955 StaticObject static_old = n->second;
1957 float save_movem = obj->getMinimumSavedMovement();
1959 if (static_old.data == s_obj.data &&
1960 (static_old.pos - objectpos).getLength() < save_movem)
1961 data_changed = false;
1963 warningstream << "ServerEnvironment::deactivateFarObjects(): "
1964 << "id=" << id << " m_static_exists=true but "
1965 << "static data doesn't actually exist in "
1966 << PP(obj->m_static_block) << std::endl;
1972 While changes are always saved, blocks are only marked as modified
1973 if the object has moved or different staticdata. (see above)
1975 bool shall_be_written = (!stays_in_same_block || data_changed);
1976 u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
1978 // Delete old static object
1979 deleteStaticFromBlock(obj, id, reason, false);
1981 // Add to the block where the object is located in
1982 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1983 u16 store_id = pending_delete ? id : 0;
1984 if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
1985 force_delete = true;
1989 If known by some client, set pending deactivation.
1990 Otherwise delete it immediately.
1992 if (pending_delete && !force_delete) {
1993 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
1994 << "object id=" << id << " is known by clients"
1995 << "; not deleting yet" << std::endl;
1997 obj->m_pending_deactivation = true;
2001 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2002 << "object id=" << id << " is not known by clients"
2003 << "; deleting" << std::endl;
2005 // Tell the object about removal
2006 obj->removingFromEnvironment();
2007 // Deregister in scripting api
2008 m_script->removeObjectReference(obj);
2010 // Delete active object
2011 if (obj->environmentDeletes())
2017 m_ao_manager.clear(cb_deactivate);
2020 void ServerEnvironment::deleteStaticFromBlock(
2021 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2023 if (!obj->m_static_exists)
2028 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2030 block = m_map->emergeBlock(obj->m_static_block, false);
2033 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2034 << " when deleting static data of object from it. id=" << id << std::endl;
2038 block->m_static_objects.remove(id);
2039 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2040 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2042 obj->m_static_exists = false;
2045 bool ServerEnvironment::saveStaticToBlock(
2046 v3s16 blockpos, u16 store_id,
2047 ServerActiveObject *obj, const StaticObject &s_obj,
2050 MapBlock *block = nullptr;
2052 block = m_map->emergeBlock(blockpos);
2053 } catch (InvalidPositionException &e) {
2054 // Handled via NULL pointer
2055 // NOTE: emergeBlock's failure is usually determined by it
2056 // actually returning NULL
2060 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2061 << " when saving static data of object to it. id=" << store_id << std::endl;
2064 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2065 warningstream << "ServerEnv: Trying to store id = " << store_id
2066 << " statically but block " << PP(blockpos)
2067 << " already contains "
2068 << block->m_static_objects.m_stored.size()
2069 << " objects." << std::endl;
2073 block->m_static_objects.insert(store_id, s_obj);
2074 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2075 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2077 obj->m_static_exists = true;
2078 obj->m_static_block = blockpos;
2083 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2084 const std::string &savedir, const Settings &conf)
2087 if (name == "sqlite3")
2088 return new PlayerDatabaseSQLite3(savedir);
2090 if (name == "dummy")
2091 return new Database_Dummy();
2094 if (name == "postgresql") {
2095 std::string connect_string;
2096 conf.getNoEx("pgsql_player_connection", connect_string);
2097 return new PlayerDatabasePostgreSQL(connect_string);
2102 if (name == "leveldb")
2103 return new PlayerDatabaseLevelDB(savedir);
2106 if (name == "files")
2107 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2109 throw BaseException(std::string("Database backend ") + name + " not supported.");
2112 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2113 const Settings &cmd_args)
2115 std::string migrate_to = cmd_args.get("migrate-players");
2117 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2118 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2119 errorstream << "Cannot read world.mt!" << std::endl;
2123 if (!world_mt.exists("player_backend")) {
2124 errorstream << "Please specify your current backend in world.mt:"
2126 << " player_backend = {files|sqlite3|leveldb|postgresql}"
2131 std::string backend = world_mt.get("player_backend");
2132 if (backend == migrate_to) {
2133 errorstream << "Cannot migrate: new backend is same"
2134 << " as the old one" << std::endl;
2138 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2141 if (backend == "files") {
2142 // Create backup directory
2143 fs::CreateDir(players_backup_path);
2147 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2148 game_params.world_path, world_mt);
2149 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2150 game_params.world_path, world_mt);
2152 std::vector<std::string> player_list;
2153 srcdb->listPlayers(player_list);
2154 for (std::vector<std::string>::const_iterator it = player_list.begin();
2155 it != player_list.end(); ++it) {
2156 actionstream << "Migrating player " << it->c_str() << std::endl;
2157 RemotePlayer player(it->c_str(), NULL);
2158 PlayerSAO playerSAO(NULL, &player, 15000, false);
2160 srcdb->loadPlayer(&player, &playerSAO);
2162 playerSAO.finalize(&player, std::set<std::string>());
2163 player.setPlayerSAO(&playerSAO);
2165 dstdb->savePlayer(&player);
2167 // For files source, move player files to backup dir
2168 if (backend == "files") {
2170 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2171 players_backup_path + DIR_DELIM + (*it));
2175 actionstream << "Successfully migrated " << player_list.size() << " players"
2177 world_mt.set("player_backend", migrate_to);
2178 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2179 errorstream << "Failed to update world.mt!" << std::endl;
2181 actionstream << "world.mt updated" << std::endl;
2183 // When migration is finished from file backend, remove players directory if empty
2184 if (backend == "files") {
2185 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2192 } catch (BaseException &e) {
2193 errorstream << "An error occurred during migration: " << e.what() << std::endl;
2199 AuthDatabase *ServerEnvironment::openAuthDatabase(
2200 const std::string &name, const std::string &savedir, const Settings &conf)
2202 if (name == "sqlite3")
2203 return new AuthDatabaseSQLite3(savedir);
2206 if (name == "postgresql") {
2207 std::string connect_string;
2208 conf.getNoEx("pgsql_auth_connection", connect_string);
2209 return new AuthDatabasePostgreSQL(connect_string);
2213 if (name == "files")
2214 return new AuthDatabaseFiles(savedir);
2217 if (name == "leveldb")
2218 return new AuthDatabaseLevelDB(savedir);
2221 throw BaseException(std::string("Database backend ") + name + " not supported.");
2224 bool ServerEnvironment::migrateAuthDatabase(
2225 const GameParams &game_params, const Settings &cmd_args)
2227 std::string migrate_to = cmd_args.get("migrate-auth");
2229 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2230 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2231 errorstream << "Cannot read world.mt!" << std::endl;
2235 std::string backend = "files";
2236 if (world_mt.exists("auth_backend"))
2237 backend = world_mt.get("auth_backend");
2239 warningstream << "No auth_backend found in world.mt, "
2240 "assuming \"files\"." << std::endl;
2242 if (backend == migrate_to) {
2243 errorstream << "Cannot migrate: new backend is same"
2244 << " as the old one" << std::endl;
2249 const std::unique_ptr<AuthDatabase> srcdb(ServerEnvironment::openAuthDatabase(
2250 backend, game_params.world_path, world_mt));
2251 const std::unique_ptr<AuthDatabase> dstdb(ServerEnvironment::openAuthDatabase(
2252 migrate_to, game_params.world_path, world_mt));
2254 std::vector<std::string> names_list;
2255 srcdb->listNames(names_list);
2256 for (const std::string &name : names_list) {
2257 actionstream << "Migrating auth entry for " << name << std::endl;
2259 AuthEntry authEntry;
2260 success = srcdb->getAuth(name, authEntry);
2261 success = success && dstdb->createAuth(authEntry);
2263 errorstream << "Failed to migrate " << name << std::endl;
2266 actionstream << "Successfully migrated " << names_list.size()
2267 << " auth entries" << std::endl;
2268 world_mt.set("auth_backend", migrate_to);
2269 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2270 errorstream << "Failed to update world.mt!" << std::endl;
2272 actionstream << "world.mt updated" << std::endl;
2274 if (backend == "files") {
2275 // special-case files migration:
2276 // move auth.txt to auth.txt.bak if possible
2277 std::string auth_txt_path =
2278 game_params.world_path + DIR_DELIM + "auth.txt";
2279 std::string auth_bak_path = auth_txt_path + ".bak";
2280 if (!fs::PathExists(auth_bak_path))
2281 if (fs::Rename(auth_txt_path, auth_bak_path))
2282 actionstream << "Renamed auth.txt to auth.txt.bak"
2285 errorstream << "Could not rename auth.txt to "
2286 "auth.txt.bak" << std::endl;
2288 warningstream << "auth.txt.bak already exists, auth.txt "
2289 "not renamed" << std::endl;
2292 } catch (BaseException &e) {
2293 errorstream << "An error occurred during migration: " << e.what()