3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "serverenvironment.h"
21 #include "content_sao.h"
26 #include "nodemetadata.h"
31 #include "remoteplayer.h"
32 #include "scripting_server.h"
34 #include "util/serialize.h"
35 #include "util/basic_macros.h"
36 #include "util/pointedthing.h"
37 #include "threading/mutex_auto_lock.h"
39 #include "gameparams.h"
40 #include "database/database-dummy.h"
41 #include "database/database-files.h"
42 #include "database/database-sqlite3.h"
44 #include "database/database-postgresql.h"
48 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
50 // A number that is much smaller than the timeout for particle spawners should/could ever be
51 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
57 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
60 // Initialize timer to random value to spread processing
61 float itv = abm->getTriggerInterval();
62 itv = MYMAX(0.001, itv); // No less than 1ms
63 int minval = MYMAX(-0.51*itv, -60); // Clamp to
64 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
65 timer = myrand_range(minval, maxval);
72 void LBMContentMapping::deleteContents()
74 for (auto &it : lbm_list) {
79 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
81 // Add the lbm_def to the LBMContentMapping.
82 // Unknown names get added to the global NameIdMapping.
83 INodeDefManager *nodedef = gamedef->ndef();
85 lbm_list.push_back(lbm_def);
87 for (const std::string &nodeTrigger: lbm_def->trigger_contents) {
88 std::vector<content_t> c_ids;
89 bool found = nodedef->getIds(nodeTrigger, c_ids);
91 content_t c_id = gamedef->allocateUnknownNodeId(nodeTrigger);
92 if (c_id == CONTENT_IGNORE) {
93 // Seems it can't be allocated.
94 warningstream << "Could not internalize node name \"" << nodeTrigger
95 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
98 c_ids.push_back(c_id);
101 for (content_t c_id : c_ids) {
102 map[c_id].push_back(lbm_def);
107 const std::vector<LoadingBlockModifierDef *> *
108 LBMContentMapping::lookup(content_t c) const
110 lbm_map::const_iterator it = map.find(c);
113 // This first dereferences the iterator, returning
114 // a std::vector<LoadingBlockModifierDef *>
115 // reference, then we convert it to a pointer.
116 return &(it->second);
119 LBMManager::~LBMManager()
121 for (auto &m_lbm_def : m_lbm_defs) {
122 delete m_lbm_def.second;
125 for (auto &it : m_lbm_lookup) {
126 (it.second).deleteContents();
130 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
132 // Precondition, in query mode the map isn't used anymore
133 FATAL_ERROR_IF(m_query_mode,
134 "attempted to modify LBMManager in query mode");
136 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
137 throw ModError("Error adding LBM \"" + lbm_def->name +
138 "\": Does not follow naming conventions: "
139 "Only characters [a-z0-9_:] are allowed.");
142 m_lbm_defs[lbm_def->name] = lbm_def;
145 void LBMManager::loadIntroductionTimes(const std::string ×,
146 IGameDef *gamedef, u32 now)
151 // Storing it in a map first instead of
152 // handling the stuff directly in the loop
153 // removes all duplicate entries.
154 // TODO make this std::unordered_map
155 std::map<std::string, u32> introduction_times;
158 The introduction times string consists of name~time entries,
159 with each entry terminated by a semicolon. The time is decimal.
164 while ((idx_new = times.find(';', idx)) != std::string::npos) {
165 std::string entry = times.substr(idx, idx_new - idx);
166 std::vector<std::string> components = str_split(entry, '~');
167 if (components.size() != 2)
168 throw SerializationError("Introduction times entry \""
169 + entry + "\" requires exactly one '~'!");
170 const std::string &name = components[0];
171 u32 time = from_string<u32>(components[1]);
172 introduction_times[name] = time;
176 // Put stuff from introduction_times into m_lbm_lookup
177 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
178 it != introduction_times.end(); ++it) {
179 const std::string &name = it->first;
180 u32 time = it->second;
182 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
183 m_lbm_defs.find(name);
184 if (def_it == m_lbm_defs.end()) {
185 // This seems to be an LBM entry for
186 // an LBM we haven't loaded. Discard it.
189 LoadingBlockModifierDef *lbm_def = def_it->second;
190 if (lbm_def->run_at_every_load) {
191 // This seems to be an LBM entry for
192 // an LBM that runs at every load.
193 // Don't add it just yet.
197 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
199 // Erase the entry so that we know later
200 // what elements didn't get put into m_lbm_lookup
201 m_lbm_defs.erase(name);
204 // Now also add the elements from m_lbm_defs to m_lbm_lookup
205 // that weren't added in the previous step.
206 // They are introduced first time to this world,
207 // or are run at every load (introducement time hardcoded to U32_MAX).
209 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
210 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
212 for (auto &m_lbm_def : m_lbm_defs) {
213 if (m_lbm_def.second->run_at_every_load) {
214 lbms_running_always.addLBM(m_lbm_def.second, gamedef);
216 lbms_we_introduce_now.addLBM(m_lbm_def.second, gamedef);
220 // Clear the list, so that we don't delete remaining elements
221 // twice in the destructor
225 std::string LBMManager::createIntroductionTimesString()
227 // Precondition, we must be in query mode
228 FATAL_ERROR_IF(!m_query_mode,
229 "attempted to query on non fully set up LBMManager");
231 std::ostringstream oss;
232 for (const auto &it : m_lbm_lookup) {
234 const std::vector<LoadingBlockModifierDef *> &lbm_list = it.second.lbm_list;
235 for (const auto &lbm_def : lbm_list) {
236 // Don't add if the LBM runs at every load,
237 // then introducement time is hardcoded
238 // and doesn't need to be stored
239 if (lbm_def->run_at_every_load)
241 oss << lbm_def->name << "~" << time << ";";
247 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
249 // Precondition, we need m_lbm_lookup to be initialized
250 FATAL_ERROR_IF(!m_query_mode,
251 "attempted to query on non fully set up LBMManager");
252 v3s16 pos_of_block = block->getPosRelative();
256 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
257 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
258 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
259 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
261 n = block->getNodeNoEx(pos);
263 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
264 iit != m_lbm_lookup.end(); ++iit) {
265 const std::vector<LoadingBlockModifierDef *> *lbm_list =
266 iit->second.lookup(c);
269 for (auto lbmdef : *lbm_list) {
270 lbmdef->trigger(env, pos + pos_of_block, n);
280 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
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 ServerMap &map = getServerMap();
571 if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) {
572 actionstream << "Respawn position for player \""
573 << player->getName() << "\" outside limits, resetting" << std::endl;
574 playersao->setBasePosition(m_server->findSpawnPos());
578 // Add player to environment
581 /* Clean up old HUD elements from previous sessions */
584 /* Add object to environment */
585 addActiveObject(playersao);
590 void ServerEnvironment::saveMeta()
592 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
594 // Open file and serialize
595 std::ostringstream ss(std::ios_base::binary);
598 args.setU64("game_time", m_game_time);
599 args.setU64("time_of_day", getTimeOfDay());
600 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
601 args.setU64("lbm_introduction_times_version", 1);
602 args.set("lbm_introduction_times",
603 m_lbm_mgr.createIntroductionTimesString());
604 args.setU64("day_count", m_day_count);
608 if(!fs::safeWriteToFile(path, ss.str()))
610 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
612 throw SerializationError("Couldn't save env meta");
616 void ServerEnvironment::loadMeta()
618 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
620 // Open file and deserialize
621 std::ifstream is(path.c_str(), std::ios_base::binary);
623 infostream << "ServerEnvironment::loadMeta(): Failed to open "
624 << path << std::endl;
625 throw SerializationError("Couldn't load env meta");
630 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
631 throw SerializationError("ServerEnvironment::loadMeta(): "
632 "EnvArgsEnd not found!");
636 m_game_time = args.getU64("game_time");
637 } catch (SettingNotFoundException &e) {
638 // Getting this is crucial, otherwise timestamps are useless
639 throw SerializationError("Couldn't load env meta game_time");
642 setTimeOfDay(args.exists("time_of_day") ?
643 // set day to early morning by default
644 args.getU64("time_of_day") : 5250);
646 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
647 // If missing, do as if clearObjects was never called
648 args.getU64("last_clear_objects_time") : 0;
650 std::string lbm_introduction_times;
652 u64 ver = args.getU64("lbm_introduction_times_version");
654 lbm_introduction_times = args.get("lbm_introduction_times");
656 infostream << "ServerEnvironment::loadMeta(): Non-supported"
657 << " introduction time version " << ver << std::endl;
659 } catch (SettingNotFoundException &e) {
660 // No problem, this is expected. Just continue with an empty string
662 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
664 m_day_count = args.exists("day_count") ?
665 args.getU64("day_count") : 0;
668 void ServerEnvironment::loadDefaultMeta()
670 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
675 ActiveBlockModifier *abm;
677 std::vector<content_t> required_neighbors;
678 bool check_required_neighbors; // false if required_neighbors is known to be empty
684 ServerEnvironment *m_env;
685 std::vector<std::vector<ActiveABM> *> m_aabms;
687 ABMHandler(std::vector<ABMWithState> &abms,
688 float dtime_s, ServerEnvironment *env,
694 INodeDefManager *ndef = env->getGameDef()->ndef();
695 for (ABMWithState &abmws : abms) {
696 ActiveBlockModifier *abm = abmws.abm;
697 float trigger_interval = abm->getTriggerInterval();
698 if(trigger_interval < 0.001)
699 trigger_interval = 0.001;
700 float actual_interval = dtime_s;
702 abmws.timer += dtime_s;
703 if(abmws.timer < trigger_interval)
705 abmws.timer -= trigger_interval;
706 actual_interval = trigger_interval;
708 float chance = abm->getTriggerChance();
713 if (abm->getSimpleCatchUp()) {
714 float intervals = actual_interval / trigger_interval;
717 aabm.chance = chance / intervals;
721 aabm.chance = chance;
725 const std::vector<std::string> &required_neighbors_s =
726 abm->getRequiredNeighbors();
727 for (const std::string &required_neighbor_s : required_neighbors_s) {
728 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
730 aabm.check_required_neighbors = !required_neighbors_s.empty();
733 const std::vector<std::string> &contents_s = abm->getTriggerContents();
734 for (const std::string &content_s : contents_s) {
735 std::vector<content_t> ids;
736 ndef->getIds(content_s, ids);
737 for (content_t c : ids) {
738 if (c >= m_aabms.size())
739 m_aabms.resize(c + 256, NULL);
741 m_aabms[c] = new std::vector<ActiveABM>;
742 m_aabms[c]->push_back(aabm);
750 for (auto &aabms : m_aabms)
754 // Find out how many objects the given block and its neighbours contain.
755 // Returns the number of objects in the block, and also in 'wider' the
756 // number of objects in the block and all its neighbours. The latter
757 // may an estimate if any neighbours are unloaded.
758 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
761 u32 wider_unknown_count = 0;
762 for(s16 x=-1; x<=1; x++)
763 for(s16 y=-1; y<=1; y++)
764 for(s16 z=-1; z<=1; z++)
766 MapBlock *block2 = map->getBlockNoCreateNoEx(
767 block->getPos() + v3s16(x,y,z));
769 wider_unknown_count++;
772 wider += block2->m_static_objects.m_active.size()
773 + block2->m_static_objects.m_stored.size();
776 u32 active_object_count = block->m_static_objects.m_active.size();
777 u32 wider_known_count = 3*3*3 - wider_unknown_count;
778 wider += wider_unknown_count * wider / wider_known_count;
779 return active_object_count;
782 void apply(MapBlock *block)
784 if(m_aabms.empty() || block->isDummy())
787 ServerMap *map = &m_env->getServerMap();
789 u32 active_object_count_wider;
790 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
791 m_env->m_added_objects = 0;
794 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
795 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
796 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
798 const MapNode &n = block->getNodeUnsafe(p0);
799 content_t c = n.getContent();
801 if (c >= m_aabms.size() || !m_aabms[c])
804 v3s16 p = p0 + block->getPosRelative();
805 for (ActiveABM &aabm : *m_aabms[c]) {
806 if (myrand() % aabm.chance != 0)
810 if (aabm.check_required_neighbors) {
812 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
813 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
814 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
819 if (block->isValidPosition(p1)) {
820 // if the neighbor is found on the same map block
821 // get it straight from there
822 const MapNode &n = block->getNodeUnsafe(p1);
825 // otherwise consult the map
826 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
829 if (CONTAINS(aabm.required_neighbors, c))
832 // No required neighbor found
837 // Call all the trigger variations
838 aabm.abm->trigger(m_env, p, n);
839 aabm.abm->trigger(m_env, p, n,
840 active_object_count, active_object_count_wider);
842 // Count surrounding objects again if the abms added any
843 if(m_env->m_added_objects > 0) {
844 active_object_count = countObjects(block, map, active_object_count_wider);
845 m_env->m_added_objects = 0;
852 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
854 // Reset usage timer immediately, otherwise a block that becomes active
855 // again at around the same time as it would normally be unloaded will
856 // get unloaded incorrectly. (I think this still leaves a small possibility
857 // of a race condition between this and server::AsyncRunStep, which only
858 // some kind of synchronisation will fix, but it at least reduces the window
859 // of opportunity for it to break from seconds to nanoseconds)
860 block->resetUsageTimer();
862 // Get time difference
864 u32 stamp = block->getTimestamp();
865 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
866 dtime_s = m_game_time - stamp;
867 dtime_s += additional_dtime;
869 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
870 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
872 // Remove stored static objects if clearObjects was called since block's timestamp
873 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
874 block->m_static_objects.m_stored.clear();
875 // do not set changed flag to avoid unnecessary mapblock writes
878 // Set current time as timestamp
879 block->setTimestampNoChangedFlag(m_game_time);
881 /*infostream<<"ServerEnvironment::activateBlock(): block is "
882 <<dtime_s<<" seconds old."<<std::endl;*/
884 // Activate stored objects
885 activateObjects(block, dtime_s);
887 /* Handle LoadingBlockModifiers */
888 m_lbm_mgr.applyLBMs(this, block, stamp);
891 std::vector<NodeTimer> elapsed_timers =
892 block->m_node_timers.step((float)dtime_s);
893 if (!elapsed_timers.empty()) {
895 for (const NodeTimer &elapsed_timer : elapsed_timers) {
896 n = block->getNodeNoEx(elapsed_timer.position);
897 v3s16 p = elapsed_timer.position + block->getPosRelative();
898 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
899 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
900 elapsed_timer.position));
905 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
907 m_abms.emplace_back(abm);
910 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
912 m_lbm_mgr.addLBMDef(lbm);
915 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
917 INodeDefManager *ndef = m_server->ndef();
918 MapNode n_old = m_map->getNodeNoEx(p);
920 const ContentFeatures &cf_old = ndef->get(n_old);
923 if (cf_old.has_on_destruct)
924 m_script->node_on_destruct(p, n_old);
927 if (!m_map->addNodeWithEvent(p, n))
930 // Update active VoxelManipulator if a mapgen thread
931 m_map->updateVManip(p);
933 // Call post-destructor
934 if (cf_old.has_after_destruct)
935 m_script->node_after_destruct(p, n_old);
937 // Retrieve node content features
938 // if new node is same as old, reuse old definition to prevent a lookup
939 const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
942 if (cf_new.has_on_construct)
943 m_script->node_on_construct(p, n);
948 bool ServerEnvironment::removeNode(v3s16 p)
950 INodeDefManager *ndef = m_server->ndef();
951 MapNode n_old = m_map->getNodeNoEx(p);
954 if (ndef->get(n_old).has_on_destruct)
955 m_script->node_on_destruct(p, n_old);
958 // This is slightly optimized compared to addNodeWithEvent(air)
959 if (!m_map->removeNodeWithEvent(p))
962 // Update active VoxelManipulator if a mapgen thread
963 m_map->updateVManip(p);
965 // Call post-destructor
966 if (ndef->get(n_old).has_after_destruct)
967 m_script->node_after_destruct(p, n_old);
969 // Air doesn't require constructor
973 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
975 if (!m_map->addNodeWithEvent(p, n, false))
978 // Update active VoxelManipulator if a mapgen thread
979 m_map->updateVManip(p);
984 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
987 for (auto &activeObject : m_active_objects) {
988 ServerActiveObject* obj = activeObject.second;
989 u16 id = activeObject.first;
990 v3f objectpos = obj->getBasePosition();
991 if (objectpos.getDistanceFrom(pos) > radius)
993 objects.push_back(id);
997 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
999 infostream << "ServerEnvironment::clearObjects(): "
1000 << "Removing all active objects" << std::endl;
1001 std::vector<u16> objects_to_remove;
1002 for (auto &it : m_active_objects) {
1004 ServerActiveObject* obj = it.second;
1005 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1008 // Delete static object if block is loaded
1009 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1011 // If known by some client, don't delete immediately
1012 if (obj->m_known_by_count > 0) {
1013 obj->m_pending_removal = true;
1017 // Tell the object about removal
1018 obj->removingFromEnvironment();
1019 // Deregister in scripting api
1020 m_script->removeObjectReference(obj);
1022 // Delete active object
1023 if (obj->environmentDeletes())
1025 // Id to be removed from m_active_objects
1026 objects_to_remove.push_back(id);
1029 // Remove references from m_active_objects
1030 for (u16 i : objects_to_remove) {
1031 m_active_objects.erase(i);
1034 // Get list of loaded blocks
1035 std::vector<v3s16> loaded_blocks;
1036 infostream << "ServerEnvironment::clearObjects(): "
1037 << "Listing all loaded blocks" << std::endl;
1038 m_map->listAllLoadedBlocks(loaded_blocks);
1039 infostream << "ServerEnvironment::clearObjects(): "
1040 << "Done listing all loaded blocks: "
1041 << loaded_blocks.size()<<std::endl;
1043 // Get list of loadable blocks
1044 std::vector<v3s16> loadable_blocks;
1045 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1046 infostream << "ServerEnvironment::clearObjects(): "
1047 << "Listing all loadable blocks" << std::endl;
1048 m_map->listAllLoadableBlocks(loadable_blocks);
1049 infostream << "ServerEnvironment::clearObjects(): "
1050 << "Done listing all loadable blocks: "
1051 << loadable_blocks.size() << std::endl;
1053 loadable_blocks = loaded_blocks;
1056 actionstream << "ServerEnvironment::clearObjects(): "
1057 << "Now clearing objects in " << loadable_blocks.size()
1058 << " blocks" << std::endl;
1060 // Grab a reference on each loaded block to avoid unloading it
1061 for (v3s16 p : loaded_blocks) {
1062 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1063 assert(block != NULL);
1067 // Remove objects in all loadable blocks
1068 u32 unload_interval = U32_MAX;
1069 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1070 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1071 unload_interval = MYMAX(unload_interval, 1);
1073 u32 report_interval = loadable_blocks.size() / 10;
1074 u32 num_blocks_checked = 0;
1075 u32 num_blocks_cleared = 0;
1076 u32 num_objs_cleared = 0;
1077 for (auto i = loadable_blocks.begin();
1078 i != loadable_blocks.end(); ++i) {
1080 MapBlock *block = m_map->emergeBlock(p, false);
1082 errorstream << "ServerEnvironment::clearObjects(): "
1083 << "Failed to emerge block " << PP(p) << std::endl;
1086 u32 num_stored = block->m_static_objects.m_stored.size();
1087 u32 num_active = block->m_static_objects.m_active.size();
1088 if (num_stored != 0 || num_active != 0) {
1089 block->m_static_objects.m_stored.clear();
1090 block->m_static_objects.m_active.clear();
1091 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1092 MOD_REASON_CLEAR_ALL_OBJECTS);
1093 num_objs_cleared += num_stored + num_active;
1094 num_blocks_cleared++;
1096 num_blocks_checked++;
1098 if (report_interval != 0 &&
1099 num_blocks_checked % report_interval == 0) {
1100 float percent = 100.0 * (float)num_blocks_checked /
1101 loadable_blocks.size();
1102 actionstream << "ServerEnvironment::clearObjects(): "
1103 << "Cleared " << num_objs_cleared << " objects"
1104 << " in " << num_blocks_cleared << " blocks ("
1105 << percent << "%)" << std::endl;
1107 if (num_blocks_checked % unload_interval == 0) {
1108 m_map->unloadUnreferencedBlocks();
1111 m_map->unloadUnreferencedBlocks();
1113 // Drop references that were added above
1114 for (v3s16 p : loaded_blocks) {
1115 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1120 m_last_clear_objects_time = m_game_time;
1122 actionstream << "ServerEnvironment::clearObjects(): "
1123 << "Finished: Cleared " << num_objs_cleared << " objects"
1124 << " in " << num_blocks_cleared << " blocks" << std::endl;
1127 void ServerEnvironment::step(float dtime)
1129 /* Step time of day */
1130 stepTimeOfDay(dtime);
1133 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1134 // really matter that much.
1135 static thread_local const float server_step =
1136 g_settings->getFloat("dedicated_server_step");
1137 m_recommended_send_interval = server_step;
1143 m_game_time_fraction_counter += dtime;
1144 u32 inc_i = (u32)m_game_time_fraction_counter;
1145 m_game_time += inc_i;
1146 m_game_time_fraction_counter -= (float)inc_i;
1153 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1154 for (RemotePlayer *player : m_players) {
1155 // Ignore disconnected players
1156 if (player->getPeerId() == PEER_ID_INEXISTENT)
1160 player->move(dtime, this, 100 * BS);
1165 Manage active block list
1167 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1168 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1170 Get player block positions
1172 std::vector<PlayerSAO*> players;
1173 for (RemotePlayer *player: m_players) {
1174 // Ignore disconnected players
1175 if (player->getPeerId() == PEER_ID_INEXISTENT)
1178 PlayerSAO *playersao = player->getPlayerSAO();
1181 players.push_back(playersao);
1185 Update list of active blocks, collecting changes
1187 // use active_object_send_range_blocks since that is max distance
1188 // for active objects sent the client anyway
1189 static thread_local const s16 active_object_range =
1190 g_settings->getS16("active_object_send_range_blocks");
1191 static thread_local const s16 active_block_range =
1192 g_settings->getS16("active_block_range");
1193 std::set<v3s16> blocks_removed;
1194 std::set<v3s16> blocks_added;
1195 m_active_blocks.update(players, active_block_range, active_object_range,
1196 blocks_removed, blocks_added);
1199 Handle removed blocks
1202 // Convert active objects that are no more in active blocks to static
1203 deactivateFarObjects(false);
1205 for (const v3s16 &p: blocks_removed) {
1206 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1210 // Set current time as timestamp (and let it set ChangedFlag)
1211 block->setTimestamp(m_game_time);
1218 for (const v3s16 &p: blocks_added) {
1219 MapBlock *block = m_map->getBlockOrEmerge(p);
1221 m_active_blocks.m_list.erase(p);
1222 m_active_blocks.m_abm_list.erase(p);
1226 activateBlock(block);
1231 Mess around in active blocks
1233 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1234 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1236 float dtime = m_cache_nodetimer_interval;
1238 for (const v3s16 &p: m_active_blocks.m_list) {
1239 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1243 // Reset block usage timer
1244 block->resetUsageTimer();
1246 // Set current time as timestamp
1247 block->setTimestampNoChangedFlag(m_game_time);
1248 // If time has changed much from the one on disk,
1249 // set block to be saved when it is unloaded
1250 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1251 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1252 MOD_REASON_BLOCK_EXPIRED);
1255 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1256 if (!elapsed_timers.empty()) {
1259 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1260 n = block->getNodeNoEx(elapsed_timer.position);
1261 p2 = elapsed_timer.position + block->getPosRelative();
1262 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1263 block->setNodeTimer(NodeTimer(
1264 elapsed_timer.timeout, 0, elapsed_timer.position));
1271 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1273 if (m_active_block_interval_overload_skip > 0) {
1274 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1275 m_active_block_interval_overload_skip--;
1278 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1279 TimeTaker timer("modify in active blocks per interval");
1281 // Initialize handling of ActiveBlockModifiers
1282 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1284 for (const v3s16 &p : m_active_blocks.m_abm_list) {
1285 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1289 // Set current time as timestamp
1290 block->setTimestampNoChangedFlag(m_game_time);
1292 /* Handle ActiveBlockModifiers */
1293 abmhandler.apply(block);
1296 u32 time_ms = timer.stop(true);
1297 u32 max_time_ms = 200;
1298 if (time_ms > max_time_ms) {
1299 warningstream<<"active block modifiers took "
1300 <<time_ms<<"ms (longer than "
1301 <<max_time_ms<<"ms)"<<std::endl;
1302 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1307 Step script environment (run global on_step())
1309 m_script->environment_Step(dtime);
1315 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1316 //TimeTaker timer("Step active objects");
1318 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1320 // This helps the objects to send data at the same time
1321 bool send_recommended = false;
1322 m_send_recommended_timer += dtime;
1323 if(m_send_recommended_timer > getSendRecommendedInterval())
1325 m_send_recommended_timer -= getSendRecommendedInterval();
1326 send_recommended = true;
1329 for (auto &ao_it : m_active_objects) {
1330 ServerActiveObject* obj = ao_it.second;
1335 obj->step(dtime, send_recommended);
1336 // Read messages from object
1337 while (!obj->m_messages_out.empty()) {
1338 m_active_object_messages.push(obj->m_messages_out.front());
1339 obj->m_messages_out.pop();
1345 Manage active objects
1347 if (m_object_management_interval.step(dtime, 0.5)) {
1348 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1349 removeRemovedObjects();
1353 Manage particle spawner expiration
1355 if (m_particle_management_interval.step(dtime, 1.0)) {
1356 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1357 i != m_particle_spawners.end(); ) {
1358 //non expiring spawners
1359 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1365 if (i->second <= 0.f)
1366 m_particle_spawners.erase(i++);
1373 u32 ServerEnvironment::addParticleSpawner(float exptime)
1375 // Timers with lifetime 0 do not expire
1376 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1379 for (;;) { // look for unused particlespawner id
1381 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1382 if (f == m_particle_spawners.end()) {
1383 m_particle_spawners[id] = time;
1390 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1392 u32 id = addParticleSpawner(exptime);
1393 m_particle_spawner_attachments[id] = attached_id;
1394 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1395 obj->attachParticleSpawner(id);
1400 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1402 m_particle_spawners.erase(id);
1403 const auto &it = m_particle_spawner_attachments.find(id);
1404 if (it != m_particle_spawner_attachments.end()) {
1405 u16 obj_id = it->second;
1406 ServerActiveObject *sao = getActiveObject(obj_id);
1407 if (sao != NULL && remove_from_object) {
1408 sao->detachParticleSpawner(id);
1410 m_particle_spawner_attachments.erase(id);
1414 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1416 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1417 return (n != m_active_objects.end() ? n->second : NULL);
1420 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1425 return objects.find(id) == objects.end();
1428 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1430 //try to reuse id's as late as possible
1431 static u16 last_used_id = 0;
1432 u16 startid = last_used_id;
1436 if(isFreeServerActiveObjectId(last_used_id, objects))
1437 return last_used_id;
1439 if(last_used_id == startid)
1444 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1446 assert(object); // Pre-condition
1448 u16 id = addActiveObjectRaw(object, true, 0);
1453 Finds out what new objects have been added to
1454 inside a radius around a position
1456 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1458 std::set<u16> ¤t_objects,
1459 std::queue<u16> &added_objects)
1461 f32 radius_f = radius * BS;
1462 f32 player_radius_f = player_radius * BS;
1464 if (player_radius_f < 0)
1465 player_radius_f = 0;
1467 Go through the object list,
1468 - discard removed/deactivated objects,
1469 - discard objects that are too far away,
1470 - discard objects that are found in current_objects.
1471 - add remaining objects to added_objects
1473 for (auto &ao_it : m_active_objects) {
1474 u16 id = ao_it.first;
1477 ServerActiveObject *object = ao_it.second;
1481 if (object->isGone())
1484 f32 distance_f = object->getBasePosition().
1485 getDistanceFrom(playersao->getBasePosition());
1486 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1487 // Discard if too far
1488 if (distance_f > player_radius_f && player_radius_f != 0)
1490 } else if (distance_f > radius_f)
1493 // Discard if already on current_objects
1494 std::set<u16>::iterator n;
1495 n = current_objects.find(id);
1496 if(n != current_objects.end())
1498 // Add to added_objects
1499 added_objects.push(id);
1504 Finds out what objects have been removed from
1505 inside a radius around a position
1507 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1509 std::set<u16> ¤t_objects,
1510 std::queue<u16> &removed_objects)
1512 f32 radius_f = radius * BS;
1513 f32 player_radius_f = player_radius * BS;
1515 if (player_radius_f < 0)
1516 player_radius_f = 0;
1518 Go through current_objects; object is removed if:
1519 - object is not found in m_active_objects (this is actually an
1520 error condition; objects should be removed only after all clients
1521 have been informed about removal), or
1522 - object is to be removed or deactivated, or
1523 - object is too far away
1525 for (u16 id : current_objects) {
1526 ServerActiveObject *object = getActiveObject(id);
1528 if (object == NULL) {
1529 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1530 << " object in current_objects is NULL" << std::endl;
1531 removed_objects.push(id);
1535 if (object->isGone()) {
1536 removed_objects.push(id);
1540 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1541 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1542 if (distance_f <= player_radius_f || player_radius_f == 0)
1544 } else if (distance_f <= radius_f)
1547 // Object is no longer visible
1548 removed_objects.push(id);
1552 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1553 v3s16 blockpos, bool static_exists, v3s16 static_block)
1555 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1559 for (auto &so_it : block->m_static_objects.m_active) {
1560 // Get the ServerActiveObject counterpart to this StaticObject
1561 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it.first);
1562 if (ao_it == m_active_objects.end()) {
1563 // If this ever happens, there must be some kind of nasty bug.
1564 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1565 "Object from MapBlock::m_static_objects::m_active not found "
1566 "in m_active_objects";
1570 ServerActiveObject *sao = ao_it->second;
1571 sao->m_static_exists = static_exists;
1572 sao->m_static_block = static_block;
1576 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1578 if(m_active_object_messages.empty())
1579 return ActiveObjectMessage(0);
1581 ActiveObjectMessage message = m_active_object_messages.front();
1582 m_active_object_messages.pop();
1586 void ServerEnvironment::getSelectedActiveObjects(
1587 const core::line3d<f32> &shootline_on_map,
1588 std::vector<PointedThing> &objects)
1590 std::vector<u16> objectIds;
1591 getObjectsInsideRadius(objectIds, shootline_on_map.start,
1592 shootline_on_map.getLength() + 10.0f);
1593 const v3f line_vector = shootline_on_map.getVector();
1595 for (u16 objectId : objectIds) {
1596 ServerActiveObject* obj = getActiveObject(objectId);
1598 aabb3f selection_box;
1599 if (!obj->getSelectionBox(&selection_box))
1602 v3f pos = obj->getBasePosition();
1604 aabb3f offsetted_box(selection_box.MinEdge + pos,
1605 selection_box.MaxEdge + pos);
1607 v3f current_intersection;
1608 v3s16 current_normal;
1609 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1610 ¤t_intersection, ¤t_normal)) {
1611 objects.emplace_back(
1612 (s16) objectId, current_intersection, current_normal,
1613 (current_intersection - shootline_on_map.start).getLengthSQ());
1619 ************ Private methods *************
1622 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1623 bool set_changed, u32 dtime_s)
1625 assert(object); // Pre-condition
1626 if(object->getId() == 0){
1627 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1630 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1631 <<"no free ids available"<<std::endl;
1632 if(object->environmentDeletes())
1636 object->setId(new_id);
1639 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1640 <<"supplied with id "<<object->getId()<<std::endl;
1643 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1644 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1645 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1646 if(object->environmentDeletes())
1651 if (objectpos_over_limit(object->getBasePosition())) {
1652 v3f p = object->getBasePosition();
1653 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1654 << "object position (" << p.X << "," << p.Y << "," << p.Z
1655 << ") outside maximum range" << std::endl;
1656 if (object->environmentDeletes())
1661 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1662 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1664 m_active_objects[object->getId()] = object;
1666 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1667 <<"Added id="<<object->getId()<<"; there are now "
1668 <<m_active_objects.size()<<" active objects."
1671 // Register reference in scripting api (must be done before post-init)
1672 m_script->addObjectReference(object);
1673 // Post-initialize object
1674 object->addedToEnvironment(dtime_s);
1676 // Add static data to block
1677 if(object->isStaticAllowed())
1679 // Add static object to active static list of the block
1680 v3f objectpos = object->getBasePosition();
1681 std::string staticdata;
1682 object->getStaticData(&staticdata);
1683 StaticObject s_obj(object->getType(), objectpos, staticdata);
1684 // Add to the block where the object is located in
1685 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1686 MapBlock *block = m_map->emergeBlock(blockpos);
1688 block->m_static_objects.m_active[object->getId()] = s_obj;
1689 object->m_static_exists = true;
1690 object->m_static_block = blockpos;
1693 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1694 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1696 v3s16 p = floatToInt(objectpos, BS);
1697 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1698 <<"could not emerge block for storing id="<<object->getId()
1699 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1703 return object->getId();
1707 Remove objects that satisfy (isGone() && m_known_by_count==0)
1709 void ServerEnvironment::removeRemovedObjects()
1711 std::vector<u16> objects_to_remove;
1712 for (auto &ao_it : m_active_objects) {
1713 u16 id = ao_it.first;
1714 ServerActiveObject* obj = ao_it.second;
1716 // This shouldn't happen but check it
1718 errorstream << "ServerEnvironment::removeRemovedObjects(): "
1719 << "NULL object found. id=" << id << std::endl;
1720 objects_to_remove.push_back(id);
1725 We will handle objects marked for removal or deactivation
1731 Delete static data from block if removed
1733 if (obj->m_pending_removal)
1734 deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1736 // If still known by clients, don't actually remove. On some future
1737 // invocation this will be 0, which is when removal will continue.
1738 if(obj->m_known_by_count > 0)
1742 Move static data from active to stored if deactivated
1744 if (!obj->m_pending_removal && obj->m_static_exists) {
1745 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1747 std::map<u16, StaticObject>::iterator i =
1748 block->m_static_objects.m_active.find(id);
1749 if (i != block->m_static_objects.m_active.end()) {
1750 block->m_static_objects.m_stored.push_back(i->second);
1751 block->m_static_objects.m_active.erase(id);
1752 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1753 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1755 warningstream << "ServerEnvironment::removeRemovedObjects(): "
1756 << "id=" << id << " m_static_exists=true but "
1757 << "static data doesn't actually exist in "
1758 << PP(obj->m_static_block) << std::endl;
1761 infostream << "Failed to emerge block from which an object to "
1762 << "be deactivated was loaded from. id=" << id << std::endl;
1766 // Tell the object about removal
1767 obj->removingFromEnvironment();
1768 // Deregister in scripting api
1769 m_script->removeObjectReference(obj);
1772 if(obj->environmentDeletes())
1775 objects_to_remove.push_back(id);
1777 // Remove references from m_active_objects
1778 for (u16 i : objects_to_remove) {
1779 m_active_objects.erase(i);
1783 static void print_hexdump(std::ostream &o, const std::string &data)
1785 const int linelength = 16;
1786 for(int l=0; ; l++){
1787 int i0 = linelength * l;
1788 bool at_end = false;
1789 int thislinelength = linelength;
1790 if(i0 + thislinelength > (int)data.size()){
1791 thislinelength = data.size() - i0;
1794 for(int di=0; di<linelength; di++){
1797 if(di<thislinelength)
1798 snprintf(buf, 4, "%.2x ", data[i]);
1800 snprintf(buf, 4, " ");
1804 for(int di=0; di<thislinelength; di++){
1818 Convert stored objects from blocks near the players to active.
1820 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1825 // Ignore if no stored objects (to not set changed flag)
1826 if(block->m_static_objects.m_stored.empty())
1829 verbosestream<<"ServerEnvironment::activateObjects(): "
1830 <<"activating objects of block "<<PP(block->getPos())
1831 <<" ("<<block->m_static_objects.m_stored.size()
1832 <<" objects)"<<std::endl;
1833 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1835 errorstream<<"suspiciously large amount of objects detected: "
1836 <<block->m_static_objects.m_stored.size()<<" in "
1837 <<PP(block->getPos())
1838 <<"; removing all of them."<<std::endl;
1839 // Clear stored list
1840 block->m_static_objects.m_stored.clear();
1841 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1842 MOD_REASON_TOO_MANY_OBJECTS);
1846 // Activate stored objects
1847 std::vector<StaticObject> new_stored;
1848 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1849 // Create an active object from the data
1850 ServerActiveObject *obj = ServerActiveObject::create
1851 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1852 // If couldn't create object, store static data back.
1854 errorstream<<"ServerEnvironment::activateObjects(): "
1855 <<"failed to create active object from static object "
1856 <<"in block "<<PP(s_obj.pos/BS)
1857 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1858 print_hexdump(verbosestream, s_obj.data);
1860 new_stored.push_back(s_obj);
1863 verbosestream<<"ServerEnvironment::activateObjects(): "
1864 <<"activated static object pos="<<PP(s_obj.pos/BS)
1865 <<" type="<<(int)s_obj.type<<std::endl;
1866 // This will also add the object to the active static list
1867 addActiveObjectRaw(obj, false, dtime_s);
1870 // Clear stored list
1871 block->m_static_objects.m_stored.clear();
1872 // Add leftover failed stuff to stored list
1873 for (const StaticObject &s_obj : new_stored) {
1874 block->m_static_objects.m_stored.push_back(s_obj);
1878 Note: Block hasn't really been modified here.
1879 The objects have just been activated and moved from the stored
1880 static list to the active static list.
1881 As such, the block is essentially the same.
1882 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1883 Otherwise there would be a huge amount of unnecessary I/O.
1888 Convert objects that are not standing inside active blocks to static.
1890 If m_known_by_count != 0, active object is not deleted, but static
1891 data is still updated.
1893 If force_delete is set, active object is deleted nevertheless. It
1894 shall only be set so in the destructor of the environment.
1896 If block wasn't generated (not in memory or on disk),
1898 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1900 std::vector<u16> objects_to_remove;
1901 for (auto &ao_it : m_active_objects) {
1902 // force_delete might be overriden per object
1903 bool force_delete = _force_delete;
1905 ServerActiveObject* obj = ao_it.second;
1908 // Do not deactivate if static data creation not allowed
1909 if(!force_delete && !obj->isStaticAllowed())
1912 // removeRemovedObjects() is responsible for these
1913 if(!force_delete && obj->isGone())
1916 u16 id = ao_it.first;
1917 v3f objectpos = obj->getBasePosition();
1919 // The block in which the object resides in
1920 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1922 // If object's static data is stored in a deactivated block and object
1923 // is actually located in an active block, re-save to the block in
1924 // which the object is actually located in.
1926 obj->m_static_exists &&
1927 !m_active_blocks.contains(obj->m_static_block) &&
1928 m_active_blocks.contains(blockpos_o))
1930 // Delete from block where object was located
1931 deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
1933 std::string staticdata_new;
1934 obj->getStaticData(&staticdata_new);
1935 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1936 // Save to block where object is located
1937 saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
1942 // If block is still active, don't remove
1943 if(!force_delete && m_active_blocks.contains(blockpos_o))
1946 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
1947 << "deactivating object id=" << id << " on inactive block "
1948 << PP(blockpos_o) << std::endl;
1950 // If known by some client, don't immediately delete.
1951 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1954 Update the static data
1956 if(obj->isStaticAllowed())
1958 // Create new static object
1959 std::string staticdata_new;
1960 obj->getStaticData(&staticdata_new);
1961 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1963 bool stays_in_same_block = false;
1964 bool data_changed = true;
1966 // Check if static data has changed considerably
1967 if (obj->m_static_exists) {
1968 if (obj->m_static_block == blockpos_o)
1969 stays_in_same_block = true;
1971 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1974 std::map<u16, StaticObject>::iterator n =
1975 block->m_static_objects.m_active.find(id);
1976 if (n != block->m_static_objects.m_active.end()) {
1977 StaticObject static_old = n->second;
1979 float save_movem = obj->getMinimumSavedMovement();
1981 if (static_old.data == staticdata_new &&
1982 (static_old.pos - objectpos).getLength() < save_movem)
1983 data_changed = false;
1985 warningstream << "ServerEnvironment::deactivateFarObjects(): "
1986 << "id=" << id << " m_static_exists=true but "
1987 << "static data doesn't actually exist in "
1988 << PP(obj->m_static_block) << std::endl;
1994 While changes are always saved, blocks are only marked as modified
1995 if the object has moved or different staticdata. (see above)
1997 bool shall_be_written = (!stays_in_same_block || data_changed);
1998 u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2000 // Delete old static object
2001 deleteStaticFromBlock(obj, id, reason, false);
2003 // Add to the block where the object is located in
2004 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2005 u16 store_id = pending_delete ? id : 0;
2006 if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2007 force_delete = true;
2011 If known by some client, set pending deactivation.
2012 Otherwise delete it immediately.
2014 if(pending_delete && !force_delete)
2016 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2017 << "object id=" << id << " is known by clients"
2018 << "; not deleting yet" << std::endl;
2020 obj->m_pending_deactivation = true;
2023 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2024 << "object id=" << id << " is not known by clients"
2025 << "; deleting" << std::endl;
2027 // Tell the object about removal
2028 obj->removingFromEnvironment();
2029 // Deregister in scripting api
2030 m_script->removeObjectReference(obj);
2032 // Delete active object
2033 if(obj->environmentDeletes())
2035 // Id to be removed from m_active_objects
2036 objects_to_remove.push_back(id);
2039 // Remove references from m_active_objects
2040 for (u16 i : objects_to_remove) {
2041 m_active_objects.erase(i);
2045 void ServerEnvironment::deleteStaticFromBlock(
2046 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2048 if (!obj->m_static_exists)
2053 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2055 block = m_map->emergeBlock(obj->m_static_block, false);
2058 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2059 << " when deleting static data of object from it. id=" << id << std::endl;
2063 block->m_static_objects.remove(id);
2064 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2065 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2067 obj->m_static_exists = false;
2070 bool ServerEnvironment::saveStaticToBlock(
2071 v3s16 blockpos, u16 store_id,
2072 ServerActiveObject *obj, const StaticObject &s_obj,
2075 MapBlock *block = nullptr;
2077 block = m_map->emergeBlock(blockpos);
2078 } catch (InvalidPositionException &e) {
2079 // Handled via NULL pointer
2080 // NOTE: emergeBlock's failure is usually determined by it
2081 // actually returning NULL
2085 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2086 << " when saving static data of object to it. id=" << store_id << std::endl;
2089 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2090 warningstream << "ServerEnv: Trying to store id = " << store_id
2091 << " statically but block " << PP(blockpos)
2092 << " already contains "
2093 << block->m_static_objects.m_stored.size()
2094 << " objects." << std::endl;
2098 block->m_static_objects.insert(store_id, s_obj);
2099 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2100 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2102 obj->m_static_exists = true;
2103 obj->m_static_block = blockpos;
2108 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2109 const std::string &savedir, const Settings &conf)
2112 if (name == "sqlite3")
2113 return new PlayerDatabaseSQLite3(savedir);
2115 if (name == "dummy")
2116 return new Database_Dummy();
2118 if (name == "postgresql") {
2119 std::string connect_string;
2120 conf.getNoEx("pgsql_player_connection", connect_string);
2121 return new PlayerDatabasePostgreSQL(connect_string);
2124 if (name == "files")
2125 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2127 throw BaseException(std::string("Database backend ") + name + " not supported.");
2130 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2131 const Settings &cmd_args)
2133 std::string migrate_to = cmd_args.get("migrate-players");
2135 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2136 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2137 errorstream << "Cannot read world.mt!" << std::endl;
2141 if (!world_mt.exists("player_backend")) {
2142 errorstream << "Please specify your current backend in world.mt:"
2144 << " player_backend = {files|sqlite3|postgresql}"
2149 std::string backend = world_mt.get("player_backend");
2150 if (backend == migrate_to) {
2151 errorstream << "Cannot migrate: new backend is same"
2152 << " as the old one" << std::endl;
2156 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2159 if (backend == "files") {
2160 // Create backup directory
2161 fs::CreateDir(players_backup_path);
2165 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2166 game_params.world_path, world_mt);
2167 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2168 game_params.world_path, world_mt);
2170 std::vector<std::string> player_list;
2171 srcdb->listPlayers(player_list);
2172 for (std::vector<std::string>::const_iterator it = player_list.begin();
2173 it != player_list.end(); ++it) {
2174 actionstream << "Migrating player " << it->c_str() << std::endl;
2175 RemotePlayer player(it->c_str(), NULL);
2176 PlayerSAO playerSAO(NULL, &player, 15000, false);
2178 srcdb->loadPlayer(&player, &playerSAO);
2180 playerSAO.finalize(&player, std::set<std::string>());
2181 player.setPlayerSAO(&playerSAO);
2183 dstdb->savePlayer(&player);
2185 // For files source, move player files to backup dir
2186 if (backend == "files") {
2188 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2189 players_backup_path + DIR_DELIM + (*it));
2193 actionstream << "Successfully migrated " << player_list.size() << " players"
2195 world_mt.set("player_backend", migrate_to);
2196 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2197 errorstream << "Failed to update world.mt!" << std::endl;
2199 actionstream << "world.mt updated" << std::endl;
2201 // When migration is finished from file backend, remove players directory if empty
2202 if (backend == "files") {
2203 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2210 } catch (BaseException &e) {
2211 errorstream << "An error occured during migration: " << e.what() << std::endl;