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);
635 Settings args("EnvArgsEnd");
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);
645 if(!fs::safeWriteToFile(path, ss.str()))
647 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
649 throw SerializationError("Couldn't save env meta");
653 void ServerEnvironment::loadMeta()
655 SANITY_CHECK(!m_meta_loaded);
656 m_meta_loaded = true;
658 // If file doesn't exist, load default environment metadata
659 if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
660 infostream << "ServerEnvironment: Loading default environment metadata"
666 infostream << "ServerEnvironment: Loading environment metadata" << std::endl;
668 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
670 // Open file and deserialize
671 std::ifstream is(path.c_str(), std::ios_base::binary);
673 infostream << "ServerEnvironment::loadMeta(): Failed to open "
674 << path << std::endl;
675 throw SerializationError("Couldn't load env meta");
678 Settings args("EnvArgsEnd");
680 if (!args.parseConfigLines(is)) {
681 throw SerializationError("ServerEnvironment::loadMeta(): "
682 "EnvArgsEnd not found!");
686 m_game_time = args.getU64("game_time");
687 } catch (SettingNotFoundException &e) {
688 // Getting this is crucial, otherwise timestamps are useless
689 throw SerializationError("Couldn't load env meta game_time");
692 setTimeOfDay(args.exists("time_of_day") ?
693 // set day to early morning by default
694 args.getU64("time_of_day") : 5250);
696 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
697 // If missing, do as if clearObjects was never called
698 args.getU64("last_clear_objects_time") : 0;
700 std::string lbm_introduction_times;
702 u64 ver = args.getU64("lbm_introduction_times_version");
704 lbm_introduction_times = args.get("lbm_introduction_times");
706 infostream << "ServerEnvironment::loadMeta(): Non-supported"
707 << " introduction time version " << ver << std::endl;
709 } catch (SettingNotFoundException &e) {
710 // No problem, this is expected. Just continue with an empty string
712 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
714 m_day_count = args.exists("day_count") ?
715 args.getU64("day_count") : 0;
719 * called if env_meta.txt doesn't exist (e.g. new world)
721 void ServerEnvironment::loadDefaultMeta()
723 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
728 ActiveBlockModifier *abm;
730 std::vector<content_t> required_neighbors;
731 bool check_required_neighbors; // false if required_neighbors is known to be empty
737 ServerEnvironment *m_env;
738 std::vector<std::vector<ActiveABM> *> m_aabms;
740 ABMHandler(std::vector<ABMWithState> &abms,
741 float dtime_s, ServerEnvironment *env,
747 const NodeDefManager *ndef = env->getGameDef()->ndef();
748 for (ABMWithState &abmws : abms) {
749 ActiveBlockModifier *abm = abmws.abm;
750 float trigger_interval = abm->getTriggerInterval();
751 if(trigger_interval < 0.001)
752 trigger_interval = 0.001;
753 float actual_interval = dtime_s;
755 abmws.timer += dtime_s;
756 if(abmws.timer < trigger_interval)
758 abmws.timer -= trigger_interval;
759 actual_interval = trigger_interval;
761 float chance = abm->getTriggerChance();
766 if (abm->getSimpleCatchUp()) {
767 float intervals = actual_interval / trigger_interval;
770 aabm.chance = chance / intervals;
774 aabm.chance = chance;
778 const std::vector<std::string> &required_neighbors_s =
779 abm->getRequiredNeighbors();
780 for (const std::string &required_neighbor_s : required_neighbors_s) {
781 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
783 aabm.check_required_neighbors = !required_neighbors_s.empty();
786 const std::vector<std::string> &contents_s = abm->getTriggerContents();
787 for (const std::string &content_s : contents_s) {
788 std::vector<content_t> ids;
789 ndef->getIds(content_s, ids);
790 for (content_t c : ids) {
791 if (c >= m_aabms.size())
792 m_aabms.resize(c + 256, NULL);
794 m_aabms[c] = new std::vector<ActiveABM>;
795 m_aabms[c]->push_back(aabm);
803 for (auto &aabms : m_aabms)
807 // Find out how many objects the given block and its neighbours contain.
808 // Returns the number of objects in the block, and also in 'wider' the
809 // number of objects in the block and all its neighbours. The latter
810 // may an estimate if any neighbours are unloaded.
811 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
814 u32 wider_unknown_count = 0;
815 for(s16 x=-1; x<=1; x++)
816 for(s16 y=-1; y<=1; y++)
817 for(s16 z=-1; z<=1; z++)
819 MapBlock *block2 = map->getBlockNoCreateNoEx(
820 block->getPos() + v3s16(x,y,z));
822 wider_unknown_count++;
825 wider += block2->m_static_objects.m_active.size()
826 + block2->m_static_objects.m_stored.size();
829 u32 active_object_count = block->m_static_objects.m_active.size();
830 u32 wider_known_count = 3*3*3 - wider_unknown_count;
831 wider += wider_unknown_count * wider / wider_known_count;
832 return active_object_count;
835 void apply(MapBlock *block, int &blocks_scanned, int &abms_run, int &blocks_cached)
837 if(m_aabms.empty() || block->isDummy())
840 // Check the content type cache first
841 // to see whether there are any ABMs
842 // to be run at all for this block.
843 if (block->contents_cached) {
845 bool run_abms = false;
846 for (content_t c : block->contents) {
847 if (c < m_aabms.size() && m_aabms[c]) {
856 block->contents.clear();
860 ServerMap *map = &m_env->getServerMap();
862 u32 active_object_count_wider;
863 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
864 m_env->m_added_objects = 0;
867 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
868 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
869 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
871 const MapNode &n = block->getNodeUnsafe(p0);
872 content_t c = n.getContent();
873 // Cache content types as we go
874 if (!block->contents_cached && !block->do_not_cache_contents) {
875 block->contents.insert(c);
876 if (block->contents.size() > 64) {
877 // Too many different nodes... don't try to cache
878 block->do_not_cache_contents = true;
879 block->contents.clear();
883 if (c >= m_aabms.size() || !m_aabms[c])
886 v3s16 p = p0 + block->getPosRelative();
887 for (ActiveABM &aabm : *m_aabms[c]) {
888 if (myrand() % aabm.chance != 0)
892 if (aabm.check_required_neighbors) {
894 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
895 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
896 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
901 if (block->isValidPosition(p1)) {
902 // if the neighbor is found on the same map block
903 // get it straight from there
904 const MapNode &n = block->getNodeUnsafe(p1);
907 // otherwise consult the map
908 MapNode n = map->getNode(p1 + block->getPosRelative());
911 if (CONTAINS(aabm.required_neighbors, c))
914 // No required neighbor found
920 // Call all the trigger variations
921 aabm.abm->trigger(m_env, p, n);
922 aabm.abm->trigger(m_env, p, n,
923 active_object_count, active_object_count_wider);
925 // Count surrounding objects again if the abms added any
926 if(m_env->m_added_objects > 0) {
927 active_object_count = countObjects(block, map, active_object_count_wider);
928 m_env->m_added_objects = 0;
932 block->contents_cached = !block->do_not_cache_contents;
936 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
938 // Reset usage timer immediately, otherwise a block that becomes active
939 // again at around the same time as it would normally be unloaded will
940 // get unloaded incorrectly. (I think this still leaves a small possibility
941 // of a race condition between this and server::AsyncRunStep, which only
942 // some kind of synchronisation will fix, but it at least reduces the window
943 // of opportunity for it to break from seconds to nanoseconds)
944 block->resetUsageTimer();
946 // Get time difference
948 u32 stamp = block->getTimestamp();
949 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
950 dtime_s = m_game_time - stamp;
951 dtime_s += additional_dtime;
953 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
954 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
956 // Remove stored static objects if clearObjects was called since block's timestamp
957 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
958 block->m_static_objects.m_stored.clear();
959 // do not set changed flag to avoid unnecessary mapblock writes
962 // Set current time as timestamp
963 block->setTimestampNoChangedFlag(m_game_time);
965 /*infostream<<"ServerEnvironment::activateBlock(): block is "
966 <<dtime_s<<" seconds old."<<std::endl;*/
968 // Activate stored objects
969 activateObjects(block, dtime_s);
971 /* Handle LoadingBlockModifiers */
972 m_lbm_mgr.applyLBMs(this, block, stamp);
975 std::vector<NodeTimer> elapsed_timers =
976 block->m_node_timers.step((float)dtime_s);
977 if (!elapsed_timers.empty()) {
979 for (const NodeTimer &elapsed_timer : elapsed_timers) {
980 n = block->getNodeNoEx(elapsed_timer.position);
981 v3s16 p = elapsed_timer.position + block->getPosRelative();
982 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
983 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
984 elapsed_timer.position));
989 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
991 m_abms.emplace_back(abm);
994 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
996 m_lbm_mgr.addLBMDef(lbm);
999 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1001 const NodeDefManager *ndef = m_server->ndef();
1002 MapNode n_old = m_map->getNode(p);
1004 const ContentFeatures &cf_old = ndef->get(n_old);
1007 if (cf_old.has_on_destruct)
1008 m_script->node_on_destruct(p, n_old);
1011 if (!m_map->addNodeWithEvent(p, n))
1014 // Update active VoxelManipulator if a mapgen thread
1015 m_map->updateVManip(p);
1017 // Call post-destructor
1018 if (cf_old.has_after_destruct)
1019 m_script->node_after_destruct(p, n_old);
1021 // Retrieve node content features
1022 // if new node is same as old, reuse old definition to prevent a lookup
1023 const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
1026 if (cf_new.has_on_construct)
1027 m_script->node_on_construct(p, n);
1032 bool ServerEnvironment::removeNode(v3s16 p)
1034 const NodeDefManager *ndef = m_server->ndef();
1035 MapNode n_old = m_map->getNode(p);
1038 if (ndef->get(n_old).has_on_destruct)
1039 m_script->node_on_destruct(p, n_old);
1042 // This is slightly optimized compared to addNodeWithEvent(air)
1043 if (!m_map->removeNodeWithEvent(p))
1046 // Update active VoxelManipulator if a mapgen thread
1047 m_map->updateVManip(p);
1049 // Call post-destructor
1050 if (ndef->get(n_old).has_after_destruct)
1051 m_script->node_after_destruct(p, n_old);
1053 // Air doesn't require constructor
1057 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1059 if (!m_map->addNodeWithEvent(p, n, false))
1062 // Update active VoxelManipulator if a mapgen thread
1063 m_map->updateVManip(p);
1068 u8 ServerEnvironment::findSunlight(v3s16 pos) const
1070 // Directions for neighbouring nodes with specified order
1071 static const v3s16 dirs[] = {
1072 v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1),
1073 v3s16(0, -1, 0), v3s16(0, 1, 0)
1076 const NodeDefManager *ndef = m_server->ndef();
1078 // found_light remembers the highest known sunlight value at pos
1081 struct stack_entry {
1085 std::stack<stack_entry> stack;
1086 stack.push({pos, 0});
1088 std::unordered_map<s64, s8> dists;
1089 dists[MapDatabase::getBlockAsInteger(pos)] = 0;
1091 while (!stack.empty()) {
1092 struct stack_entry e = stack.top();
1095 v3s16 currentPos = e.pos;
1096 s8 dist = e.dist + 1;
1098 for (const v3s16& off : dirs) {
1099 v3s16 neighborPos = currentPos + off;
1100 s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos);
1102 // Do not walk neighborPos multiple times unless the distance to the start
1103 // position is shorter
1104 auto it = dists.find(neighborHash);
1105 if (it != dists.end() && dist >= it->second)
1109 bool is_position_ok;
1110 MapNode node = m_map->getNode(neighborPos, &is_position_ok);
1111 if (!is_position_ok) {
1112 // This happens very rarely because the map at currentPos is loaded
1113 m_map->emergeBlock(neighborPos, false);
1114 node = m_map->getNode(neighborPos, &is_position_ok);
1115 if (!is_position_ok)
1116 continue; // not generated
1119 const ContentFeatures &def = ndef->get(node);
1120 if (!def.sunlight_propagates) {
1121 // Do not test propagation here again
1122 dists[neighborHash] = -1;
1126 // Sunlight could have come from here
1127 dists[neighborHash] = dist;
1128 u8 daylight = node.param1 & 0x0f;
1130 // In the special case where sunlight shines from above and thus
1131 // does not decrease with upwards distance, daylight is always
1132 // bigger than nightlight, which never reaches 15
1133 int possible_finlight = daylight - dist;
1134 if (possible_finlight <= found_light) {
1135 // Light from here cannot make a brighter light at currentPos than
1140 u8 nightlight = node.param1 >> 4;
1141 if (daylight > nightlight) {
1142 // Found a valid daylight
1143 found_light = possible_finlight;
1145 // Sunlight may be darker, so walk the neighbours
1146 stack.push({neighborPos, dist});
1153 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1155 infostream << "ServerEnvironment::clearObjects(): "
1156 << "Removing all active objects" << std::endl;
1157 auto cb_removal = [this] (ServerActiveObject *obj, u16 id) {
1158 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1161 // Delete static object if block is loaded
1162 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1164 // If known by some client, don't delete immediately
1165 if (obj->m_known_by_count > 0) {
1166 obj->markForRemoval();
1170 // Tell the object about removal
1171 obj->removingFromEnvironment();
1172 // Deregister in scripting api
1173 m_script->removeObjectReference(obj);
1175 // Delete active object
1176 if (obj->environmentDeletes())
1182 m_ao_manager.clear(cb_removal);
1184 // Get list of loaded blocks
1185 std::vector<v3s16> loaded_blocks;
1186 infostream << "ServerEnvironment::clearObjects(): "
1187 << "Listing all loaded blocks" << std::endl;
1188 m_map->listAllLoadedBlocks(loaded_blocks);
1189 infostream << "ServerEnvironment::clearObjects(): "
1190 << "Done listing all loaded blocks: "
1191 << loaded_blocks.size()<<std::endl;
1193 // Get list of loadable blocks
1194 std::vector<v3s16> loadable_blocks;
1195 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1196 infostream << "ServerEnvironment::clearObjects(): "
1197 << "Listing all loadable blocks" << std::endl;
1198 m_map->listAllLoadableBlocks(loadable_blocks);
1199 infostream << "ServerEnvironment::clearObjects(): "
1200 << "Done listing all loadable blocks: "
1201 << loadable_blocks.size() << std::endl;
1203 loadable_blocks = loaded_blocks;
1206 actionstream << "ServerEnvironment::clearObjects(): "
1207 << "Now clearing objects in " << loadable_blocks.size()
1208 << " blocks" << std::endl;
1210 // Grab a reference on each loaded block to avoid unloading it
1211 for (v3s16 p : loaded_blocks) {
1212 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1213 assert(block != NULL);
1217 // Remove objects in all loadable blocks
1218 u32 unload_interval = U32_MAX;
1219 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1220 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1221 unload_interval = MYMAX(unload_interval, 1);
1223 u32 report_interval = loadable_blocks.size() / 10;
1224 u32 num_blocks_checked = 0;
1225 u32 num_blocks_cleared = 0;
1226 u32 num_objs_cleared = 0;
1227 for (auto i = loadable_blocks.begin();
1228 i != loadable_blocks.end(); ++i) {
1230 MapBlock *block = m_map->emergeBlock(p, false);
1232 errorstream << "ServerEnvironment::clearObjects(): "
1233 << "Failed to emerge block " << PP(p) << std::endl;
1236 u32 num_stored = block->m_static_objects.m_stored.size();
1237 u32 num_active = block->m_static_objects.m_active.size();
1238 if (num_stored != 0 || num_active != 0) {
1239 block->m_static_objects.m_stored.clear();
1240 block->m_static_objects.m_active.clear();
1241 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1242 MOD_REASON_CLEAR_ALL_OBJECTS);
1243 num_objs_cleared += num_stored + num_active;
1244 num_blocks_cleared++;
1246 num_blocks_checked++;
1248 if (report_interval != 0 &&
1249 num_blocks_checked % report_interval == 0) {
1250 float percent = 100.0 * (float)num_blocks_checked /
1251 loadable_blocks.size();
1252 actionstream << "ServerEnvironment::clearObjects(): "
1253 << "Cleared " << num_objs_cleared << " objects"
1254 << " in " << num_blocks_cleared << " blocks ("
1255 << percent << "%)" << std::endl;
1257 if (num_blocks_checked % unload_interval == 0) {
1258 m_map->unloadUnreferencedBlocks();
1261 m_map->unloadUnreferencedBlocks();
1263 // Drop references that were added above
1264 for (v3s16 p : loaded_blocks) {
1265 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1270 m_last_clear_objects_time = m_game_time;
1272 actionstream << "ServerEnvironment::clearObjects(): "
1273 << "Finished: Cleared " << num_objs_cleared << " objects"
1274 << " in " << num_blocks_cleared << " blocks" << std::endl;
1277 void ServerEnvironment::step(float dtime)
1279 ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
1280 /* Step time of day */
1281 stepTimeOfDay(dtime);
1284 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1285 // really matter that much.
1286 static thread_local const float server_step =
1287 g_settings->getFloat("dedicated_server_step");
1288 m_recommended_send_interval = server_step;
1294 m_game_time_fraction_counter += dtime;
1295 u32 inc_i = (u32)m_game_time_fraction_counter;
1296 m_game_time += inc_i;
1297 m_game_time_fraction_counter -= (float)inc_i;
1304 ScopeProfiler sp(g_profiler, "ServerEnv: move players", SPT_AVG);
1305 for (RemotePlayer *player : m_players) {
1306 // Ignore disconnected players
1307 if (player->getPeerId() == PEER_ID_INEXISTENT)
1311 player->move(dtime, this, 100 * BS);
1316 Manage active block list
1318 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1319 ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
1321 Get player block positions
1323 std::vector<PlayerSAO*> players;
1324 for (RemotePlayer *player: m_players) {
1325 // Ignore disconnected players
1326 if (player->getPeerId() == PEER_ID_INEXISTENT)
1329 PlayerSAO *playersao = player->getPlayerSAO();
1332 players.push_back(playersao);
1336 Update list of active blocks, collecting changes
1338 // use active_object_send_range_blocks since that is max distance
1339 // for active objects sent the client anyway
1340 static thread_local const s16 active_object_range =
1341 g_settings->getS16("active_object_send_range_blocks");
1342 static thread_local const s16 active_block_range =
1343 g_settings->getS16("active_block_range");
1344 std::set<v3s16> blocks_removed;
1345 std::set<v3s16> blocks_added;
1346 m_active_blocks.update(players, active_block_range, active_object_range,
1347 blocks_removed, blocks_added);
1350 Handle removed blocks
1353 // Convert active objects that are no more in active blocks to static
1354 deactivateFarObjects(false);
1356 for (const v3s16 &p: blocks_removed) {
1357 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1361 // Set current time as timestamp (and let it set ChangedFlag)
1362 block->setTimestamp(m_game_time);
1369 for (const v3s16 &p: blocks_added) {
1370 MapBlock *block = m_map->getBlockOrEmerge(p);
1372 m_active_blocks.m_list.erase(p);
1373 m_active_blocks.m_abm_list.erase(p);
1377 activateBlock(block);
1382 Mess around in active blocks
1384 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1385 ScopeProfiler sp(g_profiler, "ServerEnv: Run node timers", SPT_AVG);
1387 float dtime = m_cache_nodetimer_interval;
1389 for (const v3s16 &p: m_active_blocks.m_list) {
1390 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1394 // Reset block usage timer
1395 block->resetUsageTimer();
1397 // Set current time as timestamp
1398 block->setTimestampNoChangedFlag(m_game_time);
1399 // If time has changed much from the one on disk,
1400 // set block to be saved when it is unloaded
1401 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1402 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1403 MOD_REASON_BLOCK_EXPIRED);
1406 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1407 if (!elapsed_timers.empty()) {
1410 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1411 n = block->getNodeNoEx(elapsed_timer.position);
1412 p2 = elapsed_timer.position + block->getPosRelative();
1413 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1414 block->setNodeTimer(NodeTimer(
1415 elapsed_timer.timeout, 0, elapsed_timer.position));
1422 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) {
1423 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1424 TimeTaker timer("modify in active blocks per interval");
1426 // Initialize handling of ActiveBlockModifiers
1427 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1429 int blocks_scanned = 0;
1431 int blocks_cached = 0;
1433 std::vector<v3s16> output(m_active_blocks.m_abm_list.size());
1435 // Shuffle the active blocks so that each block gets an equal chance
1436 // of having its ABMs run.
1437 std::copy(m_active_blocks.m_abm_list.begin(), m_active_blocks.m_abm_list.end(), output.begin());
1438 std::shuffle(output.begin(), output.end(), m_rgen);
1441 // determine the time budget for ABMs
1442 u32 max_time_ms = m_cache_abm_interval * 1000 * m_cache_abm_time_budget;
1443 for (const v3s16 &p : output) {
1444 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1450 // Set current time as timestamp
1451 block->setTimestampNoChangedFlag(m_game_time);
1453 /* Handle ActiveBlockModifiers */
1454 abmhandler.apply(block, blocks_scanned, abms_run, blocks_cached);
1456 u32 time_ms = timer.getTimerTime();
1458 if (time_ms > max_time_ms) {
1459 warningstream << "active block modifiers took "
1460 << time_ms << "ms (processed " << i << " of "
1461 << output.size() << " active blocks)" << std::endl;
1465 g_profiler->avg("ServerEnv: active blocks", m_active_blocks.m_abm_list.size());
1466 g_profiler->avg("ServerEnv: active blocks cached", blocks_cached);
1467 g_profiler->avg("ServerEnv: active blocks scanned for ABMs", blocks_scanned);
1468 g_profiler->avg("ServerEnv: ABMs run", abms_run);
1474 Step script environment (run global on_step())
1476 m_script->environment_Step(dtime);
1482 ScopeProfiler sp(g_profiler, "ServerEnv: Run SAO::step()", SPT_AVG);
1484 // This helps the objects to send data at the same time
1485 bool send_recommended = false;
1486 m_send_recommended_timer += dtime;
1487 if (m_send_recommended_timer > getSendRecommendedInterval()) {
1488 m_send_recommended_timer -= getSendRecommendedInterval();
1489 send_recommended = true;
1492 auto cb_state = [this, dtime, send_recommended] (ServerActiveObject *obj) {
1497 obj->step(dtime, send_recommended);
1498 // Read messages from object
1499 obj->dumpAOMessagesToQueue(m_active_object_messages);
1501 m_ao_manager.step(dtime, cb_state);
1505 Manage active objects
1507 if (m_object_management_interval.step(dtime, 0.5)) {
1508 removeRemovedObjects();
1512 Manage particle spawner expiration
1514 if (m_particle_management_interval.step(dtime, 1.0)) {
1515 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1516 i != m_particle_spawners.end(); ) {
1517 //non expiring spawners
1518 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1524 if (i->second <= 0.f)
1525 m_particle_spawners.erase(i++);
1531 // Send outdated player inventories
1532 for (RemotePlayer *player : m_players) {
1533 if (player->getPeerId() == PEER_ID_INEXISTENT)
1536 PlayerSAO *sao = player->getPlayerSAO();
1537 if (sao && player->inventory.checkModified())
1538 m_server->SendInventory(sao, true);
1541 // Send outdated detached inventories
1542 m_server->sendDetachedInventories(PEER_ID_INEXISTENT, true);
1545 u32 ServerEnvironment::addParticleSpawner(float exptime)
1547 // Timers with lifetime 0 do not expire
1548 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1551 for (;;) { // look for unused particlespawner id
1553 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1554 if (f == m_particle_spawners.end()) {
1555 m_particle_spawners[id] = time;
1562 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1564 u32 id = addParticleSpawner(exptime);
1565 m_particle_spawner_attachments[id] = attached_id;
1566 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1567 obj->attachParticleSpawner(id);
1572 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1574 m_particle_spawners.erase(id);
1575 const auto &it = m_particle_spawner_attachments.find(id);
1576 if (it != m_particle_spawner_attachments.end()) {
1577 u16 obj_id = it->second;
1578 ServerActiveObject *sao = getActiveObject(obj_id);
1579 if (sao != NULL && remove_from_object) {
1580 sao->detachParticleSpawner(id);
1582 m_particle_spawner_attachments.erase(id);
1586 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1588 assert(object); // Pre-condition
1590 u16 id = addActiveObjectRaw(object, true, 0);
1595 Finds out what new objects have been added to
1596 inside a radius around a position
1598 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1600 std::set<u16> ¤t_objects,
1601 std::queue<u16> &added_objects)
1603 f32 radius_f = radius * BS;
1604 f32 player_radius_f = player_radius * BS;
1606 if (player_radius_f < 0.0f)
1607 player_radius_f = 0.0f;
1609 m_ao_manager.getAddedActiveObjectsAroundPos(playersao->getBasePosition(), radius_f,
1610 player_radius_f, current_objects, added_objects);
1614 Finds out what objects have been removed from
1615 inside a radius around a position
1617 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1619 std::set<u16> ¤t_objects,
1620 std::queue<u16> &removed_objects)
1622 f32 radius_f = radius * BS;
1623 f32 player_radius_f = player_radius * BS;
1625 if (player_radius_f < 0)
1626 player_radius_f = 0;
1628 Go through current_objects; object is removed if:
1629 - object is not found in m_active_objects (this is actually an
1630 error condition; objects should be removed only after all clients
1631 have been informed about removal), or
1632 - object is to be removed or deactivated, or
1633 - object is too far away
1635 for (u16 id : current_objects) {
1636 ServerActiveObject *object = getActiveObject(id);
1638 if (object == NULL) {
1639 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1640 << " object in current_objects is NULL" << std::endl;
1641 removed_objects.push(id);
1645 if (object->isGone()) {
1646 removed_objects.push(id);
1650 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1651 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1652 if (distance_f <= player_radius_f || player_radius_f == 0)
1654 } else if (distance_f <= radius_f)
1657 // Object is no longer visible
1658 removed_objects.push(id);
1662 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1663 v3s16 blockpos, bool static_exists, v3s16 static_block)
1665 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1669 for (auto &so_it : block->m_static_objects.m_active) {
1670 // Get the ServerActiveObject counterpart to this StaticObject
1671 ServerActiveObject *sao = m_ao_manager.getActiveObject(so_it.first);
1673 // If this ever happens, there must be some kind of nasty bug.
1674 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1675 "Object from MapBlock::m_static_objects::m_active not found "
1676 "in m_active_objects";
1680 sao->m_static_exists = static_exists;
1681 sao->m_static_block = static_block;
1685 bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest)
1687 if(m_active_object_messages.empty())
1690 *dest = std::move(m_active_object_messages.front());
1691 m_active_object_messages.pop();
1695 void ServerEnvironment::getSelectedActiveObjects(
1696 const core::line3d<f32> &shootline_on_map,
1697 std::vector<PointedThing> &objects)
1699 std::vector<ServerActiveObject *> objs;
1700 getObjectsInsideRadius(objs, shootline_on_map.start,
1701 shootline_on_map.getLength() + 10.0f, nullptr);
1702 const v3f line_vector = shootline_on_map.getVector();
1704 for (auto obj : objs) {
1707 aabb3f selection_box;
1708 if (!obj->getSelectionBox(&selection_box))
1711 v3f pos = obj->getBasePosition();
1713 aabb3f offsetted_box(selection_box.MinEdge + pos,
1714 selection_box.MaxEdge + pos);
1716 v3f current_intersection;
1717 v3s16 current_normal;
1718 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1719 ¤t_intersection, ¤t_normal)) {
1720 objects.emplace_back(
1721 (s16) obj->getId(), current_intersection, current_normal,
1722 (current_intersection - shootline_on_map.start).getLengthSQ());
1728 ************ Private methods *************
1731 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1732 bool set_changed, u32 dtime_s)
1734 if (!m_ao_manager.registerObject(object)) {
1738 // Register reference in scripting api (must be done before post-init)
1739 m_script->addObjectReference(object);
1740 // Post-initialize object
1741 object->addedToEnvironment(dtime_s);
1743 // Add static data to block
1744 if (object->isStaticAllowed()) {
1745 // Add static object to active static list of the block
1746 v3f objectpos = object->getBasePosition();
1747 StaticObject s_obj(object, objectpos);
1748 // Add to the block where the object is located in
1749 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1750 MapBlock *block = m_map->emergeBlock(blockpos);
1752 block->m_static_objects.m_active[object->getId()] = s_obj;
1753 object->m_static_exists = true;
1754 object->m_static_block = blockpos;
1757 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1758 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1760 v3s16 p = floatToInt(objectpos, BS);
1761 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1762 <<"could not emerge block for storing id="<<object->getId()
1763 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1767 return object->getId();
1771 Remove objects that satisfy (isGone() && m_known_by_count==0)
1773 void ServerEnvironment::removeRemovedObjects()
1775 ScopeProfiler sp(g_profiler, "ServerEnvironment::removeRemovedObjects()", SPT_AVG);
1777 auto clear_cb = [this] (ServerActiveObject *obj, u16 id) {
1778 // This shouldn't happen but check it
1780 errorstream << "ServerEnvironment::removeRemovedObjects(): "
1781 << "NULL object found. id=" << id << std::endl;
1786 We will handle objects marked for removal or deactivation
1792 Delete static data from block if removed
1794 if (obj->isPendingRemoval())
1795 deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1797 // If still known by clients, don't actually remove. On some future
1798 // invocation this will be 0, which is when removal will continue.
1799 if(obj->m_known_by_count > 0)
1803 Move static data from active to stored if deactivated
1805 if (!obj->isPendingRemoval() && obj->m_static_exists) {
1806 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1808 const auto i = block->m_static_objects.m_active.find(id);
1809 if (i != block->m_static_objects.m_active.end()) {
1810 block->m_static_objects.m_stored.push_back(i->second);
1811 block->m_static_objects.m_active.erase(id);
1812 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1813 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1815 warningstream << "ServerEnvironment::removeRemovedObjects(): "
1816 << "id=" << id << " m_static_exists=true but "
1817 << "static data doesn't actually exist in "
1818 << PP(obj->m_static_block) << std::endl;
1821 infostream << "Failed to emerge block from which an object to "
1822 << "be deactivated was loaded from. id=" << id << std::endl;
1826 // Tell the object about removal
1827 obj->removingFromEnvironment();
1828 // Deregister in scripting api
1829 m_script->removeObjectReference(obj);
1832 if (obj->environmentDeletes())
1838 m_ao_manager.clear(clear_cb);
1841 static void print_hexdump(std::ostream &o, const std::string &data)
1843 const int linelength = 16;
1844 for(int l=0; ; l++){
1845 int i0 = linelength * l;
1846 bool at_end = false;
1847 int thislinelength = linelength;
1848 if(i0 + thislinelength > (int)data.size()){
1849 thislinelength = data.size() - i0;
1852 for(int di=0; di<linelength; di++){
1855 if(di<thislinelength)
1856 porting::mt_snprintf(buf, sizeof(buf), "%.2x ", data[i]);
1858 porting::mt_snprintf(buf, sizeof(buf), " ");
1862 for(int di=0; di<thislinelength; di++){
1875 ServerActiveObject* ServerEnvironment::createSAO(ActiveObjectType type, v3f pos,
1876 const std::string &data)
1879 case ACTIVEOBJECT_TYPE_LUAENTITY:
1880 return new LuaEntitySAO(this, pos, data);
1882 warningstream << "ServerActiveObject: No factory for type=" << type << std::endl;
1888 Convert stored objects from blocks near the players to active.
1890 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1895 // Ignore if no stored objects (to not set changed flag)
1896 if(block->m_static_objects.m_stored.empty())
1899 verbosestream<<"ServerEnvironment::activateObjects(): "
1900 <<"activating objects of block "<<PP(block->getPos())
1901 <<" ("<<block->m_static_objects.m_stored.size()
1902 <<" objects)"<<std::endl;
1903 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1905 errorstream<<"suspiciously large amount of objects detected: "
1906 <<block->m_static_objects.m_stored.size()<<" in "
1907 <<PP(block->getPos())
1908 <<"; removing all of them."<<std::endl;
1909 // Clear stored list
1910 block->m_static_objects.m_stored.clear();
1911 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1912 MOD_REASON_TOO_MANY_OBJECTS);
1916 // Activate stored objects
1917 std::vector<StaticObject> new_stored;
1918 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1919 // Create an active object from the data
1920 ServerActiveObject *obj = createSAO((ActiveObjectType) s_obj.type, s_obj.pos,
1922 // If couldn't create object, store static data back.
1924 errorstream<<"ServerEnvironment::activateObjects(): "
1925 <<"failed to create active object from static object "
1926 <<"in block "<<PP(s_obj.pos/BS)
1927 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1928 print_hexdump(verbosestream, s_obj.data);
1930 new_stored.push_back(s_obj);
1933 verbosestream<<"ServerEnvironment::activateObjects(): "
1934 <<"activated static object pos="<<PP(s_obj.pos/BS)
1935 <<" type="<<(int)s_obj.type<<std::endl;
1936 // This will also add the object to the active static list
1937 addActiveObjectRaw(obj, false, dtime_s);
1940 // Clear stored list
1941 block->m_static_objects.m_stored.clear();
1942 // Add leftover failed stuff to stored list
1943 for (const StaticObject &s_obj : new_stored) {
1944 block->m_static_objects.m_stored.push_back(s_obj);
1948 Note: Block hasn't really been modified here.
1949 The objects have just been activated and moved from the stored
1950 static list to the active static list.
1951 As such, the block is essentially the same.
1952 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1953 Otherwise there would be a huge amount of unnecessary I/O.
1958 Convert objects that are not standing inside active blocks to static.
1960 If m_known_by_count != 0, active object is not deleted, but static
1961 data is still updated.
1963 If force_delete is set, active object is deleted nevertheless. It
1964 shall only be set so in the destructor of the environment.
1966 If block wasn't generated (not in memory or on disk),
1968 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1970 auto cb_deactivate = [this, _force_delete] (ServerActiveObject *obj, u16 id) {
1971 // force_delete might be overriden per object
1972 bool force_delete = _force_delete;
1974 // Do not deactivate if disallowed
1975 if (!force_delete && !obj->shouldUnload())
1978 // removeRemovedObjects() is responsible for these
1979 if (!force_delete && obj->isGone())
1982 const v3f &objectpos = obj->getBasePosition();
1984 // The block in which the object resides in
1985 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1987 // If object's static data is stored in a deactivated block and object
1988 // is actually located in an active block, re-save to the block in
1989 // which the object is actually located in.
1990 if (!force_delete && obj->m_static_exists &&
1991 !m_active_blocks.contains(obj->m_static_block) &&
1992 m_active_blocks.contains(blockpos_o)) {
1994 // Delete from block where object was located
1995 deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
1997 StaticObject s_obj(obj, objectpos);
1998 // Save to block where object is located
1999 saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
2004 // If block is still active, don't remove
2005 bool still_active = obj->isStaticAllowed() ?
2006 m_active_blocks.contains(blockpos_o) :
2007 getMap().getBlockNoCreateNoEx(blockpos_o) != nullptr;
2008 if (!force_delete && still_active)
2011 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2012 << "deactivating object id=" << id << " on inactive block "
2013 << PP(blockpos_o) << std::endl;
2015 // If known by some client, don't immediately delete.
2016 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2019 Update the static data
2021 if (obj->isStaticAllowed()) {
2022 // Create new static object
2023 StaticObject s_obj(obj, objectpos);
2025 bool stays_in_same_block = false;
2026 bool data_changed = true;
2028 // Check if static data has changed considerably
2029 if (obj->m_static_exists) {
2030 if (obj->m_static_block == blockpos_o)
2031 stays_in_same_block = true;
2033 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2036 const auto n = block->m_static_objects.m_active.find(id);
2037 if (n != block->m_static_objects.m_active.end()) {
2038 StaticObject static_old = n->second;
2040 float save_movem = obj->getMinimumSavedMovement();
2042 if (static_old.data == s_obj.data &&
2043 (static_old.pos - objectpos).getLength() < save_movem)
2044 data_changed = false;
2046 warningstream << "ServerEnvironment::deactivateFarObjects(): "
2047 << "id=" << id << " m_static_exists=true but "
2048 << "static data doesn't actually exist in "
2049 << PP(obj->m_static_block) << std::endl;
2055 While changes are always saved, blocks are only marked as modified
2056 if the object has moved or different staticdata. (see above)
2058 bool shall_be_written = (!stays_in_same_block || data_changed);
2059 u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2061 // Delete old static object
2062 deleteStaticFromBlock(obj, id, reason, false);
2064 // Add to the block where the object is located in
2065 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2066 u16 store_id = pending_delete ? id : 0;
2067 if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2068 force_delete = true;
2071 // Regardless of what happens to the object at this point, deactivate it first.
2072 // This ensures that LuaEntity on_deactivate is always called.
2073 obj->markForDeactivation();
2076 If known by some client, set pending deactivation.
2077 Otherwise delete it immediately.
2079 if (pending_delete && !force_delete) {
2080 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2081 << "object id=" << id << " is known by clients"
2082 << "; not deleting yet" << std::endl;
2087 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2088 << "object id=" << id << " is not known by clients"
2089 << "; deleting" << std::endl;
2091 // Tell the object about removal
2092 obj->removingFromEnvironment();
2093 // Deregister in scripting api
2094 m_script->removeObjectReference(obj);
2096 // Delete active object
2097 if (obj->environmentDeletes())
2103 m_ao_manager.clear(cb_deactivate);
2106 void ServerEnvironment::deleteStaticFromBlock(
2107 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2109 if (!obj->m_static_exists)
2114 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2116 block = m_map->emergeBlock(obj->m_static_block, false);
2119 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2120 << " when deleting static data of object from it. id=" << id << std::endl;
2124 block->m_static_objects.remove(id);
2125 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2126 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2128 obj->m_static_exists = false;
2131 bool ServerEnvironment::saveStaticToBlock(
2132 v3s16 blockpos, u16 store_id,
2133 ServerActiveObject *obj, const StaticObject &s_obj,
2136 MapBlock *block = nullptr;
2138 block = m_map->emergeBlock(blockpos);
2139 } catch (InvalidPositionException &e) {
2140 // Handled via NULL pointer
2141 // NOTE: emergeBlock's failure is usually determined by it
2142 // actually returning NULL
2146 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2147 << " when saving static data of object to it. id=" << store_id << std::endl;
2150 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2151 warningstream << "ServerEnv: Trying to store id = " << store_id
2152 << " statically but block " << PP(blockpos)
2153 << " already contains "
2154 << block->m_static_objects.m_stored.size()
2155 << " objects." << std::endl;
2159 block->m_static_objects.insert(store_id, s_obj);
2160 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2161 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2163 obj->m_static_exists = true;
2164 obj->m_static_block = blockpos;
2169 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2170 const std::string &savedir, const Settings &conf)
2173 if (name == "sqlite3")
2174 return new PlayerDatabaseSQLite3(savedir);
2176 if (name == "dummy")
2177 return new Database_Dummy();
2180 if (name == "postgresql") {
2181 std::string connect_string;
2182 conf.getNoEx("pgsql_player_connection", connect_string);
2183 return new PlayerDatabasePostgreSQL(connect_string);
2188 if (name == "leveldb")
2189 return new PlayerDatabaseLevelDB(savedir);
2192 if (name == "files")
2193 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2195 throw BaseException(std::string("Database backend ") + name + " not supported.");
2198 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2199 const Settings &cmd_args)
2201 std::string migrate_to = cmd_args.get("migrate-players");
2203 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2204 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2205 errorstream << "Cannot read world.mt!" << std::endl;
2209 if (!world_mt.exists("player_backend")) {
2210 errorstream << "Please specify your current backend in world.mt:"
2212 << " player_backend = {files|sqlite3|leveldb|postgresql}"
2217 std::string backend = world_mt.get("player_backend");
2218 if (backend == migrate_to) {
2219 errorstream << "Cannot migrate: new backend is same"
2220 << " as the old one" << std::endl;
2224 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2227 if (backend == "files") {
2228 // Create backup directory
2229 fs::CreateDir(players_backup_path);
2233 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2234 game_params.world_path, world_mt);
2235 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2236 game_params.world_path, world_mt);
2238 std::vector<std::string> player_list;
2239 srcdb->listPlayers(player_list);
2240 for (std::vector<std::string>::const_iterator it = player_list.begin();
2241 it != player_list.end(); ++it) {
2242 actionstream << "Migrating player " << it->c_str() << std::endl;
2243 RemotePlayer player(it->c_str(), NULL);
2244 PlayerSAO playerSAO(NULL, &player, 15000, false);
2246 srcdb->loadPlayer(&player, &playerSAO);
2248 playerSAO.finalize(&player, std::set<std::string>());
2249 player.setPlayerSAO(&playerSAO);
2251 dstdb->savePlayer(&player);
2253 // For files source, move player files to backup dir
2254 if (backend == "files") {
2256 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2257 players_backup_path + DIR_DELIM + (*it));
2261 actionstream << "Successfully migrated " << player_list.size() << " players"
2263 world_mt.set("player_backend", migrate_to);
2264 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2265 errorstream << "Failed to update world.mt!" << std::endl;
2267 actionstream << "world.mt updated" << std::endl;
2269 // When migration is finished from file backend, remove players directory if empty
2270 if (backend == "files") {
2271 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2278 } catch (BaseException &e) {
2279 errorstream << "An error occurred during migration: " << e.what() << std::endl;
2285 AuthDatabase *ServerEnvironment::openAuthDatabase(
2286 const std::string &name, const std::string &savedir, const Settings &conf)
2288 if (name == "sqlite3")
2289 return new AuthDatabaseSQLite3(savedir);
2292 if (name == "postgresql") {
2293 std::string connect_string;
2294 conf.getNoEx("pgsql_auth_connection", connect_string);
2295 return new AuthDatabasePostgreSQL(connect_string);
2299 if (name == "files")
2300 return new AuthDatabaseFiles(savedir);
2303 if (name == "leveldb")
2304 return new AuthDatabaseLevelDB(savedir);
2307 throw BaseException(std::string("Database backend ") + name + " not supported.");
2310 bool ServerEnvironment::migrateAuthDatabase(
2311 const GameParams &game_params, const Settings &cmd_args)
2313 std::string migrate_to = cmd_args.get("migrate-auth");
2315 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2316 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2317 errorstream << "Cannot read world.mt!" << std::endl;
2321 std::string backend = "files";
2322 if (world_mt.exists("auth_backend"))
2323 backend = world_mt.get("auth_backend");
2325 warningstream << "No auth_backend found in world.mt, "
2326 "assuming \"files\"." << std::endl;
2328 if (backend == migrate_to) {
2329 errorstream << "Cannot migrate: new backend is same"
2330 << " as the old one" << std::endl;
2335 const std::unique_ptr<AuthDatabase> srcdb(ServerEnvironment::openAuthDatabase(
2336 backend, game_params.world_path, world_mt));
2337 const std::unique_ptr<AuthDatabase> dstdb(ServerEnvironment::openAuthDatabase(
2338 migrate_to, game_params.world_path, world_mt));
2340 std::vector<std::string> names_list;
2341 srcdb->listNames(names_list);
2342 for (const std::string &name : names_list) {
2343 actionstream << "Migrating auth entry for " << name << std::endl;
2345 AuthEntry authEntry;
2346 success = srcdb->getAuth(name, authEntry);
2347 success = success && dstdb->createAuth(authEntry);
2349 errorstream << "Failed to migrate " << name << std::endl;
2352 actionstream << "Successfully migrated " << names_list.size()
2353 << " auth entries" << std::endl;
2354 world_mt.set("auth_backend", migrate_to);
2355 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2356 errorstream << "Failed to update world.mt!" << std::endl;
2358 actionstream << "world.mt updated" << std::endl;
2360 if (backend == "files") {
2361 // special-case files migration:
2362 // move auth.txt to auth.txt.bak if possible
2363 std::string auth_txt_path =
2364 game_params.world_path + DIR_DELIM + "auth.txt";
2365 std::string auth_bak_path = auth_txt_path + ".bak";
2366 if (!fs::PathExists(auth_bak_path))
2367 if (fs::Rename(auth_txt_path, auth_bak_path))
2368 actionstream << "Renamed auth.txt to auth.txt.bak"
2371 errorstream << "Could not rename auth.txt to "
2372 "auth.txt.bak" << std::endl;
2374 warningstream << "auth.txt.bak already exists, auth.txt "
2375 "not renamed" << std::endl;
2378 } catch (BaseException &e) {
2379 errorstream << "An error occurred during migration: " << e.what()