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
739 ServerEnvironment *m_env;
740 std::vector<std::vector<ActiveABM> *> m_aabms;
742 ABMHandler(std::vector<ABMWithState> &abms,
743 float dtime_s, ServerEnvironment *env,
749 const NodeDefManager *ndef = env->getGameDef()->ndef();
750 for (ABMWithState &abmws : abms) {
751 ActiveBlockModifier *abm = abmws.abm;
752 float trigger_interval = abm->getTriggerInterval();
753 if(trigger_interval < 0.001)
754 trigger_interval = 0.001;
755 float actual_interval = dtime_s;
757 abmws.timer += dtime_s;
758 if(abmws.timer < trigger_interval)
760 abmws.timer -= trigger_interval;
761 actual_interval = trigger_interval;
763 float chance = abm->getTriggerChance();
768 if (abm->getSimpleCatchUp()) {
769 float intervals = actual_interval / trigger_interval;
772 aabm.chance = chance / intervals;
776 aabm.chance = chance;
779 aabm.min_y = abm->getMinY();
780 aabm.max_y = abm->getMaxY();
783 const std::vector<std::string> &required_neighbors_s =
784 abm->getRequiredNeighbors();
785 for (const std::string &required_neighbor_s : required_neighbors_s) {
786 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
788 aabm.check_required_neighbors = !required_neighbors_s.empty();
791 const std::vector<std::string> &contents_s = abm->getTriggerContents();
792 for (const std::string &content_s : contents_s) {
793 std::vector<content_t> ids;
794 ndef->getIds(content_s, ids);
795 for (content_t c : ids) {
796 if (c >= m_aabms.size())
797 m_aabms.resize(c + 256, NULL);
799 m_aabms[c] = new std::vector<ActiveABM>;
800 m_aabms[c]->push_back(aabm);
808 for (auto &aabms : m_aabms)
812 // Find out how many objects the given block and its neighbours contain.
813 // Returns the number of objects in the block, and also in 'wider' the
814 // number of objects in the block and all its neighbours. The latter
815 // may an estimate if any neighbours are unloaded.
816 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
819 u32 wider_unknown_count = 0;
820 for(s16 x=-1; x<=1; x++)
821 for(s16 y=-1; y<=1; y++)
822 for(s16 z=-1; z<=1; z++)
824 MapBlock *block2 = map->getBlockNoCreateNoEx(
825 block->getPos() + v3s16(x,y,z));
827 wider_unknown_count++;
830 wider += block2->m_static_objects.m_active.size()
831 + block2->m_static_objects.m_stored.size();
834 u32 active_object_count = block->m_static_objects.m_active.size();
835 u32 wider_known_count = 3*3*3 - wider_unknown_count;
836 wider += wider_unknown_count * wider / wider_known_count;
837 return active_object_count;
840 void apply(MapBlock *block, int &blocks_scanned, int &abms_run, int &blocks_cached)
842 if(m_aabms.empty() || block->isDummy())
845 // Check the content type cache first
846 // to see whether there are any ABMs
847 // to be run at all for this block.
848 if (block->contents_cached) {
850 bool run_abms = false;
851 for (content_t c : block->contents) {
852 if (c < m_aabms.size() && m_aabms[c]) {
861 block->contents.clear();
865 ServerMap *map = &m_env->getServerMap();
867 u32 active_object_count_wider;
868 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
869 m_env->m_added_objects = 0;
872 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
873 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
874 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
876 const MapNode &n = block->getNodeUnsafe(p0);
877 content_t c = n.getContent();
878 // Cache content types as we go
879 if (!block->contents_cached && !block->do_not_cache_contents) {
880 block->contents.insert(c);
881 if (block->contents.size() > 64) {
882 // Too many different nodes... don't try to cache
883 block->do_not_cache_contents = true;
884 block->contents.clear();
888 if (c >= m_aabms.size() || !m_aabms[c])
891 v3s16 p = p0 + block->getPosRelative();
892 for (ActiveABM &aabm : *m_aabms[c]) {
893 if ((p.Y < aabm.min_y) || (p.Y > aabm.max_y))
896 if (myrand() % aabm.chance != 0)
900 if (aabm.check_required_neighbors) {
902 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
903 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
904 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
909 if (block->isValidPosition(p1)) {
910 // if the neighbor is found on the same map block
911 // get it straight from there
912 const MapNode &n = block->getNodeUnsafe(p1);
915 // otherwise consult the map
916 MapNode n = map->getNode(p1 + block->getPosRelative());
919 if (CONTAINS(aabm.required_neighbors, c))
922 // No required neighbor found
928 // Call all the trigger variations
929 aabm.abm->trigger(m_env, p, n);
930 aabm.abm->trigger(m_env, p, n,
931 active_object_count, active_object_count_wider);
933 // Count surrounding objects again if the abms added any
934 if(m_env->m_added_objects > 0) {
935 active_object_count = countObjects(block, map, active_object_count_wider);
936 m_env->m_added_objects = 0;
940 block->contents_cached = !block->do_not_cache_contents;
944 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
946 // Reset usage timer immediately, otherwise a block that becomes active
947 // again at around the same time as it would normally be unloaded will
948 // get unloaded incorrectly. (I think this still leaves a small possibility
949 // of a race condition between this and server::AsyncRunStep, which only
950 // some kind of synchronisation will fix, but it at least reduces the window
951 // of opportunity for it to break from seconds to nanoseconds)
952 block->resetUsageTimer();
954 // Get time difference
956 u32 stamp = block->getTimestamp();
957 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
958 dtime_s = m_game_time - stamp;
959 dtime_s += additional_dtime;
961 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
962 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
964 // Remove stored static objects if clearObjects was called since block's timestamp
965 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
966 block->m_static_objects.m_stored.clear();
967 // do not set changed flag to avoid unnecessary mapblock writes
970 // Set current time as timestamp
971 block->setTimestampNoChangedFlag(m_game_time);
973 /*infostream<<"ServerEnvironment::activateBlock(): block is "
974 <<dtime_s<<" seconds old."<<std::endl;*/
976 // Activate stored objects
977 activateObjects(block, dtime_s);
979 /* Handle LoadingBlockModifiers */
980 m_lbm_mgr.applyLBMs(this, block, stamp);
983 std::vector<NodeTimer> elapsed_timers =
984 block->m_node_timers.step((float)dtime_s);
985 if (!elapsed_timers.empty()) {
987 for (const NodeTimer &elapsed_timer : elapsed_timers) {
988 n = block->getNodeNoEx(elapsed_timer.position);
989 v3s16 p = elapsed_timer.position + block->getPosRelative();
990 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
991 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
992 elapsed_timer.position));
997 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
999 m_abms.emplace_back(abm);
1002 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1004 m_lbm_mgr.addLBMDef(lbm);
1007 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1009 const NodeDefManager *ndef = m_server->ndef();
1010 MapNode n_old = m_map->getNode(p);
1012 const ContentFeatures &cf_old = ndef->get(n_old);
1015 if (cf_old.has_on_destruct)
1016 m_script->node_on_destruct(p, n_old);
1019 if (!m_map->addNodeWithEvent(p, n))
1022 // Update active VoxelManipulator if a mapgen thread
1023 m_map->updateVManip(p);
1025 // Call post-destructor
1026 if (cf_old.has_after_destruct)
1027 m_script->node_after_destruct(p, n_old);
1029 // Retrieve node content features
1030 // if new node is same as old, reuse old definition to prevent a lookup
1031 const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
1034 if (cf_new.has_on_construct)
1035 m_script->node_on_construct(p, n);
1040 bool ServerEnvironment::removeNode(v3s16 p)
1042 const NodeDefManager *ndef = m_server->ndef();
1043 MapNode n_old = m_map->getNode(p);
1046 if (ndef->get(n_old).has_on_destruct)
1047 m_script->node_on_destruct(p, n_old);
1050 // This is slightly optimized compared to addNodeWithEvent(air)
1051 if (!m_map->removeNodeWithEvent(p))
1054 // Update active VoxelManipulator if a mapgen thread
1055 m_map->updateVManip(p);
1057 // Call post-destructor
1058 if (ndef->get(n_old).has_after_destruct)
1059 m_script->node_after_destruct(p, n_old);
1061 // Air doesn't require constructor
1065 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1067 if (!m_map->addNodeWithEvent(p, n, false))
1070 // Update active VoxelManipulator if a mapgen thread
1071 m_map->updateVManip(p);
1076 u8 ServerEnvironment::findSunlight(v3s16 pos) const
1078 // Directions for neighbouring nodes with specified order
1079 static const v3s16 dirs[] = {
1080 v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1),
1081 v3s16(0, -1, 0), v3s16(0, 1, 0)
1084 const NodeDefManager *ndef = m_server->ndef();
1086 // found_light remembers the highest known sunlight value at pos
1089 struct stack_entry {
1093 std::stack<stack_entry> stack;
1094 stack.push({pos, 0});
1096 std::unordered_map<s64, s8> dists;
1097 dists[MapDatabase::getBlockAsInteger(pos)] = 0;
1099 while (!stack.empty()) {
1100 struct stack_entry e = stack.top();
1103 v3s16 currentPos = e.pos;
1104 s8 dist = e.dist + 1;
1106 for (const v3s16& off : dirs) {
1107 v3s16 neighborPos = currentPos + off;
1108 s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos);
1110 // Do not walk neighborPos multiple times unless the distance to the start
1111 // position is shorter
1112 auto it = dists.find(neighborHash);
1113 if (it != dists.end() && dist >= it->second)
1117 bool is_position_ok;
1118 MapNode node = m_map->getNode(neighborPos, &is_position_ok);
1119 if (!is_position_ok) {
1120 // This happens very rarely because the map at currentPos is loaded
1121 m_map->emergeBlock(neighborPos, false);
1122 node = m_map->getNode(neighborPos, &is_position_ok);
1123 if (!is_position_ok)
1124 continue; // not generated
1127 const ContentFeatures &def = ndef->get(node);
1128 if (!def.sunlight_propagates) {
1129 // Do not test propagation here again
1130 dists[neighborHash] = -1;
1134 // Sunlight could have come from here
1135 dists[neighborHash] = dist;
1136 u8 daylight = node.param1 & 0x0f;
1138 // In the special case where sunlight shines from above and thus
1139 // does not decrease with upwards distance, daylight is always
1140 // bigger than nightlight, which never reaches 15
1141 int possible_finlight = daylight - dist;
1142 if (possible_finlight <= found_light) {
1143 // Light from here cannot make a brighter light at currentPos than
1148 u8 nightlight = node.param1 >> 4;
1149 if (daylight > nightlight) {
1150 // Found a valid daylight
1151 found_light = possible_finlight;
1153 // Sunlight may be darker, so walk the neighbours
1154 stack.push({neighborPos, dist});
1161 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1163 infostream << "ServerEnvironment::clearObjects(): "
1164 << "Removing all active objects" << std::endl;
1165 auto cb_removal = [this] (ServerActiveObject *obj, u16 id) {
1166 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1169 // Delete static object if block is loaded
1170 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1172 // If known by some client, don't delete immediately
1173 if (obj->m_known_by_count > 0) {
1174 obj->markForRemoval();
1178 // Tell the object about removal
1179 obj->removingFromEnvironment();
1180 // Deregister in scripting api
1181 m_script->removeObjectReference(obj);
1183 // Delete active object
1184 if (obj->environmentDeletes())
1190 m_ao_manager.clear(cb_removal);
1192 // Get list of loaded blocks
1193 std::vector<v3s16> loaded_blocks;
1194 infostream << "ServerEnvironment::clearObjects(): "
1195 << "Listing all loaded blocks" << std::endl;
1196 m_map->listAllLoadedBlocks(loaded_blocks);
1197 infostream << "ServerEnvironment::clearObjects(): "
1198 << "Done listing all loaded blocks: "
1199 << loaded_blocks.size()<<std::endl;
1201 // Get list of loadable blocks
1202 std::vector<v3s16> loadable_blocks;
1203 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1204 infostream << "ServerEnvironment::clearObjects(): "
1205 << "Listing all loadable blocks" << std::endl;
1206 m_map->listAllLoadableBlocks(loadable_blocks);
1207 infostream << "ServerEnvironment::clearObjects(): "
1208 << "Done listing all loadable blocks: "
1209 << loadable_blocks.size() << std::endl;
1211 loadable_blocks = loaded_blocks;
1214 actionstream << "ServerEnvironment::clearObjects(): "
1215 << "Now clearing objects in " << loadable_blocks.size()
1216 << " blocks" << std::endl;
1218 // Grab a reference on each loaded block to avoid unloading it
1219 for (v3s16 p : loaded_blocks) {
1220 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1221 assert(block != NULL);
1225 // Remove objects in all loadable blocks
1226 u32 unload_interval = U32_MAX;
1227 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1228 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1229 unload_interval = MYMAX(unload_interval, 1);
1231 u32 report_interval = loadable_blocks.size() / 10;
1232 u32 num_blocks_checked = 0;
1233 u32 num_blocks_cleared = 0;
1234 u32 num_objs_cleared = 0;
1235 for (auto i = loadable_blocks.begin();
1236 i != loadable_blocks.end(); ++i) {
1238 MapBlock *block = m_map->emergeBlock(p, false);
1240 errorstream << "ServerEnvironment::clearObjects(): "
1241 << "Failed to emerge block " << PP(p) << std::endl;
1244 u32 num_stored = block->m_static_objects.m_stored.size();
1245 u32 num_active = block->m_static_objects.m_active.size();
1246 if (num_stored != 0 || num_active != 0) {
1247 block->m_static_objects.m_stored.clear();
1248 block->m_static_objects.m_active.clear();
1249 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1250 MOD_REASON_CLEAR_ALL_OBJECTS);
1251 num_objs_cleared += num_stored + num_active;
1252 num_blocks_cleared++;
1254 num_blocks_checked++;
1256 if (report_interval != 0 &&
1257 num_blocks_checked % report_interval == 0) {
1258 float percent = 100.0 * (float)num_blocks_checked /
1259 loadable_blocks.size();
1260 actionstream << "ServerEnvironment::clearObjects(): "
1261 << "Cleared " << num_objs_cleared << " objects"
1262 << " in " << num_blocks_cleared << " blocks ("
1263 << percent << "%)" << std::endl;
1265 if (num_blocks_checked % unload_interval == 0) {
1266 m_map->unloadUnreferencedBlocks();
1269 m_map->unloadUnreferencedBlocks();
1271 // Drop references that were added above
1272 for (v3s16 p : loaded_blocks) {
1273 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1278 m_last_clear_objects_time = m_game_time;
1280 actionstream << "ServerEnvironment::clearObjects(): "
1281 << "Finished: Cleared " << num_objs_cleared << " objects"
1282 << " in " << num_blocks_cleared << " blocks" << std::endl;
1285 void ServerEnvironment::step(float dtime)
1287 ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
1288 /* Step time of day */
1289 stepTimeOfDay(dtime);
1292 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1293 // really matter that much.
1294 static thread_local const float server_step =
1295 g_settings->getFloat("dedicated_server_step");
1296 m_recommended_send_interval = server_step;
1302 m_game_time_fraction_counter += dtime;
1303 u32 inc_i = (u32)m_game_time_fraction_counter;
1304 m_game_time += inc_i;
1305 m_game_time_fraction_counter -= (float)inc_i;
1312 ScopeProfiler sp(g_profiler, "ServerEnv: move players", SPT_AVG);
1313 for (RemotePlayer *player : m_players) {
1314 // Ignore disconnected players
1315 if (player->getPeerId() == PEER_ID_INEXISTENT)
1319 player->move(dtime, this, 100 * BS);
1324 Manage active block list
1326 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1327 ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
1329 Get player block positions
1331 std::vector<PlayerSAO*> players;
1332 for (RemotePlayer *player: m_players) {
1333 // Ignore disconnected players
1334 if (player->getPeerId() == PEER_ID_INEXISTENT)
1337 PlayerSAO *playersao = player->getPlayerSAO();
1340 players.push_back(playersao);
1344 Update list of active blocks, collecting changes
1346 // use active_object_send_range_blocks since that is max distance
1347 // for active objects sent the client anyway
1348 static thread_local const s16 active_object_range =
1349 g_settings->getS16("active_object_send_range_blocks");
1350 static thread_local const s16 active_block_range =
1351 g_settings->getS16("active_block_range");
1352 std::set<v3s16> blocks_removed;
1353 std::set<v3s16> blocks_added;
1354 m_active_blocks.update(players, active_block_range, active_object_range,
1355 blocks_removed, blocks_added);
1358 Handle removed blocks
1361 // Convert active objects that are no more in active blocks to static
1362 deactivateFarObjects(false);
1364 for (const v3s16 &p: blocks_removed) {
1365 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1369 // Set current time as timestamp (and let it set ChangedFlag)
1370 block->setTimestamp(m_game_time);
1377 for (const v3s16 &p: blocks_added) {
1378 MapBlock *block = m_map->getBlockOrEmerge(p);
1380 m_active_blocks.m_list.erase(p);
1381 m_active_blocks.m_abm_list.erase(p);
1385 activateBlock(block);
1390 Mess around in active blocks
1392 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1393 ScopeProfiler sp(g_profiler, "ServerEnv: Run node timers", SPT_AVG);
1395 float dtime = m_cache_nodetimer_interval;
1397 for (const v3s16 &p: m_active_blocks.m_list) {
1398 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1402 // Reset block usage timer
1403 block->resetUsageTimer();
1405 // Set current time as timestamp
1406 block->setTimestampNoChangedFlag(m_game_time);
1407 // If time has changed much from the one on disk,
1408 // set block to be saved when it is unloaded
1409 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1410 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1411 MOD_REASON_BLOCK_EXPIRED);
1414 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1415 if (!elapsed_timers.empty()) {
1418 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1419 n = block->getNodeNoEx(elapsed_timer.position);
1420 p2 = elapsed_timer.position + block->getPosRelative();
1421 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1422 block->setNodeTimer(NodeTimer(
1423 elapsed_timer.timeout, 0, elapsed_timer.position));
1430 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) {
1431 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1432 TimeTaker timer("modify in active blocks per interval");
1434 // Initialize handling of ActiveBlockModifiers
1435 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1437 int blocks_scanned = 0;
1439 int blocks_cached = 0;
1441 std::vector<v3s16> output(m_active_blocks.m_abm_list.size());
1443 // Shuffle the active blocks so that each block gets an equal chance
1444 // of having its ABMs run.
1445 std::copy(m_active_blocks.m_abm_list.begin(), m_active_blocks.m_abm_list.end(), output.begin());
1446 std::shuffle(output.begin(), output.end(), m_rgen);
1449 // determine the time budget for ABMs
1450 u32 max_time_ms = m_cache_abm_interval * 1000 * m_cache_abm_time_budget;
1451 for (const v3s16 &p : output) {
1452 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1458 // Set current time as timestamp
1459 block->setTimestampNoChangedFlag(m_game_time);
1461 /* Handle ActiveBlockModifiers */
1462 abmhandler.apply(block, blocks_scanned, abms_run, blocks_cached);
1464 u32 time_ms = timer.getTimerTime();
1466 if (time_ms > max_time_ms) {
1467 warningstream << "active block modifiers took "
1468 << time_ms << "ms (processed " << i << " of "
1469 << output.size() << " active blocks)" << std::endl;
1473 g_profiler->avg("ServerEnv: active blocks", m_active_blocks.m_abm_list.size());
1474 g_profiler->avg("ServerEnv: active blocks cached", blocks_cached);
1475 g_profiler->avg("ServerEnv: active blocks scanned for ABMs", blocks_scanned);
1476 g_profiler->avg("ServerEnv: ABMs run", abms_run);
1482 Step script environment (run global on_step())
1484 m_script->environment_Step(dtime);
1490 ScopeProfiler sp(g_profiler, "ServerEnv: Run SAO::step()", SPT_AVG);
1492 // This helps the objects to send data at the same time
1493 bool send_recommended = false;
1494 m_send_recommended_timer += dtime;
1495 if (m_send_recommended_timer > getSendRecommendedInterval()) {
1496 m_send_recommended_timer -= getSendRecommendedInterval();
1497 send_recommended = true;
1500 auto cb_state = [this, dtime, send_recommended] (ServerActiveObject *obj) {
1505 obj->step(dtime, send_recommended);
1506 // Read messages from object
1507 obj->dumpAOMessagesToQueue(m_active_object_messages);
1509 m_ao_manager.step(dtime, cb_state);
1513 Manage active objects
1515 if (m_object_management_interval.step(dtime, 0.5)) {
1516 removeRemovedObjects();
1520 Manage particle spawner expiration
1522 if (m_particle_management_interval.step(dtime, 1.0)) {
1523 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1524 i != m_particle_spawners.end(); ) {
1525 //non expiring spawners
1526 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1532 if (i->second <= 0.f)
1533 m_particle_spawners.erase(i++);
1539 // Send outdated player inventories
1540 for (RemotePlayer *player : m_players) {
1541 if (player->getPeerId() == PEER_ID_INEXISTENT)
1544 PlayerSAO *sao = player->getPlayerSAO();
1545 if (sao && player->inventory.checkModified())
1546 m_server->SendInventory(sao, true);
1549 // Send outdated detached inventories
1550 m_server->sendDetachedInventories(PEER_ID_INEXISTENT, true);
1553 ServerEnvironment::BlockStatus ServerEnvironment::getBlockStatus(v3s16 blockpos)
1555 if (m_active_blocks.contains(blockpos))
1558 const MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1559 if (block && !block->isDummy())
1562 if (m_map->isBlockInQueue(blockpos))
1568 u32 ServerEnvironment::addParticleSpawner(float exptime)
1570 // Timers with lifetime 0 do not expire
1571 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1574 for (;;) { // look for unused particlespawner id
1576 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1577 if (f == m_particle_spawners.end()) {
1578 m_particle_spawners[id] = time;
1585 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1587 u32 id = addParticleSpawner(exptime);
1588 m_particle_spawner_attachments[id] = attached_id;
1589 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1590 obj->attachParticleSpawner(id);
1595 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1597 m_particle_spawners.erase(id);
1598 const auto &it = m_particle_spawner_attachments.find(id);
1599 if (it != m_particle_spawner_attachments.end()) {
1600 u16 obj_id = it->second;
1601 ServerActiveObject *sao = getActiveObject(obj_id);
1602 if (sao != NULL && remove_from_object) {
1603 sao->detachParticleSpawner(id);
1605 m_particle_spawner_attachments.erase(id);
1609 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1611 assert(object); // Pre-condition
1613 u16 id = addActiveObjectRaw(object, true, 0);
1618 Finds out what new objects have been added to
1619 inside a radius around a position
1621 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1623 std::set<u16> ¤t_objects,
1624 std::queue<u16> &added_objects)
1626 f32 radius_f = radius * BS;
1627 f32 player_radius_f = player_radius * BS;
1629 if (player_radius_f < 0.0f)
1630 player_radius_f = 0.0f;
1632 m_ao_manager.getAddedActiveObjectsAroundPos(playersao->getBasePosition(), radius_f,
1633 player_radius_f, current_objects, added_objects);
1637 Finds out what objects have been removed from
1638 inside a radius around a position
1640 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1642 std::set<u16> ¤t_objects,
1643 std::queue<u16> &removed_objects)
1645 f32 radius_f = radius * BS;
1646 f32 player_radius_f = player_radius * BS;
1648 if (player_radius_f < 0)
1649 player_radius_f = 0;
1651 Go through current_objects; object is removed if:
1652 - object is not found in m_active_objects (this is actually an
1653 error condition; objects should be removed only after all clients
1654 have been informed about removal), or
1655 - object is to be removed or deactivated, or
1656 - object is too far away
1658 for (u16 id : current_objects) {
1659 ServerActiveObject *object = getActiveObject(id);
1661 if (object == NULL) {
1662 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1663 << " object in current_objects is NULL" << std::endl;
1664 removed_objects.push(id);
1668 if (object->isGone()) {
1669 removed_objects.push(id);
1673 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1674 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1675 if (distance_f <= player_radius_f || player_radius_f == 0)
1677 } else if (distance_f <= radius_f)
1680 // Object is no longer visible
1681 removed_objects.push(id);
1685 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1686 v3s16 blockpos, bool static_exists, v3s16 static_block)
1688 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1692 for (auto &so_it : block->m_static_objects.m_active) {
1693 // Get the ServerActiveObject counterpart to this StaticObject
1694 ServerActiveObject *sao = m_ao_manager.getActiveObject(so_it.first);
1696 // If this ever happens, there must be some kind of nasty bug.
1697 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1698 "Object from MapBlock::m_static_objects::m_active not found "
1699 "in m_active_objects";
1703 sao->m_static_exists = static_exists;
1704 sao->m_static_block = static_block;
1708 bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest)
1710 if(m_active_object_messages.empty())
1713 *dest = std::move(m_active_object_messages.front());
1714 m_active_object_messages.pop();
1718 void ServerEnvironment::getSelectedActiveObjects(
1719 const core::line3d<f32> &shootline_on_map,
1720 std::vector<PointedThing> &objects)
1722 std::vector<ServerActiveObject *> objs;
1723 getObjectsInsideRadius(objs, shootline_on_map.start,
1724 shootline_on_map.getLength() + 10.0f, nullptr);
1725 const v3f line_vector = shootline_on_map.getVector();
1727 for (auto obj : objs) {
1730 aabb3f selection_box;
1731 if (!obj->getSelectionBox(&selection_box))
1734 v3f pos = obj->getBasePosition();
1736 aabb3f offsetted_box(selection_box.MinEdge + pos,
1737 selection_box.MaxEdge + pos);
1739 v3f current_intersection;
1740 v3s16 current_normal;
1741 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1742 ¤t_intersection, ¤t_normal)) {
1743 objects.emplace_back(
1744 (s16) obj->getId(), current_intersection, current_normal,
1745 (current_intersection - shootline_on_map.start).getLengthSQ());
1751 ************ Private methods *************
1754 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1755 bool set_changed, u32 dtime_s)
1757 if (!m_ao_manager.registerObject(object)) {
1761 // Register reference in scripting api (must be done before post-init)
1762 m_script->addObjectReference(object);
1763 // Post-initialize object
1764 object->addedToEnvironment(dtime_s);
1766 // Add static data to block
1767 if (object->isStaticAllowed()) {
1768 // Add static object to active static list of the block
1769 v3f objectpos = object->getBasePosition();
1770 StaticObject s_obj(object, objectpos);
1771 // Add to the block where the object is located in
1772 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1773 MapBlock *block = m_map->emergeBlock(blockpos);
1775 block->m_static_objects.m_active[object->getId()] = s_obj;
1776 object->m_static_exists = true;
1777 object->m_static_block = blockpos;
1780 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1781 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1783 v3s16 p = floatToInt(objectpos, BS);
1784 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1785 <<"could not emerge block for storing id="<<object->getId()
1786 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1790 return object->getId();
1794 Remove objects that satisfy (isGone() && m_known_by_count==0)
1796 void ServerEnvironment::removeRemovedObjects()
1798 ScopeProfiler sp(g_profiler, "ServerEnvironment::removeRemovedObjects()", SPT_AVG);
1800 auto clear_cb = [this] (ServerActiveObject *obj, u16 id) {
1801 // This shouldn't happen but check it
1803 errorstream << "ServerEnvironment::removeRemovedObjects(): "
1804 << "NULL object found. id=" << id << std::endl;
1809 We will handle objects marked for removal or deactivation
1815 Delete static data from block if removed
1817 if (obj->isPendingRemoval())
1818 deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1820 // If still known by clients, don't actually remove. On some future
1821 // invocation this will be 0, which is when removal will continue.
1822 if(obj->m_known_by_count > 0)
1826 Move static data from active to stored if deactivated
1828 if (!obj->isPendingRemoval() && obj->m_static_exists) {
1829 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1831 const auto i = block->m_static_objects.m_active.find(id);
1832 if (i != block->m_static_objects.m_active.end()) {
1833 block->m_static_objects.m_stored.push_back(i->second);
1834 block->m_static_objects.m_active.erase(id);
1835 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1836 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1838 warningstream << "ServerEnvironment::removeRemovedObjects(): "
1839 << "id=" << id << " m_static_exists=true but "
1840 << "static data doesn't actually exist in "
1841 << PP(obj->m_static_block) << std::endl;
1844 infostream << "Failed to emerge block from which an object to "
1845 << "be deactivated was loaded from. id=" << id << std::endl;
1849 // Tell the object about removal
1850 obj->removingFromEnvironment();
1851 // Deregister in scripting api
1852 m_script->removeObjectReference(obj);
1855 if (obj->environmentDeletes())
1861 m_ao_manager.clear(clear_cb);
1864 static void print_hexdump(std::ostream &o, const std::string &data)
1866 const int linelength = 16;
1867 for(int l=0; ; l++){
1868 int i0 = linelength * l;
1869 bool at_end = false;
1870 int thislinelength = linelength;
1871 if(i0 + thislinelength > (int)data.size()){
1872 thislinelength = data.size() - i0;
1875 for(int di=0; di<linelength; di++){
1878 if(di<thislinelength)
1879 porting::mt_snprintf(buf, sizeof(buf), "%.2x ", data[i]);
1881 porting::mt_snprintf(buf, sizeof(buf), " ");
1885 for(int di=0; di<thislinelength; di++){
1898 ServerActiveObject* ServerEnvironment::createSAO(ActiveObjectType type, v3f pos,
1899 const std::string &data)
1902 case ACTIVEOBJECT_TYPE_LUAENTITY:
1903 return new LuaEntitySAO(this, pos, data);
1905 warningstream << "ServerActiveObject: No factory for type=" << type << std::endl;
1911 Convert stored objects from blocks near the players to active.
1913 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1918 // Ignore if no stored objects (to not set changed flag)
1919 if(block->m_static_objects.m_stored.empty())
1922 verbosestream<<"ServerEnvironment::activateObjects(): "
1923 <<"activating objects of block "<<PP(block->getPos())
1924 <<" ("<<block->m_static_objects.m_stored.size()
1925 <<" objects)"<<std::endl;
1926 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1928 errorstream<<"suspiciously large amount of objects detected: "
1929 <<block->m_static_objects.m_stored.size()<<" in "
1930 <<PP(block->getPos())
1931 <<"; removing all of them."<<std::endl;
1932 // Clear stored list
1933 block->m_static_objects.m_stored.clear();
1934 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1935 MOD_REASON_TOO_MANY_OBJECTS);
1939 // Activate stored objects
1940 std::vector<StaticObject> new_stored;
1941 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1942 // Create an active object from the data
1943 ServerActiveObject *obj = createSAO((ActiveObjectType) s_obj.type, s_obj.pos,
1945 // If couldn't create object, store static data back.
1947 errorstream<<"ServerEnvironment::activateObjects(): "
1948 <<"failed to create active object from static object "
1949 <<"in block "<<PP(s_obj.pos/BS)
1950 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1951 print_hexdump(verbosestream, s_obj.data);
1953 new_stored.push_back(s_obj);
1956 verbosestream<<"ServerEnvironment::activateObjects(): "
1957 <<"activated static object pos="<<PP(s_obj.pos/BS)
1958 <<" type="<<(int)s_obj.type<<std::endl;
1959 // This will also add the object to the active static list
1960 addActiveObjectRaw(obj, false, dtime_s);
1963 // Clear stored list
1964 block->m_static_objects.m_stored.clear();
1965 // Add leftover failed stuff to stored list
1966 for (const StaticObject &s_obj : new_stored) {
1967 block->m_static_objects.m_stored.push_back(s_obj);
1971 Note: Block hasn't really been modified here.
1972 The objects have just been activated and moved from the stored
1973 static list to the active static list.
1974 As such, the block is essentially the same.
1975 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1976 Otherwise there would be a huge amount of unnecessary I/O.
1981 Convert objects that are not standing inside active blocks to static.
1983 If m_known_by_count != 0, active object is not deleted, but static
1984 data is still updated.
1986 If force_delete is set, active object is deleted nevertheless. It
1987 shall only be set so in the destructor of the environment.
1989 If block wasn't generated (not in memory or on disk),
1991 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1993 auto cb_deactivate = [this, _force_delete] (ServerActiveObject *obj, u16 id) {
1994 // force_delete might be overriden per object
1995 bool force_delete = _force_delete;
1997 // Do not deactivate if disallowed
1998 if (!force_delete && !obj->shouldUnload())
2001 // removeRemovedObjects() is responsible for these
2002 if (!force_delete && obj->isGone())
2005 const v3f &objectpos = obj->getBasePosition();
2007 // The block in which the object resides in
2008 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2010 // If object's static data is stored in a deactivated block and object
2011 // is actually located in an active block, re-save to the block in
2012 // which the object is actually located in.
2013 if (!force_delete && obj->m_static_exists &&
2014 !m_active_blocks.contains(obj->m_static_block) &&
2015 m_active_blocks.contains(blockpos_o)) {
2017 // Delete from block where object was located
2018 deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
2020 StaticObject s_obj(obj, objectpos);
2021 // Save to block where object is located
2022 saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
2027 // If block is still active, don't remove
2028 bool still_active = obj->isStaticAllowed() ?
2029 m_active_blocks.contains(blockpos_o) :
2030 getMap().getBlockNoCreateNoEx(blockpos_o) != nullptr;
2031 if (!force_delete && still_active)
2034 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2035 << "deactivating object id=" << id << " on inactive block "
2036 << PP(blockpos_o) << std::endl;
2038 // If known by some client, don't immediately delete.
2039 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2042 Update the static data
2044 if (obj->isStaticAllowed()) {
2045 // Create new static object
2046 StaticObject s_obj(obj, objectpos);
2048 bool stays_in_same_block = false;
2049 bool data_changed = true;
2051 // Check if static data has changed considerably
2052 if (obj->m_static_exists) {
2053 if (obj->m_static_block == blockpos_o)
2054 stays_in_same_block = true;
2056 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2059 const auto n = block->m_static_objects.m_active.find(id);
2060 if (n != block->m_static_objects.m_active.end()) {
2061 StaticObject static_old = n->second;
2063 float save_movem = obj->getMinimumSavedMovement();
2065 if (static_old.data == s_obj.data &&
2066 (static_old.pos - objectpos).getLength() < save_movem)
2067 data_changed = false;
2069 warningstream << "ServerEnvironment::deactivateFarObjects(): "
2070 << "id=" << id << " m_static_exists=true but "
2071 << "static data doesn't actually exist in "
2072 << PP(obj->m_static_block) << std::endl;
2078 While changes are always saved, blocks are only marked as modified
2079 if the object has moved or different staticdata. (see above)
2081 bool shall_be_written = (!stays_in_same_block || data_changed);
2082 u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2084 // Delete old static object
2085 deleteStaticFromBlock(obj, id, reason, false);
2087 // Add to the block where the object is located in
2088 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2089 u16 store_id = pending_delete ? id : 0;
2090 if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2091 force_delete = true;
2094 // Regardless of what happens to the object at this point, deactivate it first.
2095 // This ensures that LuaEntity on_deactivate is always called.
2096 obj->markForDeactivation();
2099 If known by some client, set pending deactivation.
2100 Otherwise delete it immediately.
2102 if (pending_delete && !force_delete) {
2103 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2104 << "object id=" << id << " is known by clients"
2105 << "; not deleting yet" << std::endl;
2110 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2111 << "object id=" << id << " is not known by clients"
2112 << "; deleting" << std::endl;
2114 // Tell the object about removal
2115 obj->removingFromEnvironment();
2116 // Deregister in scripting api
2117 m_script->removeObjectReference(obj);
2119 // Delete active object
2120 if (obj->environmentDeletes())
2126 m_ao_manager.clear(cb_deactivate);
2129 void ServerEnvironment::deleteStaticFromBlock(
2130 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2132 if (!obj->m_static_exists)
2137 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2139 block = m_map->emergeBlock(obj->m_static_block, false);
2142 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2143 << " when deleting static data of object from it. id=" << id << std::endl;
2147 block->m_static_objects.remove(id);
2148 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2149 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2151 obj->m_static_exists = false;
2154 bool ServerEnvironment::saveStaticToBlock(
2155 v3s16 blockpos, u16 store_id,
2156 ServerActiveObject *obj, const StaticObject &s_obj,
2159 MapBlock *block = nullptr;
2161 block = m_map->emergeBlock(blockpos);
2162 } catch (InvalidPositionException &e) {
2163 // Handled via NULL pointer
2164 // NOTE: emergeBlock's failure is usually determined by it
2165 // actually returning NULL
2169 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2170 << " when saving static data of object to it. id=" << store_id << std::endl;
2173 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2174 warningstream << "ServerEnv: Trying to store id = " << store_id
2175 << " statically but block " << PP(blockpos)
2176 << " already contains "
2177 << block->m_static_objects.m_stored.size()
2178 << " objects." << std::endl;
2182 block->m_static_objects.insert(store_id, s_obj);
2183 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2184 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2186 obj->m_static_exists = true;
2187 obj->m_static_block = blockpos;
2192 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2193 const std::string &savedir, const Settings &conf)
2196 if (name == "sqlite3")
2197 return new PlayerDatabaseSQLite3(savedir);
2199 if (name == "dummy")
2200 return new Database_Dummy();
2203 if (name == "postgresql") {
2204 std::string connect_string;
2205 conf.getNoEx("pgsql_player_connection", connect_string);
2206 return new PlayerDatabasePostgreSQL(connect_string);
2211 if (name == "leveldb")
2212 return new PlayerDatabaseLevelDB(savedir);
2215 if (name == "files")
2216 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2218 throw BaseException(std::string("Database backend ") + name + " not supported.");
2221 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2222 const Settings &cmd_args)
2224 std::string migrate_to = cmd_args.get("migrate-players");
2226 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2227 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2228 errorstream << "Cannot read world.mt!" << std::endl;
2232 if (!world_mt.exists("player_backend")) {
2233 errorstream << "Please specify your current backend in world.mt:"
2235 << " player_backend = {files|sqlite3|leveldb|postgresql}"
2240 std::string backend = world_mt.get("player_backend");
2241 if (backend == migrate_to) {
2242 errorstream << "Cannot migrate: new backend is same"
2243 << " as the old one" << std::endl;
2247 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2250 if (backend == "files") {
2251 // Create backup directory
2252 fs::CreateDir(players_backup_path);
2256 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2257 game_params.world_path, world_mt);
2258 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2259 game_params.world_path, world_mt);
2261 std::vector<std::string> player_list;
2262 srcdb->listPlayers(player_list);
2263 for (std::vector<std::string>::const_iterator it = player_list.begin();
2264 it != player_list.end(); ++it) {
2265 actionstream << "Migrating player " << it->c_str() << std::endl;
2266 RemotePlayer player(it->c_str(), NULL);
2267 PlayerSAO playerSAO(NULL, &player, 15000, false);
2269 srcdb->loadPlayer(&player, &playerSAO);
2271 playerSAO.finalize(&player, std::set<std::string>());
2272 player.setPlayerSAO(&playerSAO);
2274 dstdb->savePlayer(&player);
2276 // For files source, move player files to backup dir
2277 if (backend == "files") {
2279 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2280 players_backup_path + DIR_DELIM + (*it));
2284 actionstream << "Successfully migrated " << player_list.size() << " players"
2286 world_mt.set("player_backend", migrate_to);
2287 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2288 errorstream << "Failed to update world.mt!" << std::endl;
2290 actionstream << "world.mt updated" << std::endl;
2292 // When migration is finished from file backend, remove players directory if empty
2293 if (backend == "files") {
2294 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2301 } catch (BaseException &e) {
2302 errorstream << "An error occurred during migration: " << e.what() << std::endl;
2308 AuthDatabase *ServerEnvironment::openAuthDatabase(
2309 const std::string &name, const std::string &savedir, const Settings &conf)
2311 if (name == "sqlite3")
2312 return new AuthDatabaseSQLite3(savedir);
2315 if (name == "postgresql") {
2316 std::string connect_string;
2317 conf.getNoEx("pgsql_auth_connection", connect_string);
2318 return new AuthDatabasePostgreSQL(connect_string);
2322 if (name == "files")
2323 return new AuthDatabaseFiles(savedir);
2326 if (name == "leveldb")
2327 return new AuthDatabaseLevelDB(savedir);
2330 throw BaseException(std::string("Database backend ") + name + " not supported.");
2333 bool ServerEnvironment::migrateAuthDatabase(
2334 const GameParams &game_params, const Settings &cmd_args)
2336 std::string migrate_to = cmd_args.get("migrate-auth");
2338 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2339 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2340 errorstream << "Cannot read world.mt!" << std::endl;
2344 std::string backend = "files";
2345 if (world_mt.exists("auth_backend"))
2346 backend = world_mt.get("auth_backend");
2348 warningstream << "No auth_backend found in world.mt, "
2349 "assuming \"files\"." << std::endl;
2351 if (backend == migrate_to) {
2352 errorstream << "Cannot migrate: new backend is same"
2353 << " as the old one" << std::endl;
2358 const std::unique_ptr<AuthDatabase> srcdb(ServerEnvironment::openAuthDatabase(
2359 backend, game_params.world_path, world_mt));
2360 const std::unique_ptr<AuthDatabase> dstdb(ServerEnvironment::openAuthDatabase(
2361 migrate_to, game_params.world_path, world_mt));
2363 std::vector<std::string> names_list;
2364 srcdb->listNames(names_list);
2365 for (const std::string &name : names_list) {
2366 actionstream << "Migrating auth entry for " << name << std::endl;
2368 AuthEntry authEntry;
2369 success = srcdb->getAuth(name, authEntry);
2370 success = success && dstdb->createAuth(authEntry);
2372 errorstream << "Failed to migrate " << name << std::endl;
2375 actionstream << "Successfully migrated " << names_list.size()
2376 << " auth entries" << std::endl;
2377 world_mt.set("auth_backend", migrate_to);
2378 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2379 errorstream << "Failed to update world.mt!" << std::endl;
2381 actionstream << "world.mt updated" << std::endl;
2383 if (backend == "files") {
2384 // special-case files migration:
2385 // move auth.txt to auth.txt.bak if possible
2386 std::string auth_txt_path =
2387 game_params.world_path + DIR_DELIM + "auth.txt";
2388 std::string auth_bak_path = auth_txt_path + ".bak";
2389 if (!fs::PathExists(auth_bak_path))
2390 if (fs::Rename(auth_txt_path, auth_bak_path))
2391 actionstream << "Renamed auth.txt to auth.txt.bak"
2394 errorstream << "Could not rename auth.txt to "
2395 "auth.txt.bak" << std::endl;
2397 warningstream << "auth.txt.bak already exists, auth.txt "
2398 "not renamed" << std::endl;
2401 } catch (BaseException &e) {
2402 errorstream << "An error occurred during migration: " << e.what()