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 INodeDefManager *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 (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
258 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
259 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
261 n = block->getNodeNoEx(pos);
263 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
264 iit != m_lbm_lookup.end(); ++iit) {
265 const std::vector<LoadingBlockModifierDef *> *lbm_list =
266 iit->second.lookup(c);
269 for (auto lbmdef : *lbm_list) {
270 lbmdef->trigger(env, pos + pos_of_block, n);
280 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
282 const s16 r2 = r * r;
284 for (p.X = p0.X - r; p.X <= p0.X + r; p.X++)
285 for (p.Y = p0.Y - r; p.Y <= p0.Y + r; p.Y++)
286 for (p.Z = p0.Z - r; p.Z <= p0.Z + r; p.Z++) {
288 if (p.getDistanceFromSQ(p0) <= r2) {
295 void fillViewConeBlock(v3s16 p0,
297 const v3f camera_pos,
298 const v3f camera_dir,
299 const float camera_fov,
300 std::set<v3s16> &list)
303 const s16 r_nodes = r * BS * MAP_BLOCKSIZE;
304 for (p.X = p0.X - r; p.X <= p0.X+r; p.X++)
305 for (p.Y = p0.Y - r; p.Y <= p0.Y+r; p.Y++)
306 for (p.Z = p0.Z - r; p.Z <= p0.Z+r; p.Z++) {
307 if (isBlockInSight(p, camera_pos, camera_dir, camera_fov, r_nodes)) {
313 void ActiveBlockList::update(std::vector<PlayerSAO*> &active_players,
314 s16 active_block_range,
315 s16 active_object_range,
316 std::set<v3s16> &blocks_removed,
317 std::set<v3s16> &blocks_added)
322 std::set<v3s16> newlist = m_forceloaded_list;
323 m_abm_list = m_forceloaded_list;
324 for (const PlayerSAO *playersao : active_players) {
325 v3s16 pos = getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS));
326 fillRadiusBlock(pos, active_block_range, m_abm_list);
327 fillRadiusBlock(pos, active_block_range, newlist);
329 s16 player_ao_range = std::min(active_object_range, playersao->getWantedRange());
330 // only do this if this would add blocks
331 if (player_ao_range > active_block_range) {
332 v3f camera_dir = v3f(0,0,1);
333 camera_dir.rotateYZBy(playersao->getPitch());
334 camera_dir.rotateXZBy(playersao->getYaw());
335 fillViewConeBlock(pos,
337 playersao->getEyePosition(),
345 Find out which blocks on the old list are not on the new list
347 // Go through old list
348 for (v3s16 p : m_list) {
349 // If not on new list, it's been removed
350 if (newlist.find(p) == newlist.end())
351 blocks_removed.insert(p);
355 Find out which blocks on the new list are not on the old list
357 // Go through new list
358 for (v3s16 p : newlist) {
359 // If not on old list, it's been added
360 if(m_list.find(p) == m_list.end())
361 blocks_added.insert(p);
374 ServerEnvironment::ServerEnvironment(ServerMap *map,
375 ServerScripting *scriptIface, Server *server,
376 const std::string &path_world):
379 m_script(scriptIface),
381 m_path_world(path_world)
383 // Determine which database backend to use
384 std::string conf_path = path_world + DIR_DELIM + "world.mt";
386 bool succeeded = conf.readConfigFile(conf_path.c_str());
387 if (!succeeded || !conf.exists("player_backend")) {
388 // fall back to files
389 conf.set("player_backend", "files");
390 warningstream << "/!\\ You are using old player file backend. "
391 << "This backend is deprecated and will be removed in next release /!\\"
392 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
393 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
395 if (!conf.updateConfigFile(conf_path.c_str())) {
396 errorstream << "ServerEnvironment::ServerEnvironment(): "
397 << "Failed to update world.mt!" << std::endl;
402 conf.getNoEx("player_backend", name);
403 m_player_database = openPlayerDatabase(name, path_world, conf);
406 ServerEnvironment::~ServerEnvironment()
408 // Clear active block list.
409 // This makes the next one delete all active objects.
410 m_active_blocks.clear();
412 // Convert all objects to static and delete the active objects
413 deactivateFarObjects(true);
418 // Delete ActiveBlockModifiers
419 for (ABMWithState &m_abm : m_abms) {
423 // Deallocate players
424 for (RemotePlayer *m_player : m_players) {
428 delete m_player_database;
431 Map & ServerEnvironment::getMap()
436 ServerMap & ServerEnvironment::getServerMap()
441 RemotePlayer *ServerEnvironment::getPlayer(const session_t peer_id)
443 for (RemotePlayer *player : m_players) {
444 if (player->getPeerId() == peer_id)
450 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
452 for (RemotePlayer *player : m_players) {
453 if (strcmp(player->getName(), name) == 0)
459 void ServerEnvironment::addPlayer(RemotePlayer *player)
462 Check that peer_ids are unique.
463 Also check that names are unique.
464 Exception: there can be multiple players with peer_id=0
466 // If peer id is non-zero, it has to be unique.
467 if (player->getPeerId() != PEER_ID_INEXISTENT)
468 FATAL_ERROR_IF(getPlayer(player->getPeerId()) != NULL, "Peer id not unique");
469 // Name has to be unique.
470 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
472 m_players.push_back(player);
475 void ServerEnvironment::removePlayer(RemotePlayer *player)
477 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
478 it != m_players.end(); ++it) {
479 if ((*it) == player) {
487 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
489 return m_player_database->removePlayer(name);
492 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, v3s16 *p)
494 // Iterate trough nodes on the line
495 voxalgo::VoxelLineIterator iterator(pos1 / BS, (pos2 - pos1) / BS);
497 MapNode n = getMap().getNodeNoEx(iterator.m_current_node_pos);
500 if (n.param0 != CONTENT_AIR) {
502 *p = iterator.m_current_node_pos;
506 } while (iterator.m_current_index <= iterator.m_last_index);
510 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
511 const std::string &str_reason, bool reconnect)
513 for (RemotePlayer *player : m_players) {
514 m_server->DenyAccessVerCompliant(player->getPeerId(),
515 player->protocol_version, reason, str_reason, reconnect);
519 void ServerEnvironment::saveLoadedPlayers()
521 std::string players_path = m_path_world + DIR_DELIM + "players";
522 fs::CreateDir(players_path);
524 for (RemotePlayer *player : m_players) {
525 if (player->checkModified() || (player->getPlayerSAO() &&
526 player->getPlayerSAO()->extendedAttributesModified())) {
528 m_player_database->savePlayer(player);
529 } catch (DatabaseException &e) {
530 errorstream << "Failed to save player " << player->getName() << " exception: "
531 << e.what() << std::endl;
538 void ServerEnvironment::savePlayer(RemotePlayer *player)
541 m_player_database->savePlayer(player);
542 } catch (DatabaseException &e) {
543 errorstream << "Failed to save player " << player->getName() << " exception: "
544 << e.what() << std::endl;
549 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
550 session_t peer_id, bool is_singleplayer)
552 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
553 // Create player if it doesn't exist
554 if (!m_player_database->loadPlayer(player, playersao)) {
556 // Set player position
557 infostream << "Server: Finding spawn place for player \""
558 << player->getName() << "\"" << std::endl;
559 playersao->setBasePosition(m_server->findSpawnPos());
561 // Make sure the player is saved
562 player->setModified(true);
564 // If the player exists, ensure that they respawn inside legal bounds
565 // This fixes an assert crash when the player can't be added
566 // to the environment
567 ServerMap &map = getServerMap();
568 if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) {
569 actionstream << "Respawn position for player \""
570 << player->getName() << "\" outside limits, resetting" << std::endl;
571 playersao->setBasePosition(m_server->findSpawnPos());
575 // Add player to environment
578 /* Clean up old HUD elements from previous sessions */
581 /* Add object to environment */
582 addActiveObject(playersao);
587 void ServerEnvironment::saveMeta()
589 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
591 // Open file and serialize
592 std::ostringstream ss(std::ios_base::binary);
595 args.setU64("game_time", m_game_time);
596 args.setU64("time_of_day", getTimeOfDay());
597 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
598 args.setU64("lbm_introduction_times_version", 1);
599 args.set("lbm_introduction_times",
600 m_lbm_mgr.createIntroductionTimesString());
601 args.setU64("day_count", m_day_count);
605 if(!fs::safeWriteToFile(path, ss.str()))
607 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
609 throw SerializationError("Couldn't save env meta");
613 void ServerEnvironment::loadMeta()
615 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
617 // Open file and deserialize
618 std::ifstream is(path.c_str(), std::ios_base::binary);
620 infostream << "ServerEnvironment::loadMeta(): Failed to open "
621 << path << std::endl;
622 throw SerializationError("Couldn't load env meta");
627 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
628 throw SerializationError("ServerEnvironment::loadMeta(): "
629 "EnvArgsEnd not found!");
633 m_game_time = args.getU64("game_time");
634 } catch (SettingNotFoundException &e) {
635 // Getting this is crucial, otherwise timestamps are useless
636 throw SerializationError("Couldn't load env meta game_time");
639 setTimeOfDay(args.exists("time_of_day") ?
640 // set day to early morning by default
641 args.getU64("time_of_day") : 5250);
643 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
644 // If missing, do as if clearObjects was never called
645 args.getU64("last_clear_objects_time") : 0;
647 std::string lbm_introduction_times;
649 u64 ver = args.getU64("lbm_introduction_times_version");
651 lbm_introduction_times = args.get("lbm_introduction_times");
653 infostream << "ServerEnvironment::loadMeta(): Non-supported"
654 << " introduction time version " << ver << std::endl;
656 } catch (SettingNotFoundException &e) {
657 // No problem, this is expected. Just continue with an empty string
659 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
661 m_day_count = args.exists("day_count") ?
662 args.getU64("day_count") : 0;
665 void ServerEnvironment::loadDefaultMeta()
667 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
672 ActiveBlockModifier *abm;
674 std::vector<content_t> required_neighbors;
675 bool check_required_neighbors; // false if required_neighbors is known to be empty
681 ServerEnvironment *m_env;
682 std::vector<std::vector<ActiveABM> *> m_aabms;
684 ABMHandler(std::vector<ABMWithState> &abms,
685 float dtime_s, ServerEnvironment *env,
691 INodeDefManager *ndef = env->getGameDef()->ndef();
692 for (ABMWithState &abmws : abms) {
693 ActiveBlockModifier *abm = abmws.abm;
694 float trigger_interval = abm->getTriggerInterval();
695 if(trigger_interval < 0.001)
696 trigger_interval = 0.001;
697 float actual_interval = dtime_s;
699 abmws.timer += dtime_s;
700 if(abmws.timer < trigger_interval)
702 abmws.timer -= trigger_interval;
703 actual_interval = trigger_interval;
705 float chance = abm->getTriggerChance();
710 if (abm->getSimpleCatchUp()) {
711 float intervals = actual_interval / trigger_interval;
714 aabm.chance = chance / intervals;
718 aabm.chance = chance;
722 const std::vector<std::string> &required_neighbors_s =
723 abm->getRequiredNeighbors();
724 for (const std::string &required_neighbor_s : required_neighbors_s) {
725 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
727 aabm.check_required_neighbors = !required_neighbors_s.empty();
730 const std::vector<std::string> &contents_s = abm->getTriggerContents();
731 for (const std::string &content_s : contents_s) {
732 std::vector<content_t> ids;
733 ndef->getIds(content_s, ids);
734 for (content_t c : ids) {
735 if (c >= m_aabms.size())
736 m_aabms.resize(c + 256, NULL);
738 m_aabms[c] = new std::vector<ActiveABM>;
739 m_aabms[c]->push_back(aabm);
747 for (auto &aabms : m_aabms)
751 // Find out how many objects the given block and its neighbours contain.
752 // Returns the number of objects in the block, and also in 'wider' the
753 // number of objects in the block and all its neighbours. The latter
754 // may an estimate if any neighbours are unloaded.
755 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
758 u32 wider_unknown_count = 0;
759 for(s16 x=-1; x<=1; x++)
760 for(s16 y=-1; y<=1; y++)
761 for(s16 z=-1; z<=1; z++)
763 MapBlock *block2 = map->getBlockNoCreateNoEx(
764 block->getPos() + v3s16(x,y,z));
766 wider_unknown_count++;
769 wider += block2->m_static_objects.m_active.size()
770 + block2->m_static_objects.m_stored.size();
773 u32 active_object_count = block->m_static_objects.m_active.size();
774 u32 wider_known_count = 3*3*3 - wider_unknown_count;
775 wider += wider_unknown_count * wider / wider_known_count;
776 return active_object_count;
779 void apply(MapBlock *block)
781 if(m_aabms.empty() || block->isDummy())
784 ServerMap *map = &m_env->getServerMap();
786 u32 active_object_count_wider;
787 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
788 m_env->m_added_objects = 0;
791 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
792 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
793 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
795 const MapNode &n = block->getNodeUnsafe(p0);
796 content_t c = n.getContent();
798 if (c >= m_aabms.size() || !m_aabms[c])
801 v3s16 p = p0 + block->getPosRelative();
802 for (ActiveABM &aabm : *m_aabms[c]) {
803 if (myrand() % aabm.chance != 0)
807 if (aabm.check_required_neighbors) {
809 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
810 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
811 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
816 if (block->isValidPosition(p1)) {
817 // if the neighbor is found on the same map block
818 // get it straight from there
819 const MapNode &n = block->getNodeUnsafe(p1);
822 // otherwise consult the map
823 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
826 if (CONTAINS(aabm.required_neighbors, c))
829 // No required neighbor found
834 // Call all the trigger variations
835 aabm.abm->trigger(m_env, p, n);
836 aabm.abm->trigger(m_env, p, n,
837 active_object_count, active_object_count_wider);
839 // Count surrounding objects again if the abms added any
840 if(m_env->m_added_objects > 0) {
841 active_object_count = countObjects(block, map, active_object_count_wider);
842 m_env->m_added_objects = 0;
849 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
851 // Reset usage timer immediately, otherwise a block that becomes active
852 // again at around the same time as it would normally be unloaded will
853 // get unloaded incorrectly. (I think this still leaves a small possibility
854 // of a race condition between this and server::AsyncRunStep, which only
855 // some kind of synchronisation will fix, but it at least reduces the window
856 // of opportunity for it to break from seconds to nanoseconds)
857 block->resetUsageTimer();
859 // Get time difference
861 u32 stamp = block->getTimestamp();
862 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
863 dtime_s = m_game_time - stamp;
864 dtime_s += additional_dtime;
866 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
867 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
869 // Remove stored static objects if clearObjects was called since block's timestamp
870 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
871 block->m_static_objects.m_stored.clear();
872 // do not set changed flag to avoid unnecessary mapblock writes
875 // Set current time as timestamp
876 block->setTimestampNoChangedFlag(m_game_time);
878 /*infostream<<"ServerEnvironment::activateBlock(): block is "
879 <<dtime_s<<" seconds old."<<std::endl;*/
881 // Activate stored objects
882 activateObjects(block, dtime_s);
884 /* Handle LoadingBlockModifiers */
885 m_lbm_mgr.applyLBMs(this, block, stamp);
888 std::vector<NodeTimer> elapsed_timers =
889 block->m_node_timers.step((float)dtime_s);
890 if (!elapsed_timers.empty()) {
892 for (const NodeTimer &elapsed_timer : elapsed_timers) {
893 n = block->getNodeNoEx(elapsed_timer.position);
894 v3s16 p = elapsed_timer.position + block->getPosRelative();
895 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
896 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
897 elapsed_timer.position));
902 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
904 m_abms.emplace_back(abm);
907 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
909 m_lbm_mgr.addLBMDef(lbm);
912 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
914 INodeDefManager *ndef = m_server->ndef();
915 MapNode n_old = m_map->getNodeNoEx(p);
918 if (ndef->get(n_old).has_on_destruct)
919 m_script->node_on_destruct(p, n_old);
922 if (!m_map->addNodeWithEvent(p, n))
925 // Update active VoxelManipulator if a mapgen thread
926 m_map->updateVManip(p);
928 // Call post-destructor
929 if (ndef->get(n_old).has_after_destruct)
930 m_script->node_after_destruct(p, n_old);
933 if (ndef->get(n).has_on_construct)
934 m_script->node_on_construct(p, n);
939 bool ServerEnvironment::removeNode(v3s16 p)
941 INodeDefManager *ndef = m_server->ndef();
942 MapNode n_old = m_map->getNodeNoEx(p);
945 if (ndef->get(n_old).has_on_destruct)
946 m_script->node_on_destruct(p, n_old);
949 // This is slightly optimized compared to addNodeWithEvent(air)
950 if (!m_map->removeNodeWithEvent(p))
953 // Update active VoxelManipulator if a mapgen thread
954 m_map->updateVManip(p);
956 // Call post-destructor
957 if (ndef->get(n_old).has_after_destruct)
958 m_script->node_after_destruct(p, n_old);
960 // Air doesn't require constructor
964 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
966 if (!m_map->addNodeWithEvent(p, n, false))
969 // Update active VoxelManipulator if a mapgen thread
970 m_map->updateVManip(p);
975 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
978 for (auto &activeObject : m_active_objects) {
979 ServerActiveObject* obj = activeObject.second;
980 u16 id = activeObject.first;
981 v3f objectpos = obj->getBasePosition();
982 if (objectpos.getDistanceFrom(pos) > radius)
984 objects.push_back(id);
988 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
990 infostream << "ServerEnvironment::clearObjects(): "
991 << "Removing all active objects" << std::endl;
992 std::vector<u16> objects_to_remove;
993 for (auto &it : m_active_objects) {
995 ServerActiveObject* obj = it.second;
996 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
999 // Delete static object if block is loaded
1000 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1002 // If known by some client, don't delete immediately
1003 if (obj->m_known_by_count > 0) {
1004 obj->m_pending_removal = true;
1008 // Tell the object about removal
1009 obj->removingFromEnvironment();
1010 // Deregister in scripting api
1011 m_script->removeObjectReference(obj);
1013 // Delete active object
1014 if (obj->environmentDeletes())
1016 // Id to be removed from m_active_objects
1017 objects_to_remove.push_back(id);
1020 // Remove references from m_active_objects
1021 for (u16 i : objects_to_remove) {
1022 m_active_objects.erase(i);
1025 // Get list of loaded blocks
1026 std::vector<v3s16> loaded_blocks;
1027 infostream << "ServerEnvironment::clearObjects(): "
1028 << "Listing all loaded blocks" << std::endl;
1029 m_map->listAllLoadedBlocks(loaded_blocks);
1030 infostream << "ServerEnvironment::clearObjects(): "
1031 << "Done listing all loaded blocks: "
1032 << loaded_blocks.size()<<std::endl;
1034 // Get list of loadable blocks
1035 std::vector<v3s16> loadable_blocks;
1036 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1037 infostream << "ServerEnvironment::clearObjects(): "
1038 << "Listing all loadable blocks" << std::endl;
1039 m_map->listAllLoadableBlocks(loadable_blocks);
1040 infostream << "ServerEnvironment::clearObjects(): "
1041 << "Done listing all loadable blocks: "
1042 << loadable_blocks.size() << std::endl;
1044 loadable_blocks = loaded_blocks;
1047 actionstream << "ServerEnvironment::clearObjects(): "
1048 << "Now clearing objects in " << loadable_blocks.size()
1049 << " blocks" << std::endl;
1051 // Grab a reference on each loaded block to avoid unloading it
1052 for (v3s16 p : loaded_blocks) {
1053 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1054 assert(block != NULL);
1058 // Remove objects in all loadable blocks
1059 u32 unload_interval = U32_MAX;
1060 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1061 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1062 unload_interval = MYMAX(unload_interval, 1);
1064 u32 report_interval = loadable_blocks.size() / 10;
1065 u32 num_blocks_checked = 0;
1066 u32 num_blocks_cleared = 0;
1067 u32 num_objs_cleared = 0;
1068 for (auto i = loadable_blocks.begin();
1069 i != loadable_blocks.end(); ++i) {
1071 MapBlock *block = m_map->emergeBlock(p, false);
1073 errorstream << "ServerEnvironment::clearObjects(): "
1074 << "Failed to emerge block " << PP(p) << std::endl;
1077 u32 num_stored = block->m_static_objects.m_stored.size();
1078 u32 num_active = block->m_static_objects.m_active.size();
1079 if (num_stored != 0 || num_active != 0) {
1080 block->m_static_objects.m_stored.clear();
1081 block->m_static_objects.m_active.clear();
1082 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1083 MOD_REASON_CLEAR_ALL_OBJECTS);
1084 num_objs_cleared += num_stored + num_active;
1085 num_blocks_cleared++;
1087 num_blocks_checked++;
1089 if (report_interval != 0 &&
1090 num_blocks_checked % report_interval == 0) {
1091 float percent = 100.0 * (float)num_blocks_checked /
1092 loadable_blocks.size();
1093 actionstream << "ServerEnvironment::clearObjects(): "
1094 << "Cleared " << num_objs_cleared << " objects"
1095 << " in " << num_blocks_cleared << " blocks ("
1096 << percent << "%)" << std::endl;
1098 if (num_blocks_checked % unload_interval == 0) {
1099 m_map->unloadUnreferencedBlocks();
1102 m_map->unloadUnreferencedBlocks();
1104 // Drop references that were added above
1105 for (v3s16 p : loaded_blocks) {
1106 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1111 m_last_clear_objects_time = m_game_time;
1113 actionstream << "ServerEnvironment::clearObjects(): "
1114 << "Finished: Cleared " << num_objs_cleared << " objects"
1115 << " in " << num_blocks_cleared << " blocks" << std::endl;
1118 void ServerEnvironment::step(float dtime)
1120 /* Step time of day */
1121 stepTimeOfDay(dtime);
1124 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1125 // really matter that much.
1126 static thread_local const float server_step =
1127 g_settings->getFloat("dedicated_server_step");
1128 m_recommended_send_interval = server_step;
1134 m_game_time_fraction_counter += dtime;
1135 u32 inc_i = (u32)m_game_time_fraction_counter;
1136 m_game_time += inc_i;
1137 m_game_time_fraction_counter -= (float)inc_i;
1144 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1145 for (RemotePlayer *player : m_players) {
1146 // Ignore disconnected players
1147 if (player->getPeerId() == PEER_ID_INEXISTENT)
1151 player->move(dtime, this, 100 * BS);
1156 Manage active block list
1158 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1159 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1161 Get player block positions
1163 std::vector<PlayerSAO*> players;
1164 for (RemotePlayer *player: m_players) {
1165 // Ignore disconnected players
1166 if (player->getPeerId() == PEER_ID_INEXISTENT)
1169 PlayerSAO *playersao = player->getPlayerSAO();
1172 players.push_back(playersao);
1176 Update list of active blocks, collecting changes
1178 // use active_object_send_range_blocks since that is max distance
1179 // for active objects sent the client anyway
1180 static thread_local const s16 active_object_range =
1181 g_settings->getS16("active_object_send_range_blocks");
1182 static thread_local const s16 active_block_range =
1183 g_settings->getS16("active_block_range");
1184 std::set<v3s16> blocks_removed;
1185 std::set<v3s16> blocks_added;
1186 m_active_blocks.update(players, active_block_range, active_object_range,
1187 blocks_removed, blocks_added);
1190 Handle removed blocks
1193 // Convert active objects that are no more in active blocks to static
1194 deactivateFarObjects(false);
1196 for (const v3s16 &p: blocks_removed) {
1197 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1201 // Set current time as timestamp (and let it set ChangedFlag)
1202 block->setTimestamp(m_game_time);
1209 for (const v3s16 &p: blocks_added) {
1210 MapBlock *block = m_map->getBlockOrEmerge(p);
1212 m_active_blocks.m_list.erase(p);
1213 m_active_blocks.m_abm_list.erase(p);
1217 activateBlock(block);
1221 // placeholder for the "real" time passed
1225 Mess around in active blocks
1227 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval,
1229 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1231 for (const v3s16 &p: m_active_blocks.m_list) {
1232 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1236 // Reset block usage timer
1237 block->resetUsageTimer();
1239 // Set current time as timestamp
1240 block->setTimestampNoChangedFlag(m_game_time);
1241 // If time has changed much from the one on disk,
1242 // set block to be saved when it is unloaded
1243 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1244 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1245 MOD_REASON_BLOCK_EXPIRED);
1248 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(elapsed_time);
1249 if (!elapsed_timers.empty()) {
1252 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1253 n = block->getNodeNoEx(elapsed_timer.position);
1254 p2 = elapsed_timer.position + block->getPosRelative();
1255 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1256 block->setNodeTimer(NodeTimer(
1257 elapsed_timer.timeout, 0, elapsed_timer.position));
1264 if (m_active_block_modifier_interval.step(dtime,
1265 m_cache_abm_interval * m_active_block_interval_overload_skip, &elapsed_time))
1267 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1268 TimeTaker timer("modify in active blocks per interval");
1270 // Initialize handling of ActiveBlockModifiers
1271 ABMHandler abmhandler(m_abms, elapsed_time, this, true);
1273 for (const v3s16 &p : m_active_blocks.m_abm_list) {
1274 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1278 // Set current time as timestamp
1279 block->setTimestampNoChangedFlag(m_game_time);
1281 /* Handle ActiveBlockModifiers */
1282 abmhandler.apply(block);
1285 const u32 time_ms = timer.stop(true);
1286 // allow up to 10% of the budget interval
1287 const u32 max_time_ms = m_cache_abm_interval * 1000.0f * 0.1f;
1288 if (time_ms > max_time_ms) {
1289 warningstream << "active block modifiers took "
1290 << time_ms << "ms (longer than "
1291 << max_time_ms << "ms)" << std::endl;
1292 m_active_block_interval_overload_skip = ((float)time_ms / max_time_ms);
1294 m_active_block_interval_overload_skip = 1.0f;
1299 Step script environment (run global on_step())
1301 m_script->environment_Step(dtime);
1306 if (m_active_object_interval.step(dtime,
1307 m_cache_ao_interval * m_active_object_interval_overload_skip, &elapsed_time)) {
1309 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1310 TimeTaker timer("Step active objects");
1312 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1314 // This helps the objects to send data at the same time
1315 bool send_recommended = false;
1316 m_send_recommended_timer += elapsed_time;
1317 if(m_send_recommended_timer > getSendRecommendedInterval())
1319 m_send_recommended_timer -= getSendRecommendedInterval();
1320 send_recommended = true;
1323 for (auto &ao_it : m_active_objects) {
1324 ServerActiveObject* obj = ao_it.second;
1329 obj->step(elapsed_time, send_recommended);
1330 // Read messages from object
1331 while (!obj->m_messages_out.empty()) {
1332 m_active_object_messages.push(obj->m_messages_out.front());
1333 obj->m_messages_out.pop();
1337 // calculate a simple moving average
1338 m_avg_ao_time = m_avg_ao_time * 0.9f + timer.stop(true) * 0.1f;
1340 // allow up to 20% of the budget interval
1341 const float max_time_ms = m_cache_ao_interval * 1000.0f * 0.2f;
1342 if (m_avg_ao_time > max_time_ms) {
1343 warningstream << "active objects took "
1344 << m_avg_ao_time << "ms (longer than "
1345 << max_time_ms << "ms)" << std::endl;
1347 m_active_object_interval_overload_skip = m_avg_ao_time / max_time_ms;
1349 m_active_object_interval_overload_skip = 1.0f;
1354 Manage active objects
1356 if (m_object_management_interval.step(dtime, 0.5)) {
1357 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1358 removeRemovedObjects();
1362 Manage particle spawner expiration
1364 if (m_particle_management_interval.step(dtime, 1.0)) {
1365 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1366 i != m_particle_spawners.end(); ) {
1367 //non expiring spawners
1368 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1374 if (i->second <= 0.f)
1375 m_particle_spawners.erase(i++);
1382 u32 ServerEnvironment::addParticleSpawner(float exptime)
1384 // Timers with lifetime 0 do not expire
1385 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1388 for (;;) { // look for unused particlespawner id
1390 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1391 if (f == m_particle_spawners.end()) {
1392 m_particle_spawners[id] = time;
1399 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1401 u32 id = addParticleSpawner(exptime);
1402 m_particle_spawner_attachments[id] = attached_id;
1403 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1404 obj->attachParticleSpawner(id);
1409 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1411 m_particle_spawners.erase(id);
1412 const auto &it = m_particle_spawner_attachments.find(id);
1413 if (it != m_particle_spawner_attachments.end()) {
1414 u16 obj_id = it->second;
1415 ServerActiveObject *sao = getActiveObject(obj_id);
1416 if (sao != NULL && remove_from_object) {
1417 sao->detachParticleSpawner(id);
1419 m_particle_spawner_attachments.erase(id);
1423 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1425 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1426 return (n != m_active_objects.end() ? n->second : NULL);
1429 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1434 return objects.find(id) == objects.end();
1437 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1439 //try to reuse id's as late as possible
1440 static u16 last_used_id = 0;
1441 u16 startid = last_used_id;
1445 if(isFreeServerActiveObjectId(last_used_id, objects))
1446 return last_used_id;
1448 if(last_used_id == startid)
1453 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1455 assert(object); // Pre-condition
1457 u16 id = addActiveObjectRaw(object, true, 0);
1462 Finds out what new objects have been added to
1463 inside a radius around a position
1465 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1467 std::set<u16> ¤t_objects,
1468 std::queue<u16> &added_objects)
1470 f32 radius_f = radius * BS;
1471 f32 player_radius_f = player_radius * BS;
1473 if (player_radius_f < 0)
1474 player_radius_f = 0;
1476 Go through the object list,
1477 - discard removed/deactivated objects,
1478 - discard objects that are too far away,
1479 - discard objects that are found in current_objects.
1480 - add remaining objects to added_objects
1482 for (auto &ao_it : m_active_objects) {
1483 u16 id = ao_it.first;
1486 ServerActiveObject *object = ao_it.second;
1490 if (object->isGone())
1493 f32 distance_f = object->getBasePosition().
1494 getDistanceFrom(playersao->getBasePosition());
1495 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1496 // Discard if too far
1497 if (distance_f > player_radius_f && player_radius_f != 0)
1499 } else if (distance_f > radius_f)
1502 // Discard if already on current_objects
1503 std::set<u16>::iterator n;
1504 n = current_objects.find(id);
1505 if(n != current_objects.end())
1507 // Add to added_objects
1508 added_objects.push(id);
1513 Finds out what objects have been removed from
1514 inside a radius around a position
1516 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1518 std::set<u16> ¤t_objects,
1519 std::queue<u16> &removed_objects)
1521 f32 radius_f = radius * BS;
1522 f32 player_radius_f = player_radius * BS;
1524 if (player_radius_f < 0)
1525 player_radius_f = 0;
1527 Go through current_objects; object is removed if:
1528 - object is not found in m_active_objects (this is actually an
1529 error condition; objects should be removed only after all clients
1530 have been informed about removal), or
1531 - object is to be removed or deactivated, or
1532 - object is too far away
1534 for (u16 id : current_objects) {
1535 ServerActiveObject *object = getActiveObject(id);
1537 if (object == NULL) {
1538 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1539 << " object in current_objects is NULL" << std::endl;
1540 removed_objects.push(id);
1544 if (object->isGone()) {
1545 removed_objects.push(id);
1549 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1550 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1551 if (distance_f <= player_radius_f || player_radius_f == 0)
1553 } else if (distance_f <= radius_f)
1556 // Object is no longer visible
1557 removed_objects.push(id);
1561 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1562 v3s16 blockpos, bool static_exists, v3s16 static_block)
1564 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1568 for (auto &so_it : block->m_static_objects.m_active) {
1569 // Get the ServerActiveObject counterpart to this StaticObject
1570 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it.first);
1571 if (ao_it == m_active_objects.end()) {
1572 // If this ever happens, there must be some kind of nasty bug.
1573 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1574 "Object from MapBlock::m_static_objects::m_active not found "
1575 "in m_active_objects";
1579 ServerActiveObject *sao = ao_it->second;
1580 sao->m_static_exists = static_exists;
1581 sao->m_static_block = static_block;
1585 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1587 if(m_active_object_messages.empty())
1588 return ActiveObjectMessage(0);
1590 ActiveObjectMessage message = m_active_object_messages.front();
1591 m_active_object_messages.pop();
1595 void ServerEnvironment::getSelectedActiveObjects(
1596 const core::line3d<f32> &shootline_on_map,
1597 std::vector<PointedThing> &objects)
1599 std::vector<u16> objectIds;
1600 getObjectsInsideRadius(objectIds, shootline_on_map.start,
1601 shootline_on_map.getLength() + 10.0f);
1602 const v3f line_vector = shootline_on_map.getVector();
1604 for (u16 objectId : objectIds) {
1605 ServerActiveObject* obj = getActiveObject(objectId);
1607 aabb3f selection_box;
1608 if (!obj->getSelectionBox(&selection_box))
1611 v3f pos = obj->getBasePosition();
1613 aabb3f offsetted_box(selection_box.MinEdge + pos,
1614 selection_box.MaxEdge + pos);
1616 v3f current_intersection;
1617 v3s16 current_normal;
1618 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1619 ¤t_intersection, ¤t_normal)) {
1620 objects.emplace_back(
1621 (s16) objectId, current_intersection, current_normal,
1622 (current_intersection - shootline_on_map.start).getLengthSQ());
1628 ************ Private methods *************
1631 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1632 bool set_changed, u32 dtime_s)
1634 assert(object); // Pre-condition
1635 if(object->getId() == 0){
1636 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1639 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1640 <<"no free ids available"<<std::endl;
1641 if(object->environmentDeletes())
1645 object->setId(new_id);
1648 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1649 <<"supplied with id "<<object->getId()<<std::endl;
1652 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1653 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1654 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1655 if(object->environmentDeletes())
1660 if (objectpos_over_limit(object->getBasePosition())) {
1661 v3f p = object->getBasePosition();
1662 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1663 << "object position (" << p.X << "," << p.Y << "," << p.Z
1664 << ") outside maximum range" << std::endl;
1665 if (object->environmentDeletes())
1670 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1671 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1673 m_active_objects[object->getId()] = object;
1675 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1676 <<"Added id="<<object->getId()<<"; there are now "
1677 <<m_active_objects.size()<<" active objects."
1680 // Register reference in scripting api (must be done before post-init)
1681 m_script->addObjectReference(object);
1682 // Post-initialize object
1683 object->addedToEnvironment(dtime_s);
1685 // Add static data to block
1686 if(object->isStaticAllowed())
1688 // Add static object to active static list of the block
1689 v3f objectpos = object->getBasePosition();
1690 std::string staticdata;
1691 object->getStaticData(&staticdata);
1692 StaticObject s_obj(object->getType(), objectpos, staticdata);
1693 // Add to the block where the object is located in
1694 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1695 MapBlock *block = m_map->emergeBlock(blockpos);
1697 block->m_static_objects.m_active[object->getId()] = s_obj;
1698 object->m_static_exists = true;
1699 object->m_static_block = blockpos;
1702 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1703 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1705 v3s16 p = floatToInt(objectpos, BS);
1706 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1707 <<"could not emerge block for storing id="<<object->getId()
1708 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1712 return object->getId();
1716 Remove objects that satisfy (isGone() && m_known_by_count==0)
1718 void ServerEnvironment::removeRemovedObjects()
1720 std::vector<u16> objects_to_remove;
1721 for (auto &ao_it : m_active_objects) {
1722 u16 id = ao_it.first;
1723 ServerActiveObject* obj = ao_it.second;
1725 // This shouldn't happen but check it
1727 errorstream << "ServerEnvironment::removeRemovedObjects(): "
1728 << "NULL object found. id=" << id << std::endl;
1729 objects_to_remove.push_back(id);
1734 We will handle objects marked for removal or deactivation
1740 Delete static data from block if removed
1742 if (obj->m_pending_removal)
1743 deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1745 // If still known by clients, don't actually remove. On some future
1746 // invocation this will be 0, which is when removal will continue.
1747 if(obj->m_known_by_count > 0)
1751 Move static data from active to stored if deactivated
1753 if (!obj->m_pending_removal && obj->m_static_exists) {
1754 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1756 std::map<u16, StaticObject>::iterator i =
1757 block->m_static_objects.m_active.find(id);
1758 if (i != block->m_static_objects.m_active.end()) {
1759 block->m_static_objects.m_stored.push_back(i->second);
1760 block->m_static_objects.m_active.erase(id);
1761 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1762 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1764 warningstream << "ServerEnvironment::removeRemovedObjects(): "
1765 << "id=" << id << " m_static_exists=true but "
1766 << "static data doesn't actually exist in "
1767 << PP(obj->m_static_block) << std::endl;
1770 infostream << "Failed to emerge block from which an object to "
1771 << "be deactivated was loaded from. id=" << id << std::endl;
1775 // Tell the object about removal
1776 obj->removingFromEnvironment();
1777 // Deregister in scripting api
1778 m_script->removeObjectReference(obj);
1781 if(obj->environmentDeletes())
1784 objects_to_remove.push_back(id);
1786 // Remove references from m_active_objects
1787 for (u16 i : objects_to_remove) {
1788 m_active_objects.erase(i);
1792 static void print_hexdump(std::ostream &o, const std::string &data)
1794 const int linelength = 16;
1795 for(int l=0; ; l++){
1796 int i0 = linelength * l;
1797 bool at_end = false;
1798 int thislinelength = linelength;
1799 if(i0 + thislinelength > (int)data.size()){
1800 thislinelength = data.size() - i0;
1803 for(int di=0; di<linelength; di++){
1806 if(di<thislinelength)
1807 snprintf(buf, 4, "%.2x ", data[i]);
1809 snprintf(buf, 4, " ");
1813 for(int di=0; di<thislinelength; di++){
1827 Convert stored objects from blocks near the players to active.
1829 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1834 // Ignore if no stored objects (to not set changed flag)
1835 if(block->m_static_objects.m_stored.empty())
1838 verbosestream<<"ServerEnvironment::activateObjects(): "
1839 <<"activating objects of block "<<PP(block->getPos())
1840 <<" ("<<block->m_static_objects.m_stored.size()
1841 <<" objects)"<<std::endl;
1842 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1844 errorstream<<"suspiciously large amount of objects detected: "
1845 <<block->m_static_objects.m_stored.size()<<" in "
1846 <<PP(block->getPos())
1847 <<"; removing all of them."<<std::endl;
1848 // Clear stored list
1849 block->m_static_objects.m_stored.clear();
1850 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1851 MOD_REASON_TOO_MANY_OBJECTS);
1855 // Activate stored objects
1856 std::vector<StaticObject> new_stored;
1857 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1858 // Create an active object from the data
1859 ServerActiveObject *obj = ServerActiveObject::create
1860 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1861 // If couldn't create object, store static data back.
1863 errorstream<<"ServerEnvironment::activateObjects(): "
1864 <<"failed to create active object from static object "
1865 <<"in block "<<PP(s_obj.pos/BS)
1866 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1867 print_hexdump(verbosestream, s_obj.data);
1869 new_stored.push_back(s_obj);
1872 verbosestream<<"ServerEnvironment::activateObjects(): "
1873 <<"activated static object pos="<<PP(s_obj.pos/BS)
1874 <<" type="<<(int)s_obj.type<<std::endl;
1875 // This will also add the object to the active static list
1876 addActiveObjectRaw(obj, false, dtime_s);
1879 // Clear stored list
1880 block->m_static_objects.m_stored.clear();
1881 // Add leftover failed stuff to stored list
1882 for (const StaticObject &s_obj : new_stored) {
1883 block->m_static_objects.m_stored.push_back(s_obj);
1887 Note: Block hasn't really been modified here.
1888 The objects have just been activated and moved from the stored
1889 static list to the active static list.
1890 As such, the block is essentially the same.
1891 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1892 Otherwise there would be a huge amount of unnecessary I/O.
1897 Convert objects that are not standing inside active blocks to static.
1899 If m_known_by_count != 0, active object is not deleted, but static
1900 data is still updated.
1902 If force_delete is set, active object is deleted nevertheless. It
1903 shall only be set so in the destructor of the environment.
1905 If block wasn't generated (not in memory or on disk),
1907 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1909 std::vector<u16> objects_to_remove;
1910 for (auto &ao_it : m_active_objects) {
1911 // force_delete might be overriden per object
1912 bool force_delete = _force_delete;
1914 ServerActiveObject* obj = ao_it.second;
1917 // Do not deactivate if static data creation not allowed
1918 if(!force_delete && !obj->isStaticAllowed())
1921 // removeRemovedObjects() is responsible for these
1922 if(!force_delete && obj->isGone())
1925 u16 id = ao_it.first;
1926 v3f objectpos = obj->getBasePosition();
1928 // The block in which the object resides in
1929 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1931 // If object's static data is stored in a deactivated block and object
1932 // is actually located in an active block, re-save to the block in
1933 // which the object is actually located in.
1935 obj->m_static_exists &&
1936 !m_active_blocks.contains(obj->m_static_block) &&
1937 m_active_blocks.contains(blockpos_o))
1939 // Delete from block where object was located
1940 deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
1942 std::string staticdata_new;
1943 obj->getStaticData(&staticdata_new);
1944 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1945 // Save to block where object is located
1946 saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
1951 // If block is still active, don't remove
1952 if(!force_delete && m_active_blocks.contains(blockpos_o))
1955 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
1956 << "deactivating object id=" << id << " on inactive block "
1957 << PP(blockpos_o) << std::endl;
1959 // If known by some client, don't immediately delete.
1960 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1963 Update the static data
1965 if(obj->isStaticAllowed())
1967 // Create new static object
1968 std::string staticdata_new;
1969 obj->getStaticData(&staticdata_new);
1970 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1972 bool stays_in_same_block = false;
1973 bool data_changed = true;
1975 // Check if static data has changed considerably
1976 if (obj->m_static_exists) {
1977 if (obj->m_static_block == blockpos_o)
1978 stays_in_same_block = true;
1980 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1983 std::map<u16, StaticObject>::iterator n =
1984 block->m_static_objects.m_active.find(id);
1985 if (n != block->m_static_objects.m_active.end()) {
1986 StaticObject static_old = n->second;
1988 float save_movem = obj->getMinimumSavedMovement();
1990 if (static_old.data == staticdata_new &&
1991 (static_old.pos - objectpos).getLength() < save_movem)
1992 data_changed = false;
1994 warningstream << "ServerEnvironment::deactivateFarObjects(): "
1995 << "id=" << id << " m_static_exists=true but "
1996 << "static data doesn't actually exist in "
1997 << PP(obj->m_static_block) << std::endl;
2003 While changes are always saved, blocks are only marked as modified
2004 if the object has moved or different staticdata. (see above)
2006 bool shall_be_written = (!stays_in_same_block || data_changed);
2007 u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2009 // Delete old static object
2010 deleteStaticFromBlock(obj, id, reason, false);
2012 // Add to the block where the object is located in
2013 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2014 u16 store_id = pending_delete ? id : 0;
2015 if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2016 force_delete = true;
2020 If known by some client, set pending deactivation.
2021 Otherwise delete it immediately.
2023 if(pending_delete && !force_delete)
2025 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2026 << "object id=" << id << " is known by clients"
2027 << "; not deleting yet" << std::endl;
2029 obj->m_pending_deactivation = true;
2032 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2033 << "object id=" << id << " is not known by clients"
2034 << "; deleting" << std::endl;
2036 // Tell the object about removal
2037 obj->removingFromEnvironment();
2038 // Deregister in scripting api
2039 m_script->removeObjectReference(obj);
2041 // Delete active object
2042 if(obj->environmentDeletes())
2044 // Id to be removed from m_active_objects
2045 objects_to_remove.push_back(id);
2048 // Remove references from m_active_objects
2049 for (u16 i : objects_to_remove) {
2050 m_active_objects.erase(i);
2054 void ServerEnvironment::deleteStaticFromBlock(
2055 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2057 if (!obj->m_static_exists)
2062 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2064 block = m_map->emergeBlock(obj->m_static_block, false);
2067 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2068 << " when deleting static data of object from it. id=" << id << std::endl;
2072 block->m_static_objects.remove(id);
2073 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2074 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2076 obj->m_static_exists = false;
2079 bool ServerEnvironment::saveStaticToBlock(
2080 v3s16 blockpos, u16 store_id,
2081 ServerActiveObject *obj, const StaticObject &s_obj,
2084 MapBlock *block = nullptr;
2086 block = m_map->emergeBlock(blockpos);
2087 } catch (InvalidPositionException &e) {
2088 // Handled via NULL pointer
2089 // NOTE: emergeBlock's failure is usually determined by it
2090 // actually returning NULL
2094 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2095 << " when saving static data of object to it. id=" << store_id << std::endl;
2098 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2099 warningstream << "ServerEnv: Trying to store id = " << store_id
2100 << " statically but block " << PP(blockpos)
2101 << " already contains "
2102 << block->m_static_objects.m_stored.size()
2103 << " objects." << std::endl;
2107 block->m_static_objects.insert(store_id, s_obj);
2108 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2109 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2111 obj->m_static_exists = true;
2112 obj->m_static_block = blockpos;
2117 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2118 const std::string &savedir, const Settings &conf)
2121 if (name == "sqlite3")
2122 return new PlayerDatabaseSQLite3(savedir);
2124 if (name == "dummy")
2125 return new Database_Dummy();
2127 if (name == "postgresql") {
2128 std::string connect_string;
2129 conf.getNoEx("pgsql_player_connection", connect_string);
2130 return new PlayerDatabasePostgreSQL(connect_string);
2133 if (name == "files")
2134 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2136 throw BaseException(std::string("Database backend ") + name + " not supported.");
2139 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2140 const Settings &cmd_args)
2142 std::string migrate_to = cmd_args.get("migrate-players");
2144 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2145 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2146 errorstream << "Cannot read world.mt!" << std::endl;
2150 if (!world_mt.exists("player_backend")) {
2151 errorstream << "Please specify your current backend in world.mt:"
2153 << " player_backend = {files|sqlite3|postgresql}"
2158 std::string backend = world_mt.get("player_backend");
2159 if (backend == migrate_to) {
2160 errorstream << "Cannot migrate: new backend is same"
2161 << " as the old one" << std::endl;
2165 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2168 if (backend == "files") {
2169 // Create backup directory
2170 fs::CreateDir(players_backup_path);
2174 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2175 game_params.world_path, world_mt);
2176 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2177 game_params.world_path, world_mt);
2179 std::vector<std::string> player_list;
2180 srcdb->listPlayers(player_list);
2181 for (std::vector<std::string>::const_iterator it = player_list.begin();
2182 it != player_list.end(); ++it) {
2183 actionstream << "Migrating player " << it->c_str() << std::endl;
2184 RemotePlayer player(it->c_str(), NULL);
2185 PlayerSAO playerSAO(NULL, &player, 15000, false);
2187 srcdb->loadPlayer(&player, &playerSAO);
2189 playerSAO.finalize(&player, std::set<std::string>());
2190 player.setPlayerSAO(&playerSAO);
2192 dstdb->savePlayer(&player);
2194 // For files source, move player files to backup dir
2195 if (backend == "files") {
2197 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2198 players_backup_path + DIR_DELIM + (*it));
2202 actionstream << "Successfully migrated " << player_list.size() << " players"
2204 world_mt.set("player_backend", migrate_to);
2205 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2206 errorstream << "Failed to update world.mt!" << std::endl;
2208 actionstream << "world.mt updated" << std::endl;
2210 // When migration is finished from file backend, remove players directory if empty
2211 if (backend == "files") {
2212 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2219 } catch (BaseException &e) {
2220 errorstream << "An error occured during migration: " << e.what() << std::endl;