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,
253 const u32 stamp, const float dtime_s)
255 // Precondition, we need m_lbm_lookup to be initialized
256 FATAL_ERROR_IF(!m_query_mode,
257 "attempted to query on non fully set up LBMManager");
258 v3s16 pos_of_block = block->getPosRelative();
262 auto it = getLBMsIntroducedAfter(stamp);
263 for (; it != m_lbm_lookup.end(); ++it) {
264 // Cache previous version to speedup lookup which has a very high performance
265 // penalty on each call
266 content_t previous_c = CONTENT_IGNORE;
267 const std::vector<LoadingBlockModifierDef *> *lbm_list = nullptr;
269 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
270 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
271 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) {
272 n = block->getNodeNoCheck(pos);
275 // If content_t are not matching perform an LBM lookup
276 if (previous_c != c) {
277 lbm_list = it->second.lookup(c);
283 for (auto lbmdef : *lbm_list) {
284 lbmdef->trigger(env, pos + pos_of_block, n, dtime_s);
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);
381 m_list = std::move(newlist);
388 // Random device to seed pseudo random generators.
389 static std::random_device seed;
391 ServerEnvironment::ServerEnvironment(ServerMap *map,
392 ServerScripting *script_iface, Server *server,
393 const std::string &path_world, MetricsBackend *mb):
396 m_script(script_iface),
398 m_path_world(path_world),
401 m_step_time_counter = mb->addCounter(
402 "minetest_env_step_time", "Time spent in environment step (in microseconds)");
404 m_active_block_gauge = mb->addGauge(
405 "minetest_env_active_blocks", "Number of active blocks");
407 m_active_object_gauge = mb->addGauge(
408 "minetest_env_active_objects", "Number of active objects");
411 void ServerEnvironment::init()
413 // Determine which database backend to use
414 std::string conf_path = m_path_world + DIR_DELIM + "world.mt";
417 std::string player_backend_name = "sqlite3";
418 std::string auth_backend_name = "sqlite3";
420 bool succeeded = conf.readConfigFile(conf_path.c_str());
422 // If we open world.mt read the backend configurations.
424 // Check that the world's blocksize matches the compiled MAP_BLOCKSIZE
426 conf.getU16NoEx("blocksize", blocksize);
427 if (blocksize != MAP_BLOCKSIZE) {
428 throw BaseException(std::string("The map's blocksize is not supported."));
431 // Read those values before setting defaults
432 bool player_backend_exists = conf.exists("player_backend");
433 bool auth_backend_exists = conf.exists("auth_backend");
435 // player backend is not set, assume it's legacy file backend.
436 if (!player_backend_exists) {
437 // fall back to files
438 conf.set("player_backend", "files");
439 player_backend_name = "files";
441 if (!conf.updateConfigFile(conf_path.c_str())) {
442 errorstream << "ServerEnvironment::ServerEnvironment(): "
443 << "Failed to update world.mt!" << std::endl;
446 conf.getNoEx("player_backend", player_backend_name);
449 // auth backend is not set, assume it's legacy file backend.
450 if (!auth_backend_exists) {
451 conf.set("auth_backend", "files");
452 auth_backend_name = "files";
454 if (!conf.updateConfigFile(conf_path.c_str())) {
455 errorstream << "ServerEnvironment::ServerEnvironment(): "
456 << "Failed to update world.mt!" << std::endl;
459 conf.getNoEx("auth_backend", auth_backend_name);
463 if (player_backend_name == "files") {
464 warningstream << "/!\\ You are using old player file backend. "
465 << "This backend is deprecated and will be removed in a future release /!\\"
466 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
467 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
470 if (auth_backend_name == "files") {
471 warningstream << "/!\\ You are using old auth file backend. "
472 << "This backend is deprecated and will be removed in a future release /!\\"
473 << std::endl << "Switching to SQLite3 is advised, "
474 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
477 m_player_database = openPlayerDatabase(player_backend_name, m_path_world, conf);
478 m_auth_database = openAuthDatabase(auth_backend_name, m_path_world, conf);
481 ServerEnvironment::~ServerEnvironment()
483 // Clear active block list.
484 // This makes the next one delete all active objects.
485 m_active_blocks.clear();
487 // Convert all objects to static and delete the active objects
488 deactivateFarObjects(true);
494 // Delete ActiveBlockModifiers
495 for (ABMWithState &m_abm : m_abms) {
499 // Deallocate players
500 for (RemotePlayer *m_player : m_players) {
504 delete m_player_database;
505 delete m_auth_database;
508 Map & ServerEnvironment::getMap()
513 ServerMap & ServerEnvironment::getServerMap()
518 RemotePlayer *ServerEnvironment::getPlayer(const session_t peer_id)
520 for (RemotePlayer *player : m_players) {
521 if (player->getPeerId() == peer_id)
527 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
529 for (RemotePlayer *player : m_players) {
530 if (strcmp(player->getName(), name) == 0)
536 void ServerEnvironment::addPlayer(RemotePlayer *player)
539 Check that peer_ids are unique.
540 Also check that names are unique.
541 Exception: there can be multiple players with peer_id=0
543 // If peer id is non-zero, it has to be unique.
544 if (player->getPeerId() != PEER_ID_INEXISTENT)
545 FATAL_ERROR_IF(getPlayer(player->getPeerId()) != NULL, "Peer id not unique");
546 // Name has to be unique.
547 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
549 m_players.push_back(player);
552 void ServerEnvironment::removePlayer(RemotePlayer *player)
554 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
555 it != m_players.end(); ++it) {
556 if ((*it) == player) {
564 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
566 return m_player_database->removePlayer(name);
569 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
570 const std::string &str_reason, bool reconnect)
572 for (RemotePlayer *player : m_players)
573 m_server->DenyAccess(player->getPeerId(), reason, str_reason, reconnect);
576 void ServerEnvironment::saveLoadedPlayers(bool force)
578 for (RemotePlayer *player : m_players) {
579 if (force || player->checkModified() || (player->getPlayerSAO() &&
580 player->getPlayerSAO()->getMeta().isModified())) {
582 m_player_database->savePlayer(player);
583 } catch (DatabaseException &e) {
584 errorstream << "Failed to save player " << player->getName() << " exception: "
585 << e.what() << std::endl;
592 void ServerEnvironment::savePlayer(RemotePlayer *player)
595 m_player_database->savePlayer(player);
596 } catch (DatabaseException &e) {
597 errorstream << "Failed to save player " << player->getName() << " exception: "
598 << e.what() << std::endl;
603 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
604 session_t peer_id, bool is_singleplayer)
606 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
607 // Create player if it doesn't exist
608 if (!m_player_database->loadPlayer(player, playersao)) {
610 // Set player position
611 infostream << "Server: Finding spawn place for player \""
612 << player->getName() << "\"" << std::endl;
613 playersao->setBasePosition(m_server->findSpawnPos());
615 // Make sure the player is saved
616 player->setModified(true);
618 // If the player exists, ensure that they respawn inside legal bounds
619 // This fixes an assert crash when the player can't be added
620 // to the environment
621 if (objectpos_over_limit(playersao->getBasePosition())) {
622 actionstream << "Respawn position for player \""
623 << player->getName() << "\" outside limits, resetting" << std::endl;
624 playersao->setBasePosition(m_server->findSpawnPos());
628 // Add player to environment
631 /* Clean up old HUD elements from previous sessions */
634 /* Add object to environment */
635 addActiveObject(playersao);
637 // Update active blocks quickly for a bit so objects in those blocks appear on the client
638 m_fast_active_block_divider = 10;
643 void ServerEnvironment::saveMeta()
648 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
650 // Open file and serialize
651 std::ostringstream ss(std::ios_base::binary);
653 Settings args("EnvArgsEnd");
654 args.setU64("game_time", m_game_time);
655 args.setU64("time_of_day", getTimeOfDay());
656 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
657 args.setU64("lbm_introduction_times_version", 1);
658 args.set("lbm_introduction_times",
659 m_lbm_mgr.createIntroductionTimesString());
660 args.setU64("day_count", m_day_count);
663 if(!fs::safeWriteToFile(path, ss.str()))
665 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
667 throw SerializationError("Couldn't save env meta");
671 void ServerEnvironment::loadMeta()
673 SANITY_CHECK(!m_meta_loaded);
674 m_meta_loaded = true;
676 // If file doesn't exist, load default environment metadata
677 if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
678 infostream << "ServerEnvironment: Loading default environment metadata"
684 infostream << "ServerEnvironment: Loading environment metadata" << std::endl;
686 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
688 // Open file and deserialize
689 std::ifstream is(path.c_str(), std::ios_base::binary);
691 infostream << "ServerEnvironment::loadMeta(): Failed to open "
692 << path << std::endl;
693 throw SerializationError("Couldn't load env meta");
696 Settings args("EnvArgsEnd");
698 if (!args.parseConfigLines(is)) {
699 throw SerializationError("ServerEnvironment::loadMeta(): "
700 "EnvArgsEnd not found!");
704 m_game_time = args.getU64("game_time");
705 } catch (SettingNotFoundException &e) {
706 // Getting this is crucial, otherwise timestamps are useless
707 throw SerializationError("Couldn't load env meta game_time");
710 setTimeOfDay(args.exists("time_of_day") ?
711 // set day to early morning by default
712 args.getU64("time_of_day") : 5250);
714 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
715 // If missing, do as if clearObjects was never called
716 args.getU64("last_clear_objects_time") : 0;
718 std::string lbm_introduction_times;
720 u64 ver = args.getU64("lbm_introduction_times_version");
722 lbm_introduction_times = args.get("lbm_introduction_times");
724 infostream << "ServerEnvironment::loadMeta(): Non-supported"
725 << " introduction time version " << ver << std::endl;
727 } catch (SettingNotFoundException &e) {
728 // No problem, this is expected. Just continue with an empty string
730 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
732 m_day_count = args.exists("day_count") ?
733 args.getU64("day_count") : 0;
737 * called if env_meta.txt doesn't exist (e.g. new world)
739 void ServerEnvironment::loadDefaultMeta()
741 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
746 ActiveBlockModifier *abm;
748 std::vector<content_t> required_neighbors;
749 bool check_required_neighbors; // false if required_neighbors is known to be empty
757 ServerEnvironment *m_env;
758 std::vector<std::vector<ActiveABM> *> m_aabms;
760 ABMHandler(std::vector<ABMWithState> &abms,
761 float dtime_s, ServerEnvironment *env,
767 const NodeDefManager *ndef = env->getGameDef()->ndef();
768 for (ABMWithState &abmws : abms) {
769 ActiveBlockModifier *abm = abmws.abm;
770 float trigger_interval = abm->getTriggerInterval();
771 if(trigger_interval < 0.001)
772 trigger_interval = 0.001;
773 float actual_interval = dtime_s;
775 abmws.timer += dtime_s;
776 if(abmws.timer < trigger_interval)
778 abmws.timer -= trigger_interval;
779 actual_interval = trigger_interval;
781 float chance = abm->getTriggerChance();
786 if (abm->getSimpleCatchUp()) {
787 float intervals = actual_interval / trigger_interval;
790 aabm.chance = chance / intervals;
791 if (aabm.chance == 0)
794 aabm.chance = chance;
797 aabm.min_y = abm->getMinY();
798 aabm.max_y = abm->getMaxY();
801 const std::vector<std::string> &required_neighbors_s =
802 abm->getRequiredNeighbors();
803 for (const std::string &required_neighbor_s : required_neighbors_s) {
804 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
806 aabm.check_required_neighbors = !required_neighbors_s.empty();
809 const std::vector<std::string> &contents_s = abm->getTriggerContents();
810 for (const std::string &content_s : contents_s) {
811 std::vector<content_t> ids;
812 ndef->getIds(content_s, ids);
813 for (content_t c : ids) {
814 if (c >= m_aabms.size())
815 m_aabms.resize(c + 256, NULL);
817 m_aabms[c] = new std::vector<ActiveABM>;
818 m_aabms[c]->push_back(aabm);
826 for (auto &aabms : m_aabms)
830 // Find out how many objects the given block and its neighbors contain.
831 // Returns the number of objects in the block, and also in 'wider' the
832 // number of objects in the block and all its neighbors. The latter
833 // may an estimate if any neighbors are unloaded.
834 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
837 u32 wider_unknown_count = 0;
838 for(s16 x=-1; x<=1; x++)
839 for(s16 y=-1; y<=1; y++)
840 for(s16 z=-1; z<=1; z++)
842 MapBlock *block2 = map->getBlockNoCreateNoEx(
843 block->getPos() + v3s16(x,y,z));
845 wider_unknown_count++;
848 wider += block2->m_static_objects.size();
851 u32 active_object_count = block->m_static_objects.getActiveSize();
852 u32 wider_known_count = 3 * 3 * 3 - wider_unknown_count;
853 wider += wider_unknown_count * wider / wider_known_count;
854 return active_object_count;
856 void apply(MapBlock *block, int &blocks_scanned, int &abms_run, int &blocks_cached)
861 // Check the content type cache first
862 // to see whether there are any ABMs
863 // to be run at all for this block.
864 if (block->contents_cached) {
866 bool run_abms = false;
867 for (content_t c : block->contents) {
868 if (c < m_aabms.size() && m_aabms[c]) {
877 block->contents.clear();
881 ServerMap *map = &m_env->getServerMap();
883 u32 active_object_count_wider;
884 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
885 m_env->m_added_objects = 0;
888 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
889 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
890 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
892 MapNode n = block->getNodeNoCheck(p0);
893 content_t c = n.getContent();
894 // Cache content types as we go
895 if (!block->contents_cached && !block->do_not_cache_contents) {
896 block->contents.insert(c);
897 if (block->contents.size() > 64) {
898 // Too many different nodes... don't try to cache
899 block->do_not_cache_contents = true;
900 block->contents.clear();
904 if (c >= m_aabms.size() || !m_aabms[c])
907 v3s16 p = p0 + block->getPosRelative();
908 for (ActiveABM &aabm : *m_aabms[c]) {
909 if ((p.Y < aabm.min_y) || (p.Y > aabm.max_y))
912 if (myrand() % aabm.chance != 0)
916 if (aabm.check_required_neighbors) {
918 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
919 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
920 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
925 if (block->isValidPosition(p1)) {
926 // if the neighbor is found on the same map block
927 // get it straight from there
928 const MapNode &n = block->getNodeNoCheck(p1);
931 // otherwise consult the map
932 MapNode n = map->getNode(p1 + block->getPosRelative());
935 if (CONTAINS(aabm.required_neighbors, c))
938 // No required neighbor found
944 // Call all the trigger variations
945 aabm.abm->trigger(m_env, p, n);
946 aabm.abm->trigger(m_env, p, n,
947 active_object_count, active_object_count_wider);
949 // Count surrounding objects again if the abms added any
950 if(m_env->m_added_objects > 0) {
951 active_object_count = countObjects(block, map, active_object_count_wider);
952 m_env->m_added_objects = 0;
955 // Update and check node after possible modification
956 n = block->getNodeNoCheck(p0);
957 if (n.getContent() != c)
961 block->contents_cached = !block->do_not_cache_contents;
965 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
967 // Reset usage timer immediately, otherwise a block that becomes active
968 // again at around the same time as it would normally be unloaded will
969 // get unloaded incorrectly. (I think this still leaves a small possibility
970 // of a race condition between this and server::AsyncRunStep, which only
971 // some kind of synchronisation will fix, but it at least reduces the window
972 // of opportunity for it to break from seconds to nanoseconds)
973 block->resetUsageTimer();
975 // Get time difference
977 u32 stamp = block->getTimestamp();
978 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
979 dtime_s = m_game_time - stamp;
980 dtime_s += additional_dtime;
982 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
983 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
985 // Remove stored static objects if clearObjects was called since block's timestamp
986 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
987 block->m_static_objects.clearStored();
988 // do not set changed flag to avoid unnecessary mapblock writes
991 // Set current time as timestamp
992 block->setTimestampNoChangedFlag(m_game_time);
994 /*infostream<<"ServerEnvironment::activateBlock(): block is "
995 <<dtime_s<<" seconds old."<<std::endl;*/
997 // Activate stored objects
998 activateObjects(block, dtime_s);
1000 /* Handle LoadingBlockModifiers */
1001 m_lbm_mgr.applyLBMs(this, block, stamp, (float)dtime_s);
1004 block->step((float)dtime_s, [&](v3s16 p, MapNode n, f32 d) -> bool {
1005 return m_script->node_on_timer(p, n, d);
1009 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
1011 m_abms.emplace_back(abm);
1014 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1016 m_lbm_mgr.addLBMDef(lbm);
1019 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1021 const NodeDefManager *ndef = m_server->ndef();
1022 MapNode n_old = m_map->getNode(p);
1024 const ContentFeatures &cf_old = ndef->get(n_old);
1027 if (cf_old.has_on_destruct)
1028 m_script->node_on_destruct(p, n_old);
1031 if (!m_map->addNodeWithEvent(p, n))
1034 // Update active VoxelManipulator if a mapgen thread
1035 m_map->updateVManip(p);
1037 // Call post-destructor
1038 if (cf_old.has_after_destruct)
1039 m_script->node_after_destruct(p, n_old);
1041 // Retrieve node content features
1042 // if new node is same as old, reuse old definition to prevent a lookup
1043 const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
1046 if (cf_new.has_on_construct)
1047 m_script->node_on_construct(p, n);
1052 bool ServerEnvironment::removeNode(v3s16 p)
1054 const NodeDefManager *ndef = m_server->ndef();
1055 MapNode n_old = m_map->getNode(p);
1058 if (ndef->get(n_old).has_on_destruct)
1059 m_script->node_on_destruct(p, n_old);
1062 // This is slightly optimized compared to addNodeWithEvent(air)
1063 if (!m_map->removeNodeWithEvent(p))
1066 // Update active VoxelManipulator if a mapgen thread
1067 m_map->updateVManip(p);
1069 // Call post-destructor
1070 if (ndef->get(n_old).has_after_destruct)
1071 m_script->node_after_destruct(p, n_old);
1073 // Air doesn't require constructor
1077 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1079 if (!m_map->addNodeWithEvent(p, n, false))
1082 // Update active VoxelManipulator if a mapgen thread
1083 m_map->updateVManip(p);
1088 u8 ServerEnvironment::findSunlight(v3s16 pos) const
1090 // Directions for neighboring nodes with specified order
1091 static const v3s16 dirs[] = {
1092 v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1),
1093 v3s16(0, -1, 0), v3s16(0, 1, 0)
1096 const NodeDefManager *ndef = m_server->ndef();
1098 // found_light remembers the highest known sunlight value at pos
1101 struct stack_entry {
1105 std::stack<stack_entry> stack;
1106 stack.push({pos, 0});
1108 std::unordered_map<s64, s8> dists;
1109 dists[MapDatabase::getBlockAsInteger(pos)] = 0;
1111 while (!stack.empty()) {
1112 struct stack_entry e = stack.top();
1115 v3s16 currentPos = e.pos;
1116 s8 dist = e.dist + 1;
1118 for (const v3s16& off : dirs) {
1119 v3s16 neighborPos = currentPos + off;
1120 s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos);
1122 // Do not walk neighborPos multiple times unless the distance to the start
1123 // position is shorter
1124 auto it = dists.find(neighborHash);
1125 if (it != dists.end() && dist >= it->second)
1129 bool is_position_ok;
1130 MapNode node = m_map->getNode(neighborPos, &is_position_ok);
1131 if (!is_position_ok) {
1132 // This happens very rarely because the map at currentPos is loaded
1133 m_map->emergeBlock(neighborPos, false);
1134 node = m_map->getNode(neighborPos, &is_position_ok);
1135 if (!is_position_ok)
1136 continue; // not generated
1139 const ContentFeatures &def = ndef->get(node);
1140 if (!def.sunlight_propagates) {
1141 // Do not test propagation here again
1142 dists[neighborHash] = -1;
1146 // Sunlight could have come from here
1147 dists[neighborHash] = dist;
1148 u8 daylight = node.param1 & 0x0f;
1150 // In the special case where sunlight shines from above and thus
1151 // does not decrease with upwards distance, daylight is always
1152 // bigger than nightlight, which never reaches 15
1153 int possible_finlight = daylight - dist;
1154 if (possible_finlight <= found_light) {
1155 // Light from here cannot make a brighter light at currentPos than
1160 u8 nightlight = node.param1 >> 4;
1161 if (daylight > nightlight) {
1162 // Found a valid daylight
1163 found_light = possible_finlight;
1165 // Sunlight may be darker, so walk the neighbors
1166 stack.push({neighborPos, dist});
1173 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1175 infostream << "ServerEnvironment::clearObjects(): "
1176 << "Removing all active objects" << std::endl;
1177 auto cb_removal = [this] (ServerActiveObject *obj, u16 id) {
1178 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1181 // Delete static object if block is loaded
1182 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1184 // If known by some client, don't delete immediately
1185 if (obj->m_known_by_count > 0) {
1186 obj->markForRemoval();
1190 // Tell the object about removal
1191 obj->removingFromEnvironment();
1192 // Deregister in scripting api
1193 m_script->removeObjectReference(obj);
1195 // Delete active object
1196 if (obj->environmentDeletes())
1202 m_ao_manager.clear(cb_removal);
1204 // Get list of loaded blocks
1205 std::vector<v3s16> loaded_blocks;
1206 infostream << "ServerEnvironment::clearObjects(): "
1207 << "Listing all loaded blocks" << std::endl;
1208 m_map->listAllLoadedBlocks(loaded_blocks);
1209 infostream << "ServerEnvironment::clearObjects(): "
1210 << "Done listing all loaded blocks: "
1211 << loaded_blocks.size()<<std::endl;
1213 // Get list of loadable blocks
1214 std::vector<v3s16> loadable_blocks;
1215 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1216 infostream << "ServerEnvironment::clearObjects(): "
1217 << "Listing all loadable blocks" << std::endl;
1218 m_map->listAllLoadableBlocks(loadable_blocks);
1219 infostream << "ServerEnvironment::clearObjects(): "
1220 << "Done listing all loadable blocks: "
1221 << loadable_blocks.size() << std::endl;
1223 loadable_blocks = loaded_blocks;
1226 actionstream << "ServerEnvironment::clearObjects(): "
1227 << "Now clearing objects in " << loadable_blocks.size()
1228 << " blocks" << std::endl;
1230 // Grab a reference on each loaded block to avoid unloading it
1231 for (v3s16 p : loaded_blocks) {
1232 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1233 assert(block != NULL);
1237 // Remove objects in all loadable blocks
1238 u32 unload_interval = U32_MAX;
1239 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1240 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1241 unload_interval = MYMAX(unload_interval, 1);
1243 u32 report_interval = loadable_blocks.size() / 10;
1244 u32 num_blocks_checked = 0;
1245 u32 num_blocks_cleared = 0;
1246 u32 num_objs_cleared = 0;
1247 for (auto i = loadable_blocks.begin();
1248 i != loadable_blocks.end(); ++i) {
1250 MapBlock *block = m_map->emergeBlock(p, false);
1252 errorstream << "ServerEnvironment::clearObjects(): "
1253 << "Failed to emerge block " << PP(p) << std::endl;
1257 u32 num_cleared = block->clearObjects();
1258 if (num_cleared > 0) {
1259 num_objs_cleared += num_cleared;
1260 num_blocks_cleared++;
1262 num_blocks_checked++;
1264 if (report_interval != 0 &&
1265 num_blocks_checked % report_interval == 0) {
1266 float percent = 100.0 * (float)num_blocks_checked /
1267 loadable_blocks.size();
1268 actionstream << "ServerEnvironment::clearObjects(): "
1269 << "Cleared " << num_objs_cleared << " objects"
1270 << " in " << num_blocks_cleared << " blocks ("
1271 << percent << "%)" << std::endl;
1273 if (num_blocks_checked % unload_interval == 0) {
1274 m_map->unloadUnreferencedBlocks();
1277 m_map->unloadUnreferencedBlocks();
1279 // Drop references that were added above
1280 for (v3s16 p : loaded_blocks) {
1281 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1286 m_last_clear_objects_time = m_game_time;
1288 actionstream << "ServerEnvironment::clearObjects(): "
1289 << "Finished: Cleared " << num_objs_cleared << " objects"
1290 << " in " << num_blocks_cleared << " blocks" << std::endl;
1293 void ServerEnvironment::step(float dtime)
1295 ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
1296 const auto start_time = porting::getTimeUs();
1298 /* Step time of day */
1299 stepTimeOfDay(dtime);
1302 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1303 // really matter that much.
1304 static thread_local const float server_step =
1305 g_settings->getFloat("dedicated_server_step");
1306 m_recommended_send_interval = server_step;
1312 m_game_time_fraction_counter += dtime;
1313 u32 inc_i = (u32)m_game_time_fraction_counter;
1314 m_game_time += inc_i;
1315 m_game_time_fraction_counter -= (float)inc_i;
1322 ScopeProfiler sp(g_profiler, "ServerEnv: move players", SPT_AVG);
1323 for (RemotePlayer *player : m_players) {
1324 // Ignore disconnected players
1325 if (player->getPeerId() == PEER_ID_INEXISTENT)
1329 player->move(dtime, this, 100 * BS);
1334 Manage active block list
1336 if (m_active_blocks_mgmt_interval.step(dtime, m_cache_active_block_mgmt_interval / m_fast_active_block_divider)) {
1337 ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
1340 Get player block positions
1342 std::vector<PlayerSAO*> players;
1343 players.reserve(m_players.size());
1344 for (RemotePlayer *player : m_players) {
1345 // Ignore disconnected players
1346 if (player->getPeerId() == PEER_ID_INEXISTENT)
1349 PlayerSAO *playersao = player->getPlayerSAO();
1352 players.push_back(playersao);
1356 Update list of active blocks, collecting changes
1358 // use active_object_send_range_blocks since that is max distance
1359 // for active objects sent the client anyway
1360 static thread_local const s16 active_object_range =
1361 g_settings->getS16("active_object_send_range_blocks");
1362 static thread_local const s16 active_block_range =
1363 g_settings->getS16("active_block_range");
1364 std::set<v3s16> blocks_removed;
1365 std::set<v3s16> blocks_added;
1366 m_active_blocks.update(players, active_block_range, active_object_range,
1367 blocks_removed, blocks_added);
1370 Handle removed blocks
1373 // Convert active objects that are no more in active blocks to static
1374 deactivateFarObjects(false);
1376 for (const v3s16 &p: blocks_removed) {
1377 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1381 // Set current time as timestamp (and let it set ChangedFlag)
1382 block->setTimestamp(m_game_time);
1389 for (const v3s16 &p: blocks_added) {
1390 MapBlock *block = m_map->getBlockOrEmerge(p);
1392 // TODO: The blocks removed here will only be picked up again
1393 // on the next cycle. To minimize the latency of objects being
1394 // activated we could remember the blocks pending activating
1395 // and activate them instantly as soon as they're loaded.
1396 m_active_blocks.remove(p);
1400 activateBlock(block);
1403 // Some blocks may be removed again by the code above so do this here
1404 m_active_block_gauge->set(m_active_blocks.size());
1406 if (m_fast_active_block_divider > 1)
1407 --m_fast_active_block_divider;
1411 Mess around in active blocks
1413 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1414 ScopeProfiler sp(g_profiler, "ServerEnv: Run node timers", SPT_AVG);
1416 float dtime = m_cache_nodetimer_interval;
1418 for (const v3s16 &p: m_active_blocks.m_list) {
1419 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1423 // Reset block usage timer
1424 block->resetUsageTimer();
1426 // Set current time as timestamp
1427 block->setTimestampNoChangedFlag(m_game_time);
1428 // If time has changed much from the one on disk,
1429 // set block to be saved when it is unloaded
1430 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1431 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1432 MOD_REASON_BLOCK_EXPIRED);
1435 block->step(dtime, [&](v3s16 p, MapNode n, f32 d) -> bool {
1436 return m_script->node_on_timer(p, n, d);
1441 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) {
1442 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1443 TimeTaker timer("modify in active blocks per interval");
1445 // Shuffle to prevent persistent artifacts of ordering
1446 std::shuffle(m_abms.begin(), m_abms.end(), m_rgen);
1448 // Initialize handling of ActiveBlockModifiers
1449 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1451 int blocks_scanned = 0;
1453 int blocks_cached = 0;
1455 std::vector<v3s16> output(m_active_blocks.m_abm_list.size());
1457 // Shuffle the active blocks so that each block gets an equal chance
1458 // of having its ABMs run.
1459 std::copy(m_active_blocks.m_abm_list.begin(), m_active_blocks.m_abm_list.end(), output.begin());
1460 std::shuffle(output.begin(), output.end(), m_rgen);
1463 // determine the time budget for ABMs
1464 u32 max_time_ms = m_cache_abm_interval * 1000 * m_cache_abm_time_budget;
1465 for (const v3s16 &p : output) {
1466 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1472 // Set current time as timestamp
1473 block->setTimestampNoChangedFlag(m_game_time);
1475 /* Handle ActiveBlockModifiers */
1476 abmhandler.apply(block, blocks_scanned, abms_run, blocks_cached);
1478 u32 time_ms = timer.getTimerTime();
1480 if (time_ms > max_time_ms) {
1481 warningstream << "active block modifiers took "
1482 << time_ms << "ms (processed " << i << " of "
1483 << output.size() << " active blocks)" << std::endl;
1487 g_profiler->avg("ServerEnv: active blocks", m_active_blocks.m_abm_list.size());
1488 g_profiler->avg("ServerEnv: active blocks cached", blocks_cached);
1489 g_profiler->avg("ServerEnv: active blocks scanned for ABMs", blocks_scanned);
1490 g_profiler->avg("ServerEnv: ABMs run", abms_run);
1496 Step script environment (run global on_step())
1498 m_script->environment_Step(dtime);
1500 m_script->stepAsync();
1506 ScopeProfiler sp(g_profiler, "ServerEnv: Run SAO::step()", SPT_AVG);
1508 // This helps the objects to send data at the same time
1509 bool send_recommended = false;
1510 m_send_recommended_timer += dtime;
1511 if (m_send_recommended_timer > getSendRecommendedInterval()) {
1512 m_send_recommended_timer -= getSendRecommendedInterval();
1513 send_recommended = true;
1516 u32 object_count = 0;
1518 auto cb_state = [&](ServerActiveObject *obj) {
1524 obj->step(dtime, send_recommended);
1525 // Read messages from object
1526 obj->dumpAOMessagesToQueue(m_active_object_messages);
1528 m_ao_manager.step(dtime, cb_state);
1530 m_active_object_gauge->set(object_count);
1534 Manage active objects
1536 if (m_object_management_interval.step(dtime, 0.5)) {
1537 removeRemovedObjects();
1541 Manage particle spawner expiration
1543 if (m_particle_management_interval.step(dtime, 1.0)) {
1544 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1545 i != m_particle_spawners.end(); ) {
1546 //non expiring spawners
1547 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1553 if (i->second <= 0.f)
1554 m_particle_spawners.erase(i++);
1560 // Send outdated player inventories
1561 for (RemotePlayer *player : m_players) {
1562 if (player->getPeerId() == PEER_ID_INEXISTENT)
1565 PlayerSAO *sao = player->getPlayerSAO();
1566 if (sao && player->inventory.checkModified())
1567 m_server->SendInventory(sao, true);
1570 // Send outdated detached inventories
1571 m_server->sendDetachedInventories(PEER_ID_INEXISTENT, true);
1573 const auto end_time = porting::getTimeUs();
1574 m_step_time_counter->increment(end_time - start_time);
1577 ServerEnvironment::BlockStatus ServerEnvironment::getBlockStatus(v3s16 blockpos)
1579 if (m_active_blocks.contains(blockpos))
1582 const MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1586 if (m_map->isBlockInQueue(blockpos))
1592 u32 ServerEnvironment::addParticleSpawner(float exptime)
1594 // Timers with lifetime 0 do not expire
1595 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1598 for (;;) { // look for unused particlespawner id
1600 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1601 if (f == m_particle_spawners.end()) {
1602 m_particle_spawners[id] = time;
1609 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1611 u32 id = addParticleSpawner(exptime);
1612 m_particle_spawner_attachments[id] = attached_id;
1613 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1614 obj->attachParticleSpawner(id);
1619 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1621 m_particle_spawners.erase(id);
1622 const auto &it = m_particle_spawner_attachments.find(id);
1623 if (it != m_particle_spawner_attachments.end()) {
1624 u16 obj_id = it->second;
1625 ServerActiveObject *sao = getActiveObject(obj_id);
1626 if (sao != NULL && remove_from_object) {
1627 sao->detachParticleSpawner(id);
1629 m_particle_spawner_attachments.erase(id);
1633 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1635 assert(object); // Pre-condition
1637 u16 id = addActiveObjectRaw(object, true, 0);
1642 Finds out what new objects have been added to
1643 inside a radius around a position
1645 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1647 std::set<u16> ¤t_objects,
1648 std::queue<u16> &added_objects)
1650 f32 radius_f = radius * BS;
1651 f32 player_radius_f = player_radius * BS;
1653 if (player_radius_f < 0.0f)
1654 player_radius_f = 0.0f;
1656 m_ao_manager.getAddedActiveObjectsAroundPos(playersao->getBasePosition(), radius_f,
1657 player_radius_f, current_objects, added_objects);
1661 Finds out what objects have been removed from
1662 inside a radius around a position
1664 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1666 std::set<u16> ¤t_objects,
1667 std::queue<u16> &removed_objects)
1669 f32 radius_f = radius * BS;
1670 f32 player_radius_f = player_radius * BS;
1672 if (player_radius_f < 0)
1673 player_radius_f = 0;
1675 Go through current_objects; object is removed if:
1676 - object is not found in m_active_objects (this is actually an
1677 error condition; objects should be removed only after all clients
1678 have been informed about removal), or
1679 - object is to be removed or deactivated, or
1680 - object is too far away
1682 for (u16 id : current_objects) {
1683 ServerActiveObject *object = getActiveObject(id);
1685 if (object == NULL) {
1686 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1687 << " object in current_objects is NULL" << std::endl;
1688 removed_objects.push(id);
1692 if (object->isGone()) {
1693 removed_objects.push(id);
1697 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1698 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1699 if (distance_f <= player_radius_f || player_radius_f == 0)
1701 } else if (distance_f <= radius_f)
1704 // Object is no longer visible
1705 removed_objects.push(id);
1709 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1710 v3s16 blockpos, bool static_exists, v3s16 static_block)
1712 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1716 for (auto &so_it : block->m_static_objects.getAllActives()) {
1717 // Get the ServerActiveObject counterpart to this StaticObject
1718 ServerActiveObject *sao = m_ao_manager.getActiveObject(so_it.first);
1720 // If this ever happens, there must be some kind of nasty bug.
1721 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1722 "Object from MapBlock::m_static_objects::m_active not found "
1723 "in m_active_objects";
1727 sao->m_static_exists = static_exists;
1728 sao->m_static_block = static_block;
1732 bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest)
1734 if (m_active_object_messages.empty())
1737 *dest = std::move(m_active_object_messages.front());
1738 m_active_object_messages.pop();
1742 void ServerEnvironment::getSelectedActiveObjects(
1743 const core::line3d<f32> &shootline_on_map,
1744 std::vector<PointedThing> &objects)
1746 std::vector<ServerActiveObject *> objs;
1747 getObjectsInsideRadius(objs, shootline_on_map.start,
1748 shootline_on_map.getLength() + 10.0f, nullptr);
1749 const v3f line_vector = shootline_on_map.getVector();
1751 for (auto obj : objs) {
1754 aabb3f selection_box;
1755 if (!obj->getSelectionBox(&selection_box))
1758 v3f pos = obj->getBasePosition();
1759 v3f rel_pos = shootline_on_map.start - pos;
1761 v3f current_intersection;
1763 v3f current_raw_normal;
1765 ObjectProperties *props = obj->accessObjectProperties();
1767 UnitSAO* usao = dynamic_cast<UnitSAO*>(obj);
1768 if (props->rotate_selectionbox && usao != nullptr) {
1769 collision = boxLineCollision(selection_box, usao->getTotalRotation(),
1770 rel_pos, line_vector, ¤t_intersection, ¤t_normal, ¤t_raw_normal);
1772 collision = boxLineCollision(selection_box, rel_pos, line_vector,
1773 ¤t_intersection, ¤t_normal);
1774 current_raw_normal = current_normal;
1777 current_intersection += pos;
1778 objects.emplace_back(
1779 (s16) obj->getId(), current_intersection, current_normal, current_raw_normal,
1780 (current_intersection - shootline_on_map.start).getLengthSQ());
1786 ************ Private methods *************
1789 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1790 bool set_changed, u32 dtime_s)
1792 if (!m_ao_manager.registerObject(object)) {
1796 // Register reference in scripting api (must be done before post-init)
1797 m_script->addObjectReference(object);
1798 // Post-initialize object
1799 object->addedToEnvironment(dtime_s);
1801 // Add static data to block
1802 if (object->isStaticAllowed()) {
1803 // Add static object to active static list of the block
1804 v3f objectpos = object->getBasePosition();
1805 StaticObject s_obj(object, objectpos);
1806 // Add to the block where the object is located in
1807 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1808 MapBlock *block = m_map->emergeBlock(blockpos);
1810 block->m_static_objects.setActive(object->getId(), s_obj);
1811 object->m_static_exists = true;
1812 object->m_static_block = blockpos;
1815 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1816 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1818 v3s16 p = floatToInt(objectpos, BS);
1819 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1820 <<"could not emerge block for storing id="<<object->getId()
1821 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1825 return object->getId();
1829 Remove objects that satisfy (isGone() && m_known_by_count==0)
1831 void ServerEnvironment::removeRemovedObjects()
1833 ScopeProfiler sp(g_profiler, "ServerEnvironment::removeRemovedObjects()", SPT_AVG);
1835 auto clear_cb = [this](ServerActiveObject *obj, u16 id) {
1836 // This shouldn't happen but check it
1838 errorstream << "ServerEnvironment::removeRemovedObjects(): "
1839 << "NULL object found. id=" << id << std::endl;
1844 We will handle objects marked for removal or deactivation
1850 Delete static data from block if removed
1852 if (obj->isPendingRemoval())
1853 deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1855 // If still known by clients, don't actually remove. On some future
1856 // invocation this will be 0, which is when removal will continue.
1857 if (obj->m_known_by_count > 0)
1861 Move static data from active to stored if deactivated
1863 if (!obj->isPendingRemoval() && obj->m_static_exists) {
1864 if (MapBlock *block = m_map->emergeBlock(obj->m_static_block, false)) {
1865 if (!block->storeActiveObject(id)) {
1866 warningstream << "ServerEnvironment::removeRemovedObjects(): "
1867 << "id=" << id << " m_static_exists=true but "
1868 << "static data doesn't actually exist in "
1869 << PP(obj->m_static_block) << std::endl;
1872 infostream << "Failed to emerge block from which an object to "
1873 << "be deactivated was loaded from. id=" << id << std::endl;
1877 // Tell the object about removal
1878 obj->removingFromEnvironment();
1879 // Deregister in scripting api
1880 m_script->removeObjectReference(obj);
1883 if (obj->environmentDeletes())
1889 m_ao_manager.clear(clear_cb);
1892 static void print_hexdump(std::ostream &o, const std::string &data)
1894 const int linelength = 16;
1895 for (int l = 0;; l++) {
1896 int i0 = linelength * l;
1897 bool at_end = false;
1898 int thislinelength = linelength;
1899 if (i0 + thislinelength > (int)data.size()) {
1900 thislinelength = data.size() - i0;
1903 for (int di = 0; di < linelength; di++) {
1906 if (di < thislinelength)
1907 porting::mt_snprintf(buf, sizeof(buf), "%.2x ", data[i]);
1909 porting::mt_snprintf(buf, sizeof(buf), " ");
1913 for (int di = 0; di < thislinelength; di++) {
1926 ServerActiveObject* ServerEnvironment::createSAO(ActiveObjectType type, v3f pos,
1927 const std::string &data)
1930 case ACTIVEOBJECT_TYPE_LUAENTITY:
1931 return new LuaEntitySAO(this, pos, data);
1933 warningstream << "ServerActiveObject: No factory for type=" << type << std::endl;
1939 Convert stored objects from blocks near the players to active.
1941 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1946 if (!block->onObjectsActivation())
1949 // Activate stored objects
1950 std::vector<StaticObject> new_stored;
1951 for (const StaticObject &s_obj : block->m_static_objects.getAllStored()) {
1952 // Create an active object from the data
1953 ServerActiveObject *obj =
1954 createSAO((ActiveObjectType)s_obj.type, s_obj.pos, s_obj.data);
1955 // If couldn't create object, store static data back.
1957 errorstream << "ServerEnvironment::activateObjects(): "
1958 << "failed to create active object from static object "
1959 << "in block " << PP(s_obj.pos / BS)
1960 << " type=" << (int)s_obj.type << " data:" << std::endl;
1961 print_hexdump(verbosestream, s_obj.data);
1963 new_stored.push_back(s_obj);
1966 verbosestream << "ServerEnvironment::activateObjects(): "
1967 << "activated static object pos=" << PP(s_obj.pos / BS)
1968 << " type=" << (int)s_obj.type << std::endl;
1969 // This will also add the object to the active static list
1970 addActiveObjectRaw(obj, false, dtime_s);
1973 // Clear stored list
1974 block->m_static_objects.clearStored();
1975 // Add leftover failed stuff to stored list
1976 for (const StaticObject &s_obj : new_stored) {
1977 block->m_static_objects.pushStored(s_obj);
1981 Note: Block hasn't really been modified here.
1982 The objects have just been activated and moved from the stored
1983 static list to the active static list.
1984 As such, the block is essentially the same.
1985 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1986 Otherwise there would be a huge amount of unnecessary I/O.
1991 Convert objects that are not standing inside active blocks to static.
1993 If m_known_by_count != 0, active object is not deleted, but static
1994 data is still updated.
1996 If force_delete is set, active object is deleted nevertheless. It
1997 shall only be set so in the destructor of the environment.
1999 If block wasn't generated (not in memory or on disk),
2001 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
2003 auto cb_deactivate = [this, _force_delete](ServerActiveObject *obj, u16 id) {
2004 // force_delete might be overridden per object
2005 bool force_delete = _force_delete;
2007 // Do not deactivate if disallowed
2008 if (!force_delete && !obj->shouldUnload())
2011 // removeRemovedObjects() is responsible for these
2012 if (!force_delete && obj->isGone())
2015 const v3f &objectpos = obj->getBasePosition();
2017 // The block in which the object resides in
2018 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2020 // If object's static data is stored in a deactivated block and object
2021 // is actually located in an active block, re-save to the block in
2022 // which the object is actually located in.
2023 if (!force_delete && obj->m_static_exists &&
2024 !m_active_blocks.contains(obj->m_static_block) &&
2025 m_active_blocks.contains(blockpos_o)) {
2027 // Delete from block where object was located
2028 deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
2030 StaticObject s_obj(obj, objectpos);
2031 // Save to block where object is located
2032 saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
2037 // If block is still active, don't remove
2038 bool still_active = obj->isStaticAllowed() ?
2039 m_active_blocks.contains(blockpos_o) :
2040 getMap().getBlockNoCreateNoEx(blockpos_o) != nullptr;
2041 if (!force_delete && still_active)
2044 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2045 << "deactivating object id=" << id << " on inactive block "
2046 << PP(blockpos_o) << std::endl;
2048 // If known by some client, don't immediately delete.
2049 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2052 Update the static data
2054 if (obj->isStaticAllowed()) {
2055 // Create new static object
2056 StaticObject s_obj(obj, objectpos);
2058 bool stays_in_same_block = false;
2059 bool data_changed = true;
2061 // Check if static data has changed considerably
2062 if (obj->m_static_exists) {
2063 if (obj->m_static_block == blockpos_o)
2064 stays_in_same_block = true;
2066 if (MapBlock *block = m_map->emergeBlock(obj->m_static_block, false)) {
2067 const auto n = block->m_static_objects.getAllActives().find(id);
2068 if (n != block->m_static_objects.getAllActives().end()) {
2069 StaticObject static_old = n->second;
2071 float save_movem = obj->getMinimumSavedMovement();
2073 if (static_old.data == s_obj.data &&
2074 (static_old.pos - objectpos).getLength() < save_movem)
2075 data_changed = false;
2077 warningstream << "ServerEnvironment::deactivateFarObjects(): "
2078 << "id=" << id << " m_static_exists=true but "
2079 << "static data doesn't actually exist in "
2080 << PP(obj->m_static_block) << std::endl;
2086 While changes are always saved, blocks are only marked as modified
2087 if the object has moved or different staticdata. (see above)
2089 bool shall_be_written = (!stays_in_same_block || data_changed);
2090 u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2092 // Delete old static object
2093 deleteStaticFromBlock(obj, id, reason, false);
2095 // Add to the block where the object is located in
2096 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2097 u16 store_id = pending_delete ? id : 0;
2098 if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2099 force_delete = true;
2102 // Regardless of what happens to the object at this point, deactivate it first.
2103 // This ensures that LuaEntity on_deactivate is always called.
2104 obj->markForDeactivation();
2107 If known by some client, set pending deactivation.
2108 Otherwise delete it immediately.
2110 if (pending_delete && !force_delete) {
2111 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2112 << "object id=" << id << " is known by clients"
2113 << "; not deleting yet" << std::endl;
2118 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2119 << "object id=" << id << " is not known by clients"
2120 << "; deleting" << std::endl;
2122 // Tell the object about removal
2123 obj->removingFromEnvironment();
2124 // Deregister in scripting api
2125 m_script->removeObjectReference(obj);
2127 // Delete active object
2128 if (obj->environmentDeletes())
2134 m_ao_manager.clear(cb_deactivate);
2137 void ServerEnvironment::deleteStaticFromBlock(
2138 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2140 if (!obj->m_static_exists)
2145 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2147 block = m_map->emergeBlock(obj->m_static_block, false);
2150 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2151 << " when deleting static data of object from it. id=" << id << std::endl;
2155 block->m_static_objects.remove(id);
2156 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2157 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2159 obj->m_static_exists = false;
2162 bool ServerEnvironment::saveStaticToBlock(
2163 v3s16 blockpos, u16 store_id,
2164 ServerActiveObject *obj, const StaticObject &s_obj,
2167 MapBlock *block = nullptr;
2169 block = m_map->emergeBlock(blockpos);
2170 } catch (InvalidPositionException &e) {
2171 // Handled via NULL pointer
2172 // NOTE: emergeBlock's failure is usually determined by it
2173 // actually returning NULL
2177 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2178 << " when saving static data of object to it. id=" << store_id << std::endl;
2182 if (!block->saveStaticObject(store_id, s_obj, mod_reason))
2185 obj->m_static_exists = true;
2186 obj->m_static_block = blockpos;
2191 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2192 const std::string &savedir, const Settings &conf)
2195 if (name == "sqlite3")
2196 return new PlayerDatabaseSQLite3(savedir);
2198 if (name == "dummy")
2199 return new Database_Dummy();
2202 if (name == "postgresql") {
2203 std::string connect_string;
2204 conf.getNoEx("pgsql_player_connection", connect_string);
2205 return new PlayerDatabasePostgreSQL(connect_string);
2210 if (name == "leveldb")
2211 return new PlayerDatabaseLevelDB(savedir);
2214 if (name == "files")
2215 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2217 throw BaseException(std::string("Database backend ") + name + " not supported.");
2220 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2221 const Settings &cmd_args)
2223 std::string migrate_to = cmd_args.get("migrate-players");
2225 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2226 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2227 errorstream << "Cannot read world.mt!" << std::endl;
2231 if (!world_mt.exists("player_backend")) {
2232 errorstream << "Please specify your current backend in world.mt:"
2234 << " player_backend = {files|sqlite3|leveldb|postgresql}"
2239 std::string backend = world_mt.get("player_backend");
2240 if (backend == migrate_to) {
2241 errorstream << "Cannot migrate: new backend is same"
2242 << " as the old one" << std::endl;
2246 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2249 if (backend == "files") {
2250 // Create backup directory
2251 fs::CreateDir(players_backup_path);
2255 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2256 game_params.world_path, world_mt);
2257 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2258 game_params.world_path, world_mt);
2260 std::vector<std::string> player_list;
2261 srcdb->listPlayers(player_list);
2262 for (std::vector<std::string>::const_iterator it = player_list.begin();
2263 it != player_list.end(); ++it) {
2264 actionstream << "Migrating player " << it->c_str() << std::endl;
2265 RemotePlayer player(it->c_str(), NULL);
2266 PlayerSAO playerSAO(NULL, &player, 15000, false);
2268 srcdb->loadPlayer(&player, &playerSAO);
2270 playerSAO.finalize(&player, std::set<std::string>());
2271 player.setPlayerSAO(&playerSAO);
2273 dstdb->savePlayer(&player);
2275 // For files source, move player files to backup dir
2276 if (backend == "files") {
2278 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2279 players_backup_path + DIR_DELIM + (*it));
2283 actionstream << "Successfully migrated " << player_list.size() << " players"
2285 world_mt.set("player_backend", migrate_to);
2286 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2287 errorstream << "Failed to update world.mt!" << std::endl;
2289 actionstream << "world.mt updated" << std::endl;
2291 // When migration is finished from file backend, remove players directory if empty
2292 if (backend == "files") {
2293 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2300 } catch (BaseException &e) {
2301 errorstream << "An error occurred during migration: " << e.what() << std::endl;
2307 AuthDatabase *ServerEnvironment::openAuthDatabase(
2308 const std::string &name, const std::string &savedir, const Settings &conf)
2310 if (name == "sqlite3")
2311 return new AuthDatabaseSQLite3(savedir);
2314 if (name == "postgresql") {
2315 std::string connect_string;
2316 conf.getNoEx("pgsql_auth_connection", connect_string);
2317 return new AuthDatabasePostgreSQL(connect_string);
2321 if (name == "files")
2322 return new AuthDatabaseFiles(savedir);
2325 if (name == "leveldb")
2326 return new AuthDatabaseLevelDB(savedir);
2329 throw BaseException(std::string("Database backend ") + name + " not supported.");
2332 bool ServerEnvironment::migrateAuthDatabase(
2333 const GameParams &game_params, const Settings &cmd_args)
2335 std::string migrate_to = cmd_args.get("migrate-auth");
2337 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2338 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2339 errorstream << "Cannot read world.mt!" << std::endl;
2343 std::string backend = "files";
2344 if (world_mt.exists("auth_backend"))
2345 backend = world_mt.get("auth_backend");
2347 warningstream << "No auth_backend found in world.mt, "
2348 "assuming \"files\"." << std::endl;
2350 if (backend == migrate_to) {
2351 errorstream << "Cannot migrate: new backend is same"
2352 << " as the old one" << std::endl;
2357 const std::unique_ptr<AuthDatabase> srcdb(ServerEnvironment::openAuthDatabase(
2358 backend, game_params.world_path, world_mt));
2359 const std::unique_ptr<AuthDatabase> dstdb(ServerEnvironment::openAuthDatabase(
2360 migrate_to, game_params.world_path, world_mt));
2362 std::vector<std::string> names_list;
2363 srcdb->listNames(names_list);
2364 for (const std::string &name : names_list) {
2365 actionstream << "Migrating auth entry for " << name << std::endl;
2367 AuthEntry authEntry;
2368 success = srcdb->getAuth(name, authEntry);
2369 success = success && dstdb->createAuth(authEntry);
2371 errorstream << "Failed to migrate " << name << std::endl;
2374 actionstream << "Successfully migrated " << names_list.size()
2375 << " auth entries" << std::endl;
2376 world_mt.set("auth_backend", migrate_to);
2377 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2378 errorstream << "Failed to update world.mt!" << std::endl;
2380 actionstream << "world.mt updated" << std::endl;
2382 if (backend == "files") {
2383 // special-case files migration:
2384 // move auth.txt to auth.txt.bak if possible
2385 std::string auth_txt_path =
2386 game_params.world_path + DIR_DELIM + "auth.txt";
2387 std::string auth_bak_path = auth_txt_path + ".bak";
2388 if (!fs::PathExists(auth_bak_path))
2389 if (fs::Rename(auth_txt_path, auth_bak_path))
2390 actionstream << "Renamed auth.txt to auth.txt.bak"
2393 errorstream << "Could not rename auth.txt to "
2394 "auth.txt.bak" << std::endl;
2396 warningstream << "auth.txt.bak already exists, auth.txt "
2397 "not renamed" << std::endl;
2400 } catch (BaseException &e) {
2401 errorstream << "An error occurred during migration: " << e.what()