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.
20 #include "serverenvironment.h"
21 #include "content_sao.h"
26 #include "nodemetadata.h"
31 #include "remoteplayer.h"
32 #include "scripting_server.h"
34 #include "util/serialize.h"
35 #include "util/basic_macros.h"
36 #include "util/pointedthing.h"
37 #include "threading/mutex_auto_lock.h"
39 #include "gameparams.h"
40 #include "database/database-dummy.h"
41 #include "database/database-files.h"
42 #include "database/database-sqlite3.h"
44 #include "database/database-postgresql.h"
48 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
50 // A number that is much smaller than the timeout for particle spawners should/could ever be
51 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
57 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
60 // Initialize timer to random value to spread processing
61 float itv = abm->getTriggerInterval();
62 itv = MYMAX(0.001, itv); // No less than 1ms
63 int minval = MYMAX(-0.51*itv, -60); // Clamp to
64 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
65 timer = myrand_range(minval, maxval);
72 void LBMContentMapping::deleteContents()
74 for (auto &it : lbm_list) {
79 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
81 // Add the lbm_def to the LBMContentMapping.
82 // Unknown names get added to the global NameIdMapping.
83 const NodeDefManager *nodedef = gamedef->ndef();
85 lbm_list.push_back(lbm_def);
87 for (const std::string &nodeTrigger: lbm_def->trigger_contents) {
88 std::vector<content_t> c_ids;
89 bool found = nodedef->getIds(nodeTrigger, c_ids);
91 content_t c_id = gamedef->allocateUnknownNodeId(nodeTrigger);
92 if (c_id == CONTENT_IGNORE) {
93 // Seems it can't be allocated.
94 warningstream << "Could not internalize node name \"" << nodeTrigger
95 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
98 c_ids.push_back(c_id);
101 for (content_t c_id : c_ids) {
102 map[c_id].push_back(lbm_def);
107 const std::vector<LoadingBlockModifierDef *> *
108 LBMContentMapping::lookup(content_t c) const
110 lbm_map::const_iterator it = map.find(c);
113 // This first dereferences the iterator, returning
114 // a std::vector<LoadingBlockModifierDef *>
115 // reference, then we convert it to a pointer.
116 return &(it->second);
119 LBMManager::~LBMManager()
121 for (auto &m_lbm_def : m_lbm_defs) {
122 delete m_lbm_def.second;
125 for (auto &it : m_lbm_lookup) {
126 (it.second).deleteContents();
130 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
132 // Precondition, in query mode the map isn't used anymore
133 FATAL_ERROR_IF(m_query_mode,
134 "attempted to modify LBMManager in query mode");
136 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
137 throw ModError("Error adding LBM \"" + lbm_def->name +
138 "\": Does not follow naming conventions: "
139 "Only characters [a-z0-9_:] are allowed.");
142 m_lbm_defs[lbm_def->name] = lbm_def;
145 void LBMManager::loadIntroductionTimes(const std::string ×,
146 IGameDef *gamedef, u32 now)
151 // Storing it in a map first instead of
152 // handling the stuff directly in the loop
153 // removes all duplicate entries.
154 // TODO make this std::unordered_map
155 std::map<std::string, u32> introduction_times;
158 The introduction times string consists of name~time entries,
159 with each entry terminated by a semicolon. The time is decimal.
164 while ((idx_new = times.find(';', idx)) != std::string::npos) {
165 std::string entry = times.substr(idx, idx_new - idx);
166 std::vector<std::string> components = str_split(entry, '~');
167 if (components.size() != 2)
168 throw SerializationError("Introduction times entry \""
169 + entry + "\" requires exactly one '~'!");
170 const std::string &name = components[0];
171 u32 time = from_string<u32>(components[1]);
172 introduction_times[name] = time;
176 // Put stuff from introduction_times into m_lbm_lookup
177 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
178 it != introduction_times.end(); ++it) {
179 const std::string &name = it->first;
180 u32 time = it->second;
182 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
183 m_lbm_defs.find(name);
184 if (def_it == m_lbm_defs.end()) {
185 // This seems to be an LBM entry for
186 // an LBM we haven't loaded. Discard it.
189 LoadingBlockModifierDef *lbm_def = def_it->second;
190 if (lbm_def->run_at_every_load) {
191 // This seems to be an LBM entry for
192 // an LBM that runs at every load.
193 // Don't add it just yet.
197 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
199 // Erase the entry so that we know later
200 // what elements didn't get put into m_lbm_lookup
201 m_lbm_defs.erase(name);
204 // Now also add the elements from m_lbm_defs to m_lbm_lookup
205 // that weren't added in the previous step.
206 // They are introduced first time to this world,
207 // or are run at every load (introducement time hardcoded to U32_MAX).
209 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
210 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
212 for (auto &m_lbm_def : m_lbm_defs) {
213 if (m_lbm_def.second->run_at_every_load) {
214 lbms_running_always.addLBM(m_lbm_def.second, gamedef);
216 lbms_we_introduce_now.addLBM(m_lbm_def.second, gamedef);
220 // Clear the list, so that we don't delete remaining elements
221 // twice in the destructor
225 std::string LBMManager::createIntroductionTimesString()
227 // Precondition, we must be in query mode
228 FATAL_ERROR_IF(!m_query_mode,
229 "attempted to query on non fully set up LBMManager");
231 std::ostringstream oss;
232 for (const auto &it : m_lbm_lookup) {
234 const std::vector<LoadingBlockModifierDef *> &lbm_list = it.second.lbm_list;
235 for (const auto &lbm_def : lbm_list) {
236 // Don't add if the LBM runs at every load,
237 // then introducement time is hardcoded
238 // and doesn't need to be stored
239 if (lbm_def->run_at_every_load)
241 oss << lbm_def->name << "~" << time << ";";
247 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
249 // Precondition, we need m_lbm_lookup to be initialized
250 FATAL_ERROR_IF(!m_query_mode,
251 "attempted to query on non fully set up LBMManager");
252 v3s16 pos_of_block = block->getPosRelative();
256 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
257 for (; it != m_lbm_lookup.end(); ++it) {
258 // Cache previous version to speedup lookup which has a very high performance
259 // penalty on each call
260 content_t previous_c{};
261 std::vector<LoadingBlockModifierDef *> *lbm_list = nullptr;
263 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
264 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
265 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) {
266 n = block->getNodeNoEx(pos);
269 // If content_t are not matching perform an LBM lookup
270 if (previous_c != c) {
271 lbm_list = (std::vector<LoadingBlockModifierDef *> *)
272 it->second.lookup(c);
278 for (auto lbmdef : *lbm_list) {
279 lbmdef->trigger(env, pos + pos_of_block, n);
289 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
292 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
293 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
294 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
297 if (p.getDistanceFrom(p0) <= r) {
304 void fillViewConeBlock(v3s16 p0,
306 const v3f camera_pos,
307 const v3f camera_dir,
308 const float camera_fov,
309 std::set<v3s16> &list)
312 const s16 r_nodes = r * BS * MAP_BLOCKSIZE;
313 for (p.X = p0.X - r; p.X <= p0.X+r; p.X++)
314 for (p.Y = p0.Y - r; p.Y <= p0.Y+r; p.Y++)
315 for (p.Z = p0.Z - r; p.Z <= p0.Z+r; p.Z++) {
316 if (isBlockInSight(p, camera_pos, camera_dir, camera_fov, r_nodes)) {
322 void ActiveBlockList::update(std::vector<PlayerSAO*> &active_players,
323 s16 active_block_range,
324 s16 active_object_range,
325 std::set<v3s16> &blocks_removed,
326 std::set<v3s16> &blocks_added)
331 std::set<v3s16> newlist = m_forceloaded_list;
332 m_abm_list = m_forceloaded_list;
333 for (const PlayerSAO *playersao : active_players) {
334 v3s16 pos = getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS));
335 fillRadiusBlock(pos, active_block_range, m_abm_list);
336 fillRadiusBlock(pos, active_block_range, newlist);
338 s16 player_ao_range = std::min(active_object_range, playersao->getWantedRange());
339 // only do this if this would add blocks
340 if (player_ao_range > active_block_range) {
341 v3f camera_dir = v3f(0,0,1);
342 camera_dir.rotateYZBy(playersao->getPitch());
343 camera_dir.rotateXZBy(playersao->getYaw());
344 fillViewConeBlock(pos,
346 playersao->getEyePosition(),
354 Find out which blocks on the old list are not on the new list
356 // Go through old list
357 for (v3s16 p : m_list) {
358 // If not on new list, it's been removed
359 if (newlist.find(p) == newlist.end())
360 blocks_removed.insert(p);
364 Find out which blocks on the new list are not on the old list
366 // Go through new list
367 for (v3s16 p : newlist) {
368 // If not on old list, it's been added
369 if(m_list.find(p) == m_list.end())
370 blocks_added.insert(p);
377 for (v3s16 p : newlist) {
386 ServerEnvironment::ServerEnvironment(ServerMap *map,
387 ServerScripting *scriptIface, Server *server,
388 const std::string &path_world):
391 m_script(scriptIface),
393 m_path_world(path_world)
395 // Determine which database backend to use
396 std::string conf_path = path_world + DIR_DELIM + "world.mt";
398 bool succeeded = conf.readConfigFile(conf_path.c_str());
399 if (!succeeded || !conf.exists("player_backend")) {
400 // fall back to files
401 conf.set("player_backend", "files");
402 warningstream << "/!\\ You are using old player file backend. "
403 << "This backend is deprecated and will be removed in next release /!\\"
404 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
405 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
407 if (!conf.updateConfigFile(conf_path.c_str())) {
408 errorstream << "ServerEnvironment::ServerEnvironment(): "
409 << "Failed to update world.mt!" << std::endl;
414 conf.getNoEx("player_backend", name);
415 m_player_database = openPlayerDatabase(name, path_world, conf);
418 ServerEnvironment::~ServerEnvironment()
420 // Clear active block list.
421 // This makes the next one delete all active objects.
422 m_active_blocks.clear();
424 // Convert all objects to static and delete the active objects
425 deactivateFarObjects(true);
430 // Delete ActiveBlockModifiers
431 for (ABMWithState &m_abm : m_abms) {
435 // Deallocate players
436 for (RemotePlayer *m_player : m_players) {
440 delete m_player_database;
443 Map & ServerEnvironment::getMap()
448 ServerMap & ServerEnvironment::getServerMap()
453 RemotePlayer *ServerEnvironment::getPlayer(const session_t peer_id)
455 for (RemotePlayer *player : m_players) {
456 if (player->getPeerId() == peer_id)
462 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
464 for (RemotePlayer *player : m_players) {
465 if (strcmp(player->getName(), name) == 0)
471 void ServerEnvironment::addPlayer(RemotePlayer *player)
474 Check that peer_ids are unique.
475 Also check that names are unique.
476 Exception: there can be multiple players with peer_id=0
478 // If peer id is non-zero, it has to be unique.
479 if (player->getPeerId() != PEER_ID_INEXISTENT)
480 FATAL_ERROR_IF(getPlayer(player->getPeerId()) != NULL, "Peer id not unique");
481 // Name has to be unique.
482 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
484 m_players.push_back(player);
487 void ServerEnvironment::removePlayer(RemotePlayer *player)
489 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
490 it != m_players.end(); ++it) {
491 if ((*it) == player) {
499 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
501 return m_player_database->removePlayer(name);
504 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, v3s16 *p)
506 // Iterate trough nodes on the line
507 voxalgo::VoxelLineIterator iterator(pos1 / BS, (pos2 - pos1) / BS);
509 MapNode n = getMap().getNodeNoEx(iterator.m_current_node_pos);
512 if (n.param0 != CONTENT_AIR) {
514 *p = iterator.m_current_node_pos;
518 } while (iterator.m_current_index <= iterator.m_last_index);
522 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
523 const std::string &str_reason, bool reconnect)
525 for (RemotePlayer *player : m_players) {
526 m_server->DenyAccessVerCompliant(player->getPeerId(),
527 player->protocol_version, reason, str_reason, reconnect);
531 void ServerEnvironment::saveLoadedPlayers()
533 std::string players_path = m_path_world + DIR_DELIM + "players";
534 fs::CreateDir(players_path);
536 for (RemotePlayer *player : m_players) {
537 if (player->checkModified() || (player->getPlayerSAO() &&
538 player->getPlayerSAO()->getMeta().isModified())) {
540 m_player_database->savePlayer(player);
541 } catch (DatabaseException &e) {
542 errorstream << "Failed to save player " << player->getName() << " exception: "
543 << e.what() << std::endl;
550 void ServerEnvironment::savePlayer(RemotePlayer *player)
553 m_player_database->savePlayer(player);
554 } catch (DatabaseException &e) {
555 errorstream << "Failed to save player " << player->getName() << " exception: "
556 << e.what() << std::endl;
561 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
562 session_t peer_id, bool is_singleplayer)
564 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
565 // Create player if it doesn't exist
566 if (!m_player_database->loadPlayer(player, playersao)) {
568 // Set player position
569 infostream << "Server: Finding spawn place for player \""
570 << player->getName() << "\"" << std::endl;
571 playersao->setBasePosition(m_server->findSpawnPos());
573 // Make sure the player is saved
574 player->setModified(true);
576 // If the player exists, ensure that they respawn inside legal bounds
577 // This fixes an assert crash when the player can't be added
578 // to the environment
579 if (objectpos_over_limit(playersao->getBasePosition())) {
580 actionstream << "Respawn position for player \""
581 << player->getName() << "\" outside limits, resetting" << std::endl;
582 playersao->setBasePosition(m_server->findSpawnPos());
586 // Add player to environment
589 /* Clean up old HUD elements from previous sessions */
592 /* Add object to environment */
593 addActiveObject(playersao);
598 void ServerEnvironment::saveMeta()
600 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
602 // Open file and serialize
603 std::ostringstream ss(std::ios_base::binary);
606 args.setU64("game_time", m_game_time);
607 args.setU64("time_of_day", getTimeOfDay());
608 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
609 args.setU64("lbm_introduction_times_version", 1);
610 args.set("lbm_introduction_times",
611 m_lbm_mgr.createIntroductionTimesString());
612 args.setU64("day_count", m_day_count);
616 if(!fs::safeWriteToFile(path, ss.str()))
618 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
620 throw SerializationError("Couldn't save env meta");
624 void ServerEnvironment::loadMeta()
626 // If file doesn't exist, load default environment metadata
627 if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
628 infostream << "ServerEnvironment: Loading default environment metadata"
634 infostream << "ServerEnvironment: Loading environment metadata" << std::endl;
636 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
638 // Open file and deserialize
639 std::ifstream is(path.c_str(), std::ios_base::binary);
641 infostream << "ServerEnvironment::loadMeta(): Failed to open "
642 << path << std::endl;
643 throw SerializationError("Couldn't load env meta");
648 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
649 throw SerializationError("ServerEnvironment::loadMeta(): "
650 "EnvArgsEnd not found!");
654 m_game_time = args.getU64("game_time");
655 } catch (SettingNotFoundException &e) {
656 // Getting this is crucial, otherwise timestamps are useless
657 throw SerializationError("Couldn't load env meta game_time");
660 setTimeOfDay(args.exists("time_of_day") ?
661 // set day to early morning by default
662 args.getU64("time_of_day") : 5250);
664 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
665 // If missing, do as if clearObjects was never called
666 args.getU64("last_clear_objects_time") : 0;
668 std::string lbm_introduction_times;
670 u64 ver = args.getU64("lbm_introduction_times_version");
672 lbm_introduction_times = args.get("lbm_introduction_times");
674 infostream << "ServerEnvironment::loadMeta(): Non-supported"
675 << " introduction time version " << ver << std::endl;
677 } catch (SettingNotFoundException &e) {
678 // No problem, this is expected. Just continue with an empty string
680 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
682 m_day_count = args.exists("day_count") ?
683 args.getU64("day_count") : 0;
687 * called if env_meta.txt doesn't exist (e.g. new world)
689 void ServerEnvironment::loadDefaultMeta()
691 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
696 ActiveBlockModifier *abm;
698 std::vector<content_t> required_neighbors;
699 bool check_required_neighbors; // false if required_neighbors is known to be empty
705 ServerEnvironment *m_env;
706 std::vector<std::vector<ActiveABM> *> m_aabms;
708 ABMHandler(std::vector<ABMWithState> &abms,
709 float dtime_s, ServerEnvironment *env,
715 const NodeDefManager *ndef = env->getGameDef()->ndef();
716 for (ABMWithState &abmws : abms) {
717 ActiveBlockModifier *abm = abmws.abm;
718 float trigger_interval = abm->getTriggerInterval();
719 if(trigger_interval < 0.001)
720 trigger_interval = 0.001;
721 float actual_interval = dtime_s;
723 abmws.timer += dtime_s;
724 if(abmws.timer < trigger_interval)
726 abmws.timer -= trigger_interval;
727 actual_interval = trigger_interval;
729 float chance = abm->getTriggerChance();
734 if (abm->getSimpleCatchUp()) {
735 float intervals = actual_interval / trigger_interval;
738 aabm.chance = chance / intervals;
742 aabm.chance = chance;
746 const std::vector<std::string> &required_neighbors_s =
747 abm->getRequiredNeighbors();
748 for (const std::string &required_neighbor_s : required_neighbors_s) {
749 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
751 aabm.check_required_neighbors = !required_neighbors_s.empty();
754 const std::vector<std::string> &contents_s = abm->getTriggerContents();
755 for (const std::string &content_s : contents_s) {
756 std::vector<content_t> ids;
757 ndef->getIds(content_s, ids);
758 for (content_t c : ids) {
759 if (c >= m_aabms.size())
760 m_aabms.resize(c + 256, NULL);
762 m_aabms[c] = new std::vector<ActiveABM>;
763 m_aabms[c]->push_back(aabm);
771 for (auto &aabms : m_aabms)
775 // Find out how many objects the given block and its neighbours contain.
776 // Returns the number of objects in the block, and also in 'wider' the
777 // number of objects in the block and all its neighbours. The latter
778 // may an estimate if any neighbours are unloaded.
779 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
782 u32 wider_unknown_count = 0;
783 for(s16 x=-1; x<=1; x++)
784 for(s16 y=-1; y<=1; y++)
785 for(s16 z=-1; z<=1; z++)
787 MapBlock *block2 = map->getBlockNoCreateNoEx(
788 block->getPos() + v3s16(x,y,z));
790 wider_unknown_count++;
793 wider += block2->m_static_objects.m_active.size()
794 + block2->m_static_objects.m_stored.size();
797 u32 active_object_count = block->m_static_objects.m_active.size();
798 u32 wider_known_count = 3*3*3 - wider_unknown_count;
799 wider += wider_unknown_count * wider / wider_known_count;
800 return active_object_count;
803 void apply(MapBlock *block)
805 if(m_aabms.empty() || block->isDummy())
808 ServerMap *map = &m_env->getServerMap();
810 u32 active_object_count_wider;
811 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
812 m_env->m_added_objects = 0;
815 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
816 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
817 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
819 const MapNode &n = block->getNodeUnsafe(p0);
820 content_t c = n.getContent();
822 if (c >= m_aabms.size() || !m_aabms[c])
825 v3s16 p = p0 + block->getPosRelative();
826 for (ActiveABM &aabm : *m_aabms[c]) {
827 if (myrand() % aabm.chance != 0)
831 if (aabm.check_required_neighbors) {
833 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
834 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
835 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
840 if (block->isValidPosition(p1)) {
841 // if the neighbor is found on the same map block
842 // get it straight from there
843 const MapNode &n = block->getNodeUnsafe(p1);
846 // otherwise consult the map
847 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
850 if (CONTAINS(aabm.required_neighbors, c))
853 // No required neighbor found
858 // Call all the trigger variations
859 aabm.abm->trigger(m_env, p, n);
860 aabm.abm->trigger(m_env, p, n,
861 active_object_count, active_object_count_wider);
863 // Count surrounding objects again if the abms added any
864 if(m_env->m_added_objects > 0) {
865 active_object_count = countObjects(block, map, active_object_count_wider);
866 m_env->m_added_objects = 0;
873 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
875 // Reset usage timer immediately, otherwise a block that becomes active
876 // again at around the same time as it would normally be unloaded will
877 // get unloaded incorrectly. (I think this still leaves a small possibility
878 // of a race condition between this and server::AsyncRunStep, which only
879 // some kind of synchronisation will fix, but it at least reduces the window
880 // of opportunity for it to break from seconds to nanoseconds)
881 block->resetUsageTimer();
883 // Get time difference
885 u32 stamp = block->getTimestamp();
886 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
887 dtime_s = m_game_time - stamp;
888 dtime_s += additional_dtime;
890 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
891 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
893 // Remove stored static objects if clearObjects was called since block's timestamp
894 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
895 block->m_static_objects.m_stored.clear();
896 // do not set changed flag to avoid unnecessary mapblock writes
899 // Set current time as timestamp
900 block->setTimestampNoChangedFlag(m_game_time);
902 /*infostream<<"ServerEnvironment::activateBlock(): block is "
903 <<dtime_s<<" seconds old."<<std::endl;*/
905 // Activate stored objects
906 activateObjects(block, dtime_s);
908 /* Handle LoadingBlockModifiers */
909 m_lbm_mgr.applyLBMs(this, block, stamp);
912 std::vector<NodeTimer> elapsed_timers =
913 block->m_node_timers.step((float)dtime_s);
914 if (!elapsed_timers.empty()) {
916 for (const NodeTimer &elapsed_timer : elapsed_timers) {
917 n = block->getNodeNoEx(elapsed_timer.position);
918 v3s16 p = elapsed_timer.position + block->getPosRelative();
919 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
920 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
921 elapsed_timer.position));
926 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
928 m_abms.emplace_back(abm);
931 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
933 m_lbm_mgr.addLBMDef(lbm);
936 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
938 const NodeDefManager *ndef = m_server->ndef();
939 MapNode n_old = m_map->getNodeNoEx(p);
941 const ContentFeatures &cf_old = ndef->get(n_old);
944 if (cf_old.has_on_destruct)
945 m_script->node_on_destruct(p, n_old);
948 if (!m_map->addNodeWithEvent(p, n))
951 // Update active VoxelManipulator if a mapgen thread
952 m_map->updateVManip(p);
954 // Call post-destructor
955 if (cf_old.has_after_destruct)
956 m_script->node_after_destruct(p, n_old);
958 // Retrieve node content features
959 // if new node is same as old, reuse old definition to prevent a lookup
960 const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
963 if (cf_new.has_on_construct)
964 m_script->node_on_construct(p, n);
969 bool ServerEnvironment::removeNode(v3s16 p)
971 const NodeDefManager *ndef = m_server->ndef();
972 MapNode n_old = m_map->getNodeNoEx(p);
975 if (ndef->get(n_old).has_on_destruct)
976 m_script->node_on_destruct(p, n_old);
979 // This is slightly optimized compared to addNodeWithEvent(air)
980 if (!m_map->removeNodeWithEvent(p))
983 // Update active VoxelManipulator if a mapgen thread
984 m_map->updateVManip(p);
986 // Call post-destructor
987 if (ndef->get(n_old).has_after_destruct)
988 m_script->node_after_destruct(p, n_old);
990 // Air doesn't require constructor
994 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
996 if (!m_map->addNodeWithEvent(p, n, false))
999 // Update active VoxelManipulator if a mapgen thread
1000 m_map->updateVManip(p);
1005 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
1008 for (auto &activeObject : m_active_objects) {
1009 ServerActiveObject* obj = activeObject.second;
1010 u16 id = activeObject.first;
1011 v3f objectpos = obj->getBasePosition();
1012 if (objectpos.getDistanceFrom(pos) > radius)
1014 objects.push_back(id);
1018 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1020 infostream << "ServerEnvironment::clearObjects(): "
1021 << "Removing all active objects" << std::endl;
1022 std::vector<u16> objects_to_remove;
1023 for (auto &it : m_active_objects) {
1025 ServerActiveObject* obj = it.second;
1026 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1029 // Delete static object if block is loaded
1030 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1032 // If known by some client, don't delete immediately
1033 if (obj->m_known_by_count > 0) {
1034 obj->m_pending_removal = true;
1038 // Tell the object about removal
1039 obj->removingFromEnvironment();
1040 // Deregister in scripting api
1041 m_script->removeObjectReference(obj);
1043 // Delete active object
1044 if (obj->environmentDeletes())
1046 // Id to be removed from m_active_objects
1047 objects_to_remove.push_back(id);
1050 // Remove references from m_active_objects
1051 for (u16 i : objects_to_remove) {
1052 m_active_objects.erase(i);
1055 // Get list of loaded blocks
1056 std::vector<v3s16> loaded_blocks;
1057 infostream << "ServerEnvironment::clearObjects(): "
1058 << "Listing all loaded blocks" << std::endl;
1059 m_map->listAllLoadedBlocks(loaded_blocks);
1060 infostream << "ServerEnvironment::clearObjects(): "
1061 << "Done listing all loaded blocks: "
1062 << loaded_blocks.size()<<std::endl;
1064 // Get list of loadable blocks
1065 std::vector<v3s16> loadable_blocks;
1066 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1067 infostream << "ServerEnvironment::clearObjects(): "
1068 << "Listing all loadable blocks" << std::endl;
1069 m_map->listAllLoadableBlocks(loadable_blocks);
1070 infostream << "ServerEnvironment::clearObjects(): "
1071 << "Done listing all loadable blocks: "
1072 << loadable_blocks.size() << std::endl;
1074 loadable_blocks = loaded_blocks;
1077 actionstream << "ServerEnvironment::clearObjects(): "
1078 << "Now clearing objects in " << loadable_blocks.size()
1079 << " blocks" << std::endl;
1081 // Grab a reference on each loaded block to avoid unloading it
1082 for (v3s16 p : loaded_blocks) {
1083 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1084 assert(block != NULL);
1088 // Remove objects in all loadable blocks
1089 u32 unload_interval = U32_MAX;
1090 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1091 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1092 unload_interval = MYMAX(unload_interval, 1);
1094 u32 report_interval = loadable_blocks.size() / 10;
1095 u32 num_blocks_checked = 0;
1096 u32 num_blocks_cleared = 0;
1097 u32 num_objs_cleared = 0;
1098 for (auto i = loadable_blocks.begin();
1099 i != loadable_blocks.end(); ++i) {
1101 MapBlock *block = m_map->emergeBlock(p, false);
1103 errorstream << "ServerEnvironment::clearObjects(): "
1104 << "Failed to emerge block " << PP(p) << std::endl;
1107 u32 num_stored = block->m_static_objects.m_stored.size();
1108 u32 num_active = block->m_static_objects.m_active.size();
1109 if (num_stored != 0 || num_active != 0) {
1110 block->m_static_objects.m_stored.clear();
1111 block->m_static_objects.m_active.clear();
1112 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1113 MOD_REASON_CLEAR_ALL_OBJECTS);
1114 num_objs_cleared += num_stored + num_active;
1115 num_blocks_cleared++;
1117 num_blocks_checked++;
1119 if (report_interval != 0 &&
1120 num_blocks_checked % report_interval == 0) {
1121 float percent = 100.0 * (float)num_blocks_checked /
1122 loadable_blocks.size();
1123 actionstream << "ServerEnvironment::clearObjects(): "
1124 << "Cleared " << num_objs_cleared << " objects"
1125 << " in " << num_blocks_cleared << " blocks ("
1126 << percent << "%)" << std::endl;
1128 if (num_blocks_checked % unload_interval == 0) {
1129 m_map->unloadUnreferencedBlocks();
1132 m_map->unloadUnreferencedBlocks();
1134 // Drop references that were added above
1135 for (v3s16 p : loaded_blocks) {
1136 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1141 m_last_clear_objects_time = m_game_time;
1143 actionstream << "ServerEnvironment::clearObjects(): "
1144 << "Finished: Cleared " << num_objs_cleared << " objects"
1145 << " in " << num_blocks_cleared << " blocks" << std::endl;
1148 void ServerEnvironment::step(float dtime)
1150 /* Step time of day */
1151 stepTimeOfDay(dtime);
1154 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1155 // really matter that much.
1156 static thread_local const float server_step =
1157 g_settings->getFloat("dedicated_server_step");
1158 m_recommended_send_interval = server_step;
1164 m_game_time_fraction_counter += dtime;
1165 u32 inc_i = (u32)m_game_time_fraction_counter;
1166 m_game_time += inc_i;
1167 m_game_time_fraction_counter -= (float)inc_i;
1174 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1175 for (RemotePlayer *player : m_players) {
1176 // Ignore disconnected players
1177 if (player->getPeerId() == PEER_ID_INEXISTENT)
1181 player->move(dtime, this, 100 * BS);
1186 Manage active block list
1188 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1189 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1191 Get player block positions
1193 std::vector<PlayerSAO*> players;
1194 for (RemotePlayer *player: m_players) {
1195 // Ignore disconnected players
1196 if (player->getPeerId() == PEER_ID_INEXISTENT)
1199 PlayerSAO *playersao = player->getPlayerSAO();
1202 players.push_back(playersao);
1206 Update list of active blocks, collecting changes
1208 // use active_object_send_range_blocks since that is max distance
1209 // for active objects sent the client anyway
1210 static thread_local const s16 active_object_range =
1211 g_settings->getS16("active_object_send_range_blocks");
1212 static thread_local const s16 active_block_range =
1213 g_settings->getS16("active_block_range");
1214 std::set<v3s16> blocks_removed;
1215 std::set<v3s16> blocks_added;
1216 m_active_blocks.update(players, active_block_range, active_object_range,
1217 blocks_removed, blocks_added);
1220 Handle removed blocks
1223 // Convert active objects that are no more in active blocks to static
1224 deactivateFarObjects(false);
1226 for (const v3s16 &p: blocks_removed) {
1227 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1231 // Set current time as timestamp (and let it set ChangedFlag)
1232 block->setTimestamp(m_game_time);
1239 for (const v3s16 &p: blocks_added) {
1240 MapBlock *block = m_map->getBlockOrEmerge(p);
1242 m_active_blocks.m_list.erase(p);
1243 m_active_blocks.m_abm_list.erase(p);
1247 activateBlock(block);
1252 Mess around in active blocks
1254 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1255 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1257 float dtime = m_cache_nodetimer_interval;
1259 for (const v3s16 &p: m_active_blocks.m_list) {
1260 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1264 // Reset block usage timer
1265 block->resetUsageTimer();
1267 // Set current time as timestamp
1268 block->setTimestampNoChangedFlag(m_game_time);
1269 // If time has changed much from the one on disk,
1270 // set block to be saved when it is unloaded
1271 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1272 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1273 MOD_REASON_BLOCK_EXPIRED);
1276 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1277 if (!elapsed_timers.empty()) {
1280 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1281 n = block->getNodeNoEx(elapsed_timer.position);
1282 p2 = elapsed_timer.position + block->getPosRelative();
1283 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1284 block->setNodeTimer(NodeTimer(
1285 elapsed_timer.timeout, 0, elapsed_timer.position));
1292 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1294 if (m_active_block_interval_overload_skip > 0) {
1295 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1296 m_active_block_interval_overload_skip--;
1299 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1300 TimeTaker timer("modify in active blocks per interval");
1302 // Initialize handling of ActiveBlockModifiers
1303 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1305 for (const v3s16 &p : m_active_blocks.m_abm_list) {
1306 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1310 // Set current time as timestamp
1311 block->setTimestampNoChangedFlag(m_game_time);
1313 /* Handle ActiveBlockModifiers */
1314 abmhandler.apply(block);
1317 u32 time_ms = timer.stop(true);
1318 u32 max_time_ms = 200;
1319 if (time_ms > max_time_ms) {
1320 warningstream<<"active block modifiers took "
1321 <<time_ms<<"ms (longer than "
1322 <<max_time_ms<<"ms)"<<std::endl;
1323 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1328 Step script environment (run global on_step())
1330 m_script->environment_Step(dtime);
1336 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1337 //TimeTaker timer("Step active objects");
1339 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1341 // This helps the objects to send data at the same time
1342 bool send_recommended = false;
1343 m_send_recommended_timer += dtime;
1344 if(m_send_recommended_timer > getSendRecommendedInterval())
1346 m_send_recommended_timer -= getSendRecommendedInterval();
1347 send_recommended = true;
1350 for (auto &ao_it : m_active_objects) {
1351 ServerActiveObject* obj = ao_it.second;
1356 obj->step(dtime, send_recommended);
1357 // Read messages from object
1358 while (!obj->m_messages_out.empty()) {
1359 m_active_object_messages.push(obj->m_messages_out.front());
1360 obj->m_messages_out.pop();
1366 Manage active objects
1368 if (m_object_management_interval.step(dtime, 0.5)) {
1369 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1370 removeRemovedObjects();
1374 Manage particle spawner expiration
1376 if (m_particle_management_interval.step(dtime, 1.0)) {
1377 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1378 i != m_particle_spawners.end(); ) {
1379 //non expiring spawners
1380 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1386 if (i->second <= 0.f)
1387 m_particle_spawners.erase(i++);
1394 u32 ServerEnvironment::addParticleSpawner(float exptime)
1396 // Timers with lifetime 0 do not expire
1397 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1400 for (;;) { // look for unused particlespawner id
1402 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1403 if (f == m_particle_spawners.end()) {
1404 m_particle_spawners[id] = time;
1411 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1413 u32 id = addParticleSpawner(exptime);
1414 m_particle_spawner_attachments[id] = attached_id;
1415 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1416 obj->attachParticleSpawner(id);
1421 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1423 m_particle_spawners.erase(id);
1424 const auto &it = m_particle_spawner_attachments.find(id);
1425 if (it != m_particle_spawner_attachments.end()) {
1426 u16 obj_id = it->second;
1427 ServerActiveObject *sao = getActiveObject(obj_id);
1428 if (sao != NULL && remove_from_object) {
1429 sao->detachParticleSpawner(id);
1431 m_particle_spawner_attachments.erase(id);
1435 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1437 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1438 return (n != m_active_objects.end() ? n->second : NULL);
1442 * Verify if id is a free active object id
1444 * @return true if slot is free
1446 bool ServerEnvironment::isFreeServerActiveObjectId(u16 id) const
1451 return m_active_objects.find(id) == m_active_objects.end();
1455 * Retrieve the first free ActiveObject ID
1456 * @return free activeobject ID or 0 if none was found
1458 u16 ServerEnvironment::getFreeServerActiveObjectId()
1460 // try to reuse id's as late as possible
1461 static u16 last_used_id = 0;
1462 u16 startid = last_used_id;
1465 if (isFreeServerActiveObjectId(last_used_id))
1466 return last_used_id;
1468 if (last_used_id == startid)
1473 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1475 assert(object); // Pre-condition
1477 u16 id = addActiveObjectRaw(object, true, 0);
1482 Finds out what new objects have been added to
1483 inside a radius around a position
1485 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1487 std::set<u16> ¤t_objects,
1488 std::queue<u16> &added_objects)
1490 f32 radius_f = radius * BS;
1491 f32 player_radius_f = player_radius * BS;
1493 if (player_radius_f < 0)
1494 player_radius_f = 0;
1496 Go through the object list,
1497 - discard removed/deactivated objects,
1498 - discard objects that are too far away,
1499 - discard objects that are found in current_objects.
1500 - add remaining objects to added_objects
1502 for (auto &ao_it : m_active_objects) {
1503 u16 id = ao_it.first;
1506 ServerActiveObject *object = ao_it.second;
1510 if (object->isGone())
1513 f32 distance_f = object->getBasePosition().
1514 getDistanceFrom(playersao->getBasePosition());
1515 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1516 // Discard if too far
1517 if (distance_f > player_radius_f && player_radius_f != 0)
1519 } else if (distance_f > radius_f)
1522 // Discard if already on current_objects
1523 std::set<u16>::iterator n;
1524 n = current_objects.find(id);
1525 if(n != current_objects.end())
1527 // Add to added_objects
1528 added_objects.push(id);
1533 Finds out what objects have been removed from
1534 inside a radius around a position
1536 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1538 std::set<u16> ¤t_objects,
1539 std::queue<u16> &removed_objects)
1541 f32 radius_f = radius * BS;
1542 f32 player_radius_f = player_radius * BS;
1544 if (player_radius_f < 0)
1545 player_radius_f = 0;
1547 Go through current_objects; object is removed if:
1548 - object is not found in m_active_objects (this is actually an
1549 error condition; objects should be removed only after all clients
1550 have been informed about removal), or
1551 - object is to be removed or deactivated, or
1552 - object is too far away
1554 for (u16 id : current_objects) {
1555 ServerActiveObject *object = getActiveObject(id);
1557 if (object == NULL) {
1558 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1559 << " object in current_objects is NULL" << std::endl;
1560 removed_objects.push(id);
1564 if (object->isGone()) {
1565 removed_objects.push(id);
1569 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1570 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1571 if (distance_f <= player_radius_f || player_radius_f == 0)
1573 } else if (distance_f <= radius_f)
1576 // Object is no longer visible
1577 removed_objects.push(id);
1581 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1582 v3s16 blockpos, bool static_exists, v3s16 static_block)
1584 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1588 for (auto &so_it : block->m_static_objects.m_active) {
1589 // Get the ServerActiveObject counterpart to this StaticObject
1590 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it.first);
1591 if (ao_it == m_active_objects.end()) {
1592 // If this ever happens, there must be some kind of nasty bug.
1593 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1594 "Object from MapBlock::m_static_objects::m_active not found "
1595 "in m_active_objects";
1599 ServerActiveObject *sao = ao_it->second;
1600 sao->m_static_exists = static_exists;
1601 sao->m_static_block = static_block;
1605 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1607 if(m_active_object_messages.empty())
1608 return ActiveObjectMessage(0);
1610 ActiveObjectMessage message = m_active_object_messages.front();
1611 m_active_object_messages.pop();
1615 void ServerEnvironment::getSelectedActiveObjects(
1616 const core::line3d<f32> &shootline_on_map,
1617 std::vector<PointedThing> &objects)
1619 std::vector<u16> objectIds;
1620 getObjectsInsideRadius(objectIds, shootline_on_map.start,
1621 shootline_on_map.getLength() + 10.0f);
1622 const v3f line_vector = shootline_on_map.getVector();
1624 for (u16 objectId : objectIds) {
1625 ServerActiveObject* obj = getActiveObject(objectId);
1627 aabb3f selection_box;
1628 if (!obj->getSelectionBox(&selection_box))
1631 v3f pos = obj->getBasePosition();
1633 aabb3f offsetted_box(selection_box.MinEdge + pos,
1634 selection_box.MaxEdge + pos);
1636 v3f current_intersection;
1637 v3s16 current_normal;
1638 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1639 ¤t_intersection, ¤t_normal)) {
1640 objects.emplace_back(
1641 (s16) objectId, current_intersection, current_normal,
1642 (current_intersection - shootline_on_map.start).getLengthSQ());
1648 ************ Private methods *************
1651 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1652 bool set_changed, u32 dtime_s)
1654 assert(object); // Pre-condition
1655 if(object->getId() == 0){
1656 u16 new_id = getFreeServerActiveObjectId();
1659 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1660 <<"no free ids available"<<std::endl;
1661 if(object->environmentDeletes())
1665 object->setId(new_id);
1668 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1669 <<"supplied with id "<<object->getId()<<std::endl;
1672 if(!isFreeServerActiveObjectId(object->getId())) {
1673 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1674 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1675 if(object->environmentDeletes())
1680 if (objectpos_over_limit(object->getBasePosition())) {
1681 v3f p = object->getBasePosition();
1682 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1683 << "object position (" << p.X << "," << p.Y << "," << p.Z
1684 << ") outside maximum range" << std::endl;
1685 if (object->environmentDeletes())
1690 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1691 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1693 m_active_objects[object->getId()] = object;
1695 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1696 <<"Added id="<<object->getId()<<"; there are now "
1697 <<m_active_objects.size()<<" active objects."
1700 // Register reference in scripting api (must be done before post-init)
1701 m_script->addObjectReference(object);
1702 // Post-initialize object
1703 object->addedToEnvironment(dtime_s);
1705 // Add static data to block
1706 if(object->isStaticAllowed())
1708 // Add static object to active static list of the block
1709 v3f objectpos = object->getBasePosition();
1710 StaticObject s_obj(object, objectpos);
1711 // Add to the block where the object is located in
1712 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1713 MapBlock *block = m_map->emergeBlock(blockpos);
1715 block->m_static_objects.m_active[object->getId()] = s_obj;
1716 object->m_static_exists = true;
1717 object->m_static_block = blockpos;
1720 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1721 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1723 v3s16 p = floatToInt(objectpos, BS);
1724 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1725 <<"could not emerge block for storing id="<<object->getId()
1726 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1730 return object->getId();
1734 Remove objects that satisfy (isGone() && m_known_by_count==0)
1736 void ServerEnvironment::removeRemovedObjects()
1738 std::vector<u16> objects_to_remove;
1739 for (auto &ao_it : m_active_objects) {
1740 u16 id = ao_it.first;
1741 ServerActiveObject* obj = ao_it.second;
1743 // This shouldn't happen but check it
1745 errorstream << "ServerEnvironment::removeRemovedObjects(): "
1746 << "NULL object found. id=" << id << std::endl;
1747 objects_to_remove.push_back(id);
1752 We will handle objects marked for removal or deactivation
1758 Delete static data from block if removed
1760 if (obj->m_pending_removal)
1761 deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1763 // If still known by clients, don't actually remove. On some future
1764 // invocation this will be 0, which is when removal will continue.
1765 if(obj->m_known_by_count > 0)
1769 Move static data from active to stored if deactivated
1771 if (!obj->m_pending_removal && obj->m_static_exists) {
1772 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1774 std::map<u16, StaticObject>::iterator i =
1775 block->m_static_objects.m_active.find(id);
1776 if (i != block->m_static_objects.m_active.end()) {
1777 block->m_static_objects.m_stored.push_back(i->second);
1778 block->m_static_objects.m_active.erase(id);
1779 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1780 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1782 warningstream << "ServerEnvironment::removeRemovedObjects(): "
1783 << "id=" << id << " m_static_exists=true but "
1784 << "static data doesn't actually exist in "
1785 << PP(obj->m_static_block) << std::endl;
1788 infostream << "Failed to emerge block from which an object to "
1789 << "be deactivated was loaded from. id=" << id << std::endl;
1793 // Tell the object about removal
1794 obj->removingFromEnvironment();
1795 // Deregister in scripting api
1796 m_script->removeObjectReference(obj);
1799 if(obj->environmentDeletes())
1802 objects_to_remove.push_back(id);
1804 // Remove references from m_active_objects
1805 for (u16 i : objects_to_remove) {
1806 m_active_objects.erase(i);
1810 static void print_hexdump(std::ostream &o, const std::string &data)
1812 const int linelength = 16;
1813 for(int l=0; ; l++){
1814 int i0 = linelength * l;
1815 bool at_end = false;
1816 int thislinelength = linelength;
1817 if(i0 + thislinelength > (int)data.size()){
1818 thislinelength = data.size() - i0;
1821 for(int di=0; di<linelength; di++){
1824 if(di<thislinelength)
1825 snprintf(buf, 4, "%.2x ", data[i]);
1827 snprintf(buf, 4, " ");
1831 for(int di=0; di<thislinelength; di++){
1845 Convert stored objects from blocks near the players to active.
1847 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1852 // Ignore if no stored objects (to not set changed flag)
1853 if(block->m_static_objects.m_stored.empty())
1856 verbosestream<<"ServerEnvironment::activateObjects(): "
1857 <<"activating objects of block "<<PP(block->getPos())
1858 <<" ("<<block->m_static_objects.m_stored.size()
1859 <<" objects)"<<std::endl;
1860 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1862 errorstream<<"suspiciously large amount of objects detected: "
1863 <<block->m_static_objects.m_stored.size()<<" in "
1864 <<PP(block->getPos())
1865 <<"; removing all of them."<<std::endl;
1866 // Clear stored list
1867 block->m_static_objects.m_stored.clear();
1868 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1869 MOD_REASON_TOO_MANY_OBJECTS);
1873 // Activate stored objects
1874 std::vector<StaticObject> new_stored;
1875 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1876 // Create an active object from the data
1877 ServerActiveObject *obj = ServerActiveObject::create
1878 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1879 // If couldn't create object, store static data back.
1881 errorstream<<"ServerEnvironment::activateObjects(): "
1882 <<"failed to create active object from static object "
1883 <<"in block "<<PP(s_obj.pos/BS)
1884 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1885 print_hexdump(verbosestream, s_obj.data);
1887 new_stored.push_back(s_obj);
1890 verbosestream<<"ServerEnvironment::activateObjects(): "
1891 <<"activated static object pos="<<PP(s_obj.pos/BS)
1892 <<" type="<<(int)s_obj.type<<std::endl;
1893 // This will also add the object to the active static list
1894 addActiveObjectRaw(obj, false, dtime_s);
1897 // Clear stored list
1898 block->m_static_objects.m_stored.clear();
1899 // Add leftover failed stuff to stored list
1900 for (const StaticObject &s_obj : new_stored) {
1901 block->m_static_objects.m_stored.push_back(s_obj);
1905 Note: Block hasn't really been modified here.
1906 The objects have just been activated and moved from the stored
1907 static list to the active static list.
1908 As such, the block is essentially the same.
1909 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1910 Otherwise there would be a huge amount of unnecessary I/O.
1915 Convert objects that are not standing inside active blocks to static.
1917 If m_known_by_count != 0, active object is not deleted, but static
1918 data is still updated.
1920 If force_delete is set, active object is deleted nevertheless. It
1921 shall only be set so in the destructor of the environment.
1923 If block wasn't generated (not in memory or on disk),
1925 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1927 std::vector<u16> objects_to_remove;
1928 for (auto &ao_it : m_active_objects) {
1929 // force_delete might be overriden per object
1930 bool force_delete = _force_delete;
1932 ServerActiveObject* obj = ao_it.second;
1935 // Do not deactivate if static data creation not allowed
1936 if(!force_delete && !obj->isStaticAllowed())
1939 // removeRemovedObjects() is responsible for these
1940 if(!force_delete && obj->isGone())
1943 u16 id = ao_it.first;
1944 v3f objectpos = obj->getBasePosition();
1946 // The block in which the object resides in
1947 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1949 // If object's static data is stored in a deactivated block and object
1950 // is actually located in an active block, re-save to the block in
1951 // which the object is actually located in.
1953 obj->m_static_exists &&
1954 !m_active_blocks.contains(obj->m_static_block) &&
1955 m_active_blocks.contains(blockpos_o))
1957 // Delete from block where object was located
1958 deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
1960 StaticObject s_obj(obj, objectpos);
1961 // Save to block where object is located
1962 saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
1967 // If block is still active, don't remove
1968 if(!force_delete && m_active_blocks.contains(blockpos_o))
1971 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
1972 << "deactivating object id=" << id << " on inactive block "
1973 << PP(blockpos_o) << std::endl;
1975 // If known by some client, don't immediately delete.
1976 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1979 Update the static data
1981 if (obj->isStaticAllowed()) {
1982 // Create new static object
1983 StaticObject s_obj(obj, objectpos);
1985 bool stays_in_same_block = false;
1986 bool data_changed = true;
1988 // Check if static data has changed considerably
1989 if (obj->m_static_exists) {
1990 if (obj->m_static_block == blockpos_o)
1991 stays_in_same_block = true;
1993 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1996 std::map<u16, StaticObject>::iterator n =
1997 block->m_static_objects.m_active.find(id);
1998 if (n != block->m_static_objects.m_active.end()) {
1999 StaticObject static_old = n->second;
2001 float save_movem = obj->getMinimumSavedMovement();
2003 if (static_old.data == s_obj.data &&
2004 (static_old.pos - objectpos).getLength() < save_movem)
2005 data_changed = false;
2007 warningstream << "ServerEnvironment::deactivateFarObjects(): "
2008 << "id=" << id << " m_static_exists=true but "
2009 << "static data doesn't actually exist in "
2010 << PP(obj->m_static_block) << std::endl;
2016 While changes are always saved, blocks are only marked as modified
2017 if the object has moved or different staticdata. (see above)
2019 bool shall_be_written = (!stays_in_same_block || data_changed);
2020 u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2022 // Delete old static object
2023 deleteStaticFromBlock(obj, id, reason, false);
2025 // Add to the block where the object is located in
2026 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2027 u16 store_id = pending_delete ? id : 0;
2028 if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2029 force_delete = true;
2033 If known by some client, set pending deactivation.
2034 Otherwise delete it immediately.
2036 if(pending_delete && !force_delete)
2038 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2039 << "object id=" << id << " is known by clients"
2040 << "; not deleting yet" << std::endl;
2042 obj->m_pending_deactivation = true;
2045 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2046 << "object id=" << id << " is not known by clients"
2047 << "; deleting" << std::endl;
2049 // Tell the object about removal
2050 obj->removingFromEnvironment();
2051 // Deregister in scripting api
2052 m_script->removeObjectReference(obj);
2054 // Delete active object
2055 if(obj->environmentDeletes())
2057 // Id to be removed from m_active_objects
2058 objects_to_remove.push_back(id);
2061 // Remove references from m_active_objects
2062 for (u16 i : objects_to_remove) {
2063 m_active_objects.erase(i);
2067 void ServerEnvironment::deleteStaticFromBlock(
2068 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2070 if (!obj->m_static_exists)
2075 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2077 block = m_map->emergeBlock(obj->m_static_block, false);
2080 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2081 << " when deleting static data of object from it. id=" << id << std::endl;
2085 block->m_static_objects.remove(id);
2086 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2087 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2089 obj->m_static_exists = false;
2092 bool ServerEnvironment::saveStaticToBlock(
2093 v3s16 blockpos, u16 store_id,
2094 ServerActiveObject *obj, const StaticObject &s_obj,
2097 MapBlock *block = nullptr;
2099 block = m_map->emergeBlock(blockpos);
2100 } catch (InvalidPositionException &e) {
2101 // Handled via NULL pointer
2102 // NOTE: emergeBlock's failure is usually determined by it
2103 // actually returning NULL
2107 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2108 << " when saving static data of object to it. id=" << store_id << std::endl;
2111 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2112 warningstream << "ServerEnv: Trying to store id = " << store_id
2113 << " statically but block " << PP(blockpos)
2114 << " already contains "
2115 << block->m_static_objects.m_stored.size()
2116 << " objects." << std::endl;
2120 block->m_static_objects.insert(store_id, s_obj);
2121 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2122 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2124 obj->m_static_exists = true;
2125 obj->m_static_block = blockpos;
2130 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2131 const std::string &savedir, const Settings &conf)
2134 if (name == "sqlite3")
2135 return new PlayerDatabaseSQLite3(savedir);
2137 if (name == "dummy")
2138 return new Database_Dummy();
2140 if (name == "postgresql") {
2141 std::string connect_string;
2142 conf.getNoEx("pgsql_player_connection", connect_string);
2143 return new PlayerDatabasePostgreSQL(connect_string);
2146 if (name == "files")
2147 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2149 throw BaseException(std::string("Database backend ") + name + " not supported.");
2152 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2153 const Settings &cmd_args)
2155 std::string migrate_to = cmd_args.get("migrate-players");
2157 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2158 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2159 errorstream << "Cannot read world.mt!" << std::endl;
2163 if (!world_mt.exists("player_backend")) {
2164 errorstream << "Please specify your current backend in world.mt:"
2166 << " player_backend = {files|sqlite3|postgresql}"
2171 std::string backend = world_mt.get("player_backend");
2172 if (backend == migrate_to) {
2173 errorstream << "Cannot migrate: new backend is same"
2174 << " as the old one" << std::endl;
2178 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2181 if (backend == "files") {
2182 // Create backup directory
2183 fs::CreateDir(players_backup_path);
2187 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2188 game_params.world_path, world_mt);
2189 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2190 game_params.world_path, world_mt);
2192 std::vector<std::string> player_list;
2193 srcdb->listPlayers(player_list);
2194 for (std::vector<std::string>::const_iterator it = player_list.begin();
2195 it != player_list.end(); ++it) {
2196 actionstream << "Migrating player " << it->c_str() << std::endl;
2197 RemotePlayer player(it->c_str(), NULL);
2198 PlayerSAO playerSAO(NULL, &player, 15000, false);
2200 srcdb->loadPlayer(&player, &playerSAO);
2202 playerSAO.finalize(&player, std::set<std::string>());
2203 player.setPlayerSAO(&playerSAO);
2205 dstdb->savePlayer(&player);
2207 // For files source, move player files to backup dir
2208 if (backend == "files") {
2210 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2211 players_backup_path + DIR_DELIM + (*it));
2215 actionstream << "Successfully migrated " << player_list.size() << " players"
2217 world_mt.set("player_backend", migrate_to);
2218 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2219 errorstream << "Failed to update world.mt!" << std::endl;
2221 actionstream << "world.mt updated" << std::endl;
2223 // When migration is finished from file backend, remove players directory if empty
2224 if (backend == "files") {
2225 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2232 } catch (BaseException &e) {
2233 errorstream << "An error occured during migration: " << e.what() << std::endl;