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);
632 void ServerEnvironment::saveMeta()
637 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
639 // Open file and serialize
640 std::ostringstream ss(std::ios_base::binary);
642 Settings args("EnvArgsEnd");
643 args.setU64("game_time", m_game_time);
644 args.setU64("time_of_day", getTimeOfDay());
645 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
646 args.setU64("lbm_introduction_times_version", 1);
647 args.set("lbm_introduction_times",
648 m_lbm_mgr.createIntroductionTimesString());
649 args.setU64("day_count", m_day_count);
652 if(!fs::safeWriteToFile(path, ss.str()))
654 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
656 throw SerializationError("Couldn't save env meta");
660 void ServerEnvironment::loadMeta()
662 SANITY_CHECK(!m_meta_loaded);
663 m_meta_loaded = true;
665 // If file doesn't exist, load default environment metadata
666 if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
667 infostream << "ServerEnvironment: Loading default environment metadata"
673 infostream << "ServerEnvironment: Loading environment metadata" << std::endl;
675 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
677 // Open file and deserialize
678 std::ifstream is(path.c_str(), std::ios_base::binary);
680 infostream << "ServerEnvironment::loadMeta(): Failed to open "
681 << path << std::endl;
682 throw SerializationError("Couldn't load env meta");
685 Settings args("EnvArgsEnd");
687 if (!args.parseConfigLines(is)) {
688 throw SerializationError("ServerEnvironment::loadMeta(): "
689 "EnvArgsEnd not found!");
693 m_game_time = args.getU64("game_time");
694 } catch (SettingNotFoundException &e) {
695 // Getting this is crucial, otherwise timestamps are useless
696 throw SerializationError("Couldn't load env meta game_time");
699 setTimeOfDay(args.exists("time_of_day") ?
700 // set day to early morning by default
701 args.getU64("time_of_day") : 5250);
703 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
704 // If missing, do as if clearObjects was never called
705 args.getU64("last_clear_objects_time") : 0;
707 std::string lbm_introduction_times;
709 u64 ver = args.getU64("lbm_introduction_times_version");
711 lbm_introduction_times = args.get("lbm_introduction_times");
713 infostream << "ServerEnvironment::loadMeta(): Non-supported"
714 << " introduction time version " << ver << std::endl;
716 } catch (SettingNotFoundException &e) {
717 // No problem, this is expected. Just continue with an empty string
719 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
721 m_day_count = args.exists("day_count") ?
722 args.getU64("day_count") : 0;
726 * called if env_meta.txt doesn't exist (e.g. new world)
728 void ServerEnvironment::loadDefaultMeta()
730 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
735 ActiveBlockModifier *abm;
737 std::vector<content_t> required_neighbors;
738 bool check_required_neighbors; // false if required_neighbors is known to be empty
746 ServerEnvironment *m_env;
747 std::vector<std::vector<ActiveABM> *> m_aabms;
749 ABMHandler(std::vector<ABMWithState> &abms,
750 float dtime_s, ServerEnvironment *env,
756 const NodeDefManager *ndef = env->getGameDef()->ndef();
757 for (ABMWithState &abmws : abms) {
758 ActiveBlockModifier *abm = abmws.abm;
759 float trigger_interval = abm->getTriggerInterval();
760 if(trigger_interval < 0.001)
761 trigger_interval = 0.001;
762 float actual_interval = dtime_s;
764 abmws.timer += dtime_s;
765 if(abmws.timer < trigger_interval)
767 abmws.timer -= trigger_interval;
768 actual_interval = trigger_interval;
770 float chance = abm->getTriggerChance();
775 if (abm->getSimpleCatchUp()) {
776 float intervals = actual_interval / trigger_interval;
779 aabm.chance = chance / intervals;
783 aabm.chance = chance;
786 aabm.min_y = abm->getMinY();
787 aabm.max_y = abm->getMaxY();
790 const std::vector<std::string> &required_neighbors_s =
791 abm->getRequiredNeighbors();
792 for (const std::string &required_neighbor_s : required_neighbors_s) {
793 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
795 aabm.check_required_neighbors = !required_neighbors_s.empty();
798 const std::vector<std::string> &contents_s = abm->getTriggerContents();
799 for (const std::string &content_s : contents_s) {
800 std::vector<content_t> ids;
801 ndef->getIds(content_s, ids);
802 for (content_t c : ids) {
803 if (c >= m_aabms.size())
804 m_aabms.resize(c + 256, NULL);
806 m_aabms[c] = new std::vector<ActiveABM>;
807 m_aabms[c]->push_back(aabm);
815 for (auto &aabms : m_aabms)
819 // Find out how many objects the given block and its neighbours contain.
820 // Returns the number of objects in the block, and also in 'wider' the
821 // number of objects in the block and all its neighbours. The latter
822 // may an estimate if any neighbours are unloaded.
823 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
826 u32 wider_unknown_count = 0;
827 for(s16 x=-1; x<=1; x++)
828 for(s16 y=-1; y<=1; y++)
829 for(s16 z=-1; z<=1; z++)
831 MapBlock *block2 = map->getBlockNoCreateNoEx(
832 block->getPos() + v3s16(x,y,z));
834 wider_unknown_count++;
837 wider += block2->m_static_objects.m_active.size()
838 + block2->m_static_objects.m_stored.size();
841 u32 active_object_count = block->m_static_objects.m_active.size();
842 u32 wider_known_count = 3*3*3 - wider_unknown_count;
843 wider += wider_unknown_count * wider / wider_known_count;
844 return active_object_count;
847 void apply(MapBlock *block, int &blocks_scanned, int &abms_run, int &blocks_cached)
849 if(m_aabms.empty() || block->isDummy())
852 // Check the content type cache first
853 // to see whether there are any ABMs
854 // to be run at all for this block.
855 if (block->contents_cached) {
857 bool run_abms = false;
858 for (content_t c : block->contents) {
859 if (c < m_aabms.size() && m_aabms[c]) {
868 block->contents.clear();
872 ServerMap *map = &m_env->getServerMap();
874 u32 active_object_count_wider;
875 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
876 m_env->m_added_objects = 0;
879 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
880 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
881 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
883 const MapNode &n = block->getNodeUnsafe(p0);
884 content_t c = n.getContent();
885 // Cache content types as we go
886 if (!block->contents_cached && !block->do_not_cache_contents) {
887 block->contents.insert(c);
888 if (block->contents.size() > 64) {
889 // Too many different nodes... don't try to cache
890 block->do_not_cache_contents = true;
891 block->contents.clear();
895 if (c >= m_aabms.size() || !m_aabms[c])
898 v3s16 p = p0 + block->getPosRelative();
899 for (ActiveABM &aabm : *m_aabms[c]) {
900 if ((p.Y < aabm.min_y) || (p.Y > aabm.max_y))
903 if (myrand() % aabm.chance != 0)
907 if (aabm.check_required_neighbors) {
909 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
910 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
911 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
916 if (block->isValidPosition(p1)) {
917 // if the neighbor is found on the same map block
918 // get it straight from there
919 const MapNode &n = block->getNodeUnsafe(p1);
922 // otherwise consult the map
923 MapNode n = map->getNode(p1 + block->getPosRelative());
926 if (CONTAINS(aabm.required_neighbors, c))
929 // No required neighbor found
935 // Call all the trigger variations
936 aabm.abm->trigger(m_env, p, n);
937 aabm.abm->trigger(m_env, p, n,
938 active_object_count, active_object_count_wider);
940 // Count surrounding objects again if the abms added any
941 if(m_env->m_added_objects > 0) {
942 active_object_count = countObjects(block, map, active_object_count_wider);
943 m_env->m_added_objects = 0;
947 block->contents_cached = !block->do_not_cache_contents;
951 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
953 // Reset usage timer immediately, otherwise a block that becomes active
954 // again at around the same time as it would normally be unloaded will
955 // get unloaded incorrectly. (I think this still leaves a small possibility
956 // of a race condition between this and server::AsyncRunStep, which only
957 // some kind of synchronisation will fix, but it at least reduces the window
958 // of opportunity for it to break from seconds to nanoseconds)
959 block->resetUsageTimer();
961 // Get time difference
963 u32 stamp = block->getTimestamp();
964 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
965 dtime_s = m_game_time - stamp;
966 dtime_s += additional_dtime;
968 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
969 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
971 // Remove stored static objects if clearObjects was called since block's timestamp
972 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
973 block->m_static_objects.m_stored.clear();
974 // do not set changed flag to avoid unnecessary mapblock writes
977 // Set current time as timestamp
978 block->setTimestampNoChangedFlag(m_game_time);
980 /*infostream<<"ServerEnvironment::activateBlock(): block is "
981 <<dtime_s<<" seconds old."<<std::endl;*/
983 // Activate stored objects
984 activateObjects(block, dtime_s);
986 /* Handle LoadingBlockModifiers */
987 m_lbm_mgr.applyLBMs(this, block, stamp);
990 std::vector<NodeTimer> elapsed_timers =
991 block->m_node_timers.step((float)dtime_s);
992 if (!elapsed_timers.empty()) {
994 for (const NodeTimer &elapsed_timer : elapsed_timers) {
995 n = block->getNodeNoEx(elapsed_timer.position);
996 v3s16 p = elapsed_timer.position + block->getPosRelative();
997 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
998 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
999 elapsed_timer.position));
1004 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
1006 m_abms.emplace_back(abm);
1009 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1011 m_lbm_mgr.addLBMDef(lbm);
1014 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1016 const NodeDefManager *ndef = m_server->ndef();
1017 MapNode n_old = m_map->getNode(p);
1019 const ContentFeatures &cf_old = ndef->get(n_old);
1022 if (cf_old.has_on_destruct)
1023 m_script->node_on_destruct(p, n_old);
1026 if (!m_map->addNodeWithEvent(p, n))
1029 // Update active VoxelManipulator if a mapgen thread
1030 m_map->updateVManip(p);
1032 // Call post-destructor
1033 if (cf_old.has_after_destruct)
1034 m_script->node_after_destruct(p, n_old);
1036 // Retrieve node content features
1037 // if new node is same as old, reuse old definition to prevent a lookup
1038 const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
1041 if (cf_new.has_on_construct)
1042 m_script->node_on_construct(p, n);
1047 bool ServerEnvironment::removeNode(v3s16 p)
1049 const NodeDefManager *ndef = m_server->ndef();
1050 MapNode n_old = m_map->getNode(p);
1053 if (ndef->get(n_old).has_on_destruct)
1054 m_script->node_on_destruct(p, n_old);
1057 // This is slightly optimized compared to addNodeWithEvent(air)
1058 if (!m_map->removeNodeWithEvent(p))
1061 // Update active VoxelManipulator if a mapgen thread
1062 m_map->updateVManip(p);
1064 // Call post-destructor
1065 if (ndef->get(n_old).has_after_destruct)
1066 m_script->node_after_destruct(p, n_old);
1068 // Air doesn't require constructor
1072 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1074 if (!m_map->addNodeWithEvent(p, n, false))
1077 // Update active VoxelManipulator if a mapgen thread
1078 m_map->updateVManip(p);
1083 u8 ServerEnvironment::findSunlight(v3s16 pos) const
1085 // Directions for neighbouring nodes with specified order
1086 static const v3s16 dirs[] = {
1087 v3s16(-1, 0, 0), v3s16(1, 0, 0), v3s16(0, 0, -1), v3s16(0, 0, 1),
1088 v3s16(0, -1, 0), v3s16(0, 1, 0)
1091 const NodeDefManager *ndef = m_server->ndef();
1093 // found_light remembers the highest known sunlight value at pos
1096 struct stack_entry {
1100 std::stack<stack_entry> stack;
1101 stack.push({pos, 0});
1103 std::unordered_map<s64, s8> dists;
1104 dists[MapDatabase::getBlockAsInteger(pos)] = 0;
1106 while (!stack.empty()) {
1107 struct stack_entry e = stack.top();
1110 v3s16 currentPos = e.pos;
1111 s8 dist = e.dist + 1;
1113 for (const v3s16& off : dirs) {
1114 v3s16 neighborPos = currentPos + off;
1115 s64 neighborHash = MapDatabase::getBlockAsInteger(neighborPos);
1117 // Do not walk neighborPos multiple times unless the distance to the start
1118 // position is shorter
1119 auto it = dists.find(neighborHash);
1120 if (it != dists.end() && dist >= it->second)
1124 bool is_position_ok;
1125 MapNode node = m_map->getNode(neighborPos, &is_position_ok);
1126 if (!is_position_ok) {
1127 // This happens very rarely because the map at currentPos is loaded
1128 m_map->emergeBlock(neighborPos, false);
1129 node = m_map->getNode(neighborPos, &is_position_ok);
1130 if (!is_position_ok)
1131 continue; // not generated
1134 const ContentFeatures &def = ndef->get(node);
1135 if (!def.sunlight_propagates) {
1136 // Do not test propagation here again
1137 dists[neighborHash] = -1;
1141 // Sunlight could have come from here
1142 dists[neighborHash] = dist;
1143 u8 daylight = node.param1 & 0x0f;
1145 // In the special case where sunlight shines from above and thus
1146 // does not decrease with upwards distance, daylight is always
1147 // bigger than nightlight, which never reaches 15
1148 int possible_finlight = daylight - dist;
1149 if (possible_finlight <= found_light) {
1150 // Light from here cannot make a brighter light at currentPos than
1155 u8 nightlight = node.param1 >> 4;
1156 if (daylight > nightlight) {
1157 // Found a valid daylight
1158 found_light = possible_finlight;
1160 // Sunlight may be darker, so walk the neighbours
1161 stack.push({neighborPos, dist});
1168 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1170 infostream << "ServerEnvironment::clearObjects(): "
1171 << "Removing all active objects" << std::endl;
1172 auto cb_removal = [this] (ServerActiveObject *obj, u16 id) {
1173 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1176 // Delete static object if block is loaded
1177 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1179 // If known by some client, don't delete immediately
1180 if (obj->m_known_by_count > 0) {
1181 obj->markForRemoval();
1185 // Tell the object about removal
1186 obj->removingFromEnvironment();
1187 // Deregister in scripting api
1188 m_script->removeObjectReference(dynamic_cast<ActiveObject *>(obj));
1190 // Delete active object
1191 if (obj->environmentDeletes())
1197 m_ao_manager.clear(cb_removal);
1199 // Get list of loaded blocks
1200 std::vector<v3s16> loaded_blocks;
1201 infostream << "ServerEnvironment::clearObjects(): "
1202 << "Listing all loaded blocks" << std::endl;
1203 m_map->listAllLoadedBlocks(loaded_blocks);
1204 infostream << "ServerEnvironment::clearObjects(): "
1205 << "Done listing all loaded blocks: "
1206 << loaded_blocks.size()<<std::endl;
1208 // Get list of loadable blocks
1209 std::vector<v3s16> loadable_blocks;
1210 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1211 infostream << "ServerEnvironment::clearObjects(): "
1212 << "Listing all loadable blocks" << std::endl;
1213 m_map->listAllLoadableBlocks(loadable_blocks);
1214 infostream << "ServerEnvironment::clearObjects(): "
1215 << "Done listing all loadable blocks: "
1216 << loadable_blocks.size() << std::endl;
1218 loadable_blocks = loaded_blocks;
1221 actionstream << "ServerEnvironment::clearObjects(): "
1222 << "Now clearing objects in " << loadable_blocks.size()
1223 << " blocks" << std::endl;
1225 // Grab a reference on each loaded block to avoid unloading it
1226 for (v3s16 p : loaded_blocks) {
1227 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1228 assert(block != NULL);
1232 // Remove objects in all loadable blocks
1233 u32 unload_interval = U32_MAX;
1234 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1235 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1236 unload_interval = MYMAX(unload_interval, 1);
1238 u32 report_interval = loadable_blocks.size() / 10;
1239 u32 num_blocks_checked = 0;
1240 u32 num_blocks_cleared = 0;
1241 u32 num_objs_cleared = 0;
1242 for (auto i = loadable_blocks.begin();
1243 i != loadable_blocks.end(); ++i) {
1245 MapBlock *block = m_map->emergeBlock(p, false);
1247 errorstream << "ServerEnvironment::clearObjects(): "
1248 << "Failed to emerge block " << PP(p) << std::endl;
1251 u32 num_stored = block->m_static_objects.m_stored.size();
1252 u32 num_active = block->m_static_objects.m_active.size();
1253 if (num_stored != 0 || num_active != 0) {
1254 block->m_static_objects.m_stored.clear();
1255 block->m_static_objects.m_active.clear();
1256 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1257 MOD_REASON_CLEAR_ALL_OBJECTS);
1258 num_objs_cleared += num_stored + num_active;
1259 num_blocks_cleared++;
1261 num_blocks_checked++;
1263 if (report_interval != 0 &&
1264 num_blocks_checked % report_interval == 0) {
1265 float percent = 100.0 * (float)num_blocks_checked /
1266 loadable_blocks.size();
1267 actionstream << "ServerEnvironment::clearObjects(): "
1268 << "Cleared " << num_objs_cleared << " objects"
1269 << " in " << num_blocks_cleared << " blocks ("
1270 << percent << "%)" << std::endl;
1272 if (num_blocks_checked % unload_interval == 0) {
1273 m_map->unloadUnreferencedBlocks();
1276 m_map->unloadUnreferencedBlocks();
1278 // Drop references that were added above
1279 for (v3s16 p : loaded_blocks) {
1280 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1285 m_last_clear_objects_time = m_game_time;
1287 actionstream << "ServerEnvironment::clearObjects(): "
1288 << "Finished: Cleared " << num_objs_cleared << " objects"
1289 << " in " << num_blocks_cleared << " blocks" << std::endl;
1292 void ServerEnvironment::step(float dtime)
1294 ScopeProfiler sp2(g_profiler, "ServerEnv::step()", SPT_AVG);
1295 const auto start_time = porting::getTimeUs();
1297 /* Step time of day */
1298 stepTimeOfDay(dtime);
1301 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1302 // really matter that much.
1303 static thread_local const float server_step =
1304 g_settings->getFloat("dedicated_server_step");
1305 m_recommended_send_interval = server_step;
1311 m_game_time_fraction_counter += dtime;
1312 u32 inc_i = (u32)m_game_time_fraction_counter;
1313 m_game_time += inc_i;
1314 m_game_time_fraction_counter -= (float)inc_i;
1321 ScopeProfiler sp(g_profiler, "ServerEnv: move players", SPT_AVG);
1322 for (RemotePlayer *player : m_players) {
1323 // Ignore disconnected players
1324 if (player->getPeerId() == PEER_ID_INEXISTENT)
1328 player->move(dtime, this, 100 * BS);
1333 Manage active block list
1335 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1336 ScopeProfiler sp(g_profiler, "ServerEnv: update active blocks", SPT_AVG);
1338 Get player block positions
1340 std::vector<PlayerSAO*> players;
1341 for (RemotePlayer *player: m_players) {
1342 // Ignore disconnected players
1343 if (player->getPeerId() == PEER_ID_INEXISTENT)
1346 PlayerSAO *playersao = player->getPlayerSAO();
1349 players.push_back(playersao);
1353 Update list of active blocks, collecting changes
1355 // use active_object_send_range_blocks since that is max distance
1356 // for active objects sent the client anyway
1357 static thread_local const s16 active_object_range =
1358 g_settings->getS16("active_object_send_range_blocks");
1359 static thread_local const s16 active_block_range =
1360 g_settings->getS16("active_block_range");
1361 std::set<v3s16> blocks_removed;
1362 std::set<v3s16> blocks_added;
1363 m_active_blocks.update(players, active_block_range, active_object_range,
1364 blocks_removed, blocks_added);
1366 m_active_block_gauge->set(m_active_blocks.size());
1369 Handle removed blocks
1372 // Convert active objects that are no more in active blocks to static
1373 deactivateFarObjects(false);
1375 for (const v3s16 &p: blocks_removed) {
1376 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1380 // Set current time as timestamp (and let it set ChangedFlag)
1381 block->setTimestamp(m_game_time);
1388 for (const v3s16 &p: blocks_added) {
1389 MapBlock *block = m_map->getBlockOrEmerge(p);
1391 m_active_blocks.m_list.erase(p);
1392 m_active_blocks.m_abm_list.erase(p);
1396 activateBlock(block);
1401 Mess around in active blocks
1403 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1404 ScopeProfiler sp(g_profiler, "ServerEnv: Run node timers", SPT_AVG);
1406 float dtime = m_cache_nodetimer_interval;
1408 for (const v3s16 &p: m_active_blocks.m_list) {
1409 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1413 // Reset block usage timer
1414 block->resetUsageTimer();
1416 // Set current time as timestamp
1417 block->setTimestampNoChangedFlag(m_game_time);
1418 // If time has changed much from the one on disk,
1419 // set block to be saved when it is unloaded
1420 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1421 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1422 MOD_REASON_BLOCK_EXPIRED);
1425 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1426 if (!elapsed_timers.empty()) {
1429 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1430 n = block->getNodeNoEx(elapsed_timer.position);
1431 p2 = elapsed_timer.position + block->getPosRelative();
1432 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1433 block->setNodeTimer(NodeTimer(
1434 elapsed_timer.timeout, 0, elapsed_timer.position));
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 // Initialize handling of ActiveBlockModifiers
1446 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1448 int blocks_scanned = 0;
1450 int blocks_cached = 0;
1452 std::vector<v3s16> output(m_active_blocks.m_abm_list.size());
1454 // Shuffle the active blocks so that each block gets an equal chance
1455 // of having its ABMs run.
1456 std::copy(m_active_blocks.m_abm_list.begin(), m_active_blocks.m_abm_list.end(), output.begin());
1457 std::shuffle(output.begin(), output.end(), m_rgen);
1460 // determine the time budget for ABMs
1461 u32 max_time_ms = m_cache_abm_interval * 1000 * m_cache_abm_time_budget;
1462 for (const v3s16 &p : output) {
1463 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1469 // Set current time as timestamp
1470 block->setTimestampNoChangedFlag(m_game_time);
1472 /* Handle ActiveBlockModifiers */
1473 abmhandler.apply(block, blocks_scanned, abms_run, blocks_cached);
1475 u32 time_ms = timer.getTimerTime();
1477 if (time_ms > max_time_ms) {
1478 warningstream << "active block modifiers took "
1479 << time_ms << "ms (processed " << i << " of "
1480 << output.size() << " active blocks)" << std::endl;
1484 g_profiler->avg("ServerEnv: active blocks", m_active_blocks.m_abm_list.size());
1485 g_profiler->avg("ServerEnv: active blocks cached", blocks_cached);
1486 g_profiler->avg("ServerEnv: active blocks scanned for ABMs", blocks_scanned);
1487 g_profiler->avg("ServerEnv: ABMs run", abms_run);
1493 Step script environment (run global on_step())
1495 m_script->environment_Step(dtime);
1497 m_script->stepAsync();
1503 ScopeProfiler sp(g_profiler, "ServerEnv: Run SAO::step()", SPT_AVG);
1505 // This helps the objects to send data at the same time
1506 bool send_recommended = false;
1507 m_send_recommended_timer += dtime;
1508 if (m_send_recommended_timer > getSendRecommendedInterval()) {
1509 m_send_recommended_timer -= getSendRecommendedInterval();
1510 send_recommended = true;
1513 u32 object_count = 0;
1515 auto cb_state = [&] (ServerActiveObject *obj) {
1521 obj->step(dtime, send_recommended);
1522 // Read messages from object
1523 obj->dumpAOMessagesToQueue(m_active_object_messages);
1525 m_ao_manager.step(dtime, cb_state);
1527 m_active_object_gauge->set(object_count);
1531 Manage active objects
1533 if (m_object_management_interval.step(dtime, 0.5)) {
1534 removeRemovedObjects();
1538 Manage particle spawner expiration
1540 if (m_particle_management_interval.step(dtime, 1.0)) {
1541 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1542 i != m_particle_spawners.end(); ) {
1543 //non expiring spawners
1544 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1550 if (i->second <= 0.f)
1551 m_particle_spawners.erase(i++);
1557 // Send outdated player inventories
1558 for (RemotePlayer *player : m_players) {
1559 if (player->getPeerId() == PEER_ID_INEXISTENT)
1562 PlayerSAO *sao = player->getPlayerSAO();
1563 if (sao && player->inventory.checkModified())
1564 m_server->SendInventory(sao, true);
1567 // Send outdated detached inventories
1568 m_server->sendDetachedInventories(PEER_ID_INEXISTENT, true);
1570 const auto end_time = porting::getTimeUs();
1571 m_step_time_counter->increment(end_time - start_time);
1574 ServerEnvironment::BlockStatus ServerEnvironment::getBlockStatus(v3s16 blockpos)
1576 if (m_active_blocks.contains(blockpos))
1579 const MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1580 if (block && !block->isDummy())
1583 if (m_map->isBlockInQueue(blockpos))
1589 u32 ServerEnvironment::addParticleSpawner(float exptime)
1591 // Timers with lifetime 0 do not expire
1592 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1595 for (;;) { // look for unused particlespawner id
1597 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1598 if (f == m_particle_spawners.end()) {
1599 m_particle_spawners[id] = time;
1606 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1608 u32 id = addParticleSpawner(exptime);
1609 m_particle_spawner_attachments[id] = attached_id;
1610 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1611 obj->attachParticleSpawner(id);
1616 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1618 m_particle_spawners.erase(id);
1619 const auto &it = m_particle_spawner_attachments.find(id);
1620 if (it != m_particle_spawner_attachments.end()) {
1621 u16 obj_id = it->second;
1622 ServerActiveObject *sao = getActiveObject(obj_id);
1623 if (sao != NULL && remove_from_object) {
1624 sao->detachParticleSpawner(id);
1626 m_particle_spawner_attachments.erase(id);
1630 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1632 assert(object); // Pre-condition
1634 u16 id = addActiveObjectRaw(object, true, 0);
1639 Finds out what new objects have been added to
1640 inside a radius around a position
1642 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1644 std::set<u16> ¤t_objects,
1645 std::queue<u16> &added_objects)
1647 f32 radius_f = radius * BS;
1648 f32 player_radius_f = player_radius * BS;
1650 if (player_radius_f < 0.0f)
1651 player_radius_f = 0.0f;
1653 m_ao_manager.getAddedActiveObjectsAroundPos(playersao->getBasePosition(), radius_f,
1654 player_radius_f, current_objects, added_objects);
1658 Finds out what objects have been removed from
1659 inside a radius around a position
1661 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1663 std::set<u16> ¤t_objects,
1664 std::queue<u16> &removed_objects)
1666 f32 radius_f = radius * BS;
1667 f32 player_radius_f = player_radius * BS;
1669 if (player_radius_f < 0)
1670 player_radius_f = 0;
1672 Go through current_objects; object is removed if:
1673 - object is not found in m_active_objects (this is actually an
1674 error condition; objects should be removed only after all clients
1675 have been informed about removal), or
1676 - object is to be removed or deactivated, or
1677 - object is too far away
1679 for (u16 id : current_objects) {
1680 ServerActiveObject *object = getActiveObject(id);
1682 if (object == NULL) {
1683 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1684 << " object in current_objects is NULL" << std::endl;
1685 removed_objects.push(id);
1689 if (object->isGone()) {
1690 removed_objects.push(id);
1694 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1695 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1696 if (distance_f <= player_radius_f || player_radius_f == 0)
1698 } else if (distance_f <= radius_f)
1701 // Object is no longer visible
1702 removed_objects.push(id);
1706 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1707 v3s16 blockpos, bool static_exists, v3s16 static_block)
1709 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1713 for (auto &so_it : block->m_static_objects.m_active) {
1714 // Get the ServerActiveObject counterpart to this StaticObject
1715 ServerActiveObject *sao = m_ao_manager.getActiveObject(so_it.first);
1717 // If this ever happens, there must be some kind of nasty bug.
1718 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1719 "Object from MapBlock::m_static_objects::m_active not found "
1720 "in m_active_objects";
1724 sao->m_static_exists = static_exists;
1725 sao->m_static_block = static_block;
1729 bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest)
1731 if(m_active_object_messages.empty())
1734 *dest = std::move(m_active_object_messages.front());
1735 m_active_object_messages.pop();
1739 void ServerEnvironment::getSelectedActiveObjects(
1740 const core::line3d<f32> &shootline_on_map,
1741 std::vector<PointedThing> &objects)
1743 std::vector<ServerActiveObject *> objs;
1744 getObjectsInsideRadius(objs, shootline_on_map.start,
1745 shootline_on_map.getLength() + 10.0f, nullptr);
1746 const v3f line_vector = shootline_on_map.getVector();
1748 for (auto obj : objs) {
1751 aabb3f selection_box;
1752 if (!obj->getSelectionBox(&selection_box))
1755 v3f pos = obj->getBasePosition();
1757 aabb3f offsetted_box(selection_box.MinEdge + pos,
1758 selection_box.MaxEdge + pos);
1760 v3f current_intersection;
1761 v3s16 current_normal;
1762 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1763 ¤t_intersection, ¤t_normal)) {
1764 objects.emplace_back(
1765 (s16) obj->getId(), current_intersection, current_normal,
1766 (current_intersection - shootline_on_map.start).getLengthSQ());
1772 ************ Private methods *************
1775 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1776 bool set_changed, u32 dtime_s)
1778 if (!m_ao_manager.registerObject(object)) {
1782 // Register reference in scripting api (must be done before post-init)
1783 m_script->addObjectReference(dynamic_cast<ActiveObject *>(object));
1784 // Post-initialize object
1785 object->addedToEnvironment(dtime_s);
1787 // Add static data to block
1788 if (object->isStaticAllowed()) {
1789 // Add static object to active static list of the block
1790 v3f objectpos = object->getBasePosition();
1791 StaticObject s_obj(object, objectpos);
1792 // Add to the block where the object is located in
1793 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1794 MapBlock *block = m_map->emergeBlock(blockpos);
1796 block->m_static_objects.m_active[object->getId()] = s_obj;
1797 object->m_static_exists = true;
1798 object->m_static_block = blockpos;
1801 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1802 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1804 v3s16 p = floatToInt(objectpos, BS);
1805 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1806 <<"could not emerge block for storing id="<<object->getId()
1807 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1811 return object->getId();
1815 Remove objects that satisfy (isGone() && m_known_by_count==0)
1817 void ServerEnvironment::removeRemovedObjects()
1819 ScopeProfiler sp(g_profiler, "ServerEnvironment::removeRemovedObjects()", SPT_AVG);
1821 auto clear_cb = [this] (ServerActiveObject *obj, u16 id) {
1822 // This shouldn't happen but check it
1824 errorstream << "ServerEnvironment::removeRemovedObjects(): "
1825 << "NULL object found. id=" << id << std::endl;
1830 We will handle objects marked for removal or deactivation
1836 Delete static data from block if removed
1838 if (obj->isPendingRemoval())
1839 deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1841 // If still known by clients, don't actually remove. On some future
1842 // invocation this will be 0, which is when removal will continue.
1843 if(obj->m_known_by_count > 0)
1847 Move static data from active to stored if deactivated
1849 if (!obj->isPendingRemoval() && obj->m_static_exists) {
1850 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1852 const auto i = block->m_static_objects.m_active.find(id);
1853 if (i != block->m_static_objects.m_active.end()) {
1854 block->m_static_objects.m_stored.push_back(i->second);
1855 block->m_static_objects.m_active.erase(id);
1856 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1857 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1859 warningstream << "ServerEnvironment::removeRemovedObjects(): "
1860 << "id=" << id << " m_static_exists=true but "
1861 << "static data doesn't actually exist in "
1862 << PP(obj->m_static_block) << std::endl;
1865 infostream << "Failed to emerge block from which an object to "
1866 << "be deactivated was loaded from. id=" << id << std::endl;
1870 // Tell the object about removal
1871 obj->removingFromEnvironment();
1872 // Deregister in scripting api
1873 m_script->removeObjectReference(dynamic_cast<ActiveObject *>(obj));
1876 if (obj->environmentDeletes())
1882 m_ao_manager.clear(clear_cb);
1885 static void print_hexdump(std::ostream &o, const std::string &data)
1887 const int linelength = 16;
1888 for(int l=0; ; l++){
1889 int i0 = linelength * l;
1890 bool at_end = false;
1891 int thislinelength = linelength;
1892 if(i0 + thislinelength > (int)data.size()){
1893 thislinelength = data.size() - i0;
1896 for(int di=0; di<linelength; di++){
1899 if(di<thislinelength)
1900 porting::mt_snprintf(buf, sizeof(buf), "%.2x ", data[i]);
1902 porting::mt_snprintf(buf, sizeof(buf), " ");
1906 for(int di=0; di<thislinelength; di++){
1919 ServerActiveObject* ServerEnvironment::createSAO(ActiveObjectType type, v3f pos,
1920 const std::string &data)
1923 case ACTIVEOBJECT_TYPE_LUAENTITY:
1924 return new LuaEntitySAO(this, pos, data);
1926 warningstream << "ServerActiveObject: No factory for type=" << type << std::endl;
1932 Convert stored objects from blocks near the players to active.
1934 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1939 // Ignore if no stored objects (to not set changed flag)
1940 if(block->m_static_objects.m_stored.empty())
1943 verbosestream<<"ServerEnvironment::activateObjects(): "
1944 <<"activating objects of block "<<PP(block->getPos())
1945 <<" ("<<block->m_static_objects.m_stored.size()
1946 <<" objects)"<<std::endl;
1947 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1949 errorstream<<"suspiciously large amount of objects detected: "
1950 <<block->m_static_objects.m_stored.size()<<" in "
1951 <<PP(block->getPos())
1952 <<"; removing all of them."<<std::endl;
1953 // Clear stored list
1954 block->m_static_objects.m_stored.clear();
1955 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1956 MOD_REASON_TOO_MANY_OBJECTS);
1960 // Activate stored objects
1961 std::vector<StaticObject> new_stored;
1962 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1963 // Create an active object from the data
1964 ServerActiveObject *obj = createSAO((ActiveObjectType) s_obj.type, s_obj.pos,
1966 // If couldn't create object, store static data back.
1968 errorstream<<"ServerEnvironment::activateObjects(): "
1969 <<"failed to create active object from static object "
1970 <<"in block "<<PP(s_obj.pos/BS)
1971 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1972 print_hexdump(verbosestream, s_obj.data);
1974 new_stored.push_back(s_obj);
1977 verbosestream<<"ServerEnvironment::activateObjects(): "
1978 <<"activated static object pos="<<PP(s_obj.pos/BS)
1979 <<" type="<<(int)s_obj.type<<std::endl;
1980 // This will also add the object to the active static list
1981 addActiveObjectRaw(obj, false, dtime_s);
1984 // Clear stored list
1985 block->m_static_objects.m_stored.clear();
1986 // Add leftover failed stuff to stored list
1987 for (const StaticObject &s_obj : new_stored) {
1988 block->m_static_objects.m_stored.push_back(s_obj);
1992 Note: Block hasn't really been modified here.
1993 The objects have just been activated and moved from the stored
1994 static list to the active static list.
1995 As such, the block is essentially the same.
1996 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1997 Otherwise there would be a huge amount of unnecessary I/O.
2002 Convert objects that are not standing inside active blocks to static.
2004 If m_known_by_count != 0, active object is not deleted, but static
2005 data is still updated.
2007 If force_delete is set, active object is deleted nevertheless. It
2008 shall only be set so in the destructor of the environment.
2010 If block wasn't generated (not in memory or on disk),
2012 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
2014 auto cb_deactivate = [this, _force_delete] (ServerActiveObject *obj, u16 id) {
2015 // force_delete might be overriden per object
2016 bool force_delete = _force_delete;
2018 // Do not deactivate if disallowed
2019 if (!force_delete && !obj->shouldUnload())
2022 // removeRemovedObjects() is responsible for these
2023 if (!force_delete && obj->isGone())
2026 const v3f &objectpos = obj->getBasePosition();
2028 // The block in which the object resides in
2029 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2031 // If object's static data is stored in a deactivated block and object
2032 // is actually located in an active block, re-save to the block in
2033 // which the object is actually located in.
2034 if (!force_delete && obj->m_static_exists &&
2035 !m_active_blocks.contains(obj->m_static_block) &&
2036 m_active_blocks.contains(blockpos_o)) {
2038 // Delete from block where object was located
2039 deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
2041 StaticObject s_obj(obj, objectpos);
2042 // Save to block where object is located
2043 saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
2048 // If block is still active, don't remove
2049 bool still_active = obj->isStaticAllowed() ?
2050 m_active_blocks.contains(blockpos_o) :
2051 getMap().getBlockNoCreateNoEx(blockpos_o) != nullptr;
2052 if (!force_delete && still_active)
2055 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2056 << "deactivating object id=" << id << " on inactive block "
2057 << PP(blockpos_o) << std::endl;
2059 // If known by some client, don't immediately delete.
2060 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2063 Update the static data
2065 if (obj->isStaticAllowed()) {
2066 // Create new static object
2067 StaticObject s_obj(obj, objectpos);
2069 bool stays_in_same_block = false;
2070 bool data_changed = true;
2072 // Check if static data has changed considerably
2073 if (obj->m_static_exists) {
2074 if (obj->m_static_block == blockpos_o)
2075 stays_in_same_block = true;
2077 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2080 const auto n = block->m_static_objects.m_active.find(id);
2081 if (n != block->m_static_objects.m_active.end()) {
2082 StaticObject static_old = n->second;
2084 float save_movem = obj->getMinimumSavedMovement();
2086 if (static_old.data == s_obj.data &&
2087 (static_old.pos - objectpos).getLength() < save_movem)
2088 data_changed = false;
2090 warningstream << "ServerEnvironment::deactivateFarObjects(): "
2091 << "id=" << id << " m_static_exists=true but "
2092 << "static data doesn't actually exist in "
2093 << PP(obj->m_static_block) << std::endl;
2099 While changes are always saved, blocks are only marked as modified
2100 if the object has moved or different staticdata. (see above)
2102 bool shall_be_written = (!stays_in_same_block || data_changed);
2103 u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2105 // Delete old static object
2106 deleteStaticFromBlock(obj, id, reason, false);
2108 // Add to the block where the object is located in
2109 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2110 u16 store_id = pending_delete ? id : 0;
2111 if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2112 force_delete = true;
2115 // Regardless of what happens to the object at this point, deactivate it first.
2116 // This ensures that LuaEntity on_deactivate is always called.
2117 obj->markForDeactivation();
2120 If known by some client, set pending deactivation.
2121 Otherwise delete it immediately.
2123 if (pending_delete && !force_delete) {
2124 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2125 << "object id=" << id << " is known by clients"
2126 << "; not deleting yet" << std::endl;
2131 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2132 << "object id=" << id << " is not known by clients"
2133 << "; deleting" << std::endl;
2135 // Tell the object about removal
2136 obj->removingFromEnvironment();
2137 // Deregister in scripting api
2138 m_script->removeObjectReference(dynamic_cast<ActiveObject *>(obj));
2140 // Delete active object
2141 if (obj->environmentDeletes())
2147 m_ao_manager.clear(cb_deactivate);
2150 void ServerEnvironment::deleteStaticFromBlock(
2151 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2153 if (!obj->m_static_exists)
2158 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2160 block = m_map->emergeBlock(obj->m_static_block, false);
2163 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2164 << " when deleting static data of object from it. id=" << id << std::endl;
2168 block->m_static_objects.remove(id);
2169 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2170 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2172 obj->m_static_exists = false;
2175 bool ServerEnvironment::saveStaticToBlock(
2176 v3s16 blockpos, u16 store_id,
2177 ServerActiveObject *obj, const StaticObject &s_obj,
2180 MapBlock *block = nullptr;
2182 block = m_map->emergeBlock(blockpos);
2183 } catch (InvalidPositionException &e) {
2184 // Handled via NULL pointer
2185 // NOTE: emergeBlock's failure is usually determined by it
2186 // actually returning NULL
2190 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2191 << " when saving static data of object to it. id=" << store_id << std::endl;
2194 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2195 warningstream << "ServerEnv: Trying to store id = " << store_id
2196 << " statically but block " << PP(blockpos)
2197 << " already contains "
2198 << block->m_static_objects.m_stored.size()
2199 << " objects." << std::endl;
2203 block->m_static_objects.insert(store_id, s_obj);
2204 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2205 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2207 obj->m_static_exists = true;
2208 obj->m_static_block = blockpos;
2213 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2214 const std::string &savedir, const Settings &conf)
2217 if (name == "sqlite3")
2218 return new PlayerDatabaseSQLite3(savedir);
2220 if (name == "dummy")
2221 return new Database_Dummy();
2224 if (name == "postgresql") {
2225 std::string connect_string;
2226 conf.getNoEx("pgsql_player_connection", connect_string);
2227 return new PlayerDatabasePostgreSQL(connect_string);
2232 if (name == "leveldb")
2233 return new PlayerDatabaseLevelDB(savedir);
2236 if (name == "files")
2237 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2239 throw BaseException(std::string("Database backend ") + name + " not supported.");
2242 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2243 const Settings &cmd_args)
2245 std::string migrate_to = cmd_args.get("migrate-players");
2247 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2248 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2249 errorstream << "Cannot read world.mt!" << std::endl;
2253 if (!world_mt.exists("player_backend")) {
2254 errorstream << "Please specify your current backend in world.mt:"
2256 << " player_backend = {files|sqlite3|leveldb|postgresql}"
2261 std::string backend = world_mt.get("player_backend");
2262 if (backend == migrate_to) {
2263 errorstream << "Cannot migrate: new backend is same"
2264 << " as the old one" << std::endl;
2268 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2271 if (backend == "files") {
2272 // Create backup directory
2273 fs::CreateDir(players_backup_path);
2277 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2278 game_params.world_path, world_mt);
2279 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2280 game_params.world_path, world_mt);
2282 std::vector<std::string> player_list;
2283 srcdb->listPlayers(player_list);
2284 for (std::vector<std::string>::const_iterator it = player_list.begin();
2285 it != player_list.end(); ++it) {
2286 actionstream << "Migrating player " << it->c_str() << std::endl;
2287 RemotePlayer player(it->c_str(), NULL);
2288 PlayerSAO playerSAO(NULL, &player, 15000, false);
2290 srcdb->loadPlayer(&player, &playerSAO);
2292 playerSAO.finalize(&player, std::set<std::string>());
2293 player.setPlayerSAO(&playerSAO);
2295 dstdb->savePlayer(&player);
2297 // For files source, move player files to backup dir
2298 if (backend == "files") {
2300 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2301 players_backup_path + DIR_DELIM + (*it));
2305 actionstream << "Successfully migrated " << player_list.size() << " players"
2307 world_mt.set("player_backend", migrate_to);
2308 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2309 errorstream << "Failed to update world.mt!" << std::endl;
2311 actionstream << "world.mt updated" << std::endl;
2313 // When migration is finished from file backend, remove players directory if empty
2314 if (backend == "files") {
2315 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2322 } catch (BaseException &e) {
2323 errorstream << "An error occurred during migration: " << e.what() << std::endl;
2329 AuthDatabase *ServerEnvironment::openAuthDatabase(
2330 const std::string &name, const std::string &savedir, const Settings &conf)
2332 if (name == "sqlite3")
2333 return new AuthDatabaseSQLite3(savedir);
2336 if (name == "postgresql") {
2337 std::string connect_string;
2338 conf.getNoEx("pgsql_auth_connection", connect_string);
2339 return new AuthDatabasePostgreSQL(connect_string);
2343 if (name == "files")
2344 return new AuthDatabaseFiles(savedir);
2347 if (name == "leveldb")
2348 return new AuthDatabaseLevelDB(savedir);
2351 throw BaseException(std::string("Database backend ") + name + " not supported.");
2354 bool ServerEnvironment::migrateAuthDatabase(
2355 const GameParams &game_params, const Settings &cmd_args)
2357 std::string migrate_to = cmd_args.get("migrate-auth");
2359 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2360 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2361 errorstream << "Cannot read world.mt!" << std::endl;
2365 std::string backend = "files";
2366 if (world_mt.exists("auth_backend"))
2367 backend = world_mt.get("auth_backend");
2369 warningstream << "No auth_backend found in world.mt, "
2370 "assuming \"files\"." << std::endl;
2372 if (backend == migrate_to) {
2373 errorstream << "Cannot migrate: new backend is same"
2374 << " as the old one" << std::endl;
2379 const std::unique_ptr<AuthDatabase> srcdb(ServerEnvironment::openAuthDatabase(
2380 backend, game_params.world_path, world_mt));
2381 const std::unique_ptr<AuthDatabase> dstdb(ServerEnvironment::openAuthDatabase(
2382 migrate_to, game_params.world_path, world_mt));
2384 std::vector<std::string> names_list;
2385 srcdb->listNames(names_list);
2386 for (const std::string &name : names_list) {
2387 actionstream << "Migrating auth entry for " << name << std::endl;
2389 AuthEntry authEntry;
2390 success = srcdb->getAuth(name, authEntry);
2391 success = success && dstdb->createAuth(authEntry);
2393 errorstream << "Failed to migrate " << name << std::endl;
2396 actionstream << "Successfully migrated " << names_list.size()
2397 << " auth entries" << std::endl;
2398 world_mt.set("auth_backend", migrate_to);
2399 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2400 errorstream << "Failed to update world.mt!" << std::endl;
2402 actionstream << "world.mt updated" << std::endl;
2404 if (backend == "files") {
2405 // special-case files migration:
2406 // move auth.txt to auth.txt.bak if possible
2407 std::string auth_txt_path =
2408 game_params.world_path + DIR_DELIM + "auth.txt";
2409 std::string auth_bak_path = auth_txt_path + ".bak";
2410 if (!fs::PathExists(auth_bak_path))
2411 if (fs::Rename(auth_txt_path, auth_bak_path))
2412 actionstream << "Renamed auth.txt to auth.txt.bak"
2415 errorstream << "Could not rename auth.txt to "
2416 "auth.txt.bak" << std::endl;
2418 warningstream << "auth.txt.bak already exists, auth.txt "
2419 "not renamed" << std::endl;
2422 } catch (BaseException &e) {
2423 errorstream << "An error occurred during migration: " << e.what()