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, MetricsBackend *mb):
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);
464 m_step_time_counter = mb->addCounter(
465 "minetest_env_step_time", "Time spent in environment step (in microseconds)");
467 m_active_block_gauge = mb->addGauge(
468 "minetest_env_active_blocks", "Number of active blocks");
470 m_active_object_gauge = mb->addGauge(
471 "minetest_env_active_objects", "Number of active objects");
474 ServerEnvironment::~ServerEnvironment()
476 // Clear active block list.
477 // This makes the next one delete all active objects.
478 m_active_blocks.clear();
480 // Convert all objects to static and delete the active objects
481 deactivateFarObjects(true);
486 // Delete ActiveBlockModifiers
487 for (ABMWithState &m_abm : m_abms) {
491 // Deallocate players
492 for (RemotePlayer *m_player : m_players) {
496 delete m_player_database;
497 delete m_auth_database;
500 Map & ServerEnvironment::getMap()
505 ServerMap & ServerEnvironment::getServerMap()
510 RemotePlayer *ServerEnvironment::getPlayer(const session_t peer_id)
512 for (RemotePlayer *player : m_players) {
513 if (player->getPeerId() == peer_id)
519 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
521 for (RemotePlayer *player : m_players) {
522 if (strcmp(player->getName(), name) == 0)
528 void ServerEnvironment::addPlayer(RemotePlayer *player)
531 Check that peer_ids are unique.
532 Also check that names are unique.
533 Exception: there can be multiple players with peer_id=0
535 // If peer id is non-zero, it has to be unique.
536 if (player->getPeerId() != PEER_ID_INEXISTENT)
537 FATAL_ERROR_IF(getPlayer(player->getPeerId()) != NULL, "Peer id not unique");
538 // Name has to be unique.
539 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
541 m_players.push_back(player);
544 void ServerEnvironment::removePlayer(RemotePlayer *player)
546 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
547 it != m_players.end(); ++it) {
548 if ((*it) == player) {
556 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
558 return m_player_database->removePlayer(name);
561 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
562 const std::string &str_reason, bool reconnect)
564 for (RemotePlayer *player : m_players)
565 m_server->DenyAccess(player->getPeerId(), reason, str_reason, reconnect);
568 void ServerEnvironment::saveLoadedPlayers(bool force)
570 for (RemotePlayer *player : m_players) {
571 if (force || player->checkModified() || (player->getPlayerSAO() &&
572 player->getPlayerSAO()->getMeta().isModified())) {
574 m_player_database->savePlayer(player);
575 } catch (DatabaseException &e) {
576 errorstream << "Failed to save player " << player->getName() << " exception: "
577 << e.what() << std::endl;
584 void ServerEnvironment::savePlayer(RemotePlayer *player)
587 m_player_database->savePlayer(player);
588 } catch (DatabaseException &e) {
589 errorstream << "Failed to save player " << player->getName() << " exception: "
590 << e.what() << std::endl;
595 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
596 session_t peer_id, bool is_singleplayer)
598 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
599 // Create player if it doesn't exist
600 if (!m_player_database->loadPlayer(player, playersao)) {
602 // Set player position
603 infostream << "Server: Finding spawn place for player \""
604 << player->getName() << "\"" << std::endl;
605 playersao->setBasePosition(m_server->findSpawnPos());
607 // Make sure the player is saved
608 player->setModified(true);
610 // If the player exists, ensure that they respawn inside legal bounds
611 // This fixes an assert crash when the player can't be added
612 // to the environment
613 if (objectpos_over_limit(playersao->getBasePosition())) {
614 actionstream << "Respawn position for player \""
615 << player->getName() << "\" outside limits, resetting" << std::endl;
616 playersao->setBasePosition(m_server->findSpawnPos());
620 // Add player to environment
623 /* Clean up old HUD elements from previous sessions */
626 /* Add object to environment */
627 addActiveObject(playersao);
629 // Update active blocks asap so objects in those blocks appear on the client
630 m_force_update_active_blocks = true;
635 void ServerEnvironment::saveMeta()
640 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
642 // Open file and serialize
643 std::ostringstream ss(std::ios_base::binary);
645 Settings args("EnvArgsEnd");
646 args.setU64("game_time", m_game_time);
647 args.setU64("time_of_day", getTimeOfDay());
648 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
649 args.setU64("lbm_introduction_times_version", 1);
650 args.set("lbm_introduction_times",
651 m_lbm_mgr.createIntroductionTimesString());
652 args.setU64("day_count", m_day_count);
655 if(!fs::safeWriteToFile(path, ss.str()))
657 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
659 throw SerializationError("Couldn't save env meta");
663 void ServerEnvironment::loadMeta()
665 SANITY_CHECK(!m_meta_loaded);
666 m_meta_loaded = true;
668 // If file doesn't exist, load default environment metadata
669 if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
670 infostream << "ServerEnvironment: Loading default environment metadata"
676 infostream << "ServerEnvironment: Loading environment metadata" << std::endl;
678 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
680 // Open file and deserialize
681 std::ifstream is(path.c_str(), std::ios_base::binary);
683 infostream << "ServerEnvironment::loadMeta(): Failed to open "
684 << path << std::endl;
685 throw SerializationError("Couldn't load env meta");
688 Settings args("EnvArgsEnd");
690 if (!args.parseConfigLines(is)) {
691 throw SerializationError("ServerEnvironment::loadMeta(): "
692 "EnvArgsEnd not found!");
696 m_game_time = args.getU64("game_time");
697 } catch (SettingNotFoundException &e) {
698 // Getting this is crucial, otherwise timestamps are useless
699 throw SerializationError("Couldn't load env meta game_time");
702 setTimeOfDay(args.exists("time_of_day") ?
703 // set day to early morning by default
704 args.getU64("time_of_day") : 5250);
706 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
707 // If missing, do as if clearObjects was never called
708 args.getU64("last_clear_objects_time") : 0;
710 std::string lbm_introduction_times;
712 u64 ver = args.getU64("lbm_introduction_times_version");
714 lbm_introduction_times = args.get("lbm_introduction_times");
716 infostream << "ServerEnvironment::loadMeta(): Non-supported"
717 << " introduction time version " << ver << std::endl;
719 } catch (SettingNotFoundException &e) {
720 // No problem, this is expected. Just continue with an empty string
722 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
724 m_day_count = args.exists("day_count") ?
725 args.getU64("day_count") : 0;
729 * called if env_meta.txt doesn't exist (e.g. new world)
731 void ServerEnvironment::loadDefaultMeta()
733 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
738 ActiveBlockModifier *abm;
740 std::vector<content_t> required_neighbors;
741 bool check_required_neighbors; // false if required_neighbors is known to be empty
749 ServerEnvironment *m_env;
750 std::vector<std::vector<ActiveABM> *> m_aabms;
752 ABMHandler(std::vector<ABMWithState> &abms,
753 float dtime_s, ServerEnvironment *env,
759 const NodeDefManager *ndef = env->getGameDef()->ndef();
760 for (ABMWithState &abmws : abms) {
761 ActiveBlockModifier *abm = abmws.abm;
762 float trigger_interval = abm->getTriggerInterval();
763 if(trigger_interval < 0.001)
764 trigger_interval = 0.001;
765 float actual_interval = dtime_s;
767 abmws.timer += dtime_s;
768 if(abmws.timer < trigger_interval)
770 abmws.timer -= trigger_interval;
771 actual_interval = trigger_interval;
773 float chance = abm->getTriggerChance();
778 if (abm->getSimpleCatchUp()) {
779 float intervals = actual_interval / trigger_interval;
782 aabm.chance = chance / intervals;
786 aabm.chance = chance;
789 aabm.min_y = abm->getMinY();
790 aabm.max_y = abm->getMaxY();
793 const std::vector<std::string> &required_neighbors_s =
794 abm->getRequiredNeighbors();
795 for (const std::string &required_neighbor_s : required_neighbors_s) {
796 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
798 aabm.check_required_neighbors = !required_neighbors_s.empty();
801 const std::vector<std::string> &contents_s = abm->getTriggerContents();
802 for (const std::string &content_s : contents_s) {
803 std::vector<content_t> ids;
804 ndef->getIds(content_s, ids);
805 for (content_t c : ids) {
806 if (c >= m_aabms.size())
807 m_aabms.resize(c + 256, NULL);
809 m_aabms[c] = new std::vector<ActiveABM>;
810 m_aabms[c]->push_back(aabm);
818 for (auto &aabms : m_aabms)
822 // Find out how many objects the given block and its neighbours contain.
823 // Returns the number of objects in the block, and also in 'wider' the
824 // number of objects in the block and all its neighbours. The latter
825 // may an estimate if any neighbours are unloaded.
826 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
829 u32 wider_unknown_count = 0;
830 for(s16 x=-1; x<=1; x++)
831 for(s16 y=-1; y<=1; y++)
832 for(s16 z=-1; z<=1; z++)
834 MapBlock *block2 = map->getBlockNoCreateNoEx(
835 block->getPos() + v3s16(x,y,z));
837 wider_unknown_count++;
840 wider += block2->m_static_objects.m_active.size()
841 + block2->m_static_objects.m_stored.size();
844 u32 active_object_count = block->m_static_objects.m_active.size();
845 u32 wider_known_count = 3*3*3 - wider_unknown_count;
846 wider += wider_unknown_count * wider / wider_known_count;
847 return active_object_count;
850 void apply(MapBlock *block, int &blocks_scanned, int &abms_run, int &blocks_cached)
852 if(m_aabms.empty() || block->isDummy())
855 // Check the content type cache first
856 // to see whether there are any ABMs
857 // to be run at all for this block.
858 if (block->contents_cached) {
860 bool run_abms = false;
861 for (content_t c : block->contents) {
862 if (c < m_aabms.size() && m_aabms[c]) {
871 block->contents.clear();
875 ServerMap *map = &m_env->getServerMap();
877 u32 active_object_count_wider;
878 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
879 m_env->m_added_objects = 0;
882 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
883 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
884 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
886 const MapNode &n = block->getNodeUnsafe(p0);
887 content_t c = n.getContent();
888 // Cache content types as we go
889 if (!block->contents_cached && !block->do_not_cache_contents) {
890 block->contents.insert(c);
891 if (block->contents.size() > 64) {
892 // Too many different nodes... don't try to cache
893 block->do_not_cache_contents = true;
894 block->contents.clear();
898 if (c >= m_aabms.size() || !m_aabms[c])
901 v3s16 p = p0 + block->getPosRelative();
902 for (ActiveABM &aabm : *m_aabms[c]) {
903 if ((p.Y < aabm.min_y) || (p.Y > aabm.max_y))
906 if (myrand() % aabm.chance != 0)
910 if (aabm.check_required_neighbors) {
912 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
913 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
914 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
919 if (block->isValidPosition(p1)) {
920 // if the neighbor is found on the same map block
921 // get it straight from there
922 const MapNode &n = block->getNodeUnsafe(p1);
925 // otherwise consult the map
926 MapNode n = map->getNode(p1 + block->getPosRelative());
929 if (CONTAINS(aabm.required_neighbors, c))
932 // No required neighbor found
938 // Call all the trigger variations
939 aabm.abm->trigger(m_env, p, n);
940 aabm.abm->trigger(m_env, p, n,
941 active_object_count, active_object_count_wider);
943 // Count surrounding objects again if the abms added any
944 if(m_env->m_added_objects > 0) {
945 active_object_count = countObjects(block, map, active_object_count_wider);
946 m_env->m_added_objects = 0;
950 block->contents_cached = !block->do_not_cache_contents;
954 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
956 // Reset usage timer immediately, otherwise a block that becomes active
957 // again at around the same time as it would normally be unloaded will
958 // get unloaded incorrectly. (I think this still leaves a small possibility
959 // of a race condition between this and server::AsyncRunStep, which only
960 // some kind of synchronisation will fix, but it at least reduces the window
961 // of opportunity for it to break from seconds to nanoseconds)
962 block->resetUsageTimer();
964 // Get time difference
966 u32 stamp = block->getTimestamp();
967 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
968 dtime_s = m_game_time - stamp;
969 dtime_s += additional_dtime;
971 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
972 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
974 // Remove stored static objects if clearObjects was called since block's timestamp
975 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
976 block->m_static_objects.m_stored.clear();
977 // do not set changed flag to avoid unnecessary mapblock writes
980 // Set current time as timestamp
981 block->setTimestampNoChangedFlag(m_game_time);
983 /*infostream<<"ServerEnvironment::activateBlock(): block is "
984 <<dtime_s<<" seconds old."<<std::endl;*/
986 // Activate stored objects
987 activateObjects(block, dtime_s);
989 /* Handle LoadingBlockModifiers */
990 m_lbm_mgr.applyLBMs(this, block, stamp);
993 std::vector<NodeTimer> elapsed_timers =
994 block->m_node_timers.step((float)dtime_s);
995 if (!elapsed_timers.empty()) {
997 for (const NodeTimer &elapsed_timer : elapsed_timers) {
998 n = block->getNodeNoEx(elapsed_timer.position);
999 v3s16 p = elapsed_timer.position + block->getPosRelative();
1000 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
1001 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
1002 elapsed_timer.position));
1007 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
1009 m_abms.emplace_back(abm);
1012 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1014 m_lbm_mgr.addLBMDef(lbm);
1017 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1019 const NodeDefManager *ndef = m_server->ndef();
1020 MapNode n_old = m_map->getNode(p);
1022 const ContentFeatures &cf_old = ndef->get(n_old);
1025 if (cf_old.has_on_destruct)
1026 m_script->node_on_destruct(p, n_old);
1029 if (!m_map->addNodeWithEvent(p, n))
1032 // Update active VoxelManipulator if a mapgen thread
1033 m_map->updateVManip(p);
1035 // Call post-destructor
1036 if (cf_old.has_after_destruct)
1037 m_script->node_after_destruct(p, n_old);
1039 // Retrieve node content features
1040 // if new node is same as old, reuse old definition to prevent a lookup
1041 const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
1044 if (cf_new.has_on_construct)
1045 m_script->node_on_construct(p, n);
1050 bool ServerEnvironment::removeNode(v3s16 p)
1052 const NodeDefManager *ndef = m_server->ndef();
1053 MapNode n_old = m_map->getNode(p);
1056 if (ndef->get(n_old).has_on_destruct)
1057 m_script->node_on_destruct(p, n_old);
1060 // This is slightly optimized compared to addNodeWithEvent(air)
1061 if (!m_map->removeNodeWithEvent(p))
1064 // Update active VoxelManipulator if a mapgen thread
1065 m_map->updateVManip(p);
1067 // Call post-destructor
1068 if (ndef->get(n_old).has_after_destruct)
1069 m_script->node_after_destruct(p, n_old);
1071 // Air doesn't require constructor
1075 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1077 if (!m_map->addNodeWithEvent(p, n, false))
1080 // Update active VoxelManipulator if a mapgen thread
1081 m_map->updateVManip(p);
1086 u8 ServerEnvironment::findSunlight(v3s16 pos) const
1088 // Directions for neighbouring nodes with specified order
1089 static const v3s16 dirs[] = {
1090 v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1),
1091 v3s16(0, -1, 0), v3s16(0, 1, 0)
1094 const NodeDefManager *ndef = m_server->ndef();
1096 // found_light remembers the highest known sunlight value at pos
1099 struct stack_entry {
1103 std::stack<stack_entry> stack;
1104 stack.push({pos, 0});
1106 std::unordered_map<s64, s8> dists;
1107 dists[MapDatabase::getBlockAsInteger(pos)] = 0;
1109 while (!stack.empty()) {
1110 struct stack_entry e = stack.top();
1113 v3s16 currentPos = e.pos;
1114 s8 dist = e.dist + 1;
1116 for (const v3s16& off : dirs) {
1117 v3s16 neighborPos = currentPos + off;
1118 s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos);
1120 // Do not walk neighborPos multiple times unless the distance to the start
1121 // position is shorter
1122 auto it = dists.find(neighborHash);
1123 if (it != dists.end() && dist >= it->second)
1127 bool is_position_ok;
1128 MapNode node = m_map->getNode(neighborPos, &is_position_ok);
1129 if (!is_position_ok) {
1130 // This happens very rarely because the map at currentPos is loaded
1131 m_map->emergeBlock(neighborPos, false);
1132 node = m_map->getNode(neighborPos, &is_position_ok);
1133 if (!is_position_ok)
1134 continue; // not generated
1137 const ContentFeatures &def = ndef->get(node);
1138 if (!def.sunlight_propagates) {
1139 // Do not test propagation here again
1140 dists[neighborHash] = -1;
1144 // Sunlight could have come from here
1145 dists[neighborHash] = dist;
1146 u8 daylight = node.param1 & 0x0f;
1148 // In the special case where sunlight shines from above and thus
1149 // does not decrease with upwards distance, daylight is always
1150 // bigger than nightlight, which never reaches 15
1151 int possible_finlight = daylight - dist;
1152 if (possible_finlight <= found_light) {
1153 // Light from here cannot make a brighter light at currentPos than
1158 u8 nightlight = node.param1 >> 4;
1159 if (daylight > nightlight) {
1160 // Found a valid daylight
1161 found_light = possible_finlight;
1163 // Sunlight may be darker, so walk the neighbours
1164 stack.push({neighborPos, dist});
1171 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1173 infostream << "ServerEnvironment::clearObjects(): "
1174 << "Removing all active objects" << std::endl;
1175 auto cb_removal = [this] (ServerActiveObject *obj, u16 id) {
1176 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1179 // Delete static object if block is loaded
1180 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1182 // If known by some client, don't delete immediately
1183 if (obj->m_known_by_count > 0) {
1184 obj->markForRemoval();
1188 // Tell the object about removal
1189 obj->removingFromEnvironment();
1190 // Deregister in scripting api
1191 m_script->removeObjectReference(obj);
1193 // Delete active object
1194 if (obj->environmentDeletes())
1200 m_ao_manager.clear(cb_removal);
1202 // Get list of loaded blocks
1203 std::vector<v3s16> loaded_blocks;
1204 infostream << "ServerEnvironment::clearObjects(): "
1205 << "Listing all loaded blocks" << std::endl;
1206 m_map->listAllLoadedBlocks(loaded_blocks);
1207 infostream << "ServerEnvironment::clearObjects(): "
1208 << "Done listing all loaded blocks: "
1209 << loaded_blocks.size()<<std::endl;
1211 // Get list of loadable blocks
1212 std::vector<v3s16> loadable_blocks;
1213 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1214 infostream << "ServerEnvironment::clearObjects(): "
1215 << "Listing all loadable blocks" << std::endl;
1216 m_map->listAllLoadableBlocks(loadable_blocks);
1217 infostream << "ServerEnvironment::clearObjects(): "
1218 << "Done listing all loadable blocks: "
1219 << loadable_blocks.size() << std::endl;
1221 loadable_blocks = loaded_blocks;
1224 actionstream << "ServerEnvironment::clearObjects(): "
1225 << "Now clearing objects in " << loadable_blocks.size()
1226 << " blocks" << std::endl;
1228 // Grab a reference on each loaded block to avoid unloading it
1229 for (v3s16 p : loaded_blocks) {
1230 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1231 assert(block != NULL);
1235 // Remove objects in all loadable blocks
1236 u32 unload_interval = U32_MAX;
1237 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1238 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1239 unload_interval = MYMAX(unload_interval, 1);
1241 u32 report_interval = loadable_blocks.size() / 10;
1242 u32 num_blocks_checked = 0;
1243 u32 num_blocks_cleared = 0;
1244 u32 num_objs_cleared = 0;
1245 for (auto i = loadable_blocks.begin();
1246 i != loadable_blocks.end(); ++i) {
1248 MapBlock *block = m_map->emergeBlock(p, false);
1250 errorstream << "ServerEnvironment::clearObjects(): "
1251 << "Failed to emerge block " << PP(p) << std::endl;
1254 u32 num_stored = block->m_static_objects.m_stored.size();
1255 u32 num_active = block->m_static_objects.m_active.size();
1256 if (num_stored != 0 || num_active != 0) {
1257 block->m_static_objects.m_stored.clear();
1258 block->m_static_objects.m_active.clear();
1259 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1260 MOD_REASON_CLEAR_ALL_OBJECTS);
1261 num_objs_cleared += num_stored + num_active;
1262 num_blocks_cleared++;
1264 num_blocks_checked++;
1266 if (report_interval != 0 &&
1267 num_blocks_checked % report_interval == 0) {
1268 float percent = 100.0 * (float)num_blocks_checked /
1269 loadable_blocks.size();
1270 actionstream << "ServerEnvironment::clearObjects(): "
1271 << "Cleared " << num_objs_cleared << " objects"
1272 << " in " << num_blocks_cleared << " blocks ("
1273 << percent << "%)" << std::endl;
1275 if (num_blocks_checked % unload_interval == 0) {
1276 m_map->unloadUnreferencedBlocks();
1279 m_map->unloadUnreferencedBlocks();
1281 // Drop references that were added above
1282 for (v3s16 p : loaded_blocks) {
1283 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1288 m_last_clear_objects_time = m_game_time;
1290 actionstream << "ServerEnvironment::clearObjects(): "
1291 << "Finished: Cleared " << num_objs_cleared << " objects"
1292 << " in " << num_blocks_cleared << " blocks" << std::endl;
1295 void ServerEnvironment::step(float dtime)
1297 ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
1298 const auto start_time = porting::getTimeUs();
1300 /* Step time of day */
1301 stepTimeOfDay(dtime);
1304 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1305 // really matter that much.
1306 static thread_local const float server_step =
1307 g_settings->getFloat("dedicated_server_step");
1308 m_recommended_send_interval = server_step;
1314 m_game_time_fraction_counter += dtime;
1315 u32 inc_i = (u32)m_game_time_fraction_counter;
1316 m_game_time += inc_i;
1317 m_game_time_fraction_counter -= (float)inc_i;
1324 ScopeProfiler sp(g_profiler, "ServerEnv: move players", SPT_AVG);
1325 for (RemotePlayer *player : m_players) {
1326 // Ignore disconnected players
1327 if (player->getPeerId() == PEER_ID_INEXISTENT)
1331 player->move(dtime, this, 100 * BS);
1336 Manage active block list
1338 if (m_active_blocks_mgmt_interval.step(dtime, m_cache_active_block_mgmt_interval) ||
1339 m_force_update_active_blocks) {
1340 ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
1343 Get player block positions
1345 std::vector<PlayerSAO*> players;
1346 for (RemotePlayer *player: m_players) {
1347 // Ignore disconnected players
1348 if (player->getPeerId() == PEER_ID_INEXISTENT)
1351 PlayerSAO *playersao = player->getPlayerSAO();
1354 players.push_back(playersao);
1358 Update list of active blocks, collecting changes
1360 // use active_object_send_range_blocks since that is max distance
1361 // for active objects sent the client anyway
1362 static thread_local const s16 active_object_range =
1363 g_settings->getS16("active_object_send_range_blocks");
1364 static thread_local const s16 active_block_range =
1365 g_settings->getS16("active_block_range");
1366 std::set<v3s16> blocks_removed;
1367 std::set<v3s16> blocks_added;
1368 m_active_blocks.update(players, active_block_range, active_object_range,
1369 blocks_removed, blocks_added);
1371 m_active_block_gauge->set(m_active_blocks.size());
1374 Handle removed blocks
1377 // Convert active objects that are no more in active blocks to static
1378 deactivateFarObjects(false);
1380 for (const v3s16 &p: blocks_removed) {
1381 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1385 // Set current time as timestamp (and let it set ChangedFlag)
1386 block->setTimestamp(m_game_time);
1393 for (const v3s16 &p: blocks_added) {
1394 MapBlock *block = m_map->getBlockOrEmerge(p);
1396 m_active_blocks.m_list.erase(p);
1397 m_active_blocks.m_abm_list.erase(p);
1401 activateBlock(block);
1404 m_force_update_active_blocks = false;
1407 Mess around in active blocks
1409 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1410 ScopeProfiler sp(g_profiler, "ServerEnv: Run node timers", SPT_AVG);
1412 float dtime = m_cache_nodetimer_interval;
1414 for (const v3s16 &p: m_active_blocks.m_list) {
1415 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1419 // Reset block usage timer
1420 block->resetUsageTimer();
1422 // Set current time as timestamp
1423 block->setTimestampNoChangedFlag(m_game_time);
1424 // If time has changed much from the one on disk,
1425 // set block to be saved when it is unloaded
1426 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1427 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1428 MOD_REASON_BLOCK_EXPIRED);
1431 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1432 if (!elapsed_timers.empty()) {
1435 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1436 n = block->getNodeNoEx(elapsed_timer.position);
1437 p2 = elapsed_timer.position + block->getPosRelative();
1438 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1439 block->setNodeTimer(NodeTimer(
1440 elapsed_timer.timeout, 0, elapsed_timer.position));
1447 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) {
1448 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1449 TimeTaker timer("modify in active blocks per interval");
1451 // Initialize handling of ActiveBlockModifiers
1452 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1454 int blocks_scanned = 0;
1456 int blocks_cached = 0;
1458 std::vector<v3s16> output(m_active_blocks.m_abm_list.size());
1460 // Shuffle the active blocks so that each block gets an equal chance
1461 // of having its ABMs run.
1462 std::copy(m_active_blocks.m_abm_list.begin(), m_active_blocks.m_abm_list.end(), output.begin());
1463 std::shuffle(output.begin(), output.end(), m_rgen);
1466 // determine the time budget for ABMs
1467 u32 max_time_ms = m_cache_abm_interval * 1000 * m_cache_abm_time_budget;
1468 for (const v3s16 &p : output) {
1469 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1475 // Set current time as timestamp
1476 block->setTimestampNoChangedFlag(m_game_time);
1478 /* Handle ActiveBlockModifiers */
1479 abmhandler.apply(block, blocks_scanned, abms_run, blocks_cached);
1481 u32 time_ms = timer.getTimerTime();
1483 if (time_ms > max_time_ms) {
1484 warningstream << "active block modifiers took "
1485 << time_ms << "ms (processed " << i << " of "
1486 << output.size() << " active blocks)" << std::endl;
1490 g_profiler->avg("ServerEnv: active blocks", m_active_blocks.m_abm_list.size());
1491 g_profiler->avg("ServerEnv: active blocks cached", blocks_cached);
1492 g_profiler->avg("ServerEnv: active blocks scanned for ABMs", blocks_scanned);
1493 g_profiler->avg("ServerEnv: ABMs run", abms_run);
1499 Step script environment (run global on_step())
1501 m_script->environment_Step(dtime);
1503 m_script->stepAsync();
1509 ScopeProfiler sp(g_profiler, "ServerEnv: Run SAO::step()", SPT_AVG);
1511 // This helps the objects to send data at the same time
1512 bool send_recommended = false;
1513 m_send_recommended_timer += dtime;
1514 if (m_send_recommended_timer > getSendRecommendedInterval()) {
1515 m_send_recommended_timer -= getSendRecommendedInterval();
1516 send_recommended = true;
1519 u32 object_count = 0;
1521 auto cb_state = [&] (ServerActiveObject *obj) {
1527 obj->step(dtime, send_recommended);
1528 // Read messages from object
1529 obj->dumpAOMessagesToQueue(m_active_object_messages);
1531 m_ao_manager.step(dtime, cb_state);
1533 m_active_object_gauge->set(object_count);
1537 Manage active objects
1539 if (m_object_management_interval.step(dtime, 0.5)) {
1540 removeRemovedObjects();
1544 Manage particle spawner expiration
1546 if (m_particle_management_interval.step(dtime, 1.0)) {
1547 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1548 i != m_particle_spawners.end(); ) {
1549 //non expiring spawners
1550 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1556 if (i->second <= 0.f)
1557 m_particle_spawners.erase(i++);
1563 // Send outdated player inventories
1564 for (RemotePlayer *player : m_players) {
1565 if (player->getPeerId() == PEER_ID_INEXISTENT)
1568 PlayerSAO *sao = player->getPlayerSAO();
1569 if (sao && player->inventory.checkModified())
1570 m_server->SendInventory(sao, true);
1573 // Send outdated detached inventories
1574 m_server->sendDetachedInventories(PEER_ID_INEXISTENT, true);
1576 const auto end_time = porting::getTimeUs();
1577 m_step_time_counter->increment(end_time - start_time);
1580 ServerEnvironment::BlockStatus ServerEnvironment::getBlockStatus(v3s16 blockpos)
1582 if (m_active_blocks.contains(blockpos))
1585 const MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1586 if (block && !block->isDummy())
1589 if (m_map->isBlockInQueue(blockpos))
1595 u32 ServerEnvironment::addParticleSpawner(float exptime)
1597 // Timers with lifetime 0 do not expire
1598 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1601 for (;;) { // look for unused particlespawner id
1603 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1604 if (f == m_particle_spawners.end()) {
1605 m_particle_spawners[id] = time;
1612 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1614 u32 id = addParticleSpawner(exptime);
1615 m_particle_spawner_attachments[id] = attached_id;
1616 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1617 obj->attachParticleSpawner(id);
1622 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1624 m_particle_spawners.erase(id);
1625 const auto &it = m_particle_spawner_attachments.find(id);
1626 if (it != m_particle_spawner_attachments.end()) {
1627 u16 obj_id = it->second;
1628 ServerActiveObject *sao = getActiveObject(obj_id);
1629 if (sao != NULL && remove_from_object) {
1630 sao->detachParticleSpawner(id);
1632 m_particle_spawner_attachments.erase(id);
1636 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1638 assert(object); // Pre-condition
1640 u16 id = addActiveObjectRaw(object, true, 0);
1645 Finds out what new objects have been added to
1646 inside a radius around a position
1648 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1650 std::set<u16> ¤t_objects,
1651 std::queue<u16> &added_objects)
1653 f32 radius_f = radius * BS;
1654 f32 player_radius_f = player_radius * BS;
1656 if (player_radius_f < 0.0f)
1657 player_radius_f = 0.0f;
1659 m_ao_manager.getAddedActiveObjectsAroundPos(playersao->getBasePosition(), radius_f,
1660 player_radius_f, current_objects, added_objects);
1664 Finds out what objects have been removed from
1665 inside a radius around a position
1667 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1669 std::set<u16> ¤t_objects,
1670 std::queue<u16> &removed_objects)
1672 f32 radius_f = radius * BS;
1673 f32 player_radius_f = player_radius * BS;
1675 if (player_radius_f < 0)
1676 player_radius_f = 0;
1678 Go through current_objects; object is removed if:
1679 - object is not found in m_active_objects (this is actually an
1680 error condition; objects should be removed only after all clients
1681 have been informed about removal), or
1682 - object is to be removed or deactivated, or
1683 - object is too far away
1685 for (u16 id : current_objects) {
1686 ServerActiveObject *object = getActiveObject(id);
1688 if (object == NULL) {
1689 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1690 << " object in current_objects is NULL" << std::endl;
1691 removed_objects.push(id);
1695 if (object->isGone()) {
1696 removed_objects.push(id);
1700 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1701 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1702 if (distance_f <= player_radius_f || player_radius_f == 0)
1704 } else if (distance_f <= radius_f)
1707 // Object is no longer visible
1708 removed_objects.push(id);
1712 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1713 v3s16 blockpos, bool static_exists, v3s16 static_block)
1715 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1719 for (auto &so_it : block->m_static_objects.m_active) {
1720 // Get the ServerActiveObject counterpart to this StaticObject
1721 ServerActiveObject *sao = m_ao_manager.getActiveObject(so_it.first);
1723 // If this ever happens, there must be some kind of nasty bug.
1724 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1725 "Object from MapBlock::m_static_objects::m_active not found "
1726 "in m_active_objects";
1730 sao->m_static_exists = static_exists;
1731 sao->m_static_block = static_block;
1735 bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest)
1737 if(m_active_object_messages.empty())
1740 *dest = std::move(m_active_object_messages.front());
1741 m_active_object_messages.pop();
1745 void ServerEnvironment::getSelectedActiveObjects(
1746 const core::line3d<f32> &shootline_on_map,
1747 std::vector<PointedThing> &objects)
1749 std::vector<ServerActiveObject *> objs;
1750 getObjectsInsideRadius(objs, shootline_on_map.start,
1751 shootline_on_map.getLength() + 10.0f, nullptr);
1752 const v3f line_vector = shootline_on_map.getVector();
1754 for (auto obj : objs) {
1757 aabb3f selection_box;
1758 if (!obj->getSelectionBox(&selection_box))
1761 v3f pos = obj->getBasePosition();
1763 aabb3f offsetted_box(selection_box.MinEdge + pos,
1764 selection_box.MaxEdge + pos);
1766 v3f current_intersection;
1767 v3s16 current_normal;
1768 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1769 ¤t_intersection, ¤t_normal)) {
1770 objects.emplace_back(
1771 (s16) obj->getId(), current_intersection, current_normal,
1772 (current_intersection - shootline_on_map.start).getLengthSQ());
1778 ************ Private methods *************
1781 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1782 bool set_changed, u32 dtime_s)
1784 if (!m_ao_manager.registerObject(object)) {
1788 // Register reference in scripting api (must be done before post-init)
1789 m_script->addObjectReference(object);
1790 // Post-initialize object
1791 object->addedToEnvironment(dtime_s);
1793 // Add static data to block
1794 if (object->isStaticAllowed()) {
1795 // Add static object to active static list of the block
1796 v3f objectpos = object->getBasePosition();
1797 StaticObject s_obj(object, objectpos);
1798 // Add to the block where the object is located in
1799 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1800 MapBlock *block = m_map->emergeBlock(blockpos);
1802 block->m_static_objects.m_active[object->getId()] = s_obj;
1803 object->m_static_exists = true;
1804 object->m_static_block = blockpos;
1807 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1808 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1810 v3s16 p = floatToInt(objectpos, BS);
1811 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1812 <<"could not emerge block for storing id="<<object->getId()
1813 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1817 return object->getId();
1821 Remove objects that satisfy (isGone() && m_known_by_count==0)
1823 void ServerEnvironment::removeRemovedObjects()
1825 ScopeProfiler sp(g_profiler, "ServerEnvironment::removeRemovedObjects()", SPT_AVG);
1827 auto clear_cb = [this] (ServerActiveObject *obj, u16 id) {
1828 // This shouldn't happen but check it
1830 errorstream << "ServerEnvironment::removeRemovedObjects(): "
1831 << "NULL object found. id=" << id << std::endl;
1836 We will handle objects marked for removal or deactivation
1842 Delete static data from block if removed
1844 if (obj->isPendingRemoval())
1845 deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1847 // If still known by clients, don't actually remove. On some future
1848 // invocation this will be 0, which is when removal will continue.
1849 if(obj->m_known_by_count > 0)
1853 Move static data from active to stored if deactivated
1855 if (!obj->isPendingRemoval() && obj->m_static_exists) {
1856 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1858 const auto i = block->m_static_objects.m_active.find(id);
1859 if (i != block->m_static_objects.m_active.end()) {
1860 block->m_static_objects.m_stored.push_back(i->second);
1861 block->m_static_objects.m_active.erase(id);
1862 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1863 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1865 warningstream << "ServerEnvironment::removeRemovedObjects(): "
1866 << "id=" << id << " m_static_exists=true but "
1867 << "static data doesn't actually exist in "
1868 << PP(obj->m_static_block) << std::endl;
1871 infostream << "Failed to emerge block from which an object to "
1872 << "be deactivated was loaded from. id=" << id << std::endl;
1876 // Tell the object about removal
1877 obj->removingFromEnvironment();
1878 // Deregister in scripting api
1879 m_script->removeObjectReference(obj);
1882 if (obj->environmentDeletes())
1888 m_ao_manager.clear(clear_cb);
1891 static void print_hexdump(std::ostream &o, const std::string &data)
1893 const int linelength = 16;
1894 for(int l=0; ; l++){
1895 int i0 = linelength * l;
1896 bool at_end = false;
1897 int thislinelength = linelength;
1898 if(i0 + thislinelength > (int)data.size()){
1899 thislinelength = data.size() - i0;
1902 for(int di=0; di<linelength; di++){
1905 if(di<thislinelength)
1906 porting::mt_snprintf(buf, sizeof(buf), "%.2x ", data[i]);
1908 porting::mt_snprintf(buf, sizeof(buf), " ");
1912 for(int di=0; di<thislinelength; di++){
1925 ServerActiveObject* ServerEnvironment::createSAO(ActiveObjectType type, v3f pos,
1926 const std::string &data)
1929 case ACTIVEOBJECT_TYPE_LUAENTITY:
1930 return new LuaEntitySAO(this, pos, data);
1932 warningstream << "ServerActiveObject: No factory for type=" << type << std::endl;
1938 Convert stored objects from blocks near the players to active.
1940 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1945 // Ignore if no stored objects (to not set changed flag)
1946 if(block->m_static_objects.m_stored.empty())
1949 verbosestream<<"ServerEnvironment::activateObjects(): "
1950 <<"activating objects of block "<<PP(block->getPos())
1951 <<" ("<<block->m_static_objects.m_stored.size()
1952 <<" objects)"<<std::endl;
1953 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1955 errorstream<<"suspiciously large amount of objects detected: "
1956 <<block->m_static_objects.m_stored.size()<<" in "
1957 <<PP(block->getPos())
1958 <<"; removing all of them."<<std::endl;
1959 // Clear stored list
1960 block->m_static_objects.m_stored.clear();
1961 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1962 MOD_REASON_TOO_MANY_OBJECTS);
1966 // Activate stored objects
1967 std::vector<StaticObject> new_stored;
1968 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1969 // Create an active object from the data
1970 ServerActiveObject *obj = createSAO((ActiveObjectType) s_obj.type, s_obj.pos,
1972 // If couldn't create object, store static data back.
1974 errorstream<<"ServerEnvironment::activateObjects(): "
1975 <<"failed to create active object from static object "
1976 <<"in block "<<PP(s_obj.pos/BS)
1977 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1978 print_hexdump(verbosestream, s_obj.data);
1980 new_stored.push_back(s_obj);
1983 verbosestream<<"ServerEnvironment::activateObjects(): "
1984 <<"activated static object pos="<<PP(s_obj.pos/BS)
1985 <<" type="<<(int)s_obj.type<<std::endl;
1986 // This will also add the object to the active static list
1987 addActiveObjectRaw(obj, false, dtime_s);
1990 // Clear stored list
1991 block->m_static_objects.m_stored.clear();
1992 // Add leftover failed stuff to stored list
1993 for (const StaticObject &s_obj : new_stored) {
1994 block->m_static_objects.m_stored.push_back(s_obj);
1998 Note: Block hasn't really been modified here.
1999 The objects have just been activated and moved from the stored
2000 static list to the active static list.
2001 As such, the block is essentially the same.
2002 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2003 Otherwise there would be a huge amount of unnecessary I/O.
2008 Convert objects that are not standing inside active blocks to static.
2010 If m_known_by_count != 0, active object is not deleted, but static
2011 data is still updated.
2013 If force_delete is set, active object is deleted nevertheless. It
2014 shall only be set so in the destructor of the environment.
2016 If block wasn't generated (not in memory or on disk),
2018 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
2020 auto cb_deactivate = [this, _force_delete] (ServerActiveObject *obj, u16 id) {
2021 // force_delete might be overriden per object
2022 bool force_delete = _force_delete;
2024 // Do not deactivate if disallowed
2025 if (!force_delete && !obj->shouldUnload())
2028 // removeRemovedObjects() is responsible for these
2029 if (!force_delete && obj->isGone())
2032 const v3f &objectpos = obj->getBasePosition();
2034 // The block in which the object resides in
2035 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2037 // If object's static data is stored in a deactivated block and object
2038 // is actually located in an active block, re-save to the block in
2039 // which the object is actually located in.
2040 if (!force_delete && obj->m_static_exists &&
2041 !m_active_blocks.contains(obj->m_static_block) &&
2042 m_active_blocks.contains(blockpos_o)) {
2044 // Delete from block where object was located
2045 deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
2047 StaticObject s_obj(obj, objectpos);
2048 // Save to block where object is located
2049 saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
2054 // If block is still active, don't remove
2055 bool still_active = obj->isStaticAllowed() ?
2056 m_active_blocks.contains(blockpos_o) :
2057 getMap().getBlockNoCreateNoEx(blockpos_o) != nullptr;
2058 if (!force_delete && still_active)
2061 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2062 << "deactivating object id=" << id << " on inactive block "
2063 << PP(blockpos_o) << std::endl;
2065 // If known by some client, don't immediately delete.
2066 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2069 Update the static data
2071 if (obj->isStaticAllowed()) {
2072 // Create new static object
2073 StaticObject s_obj(obj, objectpos);
2075 bool stays_in_same_block = false;
2076 bool data_changed = true;
2078 // Check if static data has changed considerably
2079 if (obj->m_static_exists) {
2080 if (obj->m_static_block == blockpos_o)
2081 stays_in_same_block = true;
2083 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2086 const auto n = block->m_static_objects.m_active.find(id);
2087 if (n != block->m_static_objects.m_active.end()) {
2088 StaticObject static_old = n->second;
2090 float save_movem = obj->getMinimumSavedMovement();
2092 if (static_old.data == s_obj.data &&
2093 (static_old.pos - objectpos).getLength() < save_movem)
2094 data_changed = false;
2096 warningstream << "ServerEnvironment::deactivateFarObjects(): "
2097 << "id=" << id << " m_static_exists=true but "
2098 << "static data doesn't actually exist in "
2099 << PP(obj->m_static_block) << std::endl;
2105 While changes are always saved, blocks are only marked as modified
2106 if the object has moved or different staticdata. (see above)
2108 bool shall_be_written = (!stays_in_same_block || data_changed);
2109 u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2111 // Delete old static object
2112 deleteStaticFromBlock(obj, id, reason, false);
2114 // Add to the block where the object is located in
2115 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2116 u16 store_id = pending_delete ? id : 0;
2117 if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2118 force_delete = true;
2121 // Regardless of what happens to the object at this point, deactivate it first.
2122 // This ensures that LuaEntity on_deactivate is always called.
2123 obj->markForDeactivation();
2126 If known by some client, set pending deactivation.
2127 Otherwise delete it immediately.
2129 if (pending_delete && !force_delete) {
2130 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2131 << "object id=" << id << " is known by clients"
2132 << "; not deleting yet" << std::endl;
2137 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2138 << "object id=" << id << " is not known by clients"
2139 << "; deleting" << std::endl;
2141 // Tell the object about removal
2142 obj->removingFromEnvironment();
2143 // Deregister in scripting api
2144 m_script->removeObjectReference(obj);
2146 // Delete active object
2147 if (obj->environmentDeletes())
2153 m_ao_manager.clear(cb_deactivate);
2156 void ServerEnvironment::deleteStaticFromBlock(
2157 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2159 if (!obj->m_static_exists)
2164 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2166 block = m_map->emergeBlock(obj->m_static_block, false);
2169 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2170 << " when deleting static data of object from it. id=" << id << std::endl;
2174 block->m_static_objects.remove(id);
2175 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2176 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2178 obj->m_static_exists = false;
2181 bool ServerEnvironment::saveStaticToBlock(
2182 v3s16 blockpos, u16 store_id,
2183 ServerActiveObject *obj, const StaticObject &s_obj,
2186 MapBlock *block = nullptr;
2188 block = m_map->emergeBlock(blockpos);
2189 } catch (InvalidPositionException &e) {
2190 // Handled via NULL pointer
2191 // NOTE: emergeBlock's failure is usually determined by it
2192 // actually returning NULL
2196 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2197 << " when saving static data of object to it. id=" << store_id << std::endl;
2200 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2201 warningstream << "ServerEnv: Trying to store id = " << store_id
2202 << " statically but block " << PP(blockpos)
2203 << " already contains "
2204 << block->m_static_objects.m_stored.size()
2205 << " objects." << std::endl;
2209 block->m_static_objects.insert(store_id, s_obj);
2210 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2211 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2213 obj->m_static_exists = true;
2214 obj->m_static_block = blockpos;
2219 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2220 const std::string &savedir, const Settings &conf)
2223 if (name == "sqlite3")
2224 return new PlayerDatabaseSQLite3(savedir);
2226 if (name == "dummy")
2227 return new Database_Dummy();
2230 if (name == "postgresql") {
2231 std::string connect_string;
2232 conf.getNoEx("pgsql_player_connection", connect_string);
2233 return new PlayerDatabasePostgreSQL(connect_string);
2238 if (name == "leveldb")
2239 return new PlayerDatabaseLevelDB(savedir);
2242 if (name == "files")
2243 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2245 throw BaseException(std::string("Database backend ") + name + " not supported.");
2248 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2249 const Settings &cmd_args)
2251 std::string migrate_to = cmd_args.get("migrate-players");
2253 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2254 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2255 errorstream << "Cannot read world.mt!" << std::endl;
2259 if (!world_mt.exists("player_backend")) {
2260 errorstream << "Please specify your current backend in world.mt:"
2262 << " player_backend = {files|sqlite3|leveldb|postgresql}"
2267 std::string backend = world_mt.get("player_backend");
2268 if (backend == migrate_to) {
2269 errorstream << "Cannot migrate: new backend is same"
2270 << " as the old one" << std::endl;
2274 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2277 if (backend == "files") {
2278 // Create backup directory
2279 fs::CreateDir(players_backup_path);
2283 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2284 game_params.world_path, world_mt);
2285 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2286 game_params.world_path, world_mt);
2288 std::vector<std::string> player_list;
2289 srcdb->listPlayers(player_list);
2290 for (std::vector<std::string>::const_iterator it = player_list.begin();
2291 it != player_list.end(); ++it) {
2292 actionstream << "Migrating player " << it->c_str() << std::endl;
2293 RemotePlayer player(it->c_str(), NULL);
2294 PlayerSAO playerSAO(NULL, &player, 15000, false);
2296 srcdb->loadPlayer(&player, &playerSAO);
2298 playerSAO.finalize(&player, std::set<std::string>());
2299 player.setPlayerSAO(&playerSAO);
2301 dstdb->savePlayer(&player);
2303 // For files source, move player files to backup dir
2304 if (backend == "files") {
2306 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2307 players_backup_path + DIR_DELIM + (*it));
2311 actionstream << "Successfully migrated " << player_list.size() << " players"
2313 world_mt.set("player_backend", migrate_to);
2314 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2315 errorstream << "Failed to update world.mt!" << std::endl;
2317 actionstream << "world.mt updated" << std::endl;
2319 // When migration is finished from file backend, remove players directory if empty
2320 if (backend == "files") {
2321 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2328 } catch (BaseException &e) {
2329 errorstream << "An error occurred during migration: " << e.what() << std::endl;
2335 AuthDatabase *ServerEnvironment::openAuthDatabase(
2336 const std::string &name, const std::string &savedir, const Settings &conf)
2338 if (name == "sqlite3")
2339 return new AuthDatabaseSQLite3(savedir);
2342 if (name == "postgresql") {
2343 std::string connect_string;
2344 conf.getNoEx("pgsql_auth_connection", connect_string);
2345 return new AuthDatabasePostgreSQL(connect_string);
2349 if (name == "files")
2350 return new AuthDatabaseFiles(savedir);
2353 if (name == "leveldb")
2354 return new AuthDatabaseLevelDB(savedir);
2357 throw BaseException(std::string("Database backend ") + name + " not supported.");
2360 bool ServerEnvironment::migrateAuthDatabase(
2361 const GameParams &game_params, const Settings &cmd_args)
2363 std::string migrate_to = cmd_args.get("migrate-auth");
2365 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2366 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2367 errorstream << "Cannot read world.mt!" << std::endl;
2371 std::string backend = "files";
2372 if (world_mt.exists("auth_backend"))
2373 backend = world_mt.get("auth_backend");
2375 warningstream << "No auth_backend found in world.mt, "
2376 "assuming \"files\"." << std::endl;
2378 if (backend == migrate_to) {
2379 errorstream << "Cannot migrate: new backend is same"
2380 << " as the old one" << std::endl;
2385 const std::unique_ptr<AuthDatabase> srcdb(ServerEnvironment::openAuthDatabase(
2386 backend, game_params.world_path, world_mt));
2387 const std::unique_ptr<AuthDatabase> dstdb(ServerEnvironment::openAuthDatabase(
2388 migrate_to, game_params.world_path, world_mt));
2390 std::vector<std::string> names_list;
2391 srcdb->listNames(names_list);
2392 for (const std::string &name : names_list) {
2393 actionstream << "Migrating auth entry for " << name << std::endl;
2395 AuthEntry authEntry;
2396 success = srcdb->getAuth(name, authEntry);
2397 success = success && dstdb->createAuth(authEntry);
2399 errorstream << "Failed to migrate " << name << std::endl;
2402 actionstream << "Successfully migrated " << names_list.size()
2403 << " auth entries" << std::endl;
2404 world_mt.set("auth_backend", migrate_to);
2405 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2406 errorstream << "Failed to update world.mt!" << std::endl;
2408 actionstream << "world.mt updated" << std::endl;
2410 if (backend == "files") {
2411 // special-case files migration:
2412 // move auth.txt to auth.txt.bak if possible
2413 std::string auth_txt_path =
2414 game_params.world_path + DIR_DELIM + "auth.txt";
2415 std::string auth_bak_path = auth_txt_path + ".bak";
2416 if (!fs::PathExists(auth_bak_path))
2417 if (fs::Rename(auth_txt_path, auth_bak_path))
2418 actionstream << "Renamed auth.txt to auth.txt.bak"
2421 errorstream << "Could not rename auth.txt to "
2422 "auth.txt.bak" << std::endl;
2424 warningstream << "auth.txt.bak already exists, auth.txt "
2425 "not renamed" << std::endl;
2428 } catch (BaseException &e) {
2429 errorstream << "An error occurred during migration: " << e.what()