3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "serverenvironment.h"
21 #include "content_sao.h"
26 #include "nodemetadata.h"
31 #include "remoteplayer.h"
32 #include "scripting_server.h"
34 #include "util/serialize.h"
35 #include "util/basic_macros.h"
36 #include "util/pointedthing.h"
37 #include "threading/mutex_auto_lock.h"
39 #include "gameparams.h"
40 #include "database/database-dummy.h"
41 #include "database/database-files.h"
42 #include "database/database-sqlite3.h"
44 #include "database/database-postgresql.h"
48 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
50 // A number that is much smaller than the timeout for particle spawners should/could ever be
51 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
57 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
60 // Initialize timer to random value to spread processing
61 float itv = abm->getTriggerInterval();
62 itv = MYMAX(0.001, itv); // No less than 1ms
63 int minval = MYMAX(-0.51*itv, -60); // Clamp to
64 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
65 timer = myrand_range(minval, maxval);
72 void LBMContentMapping::deleteContents()
74 for (auto &it : lbm_list) {
79 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
81 // Add the lbm_def to the LBMContentMapping.
82 // Unknown names get added to the global NameIdMapping.
83 const NodeDefManager *nodedef = gamedef->ndef();
85 lbm_list.push_back(lbm_def);
87 for (const std::string &nodeTrigger: lbm_def->trigger_contents) {
88 std::vector<content_t> c_ids;
89 bool found = nodedef->getIds(nodeTrigger, c_ids);
91 content_t c_id = gamedef->allocateUnknownNodeId(nodeTrigger);
92 if (c_id == CONTENT_IGNORE) {
93 // Seems it can't be allocated.
94 warningstream << "Could not internalize node name \"" << nodeTrigger
95 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
98 c_ids.push_back(c_id);
101 for (content_t c_id : c_ids) {
102 map[c_id].push_back(lbm_def);
107 const std::vector<LoadingBlockModifierDef *> *
108 LBMContentMapping::lookup(content_t c) const
110 lbm_map::const_iterator it = map.find(c);
113 // This first dereferences the iterator, returning
114 // a std::vector<LoadingBlockModifierDef *>
115 // reference, then we convert it to a pointer.
116 return &(it->second);
119 LBMManager::~LBMManager()
121 for (auto &m_lbm_def : m_lbm_defs) {
122 delete m_lbm_def.second;
125 for (auto &it : m_lbm_lookup) {
126 (it.second).deleteContents();
130 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
132 // Precondition, in query mode the map isn't used anymore
133 FATAL_ERROR_IF(m_query_mode,
134 "attempted to modify LBMManager in query mode");
136 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
137 throw ModError("Error adding LBM \"" + lbm_def->name +
138 "\": Does not follow naming conventions: "
139 "Only characters [a-z0-9_:] are allowed.");
142 m_lbm_defs[lbm_def->name] = lbm_def;
145 void LBMManager::loadIntroductionTimes(const std::string ×,
146 IGameDef *gamedef, u32 now)
151 // Storing it in a map first instead of
152 // handling the stuff directly in the loop
153 // removes all duplicate entries.
154 // TODO make this std::unordered_map
155 std::map<std::string, u32> introduction_times;
158 The introduction times string consists of name~time entries,
159 with each entry terminated by a semicolon. The time is decimal.
164 while ((idx_new = times.find(';', idx)) != std::string::npos) {
165 std::string entry = times.substr(idx, idx_new - idx);
166 std::vector<std::string> components = str_split(entry, '~');
167 if (components.size() != 2)
168 throw SerializationError("Introduction times entry \""
169 + entry + "\" requires exactly one '~'!");
170 const std::string &name = components[0];
171 u32 time = from_string<u32>(components[1]);
172 introduction_times[name] = time;
176 // Put stuff from introduction_times into m_lbm_lookup
177 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
178 it != introduction_times.end(); ++it) {
179 const std::string &name = it->first;
180 u32 time = it->second;
182 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
183 m_lbm_defs.find(name);
184 if (def_it == m_lbm_defs.end()) {
185 // This seems to be an LBM entry for
186 // an LBM we haven't loaded. Discard it.
189 LoadingBlockModifierDef *lbm_def = def_it->second;
190 if (lbm_def->run_at_every_load) {
191 // This seems to be an LBM entry for
192 // an LBM that runs at every load.
193 // Don't add it just yet.
197 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
199 // Erase the entry so that we know later
200 // what elements didn't get put into m_lbm_lookup
201 m_lbm_defs.erase(name);
204 // Now also add the elements from m_lbm_defs to m_lbm_lookup
205 // that weren't added in the previous step.
206 // They are introduced first time to this world,
207 // or are run at every load (introducement time hardcoded to U32_MAX).
209 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
210 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
212 for (auto &m_lbm_def : m_lbm_defs) {
213 if (m_lbm_def.second->run_at_every_load) {
214 lbms_running_always.addLBM(m_lbm_def.second, gamedef);
216 lbms_we_introduce_now.addLBM(m_lbm_def.second, gamedef);
220 // Clear the list, so that we don't delete remaining elements
221 // twice in the destructor
225 std::string LBMManager::createIntroductionTimesString()
227 // Precondition, we must be in query mode
228 FATAL_ERROR_IF(!m_query_mode,
229 "attempted to query on non fully set up LBMManager");
231 std::ostringstream oss;
232 for (const auto &it : m_lbm_lookup) {
234 const std::vector<LoadingBlockModifierDef *> &lbm_list = it.second.lbm_list;
235 for (const auto &lbm_def : lbm_list) {
236 // Don't add if the LBM runs at every load,
237 // then introducement time is hardcoded
238 // and doesn't need to be stored
239 if (lbm_def->run_at_every_load)
241 oss << lbm_def->name << "~" << time << ";";
247 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
249 // Precondition, we need m_lbm_lookup to be initialized
250 FATAL_ERROR_IF(!m_query_mode,
251 "attempted to query on non fully set up LBMManager");
252 v3s16 pos_of_block = block->getPosRelative();
256 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
257 for (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 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);
368 for (v3s16 p : newlist) {
377 ServerEnvironment::ServerEnvironment(ServerMap *map,
378 ServerScripting *scriptIface, Server *server,
379 const std::string &path_world):
382 m_script(scriptIface),
384 m_path_world(path_world)
386 // Determine which database backend to use
387 std::string conf_path = path_world + DIR_DELIM + "world.mt";
389 bool succeeded = conf.readConfigFile(conf_path.c_str());
390 if (!succeeded || !conf.exists("player_backend")) {
391 // fall back to files
392 conf.set("player_backend", "files");
393 warningstream << "/!\\ You are using old player file backend. "
394 << "This backend is deprecated and will be removed in next release /!\\"
395 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
396 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
398 if (!conf.updateConfigFile(conf_path.c_str())) {
399 errorstream << "ServerEnvironment::ServerEnvironment(): "
400 << "Failed to update world.mt!" << std::endl;
405 conf.getNoEx("player_backend", name);
406 m_player_database = openPlayerDatabase(name, path_world, conf);
409 ServerEnvironment::~ServerEnvironment()
411 // Clear active block list.
412 // This makes the next one delete all active objects.
413 m_active_blocks.clear();
415 // Convert all objects to static and delete the active objects
416 deactivateFarObjects(true);
421 // Delete ActiveBlockModifiers
422 for (ABMWithState &m_abm : m_abms) {
426 // Deallocate players
427 for (RemotePlayer *m_player : m_players) {
431 delete m_player_database;
434 Map & ServerEnvironment::getMap()
439 ServerMap & ServerEnvironment::getServerMap()
444 RemotePlayer *ServerEnvironment::getPlayer(const session_t peer_id)
446 for (RemotePlayer *player : m_players) {
447 if (player->getPeerId() == peer_id)
453 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
455 for (RemotePlayer *player : m_players) {
456 if (strcmp(player->getName(), name) == 0)
462 void ServerEnvironment::addPlayer(RemotePlayer *player)
465 Check that peer_ids are unique.
466 Also check that names are unique.
467 Exception: there can be multiple players with peer_id=0
469 // If peer id is non-zero, it has to be unique.
470 if (player->getPeerId() != PEER_ID_INEXISTENT)
471 FATAL_ERROR_IF(getPlayer(player->getPeerId()) != NULL, "Peer id not unique");
472 // Name has to be unique.
473 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
475 m_players.push_back(player);
478 void ServerEnvironment::removePlayer(RemotePlayer *player)
480 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
481 it != m_players.end(); ++it) {
482 if ((*it) == player) {
490 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
492 return m_player_database->removePlayer(name);
495 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, v3s16 *p)
497 // Iterate trough nodes on the line
498 voxalgo::VoxelLineIterator iterator(pos1 / BS, (pos2 - pos1) / BS);
500 MapNode n = getMap().getNodeNoEx(iterator.m_current_node_pos);
503 if (n.param0 != CONTENT_AIR) {
505 *p = iterator.m_current_node_pos;
509 } while (iterator.m_current_index <= iterator.m_last_index);
513 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
514 const std::string &str_reason, bool reconnect)
516 for (RemotePlayer *player : m_players) {
517 m_server->DenyAccessVerCompliant(player->getPeerId(),
518 player->protocol_version, reason, str_reason, reconnect);
522 void ServerEnvironment::saveLoadedPlayers()
524 std::string players_path = m_path_world + DIR_DELIM + "players";
525 fs::CreateDir(players_path);
527 for (RemotePlayer *player : m_players) {
528 if (player->checkModified() || (player->getPlayerSAO() &&
529 player->getPlayerSAO()->extendedAttributesModified())) {
531 m_player_database->savePlayer(player);
532 } catch (DatabaseException &e) {
533 errorstream << "Failed to save player " << player->getName() << " exception: "
534 << e.what() << std::endl;
541 void ServerEnvironment::savePlayer(RemotePlayer *player)
544 m_player_database->savePlayer(player);
545 } catch (DatabaseException &e) {
546 errorstream << "Failed to save player " << player->getName() << " exception: "
547 << e.what() << std::endl;
552 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
553 session_t peer_id, bool is_singleplayer)
555 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
556 // Create player if it doesn't exist
557 if (!m_player_database->loadPlayer(player, playersao)) {
559 // Set player position
560 infostream << "Server: Finding spawn place for player \""
561 << player->getName() << "\"" << std::endl;
562 playersao->setBasePosition(m_server->findSpawnPos());
564 // Make sure the player is saved
565 player->setModified(true);
567 // If the player exists, ensure that they respawn inside legal bounds
568 // This fixes an assert crash when the player can't be added
569 // to the environment
570 if (objectpos_over_limit(playersao->getBasePosition())) {
571 actionstream << "Respawn position for player \""
572 << player->getName() << "\" outside limits, resetting" << std::endl;
573 playersao->setBasePosition(m_server->findSpawnPos());
577 // Add player to environment
580 /* Clean up old HUD elements from previous sessions */
583 /* Add object to environment */
584 addActiveObject(playersao);
589 void ServerEnvironment::saveMeta()
591 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
593 // Open file and serialize
594 std::ostringstream ss(std::ios_base::binary);
597 args.setU64("game_time", m_game_time);
598 args.setU64("time_of_day", getTimeOfDay());
599 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
600 args.setU64("lbm_introduction_times_version", 1);
601 args.set("lbm_introduction_times",
602 m_lbm_mgr.createIntroductionTimesString());
603 args.setU64("day_count", m_day_count);
607 if(!fs::safeWriteToFile(path, ss.str()))
609 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
611 throw SerializationError("Couldn't save env meta");
615 void ServerEnvironment::loadMeta()
617 // If file doesn't exist, load default environment metadata
618 if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
619 infostream << "ServerEnvironment: Loading default environment metadata"
625 infostream << "ServerEnvironment: Loading environment metadata" << std::endl;
627 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
629 // Open file and deserialize
630 std::ifstream is(path.c_str(), std::ios_base::binary);
632 infostream << "ServerEnvironment::loadMeta(): Failed to open "
633 << path << std::endl;
634 throw SerializationError("Couldn't load env meta");
639 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
640 throw SerializationError("ServerEnvironment::loadMeta(): "
641 "EnvArgsEnd not found!");
645 m_game_time = args.getU64("game_time");
646 } catch (SettingNotFoundException &e) {
647 // Getting this is crucial, otherwise timestamps are useless
648 throw SerializationError("Couldn't load env meta game_time");
651 setTimeOfDay(args.exists("time_of_day") ?
652 // set day to early morning by default
653 args.getU64("time_of_day") : 5250);
655 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
656 // If missing, do as if clearObjects was never called
657 args.getU64("last_clear_objects_time") : 0;
659 std::string lbm_introduction_times;
661 u64 ver = args.getU64("lbm_introduction_times_version");
663 lbm_introduction_times = args.get("lbm_introduction_times");
665 infostream << "ServerEnvironment::loadMeta(): Non-supported"
666 << " introduction time version " << ver << std::endl;
668 } catch (SettingNotFoundException &e) {
669 // No problem, this is expected. Just continue with an empty string
671 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
673 m_day_count = args.exists("day_count") ?
674 args.getU64("day_count") : 0;
678 * called if env_meta.txt doesn't exist (e.g. new world)
680 void ServerEnvironment::loadDefaultMeta()
682 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
687 ActiveBlockModifier *abm;
689 std::vector<content_t> required_neighbors;
690 bool check_required_neighbors; // false if required_neighbors is known to be empty
696 ServerEnvironment *m_env;
697 std::vector<std::vector<ActiveABM> *> m_aabms;
699 ABMHandler(std::vector<ABMWithState> &abms,
700 float dtime_s, ServerEnvironment *env,
706 const NodeDefManager *ndef = env->getGameDef()->ndef();
707 for (ABMWithState &abmws : abms) {
708 ActiveBlockModifier *abm = abmws.abm;
709 float trigger_interval = abm->getTriggerInterval();
710 if(trigger_interval < 0.001)
711 trigger_interval = 0.001;
712 float actual_interval = dtime_s;
714 abmws.timer += dtime_s;
715 if(abmws.timer < trigger_interval)
717 abmws.timer -= trigger_interval;
718 actual_interval = trigger_interval;
720 float chance = abm->getTriggerChance();
725 if (abm->getSimpleCatchUp()) {
726 float intervals = actual_interval / trigger_interval;
729 aabm.chance = chance / intervals;
733 aabm.chance = chance;
737 const std::vector<std::string> &required_neighbors_s =
738 abm->getRequiredNeighbors();
739 for (const std::string &required_neighbor_s : required_neighbors_s) {
740 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
742 aabm.check_required_neighbors = !required_neighbors_s.empty();
745 const std::vector<std::string> &contents_s = abm->getTriggerContents();
746 for (const std::string &content_s : contents_s) {
747 std::vector<content_t> ids;
748 ndef->getIds(content_s, ids);
749 for (content_t c : ids) {
750 if (c >= m_aabms.size())
751 m_aabms.resize(c + 256, NULL);
753 m_aabms[c] = new std::vector<ActiveABM>;
754 m_aabms[c]->push_back(aabm);
762 for (auto &aabms : m_aabms)
766 // Find out how many objects the given block and its neighbours contain.
767 // Returns the number of objects in the block, and also in 'wider' the
768 // number of objects in the block and all its neighbours. The latter
769 // may an estimate if any neighbours are unloaded.
770 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
773 u32 wider_unknown_count = 0;
774 for(s16 x=-1; x<=1; x++)
775 for(s16 y=-1; y<=1; y++)
776 for(s16 z=-1; z<=1; z++)
778 MapBlock *block2 = map->getBlockNoCreateNoEx(
779 block->getPos() + v3s16(x,y,z));
781 wider_unknown_count++;
784 wider += block2->m_static_objects.m_active.size()
785 + block2->m_static_objects.m_stored.size();
788 u32 active_object_count = block->m_static_objects.m_active.size();
789 u32 wider_known_count = 3*3*3 - wider_unknown_count;
790 wider += wider_unknown_count * wider / wider_known_count;
791 return active_object_count;
794 void apply(MapBlock *block)
796 if(m_aabms.empty() || block->isDummy())
799 ServerMap *map = &m_env->getServerMap();
801 u32 active_object_count_wider;
802 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
803 m_env->m_added_objects = 0;
806 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
807 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
808 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
810 const MapNode &n = block->getNodeUnsafe(p0);
811 content_t c = n.getContent();
813 if (c >= m_aabms.size() || !m_aabms[c])
816 v3s16 p = p0 + block->getPosRelative();
817 for (ActiveABM &aabm : *m_aabms[c]) {
818 if (myrand() % aabm.chance != 0)
822 if (aabm.check_required_neighbors) {
824 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
825 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
826 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
831 if (block->isValidPosition(p1)) {
832 // if the neighbor is found on the same map block
833 // get it straight from there
834 const MapNode &n = block->getNodeUnsafe(p1);
837 // otherwise consult the map
838 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
841 if (CONTAINS(aabm.required_neighbors, c))
844 // No required neighbor found
849 // Call all the trigger variations
850 aabm.abm->trigger(m_env, p, n);
851 aabm.abm->trigger(m_env, p, n,
852 active_object_count, active_object_count_wider);
854 // Count surrounding objects again if the abms added any
855 if(m_env->m_added_objects > 0) {
856 active_object_count = countObjects(block, map, active_object_count_wider);
857 m_env->m_added_objects = 0;
864 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
866 // Reset usage timer immediately, otherwise a block that becomes active
867 // again at around the same time as it would normally be unloaded will
868 // get unloaded incorrectly. (I think this still leaves a small possibility
869 // of a race condition between this and server::AsyncRunStep, which only
870 // some kind of synchronisation will fix, but it at least reduces the window
871 // of opportunity for it to break from seconds to nanoseconds)
872 block->resetUsageTimer();
874 // Get time difference
876 u32 stamp = block->getTimestamp();
877 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
878 dtime_s = m_game_time - stamp;
879 dtime_s += additional_dtime;
881 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
882 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
884 // Remove stored static objects if clearObjects was called since block's timestamp
885 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
886 block->m_static_objects.m_stored.clear();
887 // do not set changed flag to avoid unnecessary mapblock writes
890 // Set current time as timestamp
891 block->setTimestampNoChangedFlag(m_game_time);
893 /*infostream<<"ServerEnvironment::activateBlock(): block is "
894 <<dtime_s<<" seconds old."<<std::endl;*/
896 // Activate stored objects
897 activateObjects(block, dtime_s);
899 /* Handle LoadingBlockModifiers */
900 m_lbm_mgr.applyLBMs(this, block, stamp);
903 std::vector<NodeTimer> elapsed_timers =
904 block->m_node_timers.step((float)dtime_s);
905 if (!elapsed_timers.empty()) {
907 for (const NodeTimer &elapsed_timer : elapsed_timers) {
908 n = block->getNodeNoEx(elapsed_timer.position);
909 v3s16 p = elapsed_timer.position + block->getPosRelative();
910 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
911 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
912 elapsed_timer.position));
917 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
919 m_abms.emplace_back(abm);
922 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
924 m_lbm_mgr.addLBMDef(lbm);
927 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
929 const NodeDefManager *ndef = m_server->ndef();
930 MapNode n_old = m_map->getNodeNoEx(p);
932 const ContentFeatures &cf_old = ndef->get(n_old);
935 if (cf_old.has_on_destruct)
936 m_script->node_on_destruct(p, n_old);
939 if (!m_map->addNodeWithEvent(p, n))
942 // Update active VoxelManipulator if a mapgen thread
943 m_map->updateVManip(p);
945 // Call post-destructor
946 if (cf_old.has_after_destruct)
947 m_script->node_after_destruct(p, n_old);
949 // Retrieve node content features
950 // if new node is same as old, reuse old definition to prevent a lookup
951 const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
954 if (cf_new.has_on_construct)
955 m_script->node_on_construct(p, n);
960 bool ServerEnvironment::removeNode(v3s16 p)
962 const NodeDefManager *ndef = m_server->ndef();
963 MapNode n_old = m_map->getNodeNoEx(p);
966 if (ndef->get(n_old).has_on_destruct)
967 m_script->node_on_destruct(p, n_old);
970 // This is slightly optimized compared to addNodeWithEvent(air)
971 if (!m_map->removeNodeWithEvent(p))
974 // Update active VoxelManipulator if a mapgen thread
975 m_map->updateVManip(p);
977 // Call post-destructor
978 if (ndef->get(n_old).has_after_destruct)
979 m_script->node_after_destruct(p, n_old);
981 // Air doesn't require constructor
985 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
987 if (!m_map->addNodeWithEvent(p, n, false))
990 // Update active VoxelManipulator if a mapgen thread
991 m_map->updateVManip(p);
996 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
999 for (auto &activeObject : m_active_objects) {
1000 ServerActiveObject* obj = activeObject.second;
1001 u16 id = activeObject.first;
1002 v3f objectpos = obj->getBasePosition();
1003 if (objectpos.getDistanceFrom(pos) > radius)
1005 objects.push_back(id);
1009 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1011 infostream << "ServerEnvironment::clearObjects(): "
1012 << "Removing all active objects" << std::endl;
1013 std::vector<u16> objects_to_remove;
1014 for (auto &it : m_active_objects) {
1016 ServerActiveObject* obj = it.second;
1017 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1020 // Delete static object if block is loaded
1021 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1023 // If known by some client, don't delete immediately
1024 if (obj->m_known_by_count > 0) {
1025 obj->m_pending_removal = true;
1029 // Tell the object about removal
1030 obj->removingFromEnvironment();
1031 // Deregister in scripting api
1032 m_script->removeObjectReference(obj);
1034 // Delete active object
1035 if (obj->environmentDeletes())
1037 // Id to be removed from m_active_objects
1038 objects_to_remove.push_back(id);
1041 // Remove references from m_active_objects
1042 for (u16 i : objects_to_remove) {
1043 m_active_objects.erase(i);
1046 // Get list of loaded blocks
1047 std::vector<v3s16> loaded_blocks;
1048 infostream << "ServerEnvironment::clearObjects(): "
1049 << "Listing all loaded blocks" << std::endl;
1050 m_map->listAllLoadedBlocks(loaded_blocks);
1051 infostream << "ServerEnvironment::clearObjects(): "
1052 << "Done listing all loaded blocks: "
1053 << loaded_blocks.size()<<std::endl;
1055 // Get list of loadable blocks
1056 std::vector<v3s16> loadable_blocks;
1057 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1058 infostream << "ServerEnvironment::clearObjects(): "
1059 << "Listing all loadable blocks" << std::endl;
1060 m_map->listAllLoadableBlocks(loadable_blocks);
1061 infostream << "ServerEnvironment::clearObjects(): "
1062 << "Done listing all loadable blocks: "
1063 << loadable_blocks.size() << std::endl;
1065 loadable_blocks = loaded_blocks;
1068 actionstream << "ServerEnvironment::clearObjects(): "
1069 << "Now clearing objects in " << loadable_blocks.size()
1070 << " blocks" << std::endl;
1072 // Grab a reference on each loaded block to avoid unloading it
1073 for (v3s16 p : loaded_blocks) {
1074 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1075 assert(block != NULL);
1079 // Remove objects in all loadable blocks
1080 u32 unload_interval = U32_MAX;
1081 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1082 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1083 unload_interval = MYMAX(unload_interval, 1);
1085 u32 report_interval = loadable_blocks.size() / 10;
1086 u32 num_blocks_checked = 0;
1087 u32 num_blocks_cleared = 0;
1088 u32 num_objs_cleared = 0;
1089 for (auto i = loadable_blocks.begin();
1090 i != loadable_blocks.end(); ++i) {
1092 MapBlock *block = m_map->emergeBlock(p, false);
1094 errorstream << "ServerEnvironment::clearObjects(): "
1095 << "Failed to emerge block " << PP(p) << std::endl;
1098 u32 num_stored = block->m_static_objects.m_stored.size();
1099 u32 num_active = block->m_static_objects.m_active.size();
1100 if (num_stored != 0 || num_active != 0) {
1101 block->m_static_objects.m_stored.clear();
1102 block->m_static_objects.m_active.clear();
1103 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1104 MOD_REASON_CLEAR_ALL_OBJECTS);
1105 num_objs_cleared += num_stored + num_active;
1106 num_blocks_cleared++;
1108 num_blocks_checked++;
1110 if (report_interval != 0 &&
1111 num_blocks_checked % report_interval == 0) {
1112 float percent = 100.0 * (float)num_blocks_checked /
1113 loadable_blocks.size();
1114 actionstream << "ServerEnvironment::clearObjects(): "
1115 << "Cleared " << num_objs_cleared << " objects"
1116 << " in " << num_blocks_cleared << " blocks ("
1117 << percent << "%)" << std::endl;
1119 if (num_blocks_checked % unload_interval == 0) {
1120 m_map->unloadUnreferencedBlocks();
1123 m_map->unloadUnreferencedBlocks();
1125 // Drop references that were added above
1126 for (v3s16 p : loaded_blocks) {
1127 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1132 m_last_clear_objects_time = m_game_time;
1134 actionstream << "ServerEnvironment::clearObjects(): "
1135 << "Finished: Cleared " << num_objs_cleared << " objects"
1136 << " in " << num_blocks_cleared << " blocks" << std::endl;
1139 void ServerEnvironment::step(float dtime)
1141 /* Step time of day */
1142 stepTimeOfDay(dtime);
1145 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1146 // really matter that much.
1147 static thread_local const float server_step =
1148 g_settings->getFloat("dedicated_server_step");
1149 m_recommended_send_interval = server_step;
1155 m_game_time_fraction_counter += dtime;
1156 u32 inc_i = (u32)m_game_time_fraction_counter;
1157 m_game_time += inc_i;
1158 m_game_time_fraction_counter -= (float)inc_i;
1165 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1166 for (RemotePlayer *player : m_players) {
1167 // Ignore disconnected players
1168 if (player->getPeerId() == PEER_ID_INEXISTENT)
1172 player->move(dtime, this, 100 * BS);
1177 Manage active block list
1179 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1180 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1182 Get player block positions
1184 std::vector<PlayerSAO*> players;
1185 for (RemotePlayer *player: m_players) {
1186 // Ignore disconnected players
1187 if (player->getPeerId() == PEER_ID_INEXISTENT)
1190 PlayerSAO *playersao = player->getPlayerSAO();
1193 players.push_back(playersao);
1197 Update list of active blocks, collecting changes
1199 // use active_object_send_range_blocks since that is max distance
1200 // for active objects sent the client anyway
1201 static thread_local const s16 active_object_range =
1202 g_settings->getS16("active_object_send_range_blocks");
1203 static thread_local const s16 active_block_range =
1204 g_settings->getS16("active_block_range");
1205 std::set<v3s16> blocks_removed;
1206 std::set<v3s16> blocks_added;
1207 m_active_blocks.update(players, active_block_range, active_object_range,
1208 blocks_removed, blocks_added);
1211 Handle removed blocks
1214 // Convert active objects that are no more in active blocks to static
1215 deactivateFarObjects(false);
1217 for (const v3s16 &p: blocks_removed) {
1218 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1222 // Set current time as timestamp (and let it set ChangedFlag)
1223 block->setTimestamp(m_game_time);
1230 for (const v3s16 &p: blocks_added) {
1231 MapBlock *block = m_map->getBlockOrEmerge(p);
1233 m_active_blocks.m_list.erase(p);
1234 m_active_blocks.m_abm_list.erase(p);
1238 activateBlock(block);
1243 Mess around in active blocks
1245 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1246 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1248 float dtime = m_cache_nodetimer_interval;
1250 for (const v3s16 &p: m_active_blocks.m_list) {
1251 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1255 // Reset block usage timer
1256 block->resetUsageTimer();
1258 // Set current time as timestamp
1259 block->setTimestampNoChangedFlag(m_game_time);
1260 // If time has changed much from the one on disk,
1261 // set block to be saved when it is unloaded
1262 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1263 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1264 MOD_REASON_BLOCK_EXPIRED);
1267 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1268 if (!elapsed_timers.empty()) {
1271 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1272 n = block->getNodeNoEx(elapsed_timer.position);
1273 p2 = elapsed_timer.position + block->getPosRelative();
1274 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1275 block->setNodeTimer(NodeTimer(
1276 elapsed_timer.timeout, 0, elapsed_timer.position));
1283 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1285 if (m_active_block_interval_overload_skip > 0) {
1286 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1287 m_active_block_interval_overload_skip--;
1290 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1291 TimeTaker timer("modify in active blocks per interval");
1293 // Initialize handling of ActiveBlockModifiers
1294 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1296 for (const v3s16 &p : m_active_blocks.m_abm_list) {
1297 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1301 // Set current time as timestamp
1302 block->setTimestampNoChangedFlag(m_game_time);
1304 /* Handle ActiveBlockModifiers */
1305 abmhandler.apply(block);
1308 u32 time_ms = timer.stop(true);
1309 u32 max_time_ms = 200;
1310 if (time_ms > max_time_ms) {
1311 warningstream<<"active block modifiers took "
1312 <<time_ms<<"ms (longer than "
1313 <<max_time_ms<<"ms)"<<std::endl;
1314 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1319 Step script environment (run global on_step())
1321 m_script->environment_Step(dtime);
1327 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1328 //TimeTaker timer("Step active objects");
1330 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1332 // This helps the objects to send data at the same time
1333 bool send_recommended = false;
1334 m_send_recommended_timer += dtime;
1335 if(m_send_recommended_timer > getSendRecommendedInterval())
1337 m_send_recommended_timer -= getSendRecommendedInterval();
1338 send_recommended = true;
1341 for (auto &ao_it : m_active_objects) {
1342 ServerActiveObject* obj = ao_it.second;
1347 obj->step(dtime, send_recommended);
1348 // Read messages from object
1349 while (!obj->m_messages_out.empty()) {
1350 m_active_object_messages.push(obj->m_messages_out.front());
1351 obj->m_messages_out.pop();
1357 Manage active objects
1359 if (m_object_management_interval.step(dtime, 0.5)) {
1360 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1361 removeRemovedObjects();
1365 Manage particle spawner expiration
1367 if (m_particle_management_interval.step(dtime, 1.0)) {
1368 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1369 i != m_particle_spawners.end(); ) {
1370 //non expiring spawners
1371 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1377 if (i->second <= 0.f)
1378 m_particle_spawners.erase(i++);
1385 u32 ServerEnvironment::addParticleSpawner(float exptime)
1387 // Timers with lifetime 0 do not expire
1388 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1391 for (;;) { // look for unused particlespawner id
1393 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1394 if (f == m_particle_spawners.end()) {
1395 m_particle_spawners[id] = time;
1402 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1404 u32 id = addParticleSpawner(exptime);
1405 m_particle_spawner_attachments[id] = attached_id;
1406 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1407 obj->attachParticleSpawner(id);
1412 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1414 m_particle_spawners.erase(id);
1415 const auto &it = m_particle_spawner_attachments.find(id);
1416 if (it != m_particle_spawner_attachments.end()) {
1417 u16 obj_id = it->second;
1418 ServerActiveObject *sao = getActiveObject(obj_id);
1419 if (sao != NULL && remove_from_object) {
1420 sao->detachParticleSpawner(id);
1422 m_particle_spawner_attachments.erase(id);
1426 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1428 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1429 return (n != m_active_objects.end() ? n->second : NULL);
1433 * Verify if id is a free active object id
1435 * @return true if slot is free
1437 bool ServerEnvironment::isFreeServerActiveObjectId(u16 id) const
1442 return m_active_objects.find(id) == m_active_objects.end();
1446 * Retrieve the first free ActiveObject ID
1447 * @return free activeobject ID or 0 if none was found
1449 u16 ServerEnvironment::getFreeServerActiveObjectId()
1451 // try to reuse id's as late as possible
1452 static u16 last_used_id = 0;
1453 u16 startid = last_used_id;
1456 if (isFreeServerActiveObjectId(last_used_id))
1457 return last_used_id;
1459 if (last_used_id == startid)
1464 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1466 assert(object); // Pre-condition
1468 u16 id = addActiveObjectRaw(object, true, 0);
1473 Finds out what new objects have been added to
1474 inside a radius around a position
1476 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1478 std::set<u16> ¤t_objects,
1479 std::queue<u16> &added_objects)
1481 f32 radius_f = radius * BS;
1482 f32 player_radius_f = player_radius * BS;
1484 if (player_radius_f < 0)
1485 player_radius_f = 0;
1487 Go through the object list,
1488 - discard removed/deactivated objects,
1489 - discard objects that are too far away,
1490 - discard objects that are found in current_objects.
1491 - add remaining objects to added_objects
1493 for (auto &ao_it : m_active_objects) {
1494 u16 id = ao_it.first;
1497 ServerActiveObject *object = ao_it.second;
1501 if (object->isGone())
1504 f32 distance_f = object->getBasePosition().
1505 getDistanceFrom(playersao->getBasePosition());
1506 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1507 // Discard if too far
1508 if (distance_f > player_radius_f && player_radius_f != 0)
1510 } else if (distance_f > radius_f)
1513 // Discard if already on current_objects
1514 std::set<u16>::iterator n;
1515 n = current_objects.find(id);
1516 if(n != current_objects.end())
1518 // Add to added_objects
1519 added_objects.push(id);
1524 Finds out what objects have been removed from
1525 inside a radius around a position
1527 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1529 std::set<u16> ¤t_objects,
1530 std::queue<u16> &removed_objects)
1532 f32 radius_f = radius * BS;
1533 f32 player_radius_f = player_radius * BS;
1535 if (player_radius_f < 0)
1536 player_radius_f = 0;
1538 Go through current_objects; object is removed if:
1539 - object is not found in m_active_objects (this is actually an
1540 error condition; objects should be removed only after all clients
1541 have been informed about removal), or
1542 - object is to be removed or deactivated, or
1543 - object is too far away
1545 for (u16 id : current_objects) {
1546 ServerActiveObject *object = getActiveObject(id);
1548 if (object == NULL) {
1549 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1550 << " object in current_objects is NULL" << std::endl;
1551 removed_objects.push(id);
1555 if (object->isGone()) {
1556 removed_objects.push(id);
1560 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1561 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1562 if (distance_f <= player_radius_f || player_radius_f == 0)
1564 } else if (distance_f <= radius_f)
1567 // Object is no longer visible
1568 removed_objects.push(id);
1572 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1573 v3s16 blockpos, bool static_exists, v3s16 static_block)
1575 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1579 for (auto &so_it : block->m_static_objects.m_active) {
1580 // Get the ServerActiveObject counterpart to this StaticObject
1581 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it.first);
1582 if (ao_it == m_active_objects.end()) {
1583 // If this ever happens, there must be some kind of nasty bug.
1584 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1585 "Object from MapBlock::m_static_objects::m_active not found "
1586 "in m_active_objects";
1590 ServerActiveObject *sao = ao_it->second;
1591 sao->m_static_exists = static_exists;
1592 sao->m_static_block = static_block;
1596 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1598 if(m_active_object_messages.empty())
1599 return ActiveObjectMessage(0);
1601 ActiveObjectMessage message = m_active_object_messages.front();
1602 m_active_object_messages.pop();
1606 void ServerEnvironment::getSelectedActiveObjects(
1607 const core::line3d<f32> &shootline_on_map,
1608 std::vector<PointedThing> &objects)
1610 std::vector<u16> objectIds;
1611 getObjectsInsideRadius(objectIds, shootline_on_map.start,
1612 shootline_on_map.getLength() + 10.0f);
1613 const v3f line_vector = shootline_on_map.getVector();
1615 for (u16 objectId : objectIds) {
1616 ServerActiveObject* obj = getActiveObject(objectId);
1618 aabb3f selection_box;
1619 if (!obj->getSelectionBox(&selection_box))
1622 v3f pos = obj->getBasePosition();
1624 aabb3f offsetted_box(selection_box.MinEdge + pos,
1625 selection_box.MaxEdge + pos);
1627 v3f current_intersection;
1628 v3s16 current_normal;
1629 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1630 ¤t_intersection, ¤t_normal)) {
1631 objects.emplace_back(
1632 (s16) objectId, current_intersection, current_normal,
1633 (current_intersection - shootline_on_map.start).getLengthSQ());
1639 ************ Private methods *************
1642 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1643 bool set_changed, u32 dtime_s)
1645 assert(object); // Pre-condition
1646 if(object->getId() == 0){
1647 u16 new_id = getFreeServerActiveObjectId();
1650 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1651 <<"no free ids available"<<std::endl;
1652 if(object->environmentDeletes())
1656 object->setId(new_id);
1659 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1660 <<"supplied with id "<<object->getId()<<std::endl;
1663 if(!isFreeServerActiveObjectId(object->getId())) {
1664 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1665 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1666 if(object->environmentDeletes())
1671 if (objectpos_over_limit(object->getBasePosition())) {
1672 v3f p = object->getBasePosition();
1673 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1674 << "object position (" << p.X << "," << p.Y << "," << p.Z
1675 << ") outside maximum range" << std::endl;
1676 if (object->environmentDeletes())
1681 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1682 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1684 m_active_objects[object->getId()] = object;
1686 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1687 <<"Added id="<<object->getId()<<"; there are now "
1688 <<m_active_objects.size()<<" active objects."
1691 // Register reference in scripting api (must be done before post-init)
1692 m_script->addObjectReference(object);
1693 // Post-initialize object
1694 object->addedToEnvironment(dtime_s);
1696 // Add static data to block
1697 if(object->isStaticAllowed())
1699 // Add static object to active static list of the block
1700 v3f objectpos = object->getBasePosition();
1701 StaticObject s_obj(object, objectpos);
1702 // Add to the block where the object is located in
1703 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1704 MapBlock *block = m_map->emergeBlock(blockpos);
1706 block->m_static_objects.m_active[object->getId()] = s_obj;
1707 object->m_static_exists = true;
1708 object->m_static_block = blockpos;
1711 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1712 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1714 v3s16 p = floatToInt(objectpos, BS);
1715 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1716 <<"could not emerge block for storing id="<<object->getId()
1717 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1721 return object->getId();
1725 Remove objects that satisfy (isGone() && m_known_by_count==0)
1727 void ServerEnvironment::removeRemovedObjects()
1729 std::vector<u16> objects_to_remove;
1730 for (auto &ao_it : m_active_objects) {
1731 u16 id = ao_it.first;
1732 ServerActiveObject* obj = ao_it.second;
1734 // This shouldn't happen but check it
1736 errorstream << "ServerEnvironment::removeRemovedObjects(): "
1737 << "NULL object found. id=" << id << std::endl;
1738 objects_to_remove.push_back(id);
1743 We will handle objects marked for removal or deactivation
1749 Delete static data from block if removed
1751 if (obj->m_pending_removal)
1752 deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1754 // If still known by clients, don't actually remove. On some future
1755 // invocation this will be 0, which is when removal will continue.
1756 if(obj->m_known_by_count > 0)
1760 Move static data from active to stored if deactivated
1762 if (!obj->m_pending_removal && obj->m_static_exists) {
1763 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1765 std::map<u16, StaticObject>::iterator i =
1766 block->m_static_objects.m_active.find(id);
1767 if (i != block->m_static_objects.m_active.end()) {
1768 block->m_static_objects.m_stored.push_back(i->second);
1769 block->m_static_objects.m_active.erase(id);
1770 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1771 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1773 warningstream << "ServerEnvironment::removeRemovedObjects(): "
1774 << "id=" << id << " m_static_exists=true but "
1775 << "static data doesn't actually exist in "
1776 << PP(obj->m_static_block) << std::endl;
1779 infostream << "Failed to emerge block from which an object to "
1780 << "be deactivated was loaded from. id=" << id << std::endl;
1784 // Tell the object about removal
1785 obj->removingFromEnvironment();
1786 // Deregister in scripting api
1787 m_script->removeObjectReference(obj);
1790 if(obj->environmentDeletes())
1793 objects_to_remove.push_back(id);
1795 // Remove references from m_active_objects
1796 for (u16 i : objects_to_remove) {
1797 m_active_objects.erase(i);
1801 static void print_hexdump(std::ostream &o, const std::string &data)
1803 const int linelength = 16;
1804 for(int l=0; ; l++){
1805 int i0 = linelength * l;
1806 bool at_end = false;
1807 int thislinelength = linelength;
1808 if(i0 + thislinelength > (int)data.size()){
1809 thislinelength = data.size() - i0;
1812 for(int di=0; di<linelength; di++){
1815 if(di<thislinelength)
1816 snprintf(buf, 4, "%.2x ", data[i]);
1818 snprintf(buf, 4, " ");
1822 for(int di=0; di<thislinelength; di++){
1836 Convert stored objects from blocks near the players to active.
1838 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1843 // Ignore if no stored objects (to not set changed flag)
1844 if(block->m_static_objects.m_stored.empty())
1847 verbosestream<<"ServerEnvironment::activateObjects(): "
1848 <<"activating objects of block "<<PP(block->getPos())
1849 <<" ("<<block->m_static_objects.m_stored.size()
1850 <<" objects)"<<std::endl;
1851 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1853 errorstream<<"suspiciously large amount of objects detected: "
1854 <<block->m_static_objects.m_stored.size()<<" in "
1855 <<PP(block->getPos())
1856 <<"; removing all of them."<<std::endl;
1857 // Clear stored list
1858 block->m_static_objects.m_stored.clear();
1859 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1860 MOD_REASON_TOO_MANY_OBJECTS);
1864 // Activate stored objects
1865 std::vector<StaticObject> new_stored;
1866 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1867 // Create an active object from the data
1868 ServerActiveObject *obj = ServerActiveObject::create
1869 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1870 // If couldn't create object, store static data back.
1872 errorstream<<"ServerEnvironment::activateObjects(): "
1873 <<"failed to create active object from static object "
1874 <<"in block "<<PP(s_obj.pos/BS)
1875 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1876 print_hexdump(verbosestream, s_obj.data);
1878 new_stored.push_back(s_obj);
1881 verbosestream<<"ServerEnvironment::activateObjects(): "
1882 <<"activated static object pos="<<PP(s_obj.pos/BS)
1883 <<" type="<<(int)s_obj.type<<std::endl;
1884 // This will also add the object to the active static list
1885 addActiveObjectRaw(obj, false, dtime_s);
1888 // Clear stored list
1889 block->m_static_objects.m_stored.clear();
1890 // Add leftover failed stuff to stored list
1891 for (const StaticObject &s_obj : new_stored) {
1892 block->m_static_objects.m_stored.push_back(s_obj);
1896 Note: Block hasn't really been modified here.
1897 The objects have just been activated and moved from the stored
1898 static list to the active static list.
1899 As such, the block is essentially the same.
1900 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1901 Otherwise there would be a huge amount of unnecessary I/O.
1906 Convert objects that are not standing inside active blocks to static.
1908 If m_known_by_count != 0, active object is not deleted, but static
1909 data is still updated.
1911 If force_delete is set, active object is deleted nevertheless. It
1912 shall only be set so in the destructor of the environment.
1914 If block wasn't generated (not in memory or on disk),
1916 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1918 std::vector<u16> objects_to_remove;
1919 for (auto &ao_it : m_active_objects) {
1920 // force_delete might be overriden per object
1921 bool force_delete = _force_delete;
1923 ServerActiveObject* obj = ao_it.second;
1926 // Do not deactivate if static data creation not allowed
1927 if(!force_delete && !obj->isStaticAllowed())
1930 // removeRemovedObjects() is responsible for these
1931 if(!force_delete && obj->isGone())
1934 u16 id = ao_it.first;
1935 v3f objectpos = obj->getBasePosition();
1937 // The block in which the object resides in
1938 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1940 // If object's static data is stored in a deactivated block and object
1941 // is actually located in an active block, re-save to the block in
1942 // which the object is actually located in.
1944 obj->m_static_exists &&
1945 !m_active_blocks.contains(obj->m_static_block) &&
1946 m_active_blocks.contains(blockpos_o))
1948 // Delete from block where object was located
1949 deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
1951 StaticObject s_obj(obj, objectpos);
1952 // Save to block where object is located
1953 saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
1958 // If block is still active, don't remove
1959 if(!force_delete && m_active_blocks.contains(blockpos_o))
1962 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
1963 << "deactivating object id=" << id << " on inactive block "
1964 << PP(blockpos_o) << std::endl;
1966 // If known by some client, don't immediately delete.
1967 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1970 Update the static data
1972 if (obj->isStaticAllowed()) {
1973 // Create new static object
1974 StaticObject s_obj(obj, objectpos);
1976 bool stays_in_same_block = false;
1977 bool data_changed = true;
1979 // Check if static data has changed considerably
1980 if (obj->m_static_exists) {
1981 if (obj->m_static_block == blockpos_o)
1982 stays_in_same_block = true;
1984 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1987 std::map<u16, StaticObject>::iterator n =
1988 block->m_static_objects.m_active.find(id);
1989 if (n != block->m_static_objects.m_active.end()) {
1990 StaticObject static_old = n->second;
1992 float save_movem = obj->getMinimumSavedMovement();
1994 if (static_old.data == s_obj.data &&
1995 (static_old.pos - objectpos).getLength() < save_movem)
1996 data_changed = false;
1998 warningstream << "ServerEnvironment::deactivateFarObjects(): "
1999 << "id=" << id << " m_static_exists=true but "
2000 << "static data doesn't actually exist in "
2001 << PP(obj->m_static_block) << std::endl;
2007 While changes are always saved, blocks are only marked as modified
2008 if the object has moved or different staticdata. (see above)
2010 bool shall_be_written = (!stays_in_same_block || data_changed);
2011 u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2013 // Delete old static object
2014 deleteStaticFromBlock(obj, id, reason, false);
2016 // Add to the block where the object is located in
2017 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2018 u16 store_id = pending_delete ? id : 0;
2019 if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2020 force_delete = true;
2024 If known by some client, set pending deactivation.
2025 Otherwise delete it immediately.
2027 if(pending_delete && !force_delete)
2029 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2030 << "object id=" << id << " is known by clients"
2031 << "; not deleting yet" << std::endl;
2033 obj->m_pending_deactivation = true;
2036 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2037 << "object id=" << id << " is not known by clients"
2038 << "; deleting" << std::endl;
2040 // Tell the object about removal
2041 obj->removingFromEnvironment();
2042 // Deregister in scripting api
2043 m_script->removeObjectReference(obj);
2045 // Delete active object
2046 if(obj->environmentDeletes())
2048 // Id to be removed from m_active_objects
2049 objects_to_remove.push_back(id);
2052 // Remove references from m_active_objects
2053 for (u16 i : objects_to_remove) {
2054 m_active_objects.erase(i);
2058 void ServerEnvironment::deleteStaticFromBlock(
2059 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2061 if (!obj->m_static_exists)
2066 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2068 block = m_map->emergeBlock(obj->m_static_block, false);
2071 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2072 << " when deleting static data of object from it. id=" << id << std::endl;
2076 block->m_static_objects.remove(id);
2077 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2078 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2080 obj->m_static_exists = false;
2083 bool ServerEnvironment::saveStaticToBlock(
2084 v3s16 blockpos, u16 store_id,
2085 ServerActiveObject *obj, const StaticObject &s_obj,
2088 MapBlock *block = nullptr;
2090 block = m_map->emergeBlock(blockpos);
2091 } catch (InvalidPositionException &e) {
2092 // Handled via NULL pointer
2093 // NOTE: emergeBlock's failure is usually determined by it
2094 // actually returning NULL
2098 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2099 << " when saving static data of object to it. id=" << store_id << std::endl;
2102 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2103 warningstream << "ServerEnv: Trying to store id = " << store_id
2104 << " statically but block " << PP(blockpos)
2105 << " already contains "
2106 << block->m_static_objects.m_stored.size()
2107 << " objects." << std::endl;
2111 block->m_static_objects.insert(store_id, s_obj);
2112 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2113 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2115 obj->m_static_exists = true;
2116 obj->m_static_block = blockpos;
2121 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2122 const std::string &savedir, const Settings &conf)
2125 if (name == "sqlite3")
2126 return new PlayerDatabaseSQLite3(savedir);
2128 if (name == "dummy")
2129 return new Database_Dummy();
2131 if (name == "postgresql") {
2132 std::string connect_string;
2133 conf.getNoEx("pgsql_player_connection", connect_string);
2134 return new PlayerDatabasePostgreSQL(connect_string);
2137 if (name == "files")
2138 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2140 throw BaseException(std::string("Database backend ") + name + " not supported.");
2143 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2144 const Settings &cmd_args)
2146 std::string migrate_to = cmd_args.get("migrate-players");
2148 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2149 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2150 errorstream << "Cannot read world.mt!" << std::endl;
2154 if (!world_mt.exists("player_backend")) {
2155 errorstream << "Please specify your current backend in world.mt:"
2157 << " player_backend = {files|sqlite3|postgresql}"
2162 std::string backend = world_mt.get("player_backend");
2163 if (backend == migrate_to) {
2164 errorstream << "Cannot migrate: new backend is same"
2165 << " as the old one" << std::endl;
2169 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2172 if (backend == "files") {
2173 // Create backup directory
2174 fs::CreateDir(players_backup_path);
2178 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2179 game_params.world_path, world_mt);
2180 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2181 game_params.world_path, world_mt);
2183 std::vector<std::string> player_list;
2184 srcdb->listPlayers(player_list);
2185 for (std::vector<std::string>::const_iterator it = player_list.begin();
2186 it != player_list.end(); ++it) {
2187 actionstream << "Migrating player " << it->c_str() << std::endl;
2188 RemotePlayer player(it->c_str(), NULL);
2189 PlayerSAO playerSAO(NULL, &player, 15000, false);
2191 srcdb->loadPlayer(&player, &playerSAO);
2193 playerSAO.finalize(&player, std::set<std::string>());
2194 player.setPlayerSAO(&playerSAO);
2196 dstdb->savePlayer(&player);
2198 // For files source, move player files to backup dir
2199 if (backend == "files") {
2201 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2202 players_backup_path + DIR_DELIM + (*it));
2206 actionstream << "Successfully migrated " << player_list.size() << " players"
2208 world_mt.set("player_backend", migrate_to);
2209 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2210 errorstream << "Failed to update world.mt!" << std::endl;
2212 actionstream << "world.mt updated" << std::endl;
2214 // When migration is finished from file backend, remove players directory if empty
2215 if (backend == "files") {
2216 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2223 } catch (BaseException &e) {
2224 errorstream << "An error occured during migration: " << e.what() << std::endl;