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-dummy.h"
41 #include "database-files.h"
42 #include "database-sqlite3.h"
44 #include "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)
283 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
284 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
285 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
288 if (p.getDistanceFrom(p0) <= r) {
295 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
297 std::set<v3s16> &blocks_removed,
298 std::set<v3s16> &blocks_added)
303 std::set<v3s16> newlist = m_forceloaded_list;
304 for (const v3s16 &active_position : active_positions) {
305 fillRadiusBlock(active_position, radius, newlist);
309 Find out which blocks on the old list are not on the new list
311 // Go through old list
312 for (v3s16 p : m_list) {
313 // If not on new list, it's been removed
314 if (newlist.find(p) == newlist.end())
315 blocks_removed.insert(p);
319 Find out which blocks on the new list are not on the old list
321 // Go through new list
322 for (v3s16 p : newlist) {
323 // If not on old list, it's been added
324 if(m_list.find(p) == m_list.end())
325 blocks_added.insert(p);
332 for (v3s16 p : newlist) {
341 ServerEnvironment::ServerEnvironment(ServerMap *map,
342 ServerScripting *scriptIface, Server *server,
343 const std::string &path_world):
346 m_script(scriptIface),
348 m_path_world(path_world)
350 // Determine which database backend to use
351 std::string conf_path = path_world + DIR_DELIM + "world.mt";
353 bool succeeded = conf.readConfigFile(conf_path.c_str());
354 if (!succeeded || !conf.exists("player_backend")) {
355 // fall back to files
356 conf.set("player_backend", "files");
357 warningstream << "/!\\ You are using old player file backend. "
358 << "This backend is deprecated and will be removed in next release /!\\"
359 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
360 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
362 if (!conf.updateConfigFile(conf_path.c_str())) {
363 errorstream << "ServerEnvironment::ServerEnvironment(): "
364 << "Failed to update world.mt!" << std::endl;
369 conf.getNoEx("player_backend", name);
370 m_player_database = openPlayerDatabase(name, path_world, conf);
373 ServerEnvironment::~ServerEnvironment()
375 // Clear active block list.
376 // This makes the next one delete all active objects.
377 m_active_blocks.clear();
379 // Convert all objects to static and delete the active objects
380 deactivateFarObjects(true);
385 // Delete ActiveBlockModifiers
386 for (ABMWithState &m_abm : m_abms) {
390 // Deallocate players
391 for (RemotePlayer *m_player : m_players) {
395 delete m_player_database;
398 Map & ServerEnvironment::getMap()
403 ServerMap & ServerEnvironment::getServerMap()
408 RemotePlayer *ServerEnvironment::getPlayer(const session_t peer_id)
410 for (RemotePlayer *player : m_players) {
411 if (player->getPeerId() == peer_id)
417 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
419 for (RemotePlayer *player : m_players) {
420 if (strcmp(player->getName(), name) == 0)
426 void ServerEnvironment::addPlayer(RemotePlayer *player)
429 Check that peer_ids are unique.
430 Also check that names are unique.
431 Exception: there can be multiple players with peer_id=0
433 // If peer id is non-zero, it has to be unique.
434 if (player->getPeerId() != PEER_ID_INEXISTENT)
435 FATAL_ERROR_IF(getPlayer(player->getPeerId()) != NULL, "Peer id not unique");
436 // Name has to be unique.
437 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
439 m_players.push_back(player);
442 void ServerEnvironment::removePlayer(RemotePlayer *player)
444 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
445 it != m_players.end(); ++it) {
446 if ((*it) == player) {
454 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
456 return m_player_database->removePlayer(name);
459 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
461 float distance = pos1.getDistanceFrom(pos2);
463 //calculate normalized direction vector
464 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
465 (pos2.Y - pos1.Y)/distance,
466 (pos2.Z - pos1.Z)/distance);
468 //find out if there's a node on path between pos1 and pos2
469 for (float i = 1; i < distance; i += stepsize) {
470 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
471 normalized_vector.Y * i,
472 normalized_vector.Z * i) +pos1,BS);
474 MapNode n = getMap().getNodeNoEx(pos);
476 if(n.param0 != CONTENT_AIR) {
486 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
487 const std::string &str_reason, bool reconnect)
489 for (RemotePlayer *player : m_players) {
490 m_server->DenyAccessVerCompliant(player->getPeerId(),
491 player->protocol_version, reason, str_reason, reconnect);
495 void ServerEnvironment::saveLoadedPlayers()
497 std::string players_path = m_path_world + DIR_DELIM + "players";
498 fs::CreateDir(players_path);
500 for (RemotePlayer *player : m_players) {
501 if (player->checkModified() || (player->getPlayerSAO() &&
502 player->getPlayerSAO()->extendedAttributesModified())) {
504 m_player_database->savePlayer(player);
505 } catch (DatabaseException &e) {
506 errorstream << "Failed to save player " << player->getName() << " exception: "
507 << e.what() << std::endl;
514 void ServerEnvironment::savePlayer(RemotePlayer *player)
517 m_player_database->savePlayer(player);
518 } catch (DatabaseException &e) {
519 errorstream << "Failed to save player " << player->getName() << " exception: "
520 << e.what() << std::endl;
525 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
526 session_t peer_id, bool is_singleplayer)
528 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
529 // Create player if it doesn't exist
530 if (!m_player_database->loadPlayer(player, playersao)) {
532 // Set player position
533 infostream << "Server: Finding spawn place for player \""
534 << player->getName() << "\"" << std::endl;
535 playersao->setBasePosition(m_server->findSpawnPos());
537 // Make sure the player is saved
538 player->setModified(true);
540 // If the player exists, ensure that they respawn inside legal bounds
541 // This fixes an assert crash when the player can't be added
542 // to the environment
543 ServerMap &map = getServerMap();
544 if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) {
545 actionstream << "Respawn position for player \""
546 << player->getName() << "\" outside limits, resetting" << std::endl;
547 playersao->setBasePosition(m_server->findSpawnPos());
551 // Add player to environment
554 /* Clean up old HUD elements from previous sessions */
557 /* Add object to environment */
558 addActiveObject(playersao);
563 void ServerEnvironment::saveMeta()
565 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
567 // Open file and serialize
568 std::ostringstream ss(std::ios_base::binary);
571 args.setU64("game_time", m_game_time);
572 args.setU64("time_of_day", getTimeOfDay());
573 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
574 args.setU64("lbm_introduction_times_version", 1);
575 args.set("lbm_introduction_times",
576 m_lbm_mgr.createIntroductionTimesString());
577 args.setU64("day_count", m_day_count);
581 if(!fs::safeWriteToFile(path, ss.str()))
583 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
585 throw SerializationError("Couldn't save env meta");
589 void ServerEnvironment::loadMeta()
591 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
593 // Open file and deserialize
594 std::ifstream is(path.c_str(), std::ios_base::binary);
596 infostream << "ServerEnvironment::loadMeta(): Failed to open "
597 << path << std::endl;
598 throw SerializationError("Couldn't load env meta");
603 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
604 throw SerializationError("ServerEnvironment::loadMeta(): "
605 "EnvArgsEnd not found!");
609 m_game_time = args.getU64("game_time");
610 } catch (SettingNotFoundException &e) {
611 // Getting this is crucial, otherwise timestamps are useless
612 throw SerializationError("Couldn't load env meta game_time");
615 setTimeOfDay(args.exists("time_of_day") ?
616 // set day to early morning by default
617 args.getU64("time_of_day") : 5250);
619 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
620 // If missing, do as if clearObjects was never called
621 args.getU64("last_clear_objects_time") : 0;
623 std::string lbm_introduction_times;
625 u64 ver = args.getU64("lbm_introduction_times_version");
627 lbm_introduction_times = args.get("lbm_introduction_times");
629 infostream << "ServerEnvironment::loadMeta(): Non-supported"
630 << " introduction time version " << ver << std::endl;
632 } catch (SettingNotFoundException &e) {
633 // No problem, this is expected. Just continue with an empty string
635 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
637 m_day_count = args.exists("day_count") ?
638 args.getU64("day_count") : 0;
641 void ServerEnvironment::loadDefaultMeta()
643 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
648 ActiveBlockModifier *abm;
650 std::vector<content_t> required_neighbors;
651 bool check_required_neighbors; // false if required_neighbors is known to be empty
657 ServerEnvironment *m_env;
658 std::vector<std::vector<ActiveABM> *> m_aabms;
660 ABMHandler(std::vector<ABMWithState> &abms,
661 float dtime_s, ServerEnvironment *env,
667 INodeDefManager *ndef = env->getGameDef()->ndef();
668 for (ABMWithState &abmws : abms) {
669 ActiveBlockModifier *abm = abmws.abm;
670 float trigger_interval = abm->getTriggerInterval();
671 if(trigger_interval < 0.001)
672 trigger_interval = 0.001;
673 float actual_interval = dtime_s;
675 abmws.timer += dtime_s;
676 if(abmws.timer < trigger_interval)
678 abmws.timer -= trigger_interval;
679 actual_interval = trigger_interval;
681 float chance = abm->getTriggerChance();
686 if (abm->getSimpleCatchUp()) {
687 float intervals = actual_interval / trigger_interval;
690 aabm.chance = chance / intervals;
694 aabm.chance = chance;
698 const std::vector<std::string> &required_neighbors_s =
699 abm->getRequiredNeighbors();
700 for (const std::string &required_neighbor_s : required_neighbors_s) {
701 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
703 aabm.check_required_neighbors = !required_neighbors_s.empty();
706 const std::vector<std::string> &contents_s = abm->getTriggerContents();
707 for (const std::string &content_s : contents_s) {
708 std::vector<content_t> ids;
709 ndef->getIds(content_s, ids);
710 for (content_t c : ids) {
711 if (c >= m_aabms.size())
712 m_aabms.resize(c + 256, NULL);
714 m_aabms[c] = new std::vector<ActiveABM>;
715 m_aabms[c]->push_back(aabm);
723 for (auto &aabms : m_aabms)
727 // Find out how many objects the given block and its neighbours contain.
728 // Returns the number of objects in the block, and also in 'wider' the
729 // number of objects in the block and all its neighbours. The latter
730 // may an estimate if any neighbours are unloaded.
731 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
734 u32 wider_unknown_count = 0;
735 for(s16 x=-1; x<=1; x++)
736 for(s16 y=-1; y<=1; y++)
737 for(s16 z=-1; z<=1; z++)
739 MapBlock *block2 = map->getBlockNoCreateNoEx(
740 block->getPos() + v3s16(x,y,z));
742 wider_unknown_count++;
745 wider += block2->m_static_objects.m_active.size()
746 + block2->m_static_objects.m_stored.size();
749 u32 active_object_count = block->m_static_objects.m_active.size();
750 u32 wider_known_count = 3*3*3 - wider_unknown_count;
751 wider += wider_unknown_count * wider / wider_known_count;
752 return active_object_count;
755 void apply(MapBlock *block)
757 if(m_aabms.empty() || block->isDummy())
760 ServerMap *map = &m_env->getServerMap();
762 u32 active_object_count_wider;
763 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
764 m_env->m_added_objects = 0;
767 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
768 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
769 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
771 const MapNode &n = block->getNodeUnsafe(p0);
772 content_t c = n.getContent();
774 if (c >= m_aabms.size() || !m_aabms[c])
777 v3s16 p = p0 + block->getPosRelative();
778 for (ActiveABM &aabm : *m_aabms[c]) {
779 if (myrand() % aabm.chance != 0)
783 if (aabm.check_required_neighbors) {
785 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
786 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
787 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
792 if (block->isValidPosition(p1)) {
793 // if the neighbor is found on the same map block
794 // get it straight from there
795 const MapNode &n = block->getNodeUnsafe(p1);
798 // otherwise consult the map
799 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
802 if (CONTAINS(aabm.required_neighbors, c))
805 // No required neighbor found
810 // Call all the trigger variations
811 aabm.abm->trigger(m_env, p, n);
812 aabm.abm->trigger(m_env, p, n,
813 active_object_count, active_object_count_wider);
815 // Count surrounding objects again if the abms added any
816 if(m_env->m_added_objects > 0) {
817 active_object_count = countObjects(block, map, active_object_count_wider);
818 m_env->m_added_objects = 0;
825 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
827 // Reset usage timer immediately, otherwise a block that becomes active
828 // again at around the same time as it would normally be unloaded will
829 // get unloaded incorrectly. (I think this still leaves a small possibility
830 // of a race condition between this and server::AsyncRunStep, which only
831 // some kind of synchronisation will fix, but it at least reduces the window
832 // of opportunity for it to break from seconds to nanoseconds)
833 block->resetUsageTimer();
835 // Get time difference
837 u32 stamp = block->getTimestamp();
838 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
839 dtime_s = m_game_time - stamp;
840 dtime_s += additional_dtime;
842 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
843 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
845 // Remove stored static objects if clearObjects was called since block's timestamp
846 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
847 block->m_static_objects.m_stored.clear();
848 // do not set changed flag to avoid unnecessary mapblock writes
851 // Set current time as timestamp
852 block->setTimestampNoChangedFlag(m_game_time);
854 /*infostream<<"ServerEnvironment::activateBlock(): block is "
855 <<dtime_s<<" seconds old."<<std::endl;*/
857 // Activate stored objects
858 activateObjects(block, dtime_s);
860 /* Handle LoadingBlockModifiers */
861 m_lbm_mgr.applyLBMs(this, block, stamp);
864 std::vector<NodeTimer> elapsed_timers =
865 block->m_node_timers.step((float)dtime_s);
866 if (!elapsed_timers.empty()) {
868 for (const NodeTimer &elapsed_timer : elapsed_timers) {
869 n = block->getNodeNoEx(elapsed_timer.position);
870 v3s16 p = elapsed_timer.position + block->getPosRelative();
871 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
872 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
873 elapsed_timer.position));
877 /* Handle ActiveBlockModifiers */
878 ABMHandler abmhandler(m_abms, dtime_s, this, false);
879 abmhandler.apply(block);
882 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
884 m_abms.emplace_back(abm);
887 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
889 m_lbm_mgr.addLBMDef(lbm);
892 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
894 INodeDefManager *ndef = m_server->ndef();
895 MapNode n_old = m_map->getNodeNoEx(p);
898 if (ndef->get(n_old).has_on_destruct)
899 m_script->node_on_destruct(p, n_old);
902 if (!m_map->addNodeWithEvent(p, n))
905 // Update active VoxelManipulator if a mapgen thread
906 m_map->updateVManip(p);
908 // Call post-destructor
909 if (ndef->get(n_old).has_after_destruct)
910 m_script->node_after_destruct(p, n_old);
913 if (ndef->get(n).has_on_construct)
914 m_script->node_on_construct(p, n);
919 bool ServerEnvironment::removeNode(v3s16 p)
921 INodeDefManager *ndef = m_server->ndef();
922 MapNode n_old = m_map->getNodeNoEx(p);
925 if (ndef->get(n_old).has_on_destruct)
926 m_script->node_on_destruct(p, n_old);
929 // This is slightly optimized compared to addNodeWithEvent(air)
930 if (!m_map->removeNodeWithEvent(p))
933 // Update active VoxelManipulator if a mapgen thread
934 m_map->updateVManip(p);
936 // Call post-destructor
937 if (ndef->get(n_old).has_after_destruct)
938 m_script->node_after_destruct(p, n_old);
940 // Air doesn't require constructor
944 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
946 if (!m_map->addNodeWithEvent(p, n, false))
949 // Update active VoxelManipulator if a mapgen thread
950 m_map->updateVManip(p);
955 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
958 for (auto &activeObject : m_active_objects) {
959 ServerActiveObject* obj = activeObject.second;
960 u16 id = activeObject.first;
961 v3f objectpos = obj->getBasePosition();
962 if (objectpos.getDistanceFrom(pos) > radius)
964 objects.push_back(id);
968 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
970 infostream << "ServerEnvironment::clearObjects(): "
971 << "Removing all active objects" << std::endl;
972 std::vector<u16> objects_to_remove;
973 for (auto &it : m_active_objects) {
975 ServerActiveObject* obj = it.second;
976 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
979 // Delete static object if block is loaded
980 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
982 // If known by some client, don't delete immediately
983 if (obj->m_known_by_count > 0) {
984 obj->m_pending_removal = true;
988 // Tell the object about removal
989 obj->removingFromEnvironment();
990 // Deregister in scripting api
991 m_script->removeObjectReference(obj);
993 // Delete active object
994 if (obj->environmentDeletes())
996 // Id to be removed from m_active_objects
997 objects_to_remove.push_back(id);
1000 // Remove references from m_active_objects
1001 for (u16 i : objects_to_remove) {
1002 m_active_objects.erase(i);
1005 // Get list of loaded blocks
1006 std::vector<v3s16> loaded_blocks;
1007 infostream << "ServerEnvironment::clearObjects(): "
1008 << "Listing all loaded blocks" << std::endl;
1009 m_map->listAllLoadedBlocks(loaded_blocks);
1010 infostream << "ServerEnvironment::clearObjects(): "
1011 << "Done listing all loaded blocks: "
1012 << loaded_blocks.size()<<std::endl;
1014 // Get list of loadable blocks
1015 std::vector<v3s16> loadable_blocks;
1016 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1017 infostream << "ServerEnvironment::clearObjects(): "
1018 << "Listing all loadable blocks" << std::endl;
1019 m_map->listAllLoadableBlocks(loadable_blocks);
1020 infostream << "ServerEnvironment::clearObjects(): "
1021 << "Done listing all loadable blocks: "
1022 << loadable_blocks.size() << std::endl;
1024 loadable_blocks = loaded_blocks;
1027 infostream << "ServerEnvironment::clearObjects(): "
1028 << "Now clearing objects in " << loadable_blocks.size()
1029 << " blocks" << std::endl;
1031 // Grab a reference on each loaded block to avoid unloading it
1032 for (v3s16 p : loaded_blocks) {
1033 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1034 assert(block != NULL);
1038 // Remove objects in all loadable blocks
1039 u32 unload_interval = U32_MAX;
1040 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1041 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1042 unload_interval = MYMAX(unload_interval, 1);
1044 u32 report_interval = loadable_blocks.size() / 10;
1045 u32 num_blocks_checked = 0;
1046 u32 num_blocks_cleared = 0;
1047 u32 num_objs_cleared = 0;
1048 for (auto i = loadable_blocks.begin();
1049 i != loadable_blocks.end(); ++i) {
1051 MapBlock *block = m_map->emergeBlock(p, false);
1053 errorstream << "ServerEnvironment::clearObjects(): "
1054 << "Failed to emerge block " << PP(p) << std::endl;
1057 u32 num_stored = block->m_static_objects.m_stored.size();
1058 u32 num_active = block->m_static_objects.m_active.size();
1059 if (num_stored != 0 || num_active != 0) {
1060 block->m_static_objects.m_stored.clear();
1061 block->m_static_objects.m_active.clear();
1062 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1063 MOD_REASON_CLEAR_ALL_OBJECTS);
1064 num_objs_cleared += num_stored + num_active;
1065 num_blocks_cleared++;
1067 num_blocks_checked++;
1069 if (report_interval != 0 &&
1070 num_blocks_checked % report_interval == 0) {
1071 float percent = 100.0 * (float)num_blocks_checked /
1072 loadable_blocks.size();
1073 infostream << "ServerEnvironment::clearObjects(): "
1074 << "Cleared " << num_objs_cleared << " objects"
1075 << " in " << num_blocks_cleared << " blocks ("
1076 << percent << "%)" << std::endl;
1078 if (num_blocks_checked % unload_interval == 0) {
1079 m_map->unloadUnreferencedBlocks();
1082 m_map->unloadUnreferencedBlocks();
1084 // Drop references that were added above
1085 for (v3s16 p : loaded_blocks) {
1086 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1091 m_last_clear_objects_time = m_game_time;
1093 infostream << "ServerEnvironment::clearObjects(): "
1094 << "Finished: Cleared " << num_objs_cleared << " objects"
1095 << " in " << num_blocks_cleared << " blocks" << std::endl;
1098 void ServerEnvironment::step(float dtime)
1100 /* Step time of day */
1101 stepTimeOfDay(dtime);
1104 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1105 // really matter that much.
1106 static thread_local const float server_step =
1107 g_settings->getFloat("dedicated_server_step");
1108 m_recommended_send_interval = server_step;
1114 m_game_time_fraction_counter += dtime;
1115 u32 inc_i = (u32)m_game_time_fraction_counter;
1116 m_game_time += inc_i;
1117 m_game_time_fraction_counter -= (float)inc_i;
1124 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1125 for (RemotePlayer *player : m_players) {
1126 // Ignore disconnected players
1127 if (player->getPeerId() == PEER_ID_INEXISTENT)
1131 player->move(dtime, this, 100 * BS);
1136 Manage active block list
1138 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1139 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1141 Get player block positions
1143 std::vector<v3s16> players_blockpos;
1144 for (RemotePlayer *player: m_players) {
1145 // Ignore disconnected players
1146 if (player->getPeerId() == PEER_ID_INEXISTENT)
1149 PlayerSAO *playersao = player->getPlayerSAO();
1152 players_blockpos.push_back(
1153 getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS)));
1157 Update list of active blocks, collecting changes
1159 static thread_local const s16 active_block_range =
1160 g_settings->getS16("active_block_range");
1161 std::set<v3s16> blocks_removed;
1162 std::set<v3s16> blocks_added;
1163 m_active_blocks.update(players_blockpos, active_block_range,
1164 blocks_removed, blocks_added);
1167 Handle removed blocks
1170 // Convert active objects that are no more in active blocks to static
1171 deactivateFarObjects(false);
1173 for (const v3s16 &p: blocks_removed) {
1174 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1178 // Set current time as timestamp (and let it set ChangedFlag)
1179 block->setTimestamp(m_game_time);
1186 for (const v3s16 &p: blocks_added) {
1187 MapBlock *block = m_map->getBlockOrEmerge(p);
1189 m_active_blocks.m_list.erase(p);
1193 activateBlock(block);
1198 Mess around in active blocks
1200 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1201 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1203 float dtime = m_cache_nodetimer_interval;
1205 for (const v3s16 &p: m_active_blocks.m_list) {
1206 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1210 // Reset block usage timer
1211 block->resetUsageTimer();
1213 // Set current time as timestamp
1214 block->setTimestampNoChangedFlag(m_game_time);
1215 // If time has changed much from the one on disk,
1216 // set block to be saved when it is unloaded
1217 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1218 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1219 MOD_REASON_BLOCK_EXPIRED);
1222 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1223 if (!elapsed_timers.empty()) {
1226 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1227 n = block->getNodeNoEx(elapsed_timer.position);
1228 p2 = elapsed_timer.position + block->getPosRelative();
1229 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1230 block->setNodeTimer(NodeTimer(
1231 elapsed_timer.timeout, 0, elapsed_timer.position));
1238 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1240 if (m_active_block_interval_overload_skip > 0) {
1241 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1242 m_active_block_interval_overload_skip--;
1245 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1246 TimeTaker timer("modify in active blocks per interval");
1248 // Initialize handling of ActiveBlockModifiers
1249 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1251 for (const v3s16 &p : m_active_blocks.m_list) {
1252 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1256 // Set current time as timestamp
1257 block->setTimestampNoChangedFlag(m_game_time);
1259 /* Handle ActiveBlockModifiers */
1260 abmhandler.apply(block);
1263 u32 time_ms = timer.stop(true);
1264 u32 max_time_ms = 200;
1265 if (time_ms > max_time_ms) {
1266 warningstream<<"active block modifiers took "
1267 <<time_ms<<"ms (longer than "
1268 <<max_time_ms<<"ms)"<<std::endl;
1269 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1274 Step script environment (run global on_step())
1276 m_script->environment_Step(dtime);
1282 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1283 //TimeTaker timer("Step active objects");
1285 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1287 // This helps the objects to send data at the same time
1288 bool send_recommended = false;
1289 m_send_recommended_timer += dtime;
1290 if(m_send_recommended_timer > getSendRecommendedInterval())
1292 m_send_recommended_timer -= getSendRecommendedInterval();
1293 send_recommended = true;
1296 for (auto &ao_it : m_active_objects) {
1297 ServerActiveObject* obj = ao_it.second;
1302 obj->step(dtime, send_recommended);
1303 // Read messages from object
1304 while (!obj->m_messages_out.empty()) {
1305 m_active_object_messages.push(obj->m_messages_out.front());
1306 obj->m_messages_out.pop();
1312 Manage active objects
1314 if (m_object_management_interval.step(dtime, 0.5)) {
1315 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1316 removeRemovedObjects();
1320 Manage particle spawner expiration
1322 if (m_particle_management_interval.step(dtime, 1.0)) {
1323 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1324 i != m_particle_spawners.end(); ) {
1325 //non expiring spawners
1326 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1332 if (i->second <= 0.f)
1333 m_particle_spawners.erase(i++);
1340 u32 ServerEnvironment::addParticleSpawner(float exptime)
1342 // Timers with lifetime 0 do not expire
1343 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1346 for (;;) { // look for unused particlespawner id
1348 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1349 if (f == m_particle_spawners.end()) {
1350 m_particle_spawners[id] = time;
1357 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1359 u32 id = addParticleSpawner(exptime);
1360 m_particle_spawner_attachments[id] = attached_id;
1361 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1362 obj->attachParticleSpawner(id);
1367 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1369 m_particle_spawners.erase(id);
1370 const auto &it = m_particle_spawner_attachments.find(id);
1371 if (it != m_particle_spawner_attachments.end()) {
1372 u16 obj_id = it->second;
1373 ServerActiveObject *sao = getActiveObject(obj_id);
1374 if (sao != NULL && remove_from_object) {
1375 sao->detachParticleSpawner(id);
1377 m_particle_spawner_attachments.erase(id);
1381 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1383 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1384 return (n != m_active_objects.end() ? n->second : NULL);
1387 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1392 return objects.find(id) == objects.end();
1395 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1397 //try to reuse id's as late as possible
1398 static u16 last_used_id = 0;
1399 u16 startid = last_used_id;
1403 if(isFreeServerActiveObjectId(last_used_id, objects))
1404 return last_used_id;
1406 if(last_used_id == startid)
1411 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1413 assert(object); // Pre-condition
1415 u16 id = addActiveObjectRaw(object, true, 0);
1420 Finds out what new objects have been added to
1421 inside a radius around a position
1423 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1425 std::set<u16> ¤t_objects,
1426 std::queue<u16> &added_objects)
1428 f32 radius_f = radius * BS;
1429 f32 player_radius_f = player_radius * BS;
1431 if (player_radius_f < 0)
1432 player_radius_f = 0;
1434 Go through the object list,
1435 - discard removed/deactivated objects,
1436 - discard objects that are too far away,
1437 - discard objects that are found in current_objects.
1438 - add remaining objects to added_objects
1440 for (auto &ao_it : m_active_objects) {
1441 u16 id = ao_it.first;
1444 ServerActiveObject *object = ao_it.second;
1448 if (object->isGone())
1451 f32 distance_f = object->getBasePosition().
1452 getDistanceFrom(playersao->getBasePosition());
1453 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1454 // Discard if too far
1455 if (distance_f > player_radius_f && player_radius_f != 0)
1457 } else if (distance_f > radius_f)
1460 // Discard if already on current_objects
1461 std::set<u16>::iterator n;
1462 n = current_objects.find(id);
1463 if(n != current_objects.end())
1465 // Add to added_objects
1466 added_objects.push(id);
1471 Finds out what objects have been removed from
1472 inside a radius around a position
1474 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1476 std::set<u16> ¤t_objects,
1477 std::queue<u16> &removed_objects)
1479 f32 radius_f = radius * BS;
1480 f32 player_radius_f = player_radius * BS;
1482 if (player_radius_f < 0)
1483 player_radius_f = 0;
1485 Go through current_objects; object is removed if:
1486 - object is not found in m_active_objects (this is actually an
1487 error condition; objects should be removed only after all clients
1488 have been informed about removal), or
1489 - object is to be removed or deactivated, or
1490 - object is too far away
1492 for (u16 id : current_objects) {
1493 ServerActiveObject *object = getActiveObject(id);
1495 if (object == NULL) {
1496 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1497 << " object in current_objects is NULL" << std::endl;
1498 removed_objects.push(id);
1502 if (object->isGone()) {
1503 removed_objects.push(id);
1507 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1508 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1509 if (distance_f <= player_radius_f || player_radius_f == 0)
1511 } else if (distance_f <= radius_f)
1514 // Object is no longer visible
1515 removed_objects.push(id);
1519 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1520 v3s16 blockpos, bool static_exists, v3s16 static_block)
1522 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1526 for (auto &so_it : block->m_static_objects.m_active) {
1527 // Get the ServerActiveObject counterpart to this StaticObject
1528 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it.first);
1529 if (ao_it == m_active_objects.end()) {
1530 // If this ever happens, there must be some kind of nasty bug.
1531 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1532 "Object from MapBlock::m_static_objects::m_active not found "
1533 "in m_active_objects";
1537 ServerActiveObject *sao = ao_it->second;
1538 sao->m_static_exists = static_exists;
1539 sao->m_static_block = static_block;
1543 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1545 if(m_active_object_messages.empty())
1546 return ActiveObjectMessage(0);
1548 ActiveObjectMessage message = m_active_object_messages.front();
1549 m_active_object_messages.pop();
1553 void ServerEnvironment::getSelectedActiveObjects(
1554 const core::line3d<f32> &shootline_on_map,
1555 std::vector<PointedThing> &objects)
1557 std::vector<u16> objectIds;
1558 getObjectsInsideRadius(objectIds, shootline_on_map.start,
1559 shootline_on_map.getLength() + 10.0f);
1560 const v3f line_vector = shootline_on_map.getVector();
1562 for (u16 objectId : objectIds) {
1563 ServerActiveObject* obj = getActiveObject(objectId);
1565 aabb3f selection_box;
1566 if (!obj->getSelectionBox(&selection_box))
1569 v3f pos = obj->getBasePosition();
1571 aabb3f offsetted_box(selection_box.MinEdge + pos,
1572 selection_box.MaxEdge + pos);
1574 v3f current_intersection;
1575 v3s16 current_normal;
1576 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1577 ¤t_intersection, ¤t_normal)) {
1578 objects.emplace_back(
1579 (s16) objectId, current_intersection, current_normal,
1580 (current_intersection - shootline_on_map.start).getLengthSQ());
1586 ************ Private methods *************
1589 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1590 bool set_changed, u32 dtime_s)
1592 assert(object); // Pre-condition
1593 if(object->getId() == 0){
1594 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1597 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1598 <<"no free ids available"<<std::endl;
1599 if(object->environmentDeletes())
1603 object->setId(new_id);
1606 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1607 <<"supplied with id "<<object->getId()<<std::endl;
1610 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1611 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1612 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1613 if(object->environmentDeletes())
1618 if (objectpos_over_limit(object->getBasePosition())) {
1619 v3f p = object->getBasePosition();
1620 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1621 << "object position (" << p.X << "," << p.Y << "," << p.Z
1622 << ") outside maximum range" << std::endl;
1623 if (object->environmentDeletes())
1628 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1629 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1631 m_active_objects[object->getId()] = object;
1633 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1634 <<"Added id="<<object->getId()<<"; there are now "
1635 <<m_active_objects.size()<<" active objects."
1638 // Register reference in scripting api (must be done before post-init)
1639 m_script->addObjectReference(object);
1640 // Post-initialize object
1641 object->addedToEnvironment(dtime_s);
1643 // Add static data to block
1644 if(object->isStaticAllowed())
1646 // Add static object to active static list of the block
1647 v3f objectpos = object->getBasePosition();
1648 std::string staticdata;
1649 object->getStaticData(&staticdata);
1650 StaticObject s_obj(object->getType(), objectpos, staticdata);
1651 // Add to the block where the object is located in
1652 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1653 MapBlock *block = m_map->emergeBlock(blockpos);
1655 block->m_static_objects.m_active[object->getId()] = s_obj;
1656 object->m_static_exists = true;
1657 object->m_static_block = blockpos;
1660 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1661 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1663 v3s16 p = floatToInt(objectpos, BS);
1664 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1665 <<"could not emerge block for storing id="<<object->getId()
1666 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1670 return object->getId();
1674 Remove objects that satisfy (isGone() && m_known_by_count==0)
1676 void ServerEnvironment::removeRemovedObjects()
1678 std::vector<u16> objects_to_remove;
1679 for (auto &ao_it : m_active_objects) {
1680 u16 id = ao_it.first;
1681 ServerActiveObject* obj = ao_it.second;
1683 // This shouldn't happen but check it
1685 errorstream << "ServerEnvironment::removeRemovedObjects(): "
1686 << "NULL object found. id=" << id << std::endl;
1687 objects_to_remove.push_back(id);
1692 We will handle objects marked for removal or deactivation
1698 Delete static data from block if removed
1700 if (obj->m_pending_removal)
1701 deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1703 // If still known by clients, don't actually remove. On some future
1704 // invocation this will be 0, which is when removal will continue.
1705 if(obj->m_known_by_count > 0)
1709 Move static data from active to stored if deactivated
1711 if (!obj->m_pending_removal && obj->m_static_exists) {
1712 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1714 std::map<u16, StaticObject>::iterator i =
1715 block->m_static_objects.m_active.find(id);
1716 if (i != block->m_static_objects.m_active.end()) {
1717 block->m_static_objects.m_stored.push_back(i->second);
1718 block->m_static_objects.m_active.erase(id);
1719 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1720 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1722 warningstream << "ServerEnvironment::removeRemovedObjects(): "
1723 << "id=" << id << " m_static_exists=true but "
1724 << "static data doesn't actually exist in "
1725 << PP(obj->m_static_block) << std::endl;
1728 infostream << "Failed to emerge block from which an object to "
1729 << "be deactivated was loaded from. id=" << id << std::endl;
1733 // Tell the object about removal
1734 obj->removingFromEnvironment();
1735 // Deregister in scripting api
1736 m_script->removeObjectReference(obj);
1739 if(obj->environmentDeletes())
1742 objects_to_remove.push_back(id);
1744 // Remove references from m_active_objects
1745 for (u16 i : objects_to_remove) {
1746 m_active_objects.erase(i);
1750 static void print_hexdump(std::ostream &o, const std::string &data)
1752 const int linelength = 16;
1753 for(int l=0; ; l++){
1754 int i0 = linelength * l;
1755 bool at_end = false;
1756 int thislinelength = linelength;
1757 if(i0 + thislinelength > (int)data.size()){
1758 thislinelength = data.size() - i0;
1761 for(int di=0; di<linelength; di++){
1764 if(di<thislinelength)
1765 snprintf(buf, 4, "%.2x ", data[i]);
1767 snprintf(buf, 4, " ");
1771 for(int di=0; di<thislinelength; di++){
1785 Convert stored objects from blocks near the players to active.
1787 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1792 // Ignore if no stored objects (to not set changed flag)
1793 if(block->m_static_objects.m_stored.empty())
1796 verbosestream<<"ServerEnvironment::activateObjects(): "
1797 <<"activating objects of block "<<PP(block->getPos())
1798 <<" ("<<block->m_static_objects.m_stored.size()
1799 <<" objects)"<<std::endl;
1800 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1802 errorstream<<"suspiciously large amount of objects detected: "
1803 <<block->m_static_objects.m_stored.size()<<" in "
1804 <<PP(block->getPos())
1805 <<"; removing all of them."<<std::endl;
1806 // Clear stored list
1807 block->m_static_objects.m_stored.clear();
1808 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1809 MOD_REASON_TOO_MANY_OBJECTS);
1813 // Activate stored objects
1814 std::vector<StaticObject> new_stored;
1815 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1816 // Create an active object from the data
1817 ServerActiveObject *obj = ServerActiveObject::create
1818 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1819 // If couldn't create object, store static data back.
1821 errorstream<<"ServerEnvironment::activateObjects(): "
1822 <<"failed to create active object from static object "
1823 <<"in block "<<PP(s_obj.pos/BS)
1824 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1825 print_hexdump(verbosestream, s_obj.data);
1827 new_stored.push_back(s_obj);
1830 verbosestream<<"ServerEnvironment::activateObjects(): "
1831 <<"activated static object pos="<<PP(s_obj.pos/BS)
1832 <<" type="<<(int)s_obj.type<<std::endl;
1833 // This will also add the object to the active static list
1834 addActiveObjectRaw(obj, false, dtime_s);
1837 // Clear stored list
1838 block->m_static_objects.m_stored.clear();
1839 // Add leftover failed stuff to stored list
1840 for (const StaticObject &s_obj : new_stored) {
1841 block->m_static_objects.m_stored.push_back(s_obj);
1845 Note: Block hasn't really been modified here.
1846 The objects have just been activated and moved from the stored
1847 static list to the active static list.
1848 As such, the block is essentially the same.
1849 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1850 Otherwise there would be a huge amount of unnecessary I/O.
1855 Convert objects that are not standing inside active blocks to static.
1857 If m_known_by_count != 0, active object is not deleted, but static
1858 data is still updated.
1860 If force_delete is set, active object is deleted nevertheless. It
1861 shall only be set so in the destructor of the environment.
1863 If block wasn't generated (not in memory or on disk),
1865 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1867 std::vector<u16> objects_to_remove;
1868 for (auto &ao_it : m_active_objects) {
1869 // force_delete might be overriden per object
1870 bool force_delete = _force_delete;
1872 ServerActiveObject* obj = ao_it.second;
1875 // Do not deactivate if static data creation not allowed
1876 if(!force_delete && !obj->isStaticAllowed())
1879 // removeRemovedObjects() is responsible for these
1880 if(!force_delete && obj->isGone())
1883 u16 id = ao_it.first;
1884 v3f objectpos = obj->getBasePosition();
1886 // The block in which the object resides in
1887 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1889 // If object's static data is stored in a deactivated block and object
1890 // is actually located in an active block, re-save to the block in
1891 // which the object is actually located in.
1893 obj->m_static_exists &&
1894 !m_active_blocks.contains(obj->m_static_block) &&
1895 m_active_blocks.contains(blockpos_o))
1897 // Delete from block where object was located
1898 deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
1900 std::string staticdata_new;
1901 obj->getStaticData(&staticdata_new);
1902 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1903 // Save to block where object is located
1904 saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
1909 // If block is still active, don't remove
1910 if(!force_delete && m_active_blocks.contains(blockpos_o))
1913 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
1914 << "deactivating object id=" << id << " on inactive block "
1915 << PP(blockpos_o) << std::endl;
1917 // If known by some client, don't immediately delete.
1918 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1921 Update the static data
1923 if(obj->isStaticAllowed())
1925 // Create new static object
1926 std::string staticdata_new;
1927 obj->getStaticData(&staticdata_new);
1928 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1930 bool stays_in_same_block = false;
1931 bool data_changed = true;
1933 // Check if static data has changed considerably
1934 if (obj->m_static_exists) {
1935 if (obj->m_static_block == blockpos_o)
1936 stays_in_same_block = true;
1938 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1941 std::map<u16, StaticObject>::iterator n =
1942 block->m_static_objects.m_active.find(id);
1943 if (n != block->m_static_objects.m_active.end()) {
1944 StaticObject static_old = n->second;
1946 float save_movem = obj->getMinimumSavedMovement();
1948 if (static_old.data == staticdata_new &&
1949 (static_old.pos - objectpos).getLength() < save_movem)
1950 data_changed = false;
1952 warningstream << "ServerEnvironment::deactivateFarObjects(): "
1953 << "id=" << id << " m_static_exists=true but "
1954 << "static data doesn't actually exist in "
1955 << PP(obj->m_static_block) << std::endl;
1961 While changes are always saved, blocks are only marked as modified
1962 if the object has moved or different staticdata. (see above)
1964 bool shall_be_written = (!stays_in_same_block || data_changed);
1965 u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
1967 // Delete old static object
1968 deleteStaticFromBlock(obj, id, reason, false);
1970 // Add to the block where the object is located in
1971 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1972 u16 store_id = pending_delete ? id : 0;
1973 if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
1974 force_delete = true;
1978 If known by some client, set pending deactivation.
1979 Otherwise delete it immediately.
1981 if(pending_delete && !force_delete)
1983 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
1984 << "object id=" << id << " is known by clients"
1985 << "; not deleting yet" << std::endl;
1987 obj->m_pending_deactivation = true;
1990 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
1991 << "object id=" << id << " is not known by clients"
1992 << "; deleting" << std::endl;
1994 // Tell the object about removal
1995 obj->removingFromEnvironment();
1996 // Deregister in scripting api
1997 m_script->removeObjectReference(obj);
1999 // Delete active object
2000 if(obj->environmentDeletes())
2002 // Id to be removed from m_active_objects
2003 objects_to_remove.push_back(id);
2006 // Remove references from m_active_objects
2007 for (u16 i : objects_to_remove) {
2008 m_active_objects.erase(i);
2012 void ServerEnvironment::deleteStaticFromBlock(
2013 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2015 if (!obj->m_static_exists)
2020 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2022 block = m_map->emergeBlock(obj->m_static_block, false);
2025 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2026 << " when deleting static data of object from it. id=" << id << std::endl;
2030 block->m_static_objects.remove(id);
2031 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2032 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2034 obj->m_static_exists = false;
2037 bool ServerEnvironment::saveStaticToBlock(
2038 v3s16 blockpos, u16 store_id,
2039 ServerActiveObject *obj, const StaticObject &s_obj,
2042 MapBlock *block = nullptr;
2044 block = m_map->emergeBlock(blockpos);
2045 } catch (InvalidPositionException &e) {
2046 // Handled via NULL pointer
2047 // NOTE: emergeBlock's failure is usually determined by it
2048 // actually returning NULL
2052 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2053 << " when saving static data of object to it. id=" << store_id << std::endl;
2056 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2057 warningstream << "ServerEnv: Trying to store id = " << store_id
2058 << " statically but block " << PP(blockpos)
2059 << " already contains "
2060 << block->m_static_objects.m_stored.size()
2061 << " objects." << std::endl;
2065 block->m_static_objects.insert(store_id, s_obj);
2066 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2067 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2069 obj->m_static_exists = true;
2070 obj->m_static_block = blockpos;
2075 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2076 const std::string &savedir, const Settings &conf)
2079 if (name == "sqlite3")
2080 return new PlayerDatabaseSQLite3(savedir);
2082 if (name == "dummy")
2083 return new Database_Dummy();
2085 if (name == "postgresql") {
2086 std::string connect_string;
2087 conf.getNoEx("pgsql_player_connection", connect_string);
2088 return new PlayerDatabasePostgreSQL(connect_string);
2091 if (name == "files")
2092 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2094 throw BaseException(std::string("Database backend ") + name + " not supported.");
2097 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2098 const Settings &cmd_args)
2100 std::string migrate_to = cmd_args.get("migrate-players");
2102 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2103 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2104 errorstream << "Cannot read world.mt!" << std::endl;
2108 if (!world_mt.exists("player_backend")) {
2109 errorstream << "Please specify your current backend in world.mt:"
2111 << " player_backend = {files|sqlite3|postgresql}"
2116 std::string backend = world_mt.get("player_backend");
2117 if (backend == migrate_to) {
2118 errorstream << "Cannot migrate: new backend is same"
2119 << " as the old one" << std::endl;
2123 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2126 if (backend == "files") {
2127 // Create backup directory
2128 fs::CreateDir(players_backup_path);
2132 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2133 game_params.world_path, world_mt);
2134 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2135 game_params.world_path, world_mt);
2137 std::vector<std::string> player_list;
2138 srcdb->listPlayers(player_list);
2139 for (std::vector<std::string>::const_iterator it = player_list.begin();
2140 it != player_list.end(); ++it) {
2141 actionstream << "Migrating player " << it->c_str() << std::endl;
2142 RemotePlayer player(it->c_str(), NULL);
2143 PlayerSAO playerSAO(NULL, &player, 15000, false);
2145 srcdb->loadPlayer(&player, &playerSAO);
2147 playerSAO.finalize(&player, std::set<std::string>());
2148 player.setPlayerSAO(&playerSAO);
2150 dstdb->savePlayer(&player);
2152 // For files source, move player files to backup dir
2153 if (backend == "files") {
2155 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2156 players_backup_path + DIR_DELIM + (*it));
2160 actionstream << "Successfully migrated " << player_list.size() << " players"
2162 world_mt.set("player_backend", migrate_to);
2163 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2164 errorstream << "Failed to update world.mt!" << std::endl;
2166 actionstream << "world.mt updated" << std::endl;
2168 // When migration is finished from file backend, remove players directory if empty
2169 if (backend == "files") {
2170 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2177 } catch (BaseException &e) {
2178 errorstream << "An error occured during migration: " << e.what() << std::endl;