3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "serverenvironment.h"
21 #include "content_sao.h"
26 #include "nodemetadata.h"
31 #include "remoteplayer.h"
32 #include "scripting_server.h"
34 #include "util/serialize.h"
35 #include "util/basic_macros.h"
36 #include "util/pointedthing.h"
37 #include "threading/mutex_auto_lock.h"
39 #include "gameparams.h"
40 #include "database-dummy.h"
41 #include "database-files.h"
42 #include "database-sqlite3.h"
44 #include "database-postgresql.h"
47 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
49 // A number that is much smaller than the timeout for particle spawners should/could ever be
50 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
56 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
59 // Initialize timer to random value to spread processing
60 float itv = abm->getTriggerInterval();
61 itv = MYMAX(0.001, itv); // No less than 1ms
62 int minval = MYMAX(-0.51*itv, -60); // Clamp to
63 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
64 timer = myrand_range(minval, maxval);
71 void LBMContentMapping::deleteContents()
73 for (auto &it : lbm_list) {
78 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
80 // Add the lbm_def to the LBMContentMapping.
81 // Unknown names get added to the global NameIdMapping.
82 INodeDefManager *nodedef = gamedef->ndef();
84 lbm_list.push_back(lbm_def);
86 for (const std::string &nodeTrigger: lbm_def->trigger_contents) {
87 std::set<content_t> c_ids;
88 bool found = nodedef->getIds(nodeTrigger, c_ids);
90 content_t c_id = gamedef->allocateUnknownNodeId(nodeTrigger);
91 if (c_id == CONTENT_IGNORE) {
92 // Seems it can't be allocated.
93 warningstream << "Could not internalize node name \"" << nodeTrigger
94 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
100 for (content_t c_id : c_ids) {
101 map[c_id].push_back(lbm_def);
106 const std::vector<LoadingBlockModifierDef *> *
107 LBMContentMapping::lookup(content_t c) const
109 lbm_map::const_iterator it = map.find(c);
112 // This first dereferences the iterator, returning
113 // a std::vector<LoadingBlockModifierDef *>
114 // reference, then we convert it to a pointer.
115 return &(it->second);
118 LBMManager::~LBMManager()
120 for (auto &m_lbm_def : m_lbm_defs) {
121 delete m_lbm_def.second;
124 for (auto &it : m_lbm_lookup) {
125 (it.second).deleteContents();
129 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
131 // Precondition, in query mode the map isn't used anymore
132 FATAL_ERROR_IF(m_query_mode,
133 "attempted to modify LBMManager in query mode");
135 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
136 throw ModError("Error adding LBM \"" + lbm_def->name +
137 "\": Does not follow naming conventions: "
138 "Only characters [a-z0-9_:] are allowed.");
141 m_lbm_defs[lbm_def->name] = lbm_def;
144 void LBMManager::loadIntroductionTimes(const std::string ×,
145 IGameDef *gamedef, u32 now)
150 // Storing it in a map first instead of
151 // handling the stuff directly in the loop
152 // removes all duplicate entries.
153 // TODO make this std::unordered_map
154 std::map<std::string, u32> introduction_times;
157 The introduction times string consists of name~time entries,
158 with each entry terminated by a semicolon. The time is decimal.
163 while ((idx_new = times.find(';', idx)) != std::string::npos) {
164 std::string entry = times.substr(idx, idx_new - idx);
165 std::vector<std::string> components = str_split(entry, '~');
166 if (components.size() != 2)
167 throw SerializationError("Introduction times entry \""
168 + entry + "\" requires exactly one '~'!");
169 const std::string &name = components[0];
170 u32 time = from_string<u32>(components[1]);
171 introduction_times[name] = time;
175 // Put stuff from introduction_times into m_lbm_lookup
176 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
177 it != introduction_times.end(); ++it) {
178 const std::string &name = it->first;
179 u32 time = it->second;
181 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
182 m_lbm_defs.find(name);
183 if (def_it == m_lbm_defs.end()) {
184 // This seems to be an LBM entry for
185 // an LBM we haven't loaded. Discard it.
188 LoadingBlockModifierDef *lbm_def = def_it->second;
189 if (lbm_def->run_at_every_load) {
190 // This seems to be an LBM entry for
191 // an LBM that runs at every load.
192 // Don't add it just yet.
196 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
198 // Erase the entry so that we know later
199 // what elements didn't get put into m_lbm_lookup
200 m_lbm_defs.erase(name);
203 // Now also add the elements from m_lbm_defs to m_lbm_lookup
204 // that weren't added in the previous step.
205 // They are introduced first time to this world,
206 // or are run at every load (introducement time hardcoded to U32_MAX).
208 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
209 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
211 for (auto &m_lbm_def : m_lbm_defs) {
212 if (m_lbm_def.second->run_at_every_load) {
213 lbms_running_always.addLBM(m_lbm_def.second, gamedef);
215 lbms_we_introduce_now.addLBM(m_lbm_def.second, gamedef);
219 // Clear the list, so that we don't delete remaining elements
220 // twice in the destructor
224 std::string LBMManager::createIntroductionTimesString()
226 // Precondition, we must be in query mode
227 FATAL_ERROR_IF(!m_query_mode,
228 "attempted to query on non fully set up LBMManager");
230 std::ostringstream oss;
231 for (const auto &it : m_lbm_lookup) {
233 const std::vector<LoadingBlockModifierDef *> &lbm_list = it.second.lbm_list;
234 for (const auto &lbm_def : lbm_list) {
235 // Don't add if the LBM runs at every load,
236 // then introducement time is hardcoded
237 // and doesn't need to be stored
238 if (lbm_def->run_at_every_load)
240 oss << lbm_def->name << "~" << time << ";";
246 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
248 // Precondition, we need m_lbm_lookup to be initialized
249 FATAL_ERROR_IF(!m_query_mode,
250 "attempted to query on non fully set up LBMManager");
251 v3s16 pos_of_block = block->getPosRelative();
255 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
256 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
257 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
258 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
260 n = block->getNodeNoEx(pos);
262 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
263 iit != m_lbm_lookup.end(); ++iit) {
264 const std::vector<LoadingBlockModifierDef *> *lbm_list =
265 iit->second.lookup(c);
268 for (auto lbmdef : *lbm_list) {
269 lbmdef->trigger(env, pos + pos_of_block, n);
279 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
282 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
283 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
284 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
287 if (p.getDistanceFrom(p0) <= r) {
294 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
296 std::set<v3s16> &blocks_removed,
297 std::set<v3s16> &blocks_added)
302 std::set<v3s16> newlist = m_forceloaded_list;
303 for (const v3s16 &active_position : active_positions) {
304 fillRadiusBlock(active_position, radius, newlist);
308 Find out which blocks on the old list are not on the new list
310 // Go through old list
311 for (v3s16 p : m_list) {
312 // If not on new list, it's been removed
313 if (newlist.find(p) == newlist.end())
314 blocks_removed.insert(p);
318 Find out which blocks on the new list are not on the old list
320 // Go through new list
321 for (v3s16 p : newlist) {
322 // If not on old list, it's been added
323 if(m_list.find(p) == m_list.end())
324 blocks_added.insert(p);
331 for (v3s16 p : newlist) {
340 ServerEnvironment::ServerEnvironment(ServerMap *map,
341 ServerScripting *scriptIface, Server *server,
342 const std::string &path_world):
345 m_script(scriptIface),
347 m_path_world(path_world)
349 // Determine which database backend to use
350 std::string conf_path = path_world + DIR_DELIM + "world.mt";
352 bool succeeded = conf.readConfigFile(conf_path.c_str());
353 if (!succeeded || !conf.exists("player_backend")) {
354 // fall back to files
355 conf.set("player_backend", "files");
356 warningstream << "/!\\ You are using old player file backend. "
357 << "This backend is deprecated and will be removed in next release /!\\"
358 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
359 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
361 if (!conf.updateConfigFile(conf_path.c_str())) {
362 errorstream << "ServerEnvironment::ServerEnvironment(): "
363 << "Failed to update world.mt!" << std::endl;
368 conf.getNoEx("player_backend", name);
369 m_player_database = openPlayerDatabase(name, path_world, conf);
372 ServerEnvironment::~ServerEnvironment()
374 // Clear active block list.
375 // This makes the next one delete all active objects.
376 m_active_blocks.clear();
378 // Convert all objects to static and delete the active objects
379 deactivateFarObjects(true);
384 // Delete ActiveBlockModifiers
385 for (ABMWithState &m_abm : m_abms) {
389 // Deallocate players
390 for (RemotePlayer *m_player : m_players) {
394 delete m_player_database;
397 Map & ServerEnvironment::getMap()
402 ServerMap & ServerEnvironment::getServerMap()
407 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
409 for (RemotePlayer *player : m_players) {
410 if (player->peer_id == peer_id)
416 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
418 for (RemotePlayer *player : m_players) {
419 if (strcmp(player->getName(), name) == 0)
425 void ServerEnvironment::addPlayer(RemotePlayer *player)
427 DSTACK(FUNCTION_NAME);
429 Check that peer_ids are unique.
430 Also check that names are unique.
431 Exception: there can be multiple players with peer_id=0
433 // If peer id is non-zero, it has to be unique.
434 if (player->peer_id != 0)
435 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
436 // Name has to be unique.
437 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
439 m_players.push_back(player);
442 void ServerEnvironment::removePlayer(RemotePlayer *player)
444 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
445 it != m_players.end(); ++it) {
446 if ((*it) == player) {
454 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
456 return m_player_database->removePlayer(name);
459 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
461 float distance = pos1.getDistanceFrom(pos2);
463 //calculate normalized direction vector
464 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
465 (pos2.Y - pos1.Y)/distance,
466 (pos2.Z - pos1.Z)/distance);
468 //find out if there's a node on path between pos1 and pos2
469 for (float i = 1; i < distance; i += stepsize) {
470 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
471 normalized_vector.Y * i,
472 normalized_vector.Z * i) +pos1,BS);
474 MapNode n = getMap().getNodeNoEx(pos);
476 if(n.param0 != CONTENT_AIR) {
486 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
487 const std::string &str_reason, bool reconnect)
489 for (RemotePlayer *player : m_players) {
490 m_server->DenyAccessVerCompliant(player->peer_id,
491 player->protocol_version, reason, str_reason, reconnect);
495 void ServerEnvironment::saveLoadedPlayers()
497 std::string players_path = m_path_world + DIR_DELIM + "players";
498 fs::CreateDir(players_path);
500 for (RemotePlayer *player : m_players) {
501 if (player->checkModified() || (player->getPlayerSAO() &&
502 player->getPlayerSAO()->extendedAttributesModified())) {
504 m_player_database->savePlayer(player);
505 } catch (DatabaseException &e) {
506 errorstream << "Failed to save player " << player->getName() << " exception: "
507 << e.what() << std::endl;
514 void ServerEnvironment::savePlayer(RemotePlayer *player)
517 m_player_database->savePlayer(player);
518 } catch (DatabaseException &e) {
519 errorstream << "Failed to save player " << player->getName() << " exception: "
520 << e.what() << std::endl;
525 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
526 u16 peer_id, bool is_singleplayer)
528 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
529 // Create player if it doesn't exist
530 if (!m_player_database->loadPlayer(player, playersao)) {
532 // Set player position
533 infostream << "Server: Finding spawn place for player \""
534 << player->getName() << "\"" << std::endl;
535 playersao->setBasePosition(m_server->findSpawnPos());
537 // Make sure the player is saved
538 player->setModified(true);
540 // If the player exists, ensure that they respawn inside legal bounds
541 // This fixes an assert crash when the player can't be added
542 // to the environment
543 ServerMap &map = getServerMap();
544 if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) {
545 actionstream << "Respawn position for player \""
546 << player->getName() << "\" outside limits, resetting" << std::endl;
547 playersao->setBasePosition(m_server->findSpawnPos());
551 // Add player to environment
554 /* Clean up old HUD elements from previous sessions */
557 /* Add object to environment */
558 addActiveObject(playersao);
563 void ServerEnvironment::saveMeta()
565 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
567 // Open file and serialize
568 std::ostringstream ss(std::ios_base::binary);
571 args.setU64("game_time", m_game_time);
572 args.setU64("time_of_day", getTimeOfDay());
573 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
574 args.setU64("lbm_introduction_times_version", 1);
575 args.set("lbm_introduction_times",
576 m_lbm_mgr.createIntroductionTimesString());
577 args.setU64("day_count", m_day_count);
581 if(!fs::safeWriteToFile(path, ss.str()))
583 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
585 throw SerializationError("Couldn't save env meta");
589 void ServerEnvironment::loadMeta()
591 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
593 // Open file and deserialize
594 std::ifstream is(path.c_str(), std::ios_base::binary);
596 infostream << "ServerEnvironment::loadMeta(): Failed to open "
597 << path << std::endl;
598 throw SerializationError("Couldn't load env meta");
603 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
604 throw SerializationError("ServerEnvironment::loadMeta(): "
605 "EnvArgsEnd not found!");
609 m_game_time = args.getU64("game_time");
610 } catch (SettingNotFoundException &e) {
611 // Getting this is crucial, otherwise timestamps are useless
612 throw SerializationError("Couldn't load env meta game_time");
615 setTimeOfDay(args.exists("time_of_day") ?
616 // set day to early morning by default
617 args.getU64("time_of_day") : 5250);
619 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
620 // If missing, do as if clearObjects was never called
621 args.getU64("last_clear_objects_time") : 0;
623 std::string lbm_introduction_times;
625 u64 ver = args.getU64("lbm_introduction_times_version");
627 lbm_introduction_times = args.get("lbm_introduction_times");
629 infostream << "ServerEnvironment::loadMeta(): Non-supported"
630 << " introduction time version " << ver << std::endl;
632 } catch (SettingNotFoundException &e) {
633 // No problem, this is expected. Just continue with an empty string
635 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
637 m_day_count = args.exists("day_count") ?
638 args.getU64("day_count") : 0;
641 void ServerEnvironment::loadDefaultMeta()
643 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
648 ActiveBlockModifier *abm;
650 std::set<content_t> required_neighbors;
656 ServerEnvironment *m_env;
657 std::vector<std::vector<ActiveABM> *> m_aabms;
659 ABMHandler(std::vector<ABMWithState> &abms,
660 float dtime_s, ServerEnvironment *env,
666 INodeDefManager *ndef = env->getGameDef()->ndef();
667 for (ABMWithState &abmws : abms) {
668 ActiveBlockModifier *abm = abmws.abm;
669 float trigger_interval = abm->getTriggerInterval();
670 if(trigger_interval < 0.001)
671 trigger_interval = 0.001;
672 float actual_interval = dtime_s;
674 abmws.timer += dtime_s;
675 if(abmws.timer < trigger_interval)
677 abmws.timer -= trigger_interval;
678 actual_interval = trigger_interval;
680 float chance = abm->getTriggerChance();
685 if (abm->getSimpleCatchUp()) {
686 float intervals = actual_interval / trigger_interval;
689 aabm.chance = chance / intervals;
693 aabm.chance = chance;
697 const std::set<std::string> &required_neighbors_s =
698 abm->getRequiredNeighbors();
699 for (const std::string &required_neighbor_s : required_neighbors_s) {
700 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
704 const std::set<std::string> &contents_s = abm->getTriggerContents();
705 for (const std::string &content_s : contents_s) {
706 std::set<content_t> ids;
707 ndef->getIds(content_s, ids);
708 for (content_t c : ids) {
709 if (c >= m_aabms.size())
710 m_aabms.resize(c + 256, NULL);
712 m_aabms[c] = new std::vector<ActiveABM>;
713 m_aabms[c]->push_back(aabm);
721 for (auto &aabms : m_aabms)
725 // Find out how many objects the given block and its neighbours contain.
726 // Returns the number of objects in the block, and also in 'wider' the
727 // number of objects in the block and all its neighbours. The latter
728 // may an estimate if any neighbours are unloaded.
729 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
732 u32 wider_unknown_count = 0;
733 for(s16 x=-1; x<=1; x++)
734 for(s16 y=-1; y<=1; y++)
735 for(s16 z=-1; z<=1; z++)
737 MapBlock *block2 = map->getBlockNoCreateNoEx(
738 block->getPos() + v3s16(x,y,z));
740 wider_unknown_count++;
743 wider += block2->m_static_objects.m_active.size()
744 + block2->m_static_objects.m_stored.size();
747 u32 active_object_count = block->m_static_objects.m_active.size();
748 u32 wider_known_count = 3*3*3 - wider_unknown_count;
749 wider += wider_unknown_count * wider / wider_known_count;
750 return active_object_count;
753 void apply(MapBlock *block)
755 if(m_aabms.empty() || block->isDummy())
758 ServerMap *map = &m_env->getServerMap();
760 u32 active_object_count_wider;
761 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
762 m_env->m_added_objects = 0;
765 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
766 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
767 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
769 const MapNode &n = block->getNodeUnsafe(p0);
770 content_t c = n.getContent();
772 if (c >= m_aabms.size() || !m_aabms[c])
775 v3s16 p = p0 + block->getPosRelative();
776 for (ActiveABM &aabm : *m_aabms[c]) {
777 if (myrand() % aabm.chance != 0)
781 if (!aabm.required_neighbors.empty()) {
783 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
784 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
785 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
790 if (block->isValidPosition(p1)) {
791 // if the neighbor is found on the same map block
792 // get it straight from there
793 const MapNode &n = block->getNodeUnsafe(p1);
796 // otherwise consult the map
797 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
800 std::set<content_t>::const_iterator k;
801 k = aabm.required_neighbors.find(c);
802 if(k != aabm.required_neighbors.end()){
806 // No required neighbor found
811 // Call all the trigger variations
812 aabm.abm->trigger(m_env, p, n);
813 aabm.abm->trigger(m_env, p, n,
814 active_object_count, active_object_count_wider);
816 // Count surrounding objects again if the abms added any
817 if(m_env->m_added_objects > 0) {
818 active_object_count = countObjects(block, map, active_object_count_wider);
819 m_env->m_added_objects = 0;
826 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
828 // Reset usage timer immediately, otherwise a block that becomes active
829 // again at around the same time as it would normally be unloaded will
830 // get unloaded incorrectly. (I think this still leaves a small possibility
831 // of a race condition between this and server::AsyncRunStep, which only
832 // some kind of synchronisation will fix, but it at least reduces the window
833 // of opportunity for it to break from seconds to nanoseconds)
834 block->resetUsageTimer();
836 // Get time difference
838 u32 stamp = block->getTimestamp();
839 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
840 dtime_s = m_game_time - stamp;
841 dtime_s += additional_dtime;
843 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
844 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
846 // Remove stored static objects if clearObjects was called since block's timestamp
847 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
848 block->m_static_objects.m_stored.clear();
849 // do not set changed flag to avoid unnecessary mapblock writes
852 // Set current time as timestamp
853 block->setTimestampNoChangedFlag(m_game_time);
855 /*infostream<<"ServerEnvironment::activateBlock(): block is "
856 <<dtime_s<<" seconds old."<<std::endl;*/
858 // Activate stored objects
859 activateObjects(block, dtime_s);
861 /* Handle LoadingBlockModifiers */
862 m_lbm_mgr.applyLBMs(this, block, stamp);
865 std::vector<NodeTimer> elapsed_timers =
866 block->m_node_timers.step((float)dtime_s);
867 if (!elapsed_timers.empty()) {
869 for (const NodeTimer &elapsed_timer : elapsed_timers) {
870 n = block->getNodeNoEx(elapsed_timer.position);
871 v3s16 p = elapsed_timer.position + block->getPosRelative();
872 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
873 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
874 elapsed_timer.position));
878 /* Handle ActiveBlockModifiers */
879 ABMHandler abmhandler(m_abms, dtime_s, this, false);
880 abmhandler.apply(block);
883 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
885 m_abms.emplace_back(abm);
888 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
890 m_lbm_mgr.addLBMDef(lbm);
893 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
895 INodeDefManager *ndef = m_server->ndef();
896 MapNode n_old = m_map->getNodeNoEx(p);
899 if (ndef->get(n_old).has_on_destruct)
900 m_script->node_on_destruct(p, n_old);
903 if (!m_map->addNodeWithEvent(p, n))
906 // Update active VoxelManipulator if a mapgen thread
907 m_map->updateVManip(p);
909 // Call post-destructor
910 if (ndef->get(n_old).has_after_destruct)
911 m_script->node_after_destruct(p, n_old);
914 if (ndef->get(n).has_on_construct)
915 m_script->node_on_construct(p, n);
920 bool ServerEnvironment::removeNode(v3s16 p)
922 INodeDefManager *ndef = m_server->ndef();
923 MapNode n_old = m_map->getNodeNoEx(p);
926 if (ndef->get(n_old).has_on_destruct)
927 m_script->node_on_destruct(p, n_old);
930 // This is slightly optimized compared to addNodeWithEvent(air)
931 if (!m_map->removeNodeWithEvent(p))
934 // Update active VoxelManipulator if a mapgen thread
935 m_map->updateVManip(p);
937 // Call post-destructor
938 if (ndef->get(n_old).has_after_destruct)
939 m_script->node_after_destruct(p, n_old);
941 // Air doesn't require constructor
945 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
947 if (!m_map->addNodeWithEvent(p, n, false))
950 // Update active VoxelManipulator if a mapgen thread
951 m_map->updateVManip(p);
956 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
959 for (auto &activeObject : m_active_objects) {
960 ServerActiveObject* obj = activeObject.second;
961 u16 id = activeObject.first;
962 v3f objectpos = obj->getBasePosition();
963 if (objectpos.getDistanceFrom(pos) > radius)
965 objects.push_back(id);
969 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
971 infostream << "ServerEnvironment::clearObjects(): "
972 << "Removing all active objects" << std::endl;
973 std::vector<u16> objects_to_remove;
974 for (auto &it : m_active_objects) {
975 ServerActiveObject* obj = it.second;
976 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
979 // Delete static object if block is loaded
980 if (obj->m_static_exists) {
981 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
983 block->m_static_objects.remove(id);
984 block->raiseModified(MOD_STATE_WRITE_NEEDED,
985 MOD_REASON_CLEAR_ALL_OBJECTS);
986 obj->m_static_exists = false;
989 // If known by some client, don't delete immediately
990 if (obj->m_known_by_count > 0) {
991 obj->m_pending_deactivation = true;
992 obj->m_removed = true;
996 // Tell the object about removal
997 obj->removingFromEnvironment();
998 // Deregister in scripting api
999 m_script->removeObjectReference(obj);
1001 // Delete active object
1002 if (obj->environmentDeletes())
1004 // Id to be removed from m_active_objects
1005 objects_to_remove.push_back(id);
1008 // Remove references from m_active_objects
1009 for (u16 i : objects_to_remove) {
1010 m_active_objects.erase(i);
1013 // Get list of loaded blocks
1014 std::vector<v3s16> loaded_blocks;
1015 infostream << "ServerEnvironment::clearObjects(): "
1016 << "Listing all loaded blocks" << std::endl;
1017 m_map->listAllLoadedBlocks(loaded_blocks);
1018 infostream << "ServerEnvironment::clearObjects(): "
1019 << "Done listing all loaded blocks: "
1020 << loaded_blocks.size()<<std::endl;
1022 // Get list of loadable blocks
1023 std::vector<v3s16> loadable_blocks;
1024 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1025 infostream << "ServerEnvironment::clearObjects(): "
1026 << "Listing all loadable blocks" << std::endl;
1027 m_map->listAllLoadableBlocks(loadable_blocks);
1028 infostream << "ServerEnvironment::clearObjects(): "
1029 << "Done listing all loadable blocks: "
1030 << loadable_blocks.size() << std::endl;
1032 loadable_blocks = loaded_blocks;
1035 infostream << "ServerEnvironment::clearObjects(): "
1036 << "Now clearing objects in " << loadable_blocks.size()
1037 << " blocks" << std::endl;
1039 // Grab a reference on each loaded block to avoid unloading it
1040 for (v3s16 p : loaded_blocks) {
1041 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1042 assert(block != NULL);
1046 // Remove objects in all loadable blocks
1047 u32 unload_interval = U32_MAX;
1048 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1049 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1050 unload_interval = MYMAX(unload_interval, 1);
1052 u32 report_interval = loadable_blocks.size() / 10;
1053 u32 num_blocks_checked = 0;
1054 u32 num_blocks_cleared = 0;
1055 u32 num_objs_cleared = 0;
1056 for (auto i = loadable_blocks.begin();
1057 i != loadable_blocks.end(); ++i) {
1059 MapBlock *block = m_map->emergeBlock(p, false);
1061 errorstream << "ServerEnvironment::clearObjects(): "
1062 << "Failed to emerge block " << PP(p) << std::endl;
1065 u32 num_stored = block->m_static_objects.m_stored.size();
1066 u32 num_active = block->m_static_objects.m_active.size();
1067 if (num_stored != 0 || num_active != 0) {
1068 block->m_static_objects.m_stored.clear();
1069 block->m_static_objects.m_active.clear();
1070 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1071 MOD_REASON_CLEAR_ALL_OBJECTS);
1072 num_objs_cleared += num_stored + num_active;
1073 num_blocks_cleared++;
1075 num_blocks_checked++;
1077 if (report_interval != 0 &&
1078 num_blocks_checked % report_interval == 0) {
1079 float percent = 100.0 * (float)num_blocks_checked /
1080 loadable_blocks.size();
1081 infostream << "ServerEnvironment::clearObjects(): "
1082 << "Cleared " << num_objs_cleared << " objects"
1083 << " in " << num_blocks_cleared << " blocks ("
1084 << percent << "%)" << std::endl;
1086 if (num_blocks_checked % unload_interval == 0) {
1087 m_map->unloadUnreferencedBlocks();
1090 m_map->unloadUnreferencedBlocks();
1092 // Drop references that were added above
1093 for (v3s16 p : loaded_blocks) {
1094 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1099 m_last_clear_objects_time = m_game_time;
1101 infostream << "ServerEnvironment::clearObjects(): "
1102 << "Finished: Cleared " << num_objs_cleared << " objects"
1103 << " in " << num_blocks_cleared << " blocks" << std::endl;
1106 void ServerEnvironment::step(float dtime)
1108 DSTACK(FUNCTION_NAME);
1110 //TimeTaker timer("ServerEnv step");
1112 /* Step time of day */
1113 stepTimeOfDay(dtime);
1116 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1117 // really matter that much.
1118 static thread_local const float server_step =
1119 g_settings->getFloat("dedicated_server_step");
1120 m_recommended_send_interval = server_step;
1126 m_game_time_fraction_counter += dtime;
1127 u32 inc_i = (u32)m_game_time_fraction_counter;
1128 m_game_time += inc_i;
1129 m_game_time_fraction_counter -= (float)inc_i;
1136 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1137 for (RemotePlayer *player : m_players) {
1138 // Ignore disconnected players
1139 if (player->peer_id == 0)
1143 player->move(dtime, this, 100 * BS);
1148 Manage active block list
1150 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1151 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1153 Get player block positions
1155 std::vector<v3s16> players_blockpos;
1156 for (RemotePlayer *player: m_players) {
1157 // Ignore disconnected players
1158 if (player->peer_id == 0)
1161 PlayerSAO *playersao = player->getPlayerSAO();
1164 players_blockpos.push_back(
1165 getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS)));
1169 Update list of active blocks, collecting changes
1171 static thread_local const s16 active_block_range =
1172 g_settings->getS16("active_block_range");
1173 std::set<v3s16> blocks_removed;
1174 std::set<v3s16> blocks_added;
1175 m_active_blocks.update(players_blockpos, active_block_range,
1176 blocks_removed, blocks_added);
1179 Handle removed blocks
1182 // Convert active objects that are no more in active blocks to static
1183 deactivateFarObjects(false);
1185 for (const v3s16 &p: blocks_removed) {
1186 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1190 // Set current time as timestamp (and let it set ChangedFlag)
1191 block->setTimestamp(m_game_time);
1198 for (const v3s16 &p: blocks_added) {
1199 MapBlock *block = m_map->getBlockOrEmerge(p);
1201 m_active_blocks.m_list.erase(p);
1205 activateBlock(block);
1210 Mess around in active blocks
1212 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1213 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1215 float dtime = m_cache_nodetimer_interval;
1217 for (const v3s16 &p: m_active_blocks.m_list) {
1218 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1222 // Reset block usage timer
1223 block->resetUsageTimer();
1225 // Set current time as timestamp
1226 block->setTimestampNoChangedFlag(m_game_time);
1227 // If time has changed much from the one on disk,
1228 // set block to be saved when it is unloaded
1229 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1230 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1231 MOD_REASON_BLOCK_EXPIRED);
1234 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1235 if (!elapsed_timers.empty()) {
1238 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1239 n = block->getNodeNoEx(elapsed_timer.position);
1240 p2 = elapsed_timer.position + block->getPosRelative();
1241 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1242 block->setNodeTimer(NodeTimer(
1243 elapsed_timer.timeout, 0, elapsed_timer.position));
1250 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1252 if (m_active_block_interval_overload_skip > 0) {
1253 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1254 m_active_block_interval_overload_skip--;
1257 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1258 TimeTaker timer("modify in active blocks per interval");
1260 // Initialize handling of ActiveBlockModifiers
1261 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1263 for (const v3s16 &p : m_active_blocks.m_list) {
1264 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1268 // Set current time as timestamp
1269 block->setTimestampNoChangedFlag(m_game_time);
1271 /* Handle ActiveBlockModifiers */
1272 abmhandler.apply(block);
1275 u32 time_ms = timer.stop(true);
1276 u32 max_time_ms = 200;
1277 if (time_ms > max_time_ms) {
1278 warningstream<<"active block modifiers took "
1279 <<time_ms<<"ms (longer than "
1280 <<max_time_ms<<"ms)"<<std::endl;
1281 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1286 Step script environment (run global on_step())
1288 m_script->environment_Step(dtime);
1294 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1295 //TimeTaker timer("Step active objects");
1297 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1299 // This helps the objects to send data at the same time
1300 bool send_recommended = false;
1301 m_send_recommended_timer += dtime;
1302 if(m_send_recommended_timer > getSendRecommendedInterval())
1304 m_send_recommended_timer -= getSendRecommendedInterval();
1305 send_recommended = true;
1308 for (auto &ao_it : m_active_objects) {
1309 ServerActiveObject* obj = ao_it.second;
1310 // Don't step if is to be removed or stored statically
1311 if(obj->m_removed || obj->m_pending_deactivation)
1314 obj->step(dtime, send_recommended);
1315 // Read messages from object
1316 while(!obj->m_messages_out.empty())
1318 m_active_object_messages.push(
1319 obj->m_messages_out.front());
1320 obj->m_messages_out.pop();
1326 Manage active objects
1328 if (m_object_management_interval.step(dtime, 0.5)) {
1329 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1331 Remove objects that satisfy (m_removed && m_known_by_count==0)
1333 removeRemovedObjects();
1337 Manage particle spawner expiration
1339 if (m_particle_management_interval.step(dtime, 1.0)) {
1340 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1341 i != m_particle_spawners.end(); ) {
1342 //non expiring spawners
1343 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1349 if (i->second <= 0.f)
1350 m_particle_spawners.erase(i++);
1357 u32 ServerEnvironment::addParticleSpawner(float exptime)
1359 // Timers with lifetime 0 do not expire
1360 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1363 for (;;) { // look for unused particlespawner id
1365 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1366 if (f == m_particle_spawners.end()) {
1367 m_particle_spawners[id] = time;
1374 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1376 u32 id = addParticleSpawner(exptime);
1377 m_particle_spawner_attachments[id] = attached_id;
1378 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1379 obj->attachParticleSpawner(id);
1384 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1386 m_particle_spawners.erase(id);
1387 const auto &it = m_particle_spawner_attachments.find(id);
1388 if (it != m_particle_spawner_attachments.end()) {
1389 u16 obj_id = it->second;
1390 ServerActiveObject *sao = getActiveObject(obj_id);
1391 if (sao != NULL && remove_from_object) {
1392 sao->detachParticleSpawner(id);
1394 m_particle_spawner_attachments.erase(id);
1398 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1400 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1401 return (n != m_active_objects.end() ? n->second : NULL);
1404 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1409 return objects.find(id) == objects.end();
1412 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1414 //try to reuse id's as late as possible
1415 static u16 last_used_id = 0;
1416 u16 startid = last_used_id;
1420 if(isFreeServerActiveObjectId(last_used_id, objects))
1421 return last_used_id;
1423 if(last_used_id == startid)
1428 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1430 assert(object); // Pre-condition
1432 u16 id = addActiveObjectRaw(object, true, 0);
1437 Finds out what new objects have been added to
1438 inside a radius around a position
1440 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1442 std::set<u16> ¤t_objects,
1443 std::queue<u16> &added_objects)
1445 f32 radius_f = radius * BS;
1446 f32 player_radius_f = player_radius * BS;
1448 if (player_radius_f < 0)
1449 player_radius_f = 0;
1451 Go through the object list,
1452 - discard m_removed objects,
1453 - discard objects that are too far away,
1454 - discard objects that are found in current_objects.
1455 - add remaining objects to added_objects
1457 for (auto &ao_it : m_active_objects) {
1458 u16 id = ao_it.first;
1461 ServerActiveObject *object = ao_it.second;
1465 // Discard if removed or deactivating
1466 if(object->m_removed || object->m_pending_deactivation)
1469 f32 distance_f = object->getBasePosition().
1470 getDistanceFrom(playersao->getBasePosition());
1471 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1472 // Discard if too far
1473 if (distance_f > player_radius_f && player_radius_f != 0)
1475 } else if (distance_f > radius_f)
1478 // Discard if already on current_objects
1479 std::set<u16>::iterator n;
1480 n = current_objects.find(id);
1481 if(n != current_objects.end())
1483 // Add to added_objects
1484 added_objects.push(id);
1489 Finds out what objects have been removed from
1490 inside a radius around a position
1492 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1494 std::set<u16> ¤t_objects,
1495 std::queue<u16> &removed_objects)
1497 f32 radius_f = radius * BS;
1498 f32 player_radius_f = player_radius * BS;
1500 if (player_radius_f < 0)
1501 player_radius_f = 0;
1503 Go through current_objects; object is removed if:
1504 - object is not found in m_active_objects (this is actually an
1505 error condition; objects should be set m_removed=true and removed
1506 only after all clients have been informed about removal), or
1507 - object has m_removed=true, or
1508 - object is too far away
1510 for (u16 id : current_objects) {
1511 ServerActiveObject *object = getActiveObject(id);
1513 if (object == NULL) {
1514 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1515 << " object in current_objects is NULL" << std::endl;
1516 removed_objects.push(id);
1520 if (object->m_removed || object->m_pending_deactivation) {
1521 removed_objects.push(id);
1525 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1526 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1527 if (distance_f <= player_radius_f || player_radius_f == 0)
1529 } else if (distance_f <= radius_f)
1532 // Object is no longer visible
1533 removed_objects.push(id);
1537 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1538 v3s16 blockpos, bool static_exists, v3s16 static_block)
1540 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1544 for (auto &so_it : block->m_static_objects.m_active) {
1545 // Get the ServerActiveObject counterpart to this StaticObject
1546 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it.first);
1547 if (ao_it == m_active_objects.end()) {
1548 // If this ever happens, there must be some kind of nasty bug.
1549 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1550 "Object from MapBlock::m_static_objects::m_active not found "
1551 "in m_active_objects";
1555 ServerActiveObject *sao = ao_it->second;
1556 sao->m_static_exists = static_exists;
1557 sao->m_static_block = static_block;
1561 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1563 if(m_active_object_messages.empty())
1564 return ActiveObjectMessage(0);
1566 ActiveObjectMessage message = m_active_object_messages.front();
1567 m_active_object_messages.pop();
1571 void ServerEnvironment::getSelectedActiveObjects(
1572 const core::line3d<f32> &shootline_on_map,
1573 std::vector<PointedThing> &objects)
1575 std::vector<u16> objectIds;
1576 getObjectsInsideRadius(objectIds, shootline_on_map.start,
1577 shootline_on_map.getLength() + 10.0f);
1578 const v3f line_vector = shootline_on_map.getVector();
1580 for (u16 objectId : objectIds) {
1581 ServerActiveObject* obj = getActiveObject(objectId);
1583 aabb3f selection_box;
1584 if (!obj->getSelectionBox(&selection_box))
1587 v3f pos = obj->getBasePosition();
1589 aabb3f offsetted_box(selection_box.MinEdge + pos,
1590 selection_box.MaxEdge + pos);
1592 v3f current_intersection;
1593 v3s16 current_normal;
1594 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1595 ¤t_intersection, ¤t_normal)) {
1596 objects.emplace_back(
1597 (s16) objectId, current_intersection, current_normal,
1598 (current_intersection - shootline_on_map.start).getLengthSQ());
1604 ************ Private methods *************
1607 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1608 bool set_changed, u32 dtime_s)
1610 assert(object); // Pre-condition
1611 if(object->getId() == 0){
1612 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1615 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1616 <<"no free ids available"<<std::endl;
1617 if(object->environmentDeletes())
1621 object->setId(new_id);
1624 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1625 <<"supplied with id "<<object->getId()<<std::endl;
1628 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1629 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1630 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1631 if(object->environmentDeletes())
1636 if (objectpos_over_limit(object->getBasePosition())) {
1637 v3f p = object->getBasePosition();
1638 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1639 << "object position (" << p.X << "," << p.Y << "," << p.Z
1640 << ") outside maximum range" << std::endl;
1641 if (object->environmentDeletes())
1646 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1647 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1649 m_active_objects[object->getId()] = object;
1651 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1652 <<"Added id="<<object->getId()<<"; there are now "
1653 <<m_active_objects.size()<<" active objects."
1656 // Register reference in scripting api (must be done before post-init)
1657 m_script->addObjectReference(object);
1658 // Post-initialize object
1659 object->addedToEnvironment(dtime_s);
1661 // Add static data to block
1662 if(object->isStaticAllowed())
1664 // Add static object to active static list of the block
1665 v3f objectpos = object->getBasePosition();
1666 std::string staticdata;
1667 object->getStaticData(&staticdata);
1668 StaticObject s_obj(object->getType(), objectpos, staticdata);
1669 // Add to the block where the object is located in
1670 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1671 MapBlock *block = m_map->emergeBlock(blockpos);
1673 block->m_static_objects.m_active[object->getId()] = s_obj;
1674 object->m_static_exists = true;
1675 object->m_static_block = blockpos;
1678 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1679 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1681 v3s16 p = floatToInt(objectpos, BS);
1682 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1683 <<"could not emerge block for storing id="<<object->getId()
1684 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1688 return object->getId();
1692 Remove objects that satisfy (m_removed && m_known_by_count==0)
1694 void ServerEnvironment::removeRemovedObjects()
1696 std::vector<u16> objects_to_remove;
1697 for (auto &ao_it : m_active_objects) {
1698 u16 id = ao_it.first;
1699 ServerActiveObject* obj = ao_it.second;
1700 // This shouldn't happen but check it
1703 infostream<<"NULL object found in ServerEnvironment"
1704 <<" while finding removed objects. id="<<id<<std::endl;
1705 // Id to be removed from m_active_objects
1706 objects_to_remove.push_back(id);
1711 We will delete objects that are marked as removed or thatare
1712 waiting for deletion after deactivation
1714 if (!obj->m_removed && !obj->m_pending_deactivation)
1718 Delete static data from block if is marked as removed
1720 if(obj->m_static_exists && obj->m_removed)
1722 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1724 block->m_static_objects.remove(id);
1725 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1726 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1727 obj->m_static_exists = false;
1729 infostream<<"Failed to emerge block from which an object to "
1730 <<"be removed was loaded from. id="<<id<<std::endl;
1734 // If m_known_by_count > 0, don't actually remove. On some future
1735 // invocation this will be 0, which is when removal will continue.
1736 if(obj->m_known_by_count > 0)
1740 Move static data from active to stored if not marked as removed
1742 if(obj->m_static_exists && !obj->m_removed){
1743 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1745 std::map<u16, StaticObject>::iterator i =
1746 block->m_static_objects.m_active.find(id);
1747 if(i != block->m_static_objects.m_active.end()){
1748 block->m_static_objects.m_stored.push_back(i->second);
1749 block->m_static_objects.m_active.erase(id);
1750 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1751 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1754 infostream<<"Failed to emerge block from which an object to "
1755 <<"be deactivated was loaded from. id="<<id<<std::endl;
1759 // Tell the object about removal
1760 obj->removingFromEnvironment();
1761 // Deregister in scripting api
1762 m_script->removeObjectReference(obj);
1765 if(obj->environmentDeletes())
1768 // Id to be removed from m_active_objects
1769 objects_to_remove.push_back(id);
1771 // Remove references from m_active_objects
1772 for (u16 i : objects_to_remove) {
1773 m_active_objects.erase(i);
1777 static void print_hexdump(std::ostream &o, const std::string &data)
1779 const int linelength = 16;
1780 for(int l=0; ; l++){
1781 int i0 = linelength * l;
1782 bool at_end = false;
1783 int thislinelength = linelength;
1784 if(i0 + thislinelength > (int)data.size()){
1785 thislinelength = data.size() - i0;
1788 for(int di=0; di<linelength; di++){
1791 if(di<thislinelength)
1792 snprintf(buf, 4, "%.2x ", data[i]);
1794 snprintf(buf, 4, " ");
1798 for(int di=0; di<thislinelength; di++){
1812 Convert stored objects from blocks near the players to active.
1814 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1819 // Ignore if no stored objects (to not set changed flag)
1820 if(block->m_static_objects.m_stored.empty())
1823 verbosestream<<"ServerEnvironment::activateObjects(): "
1824 <<"activating objects of block "<<PP(block->getPos())
1825 <<" ("<<block->m_static_objects.m_stored.size()
1826 <<" objects)"<<std::endl;
1827 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1829 errorstream<<"suspiciously large amount of objects detected: "
1830 <<block->m_static_objects.m_stored.size()<<" in "
1831 <<PP(block->getPos())
1832 <<"; removing all of them."<<std::endl;
1833 // Clear stored list
1834 block->m_static_objects.m_stored.clear();
1835 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1836 MOD_REASON_TOO_MANY_OBJECTS);
1840 // Activate stored objects
1841 std::vector<StaticObject> new_stored;
1842 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1843 // Create an active object from the data
1844 ServerActiveObject *obj = ServerActiveObject::create
1845 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1846 // If couldn't create object, store static data back.
1848 errorstream<<"ServerEnvironment::activateObjects(): "
1849 <<"failed to create active object from static object "
1850 <<"in block "<<PP(s_obj.pos/BS)
1851 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1852 print_hexdump(verbosestream, s_obj.data);
1854 new_stored.push_back(s_obj);
1857 verbosestream<<"ServerEnvironment::activateObjects(): "
1858 <<"activated static object pos="<<PP(s_obj.pos/BS)
1859 <<" type="<<(int)s_obj.type<<std::endl;
1860 // This will also add the object to the active static list
1861 addActiveObjectRaw(obj, false, dtime_s);
1863 // Clear stored list
1864 block->m_static_objects.m_stored.clear();
1865 // Add leftover failed stuff to stored list
1866 for (const StaticObject &s_obj : new_stored) {
1867 block->m_static_objects.m_stored.push_back(s_obj);
1870 // Turn the active counterparts of activated objects not pending for
1872 for (auto &i : block->m_static_objects.m_active) {
1874 ServerActiveObject *object = getActiveObject(id);
1876 object->m_pending_deactivation = false;
1880 Note: Block hasn't really been modified here.
1881 The objects have just been activated and moved from the stored
1882 static list to the active static list.
1883 As such, the block is essentially the same.
1884 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1885 Otherwise there would be a huge amount of unnecessary I/O.
1890 Convert objects that are not standing inside active blocks to static.
1892 If m_known_by_count != 0, active object is not deleted, but static
1893 data is still updated.
1895 If force_delete is set, active object is deleted nevertheless. It
1896 shall only be set so in the destructor of the environment.
1898 If block wasn't generated (not in memory or on disk),
1900 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1902 std::vector<u16> objects_to_remove;
1903 for (auto &ao_it : m_active_objects) {
1904 // force_delete might be overriden per object
1905 bool force_delete = _force_delete;
1907 ServerActiveObject* obj = ao_it.second;
1910 // Do not deactivate if static data creation not allowed
1911 if(!force_delete && !obj->isStaticAllowed())
1914 // If pending deactivation, let removeRemovedObjects() do it
1915 if(!force_delete && obj->m_pending_deactivation)
1918 u16 id = ao_it.first;
1919 v3f objectpos = obj->getBasePosition();
1921 // The block in which the object resides in
1922 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1924 // If object's static data is stored in a deactivated block and object
1925 // is actually located in an active block, re-save to the block in
1926 // which the object is actually located in.
1928 obj->m_static_exists &&
1929 !m_active_blocks.contains(obj->m_static_block) &&
1930 m_active_blocks.contains(blockpos_o))
1932 v3s16 old_static_block = obj->m_static_block;
1934 // Save to block where object is located
1935 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1937 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1938 <<"Could not save object id="<<id
1939 <<" to it's current block "<<PP(blockpos_o)
1943 std::string staticdata_new;
1944 obj->getStaticData(&staticdata_new);
1945 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1946 block->m_static_objects.insert(id, s_obj);
1947 obj->m_static_block = blockpos_o;
1948 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1949 MOD_REASON_STATIC_DATA_ADDED);
1951 // Delete from block where object was located
1952 block = m_map->emergeBlock(old_static_block, false);
1954 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1955 <<"Could not delete object id="<<id
1956 <<" from it's previous block "<<PP(old_static_block)
1960 block->m_static_objects.remove(id);
1961 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1962 MOD_REASON_STATIC_DATA_REMOVED);
1966 // If block is active, don't remove
1967 if(!force_delete && m_active_blocks.contains(blockpos_o))
1970 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1971 <<"deactivating object id="<<id<<" on inactive block "
1972 <<PP(blockpos_o)<<std::endl;
1974 // If known by some client, don't immediately delete.
1975 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1978 Update the static data
1981 if(obj->isStaticAllowed())
1983 // Create new static object
1984 std::string staticdata_new;
1985 obj->getStaticData(&staticdata_new);
1986 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1988 bool stays_in_same_block = false;
1989 bool data_changed = true;
1991 if (obj->m_static_exists) {
1992 if (obj->m_static_block == blockpos_o)
1993 stays_in_same_block = true;
1995 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1998 std::map<u16, StaticObject>::iterator n =
1999 block->m_static_objects.m_active.find(id);
2000 if (n != block->m_static_objects.m_active.end()) {
2001 StaticObject static_old = n->second;
2003 float save_movem = obj->getMinimumSavedMovement();
2005 if (static_old.data == staticdata_new &&
2006 (static_old.pos - objectpos).getLength() < save_movem)
2007 data_changed = false;
2009 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2010 <<"id="<<id<<" m_static_exists=true but "
2011 <<"static data doesn't actually exist in "
2012 <<PP(obj->m_static_block)<<std::endl;
2017 bool shall_be_written = (!stays_in_same_block || data_changed);
2019 // Delete old static object
2020 if(obj->m_static_exists)
2022 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2025 block->m_static_objects.remove(id);
2026 obj->m_static_exists = false;
2027 // Only mark block as modified if data changed considerably
2028 if(shall_be_written)
2029 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2030 MOD_REASON_STATIC_DATA_CHANGED);
2034 // Add to the block where the object is located in
2035 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2036 // Get or generate the block
2037 MapBlock *block = NULL;
2039 block = m_map->emergeBlock(blockpos);
2040 } catch(InvalidPositionException &e){
2041 // Handled via NULL pointer
2042 // NOTE: emergeBlock's failure is usually determined by it
2043 // actually returning NULL
2048 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2049 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2050 << " statically but block " << PP(blockpos)
2051 << " already contains "
2052 << block->m_static_objects.m_stored.size()
2054 << " Forcing delete." << std::endl;
2055 force_delete = true;
2057 // If static counterpart already exists in target block,
2059 // This shouldn't happen because the object is removed from
2060 // the previous block before this according to
2061 // obj->m_static_block, but happens rarely for some unknown
2062 // reason. Unsuccessful attempts have been made to find
2064 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2065 warningstream<<"ServerEnv: Performing hack #83274"
2067 block->m_static_objects.remove(id);
2069 // Store static data
2070 u16 store_id = pending_delete ? id : 0;
2071 block->m_static_objects.insert(store_id, s_obj);
2073 // Only mark block as modified if data changed considerably
2074 if(shall_be_written)
2075 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2076 MOD_REASON_STATIC_DATA_CHANGED);
2078 obj->m_static_exists = true;
2079 obj->m_static_block = block->getPos();
2084 v3s16 p = floatToInt(objectpos, BS);
2085 errorstream<<"ServerEnv: Could not find or generate "
2086 <<"a block for storing id="<<obj->getId()
2087 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2094 If known by some client, set pending deactivation.
2095 Otherwise delete it immediately.
2098 if(pending_delete && !force_delete)
2100 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2101 <<"object id="<<id<<" is known by clients"
2102 <<"; not deleting yet"<<std::endl;
2104 obj->m_pending_deactivation = true;
2108 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2109 <<"object id="<<id<<" is not known by clients"
2110 <<"; deleting"<<std::endl;
2112 // Tell the object about removal
2113 obj->removingFromEnvironment();
2114 // Deregister in scripting api
2115 m_script->removeObjectReference(obj);
2117 // Delete active object
2118 if(obj->environmentDeletes())
2120 // Id to be removed from m_active_objects
2121 objects_to_remove.push_back(id);
2124 // Remove references from m_active_objects
2125 for (u16 i : objects_to_remove) {
2126 m_active_objects.erase(i);
2130 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2131 const std::string &savedir, const Settings &conf)
2134 if (name == "sqlite3")
2135 return new PlayerDatabaseSQLite3(savedir);
2137 if (name == "dummy")
2138 return new Database_Dummy();
2140 if (name == "postgresql") {
2141 std::string connect_string;
2142 conf.getNoEx("pgsql_player_connection", connect_string);
2143 return new PlayerDatabasePostgreSQL(connect_string);
2146 if (name == "files")
2147 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2149 throw BaseException(std::string("Database backend ") + name + " not supported.");
2152 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2153 const Settings &cmd_args)
2155 std::string migrate_to = cmd_args.get("migrate-players");
2157 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2158 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2159 errorstream << "Cannot read world.mt!" << std::endl;
2163 if (!world_mt.exists("player_backend")) {
2164 errorstream << "Please specify your current backend in world.mt:"
2166 << " player_backend = {files|sqlite3|postgresql}"
2171 std::string backend = world_mt.get("player_backend");
2172 if (backend == migrate_to) {
2173 errorstream << "Cannot migrate: new backend is same"
2174 << " as the old one" << std::endl;
2178 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2181 if (backend == "files") {
2182 // Create backup directory
2183 fs::CreateDir(players_backup_path);
2187 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2188 game_params.world_path, world_mt);
2189 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2190 game_params.world_path, world_mt);
2192 std::vector<std::string> player_list;
2193 srcdb->listPlayers(player_list);
2194 for (std::vector<std::string>::const_iterator it = player_list.begin();
2195 it != player_list.end(); ++it) {
2196 actionstream << "Migrating player " << it->c_str() << std::endl;
2197 RemotePlayer player(it->c_str(), NULL);
2198 PlayerSAO playerSAO(NULL, &player, 15000, false);
2200 srcdb->loadPlayer(&player, &playerSAO);
2202 playerSAO.finalize(&player, std::set<std::string>());
2203 player.setPlayerSAO(&playerSAO);
2205 dstdb->savePlayer(&player);
2207 // For files source, move player files to backup dir
2208 if (backend == "files") {
2210 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2211 players_backup_path + DIR_DELIM + (*it));
2215 actionstream << "Successfully migrated " << player_list.size() << " players"
2217 world_mt.set("player_backend", migrate_to);
2218 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2219 errorstream << "Failed to update world.mt!" << std::endl;
2221 actionstream << "world.mt updated" << std::endl;
2223 // When migration is finished from file backend, remove players directory if empty
2224 if (backend == "files") {
2225 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2232 } catch (BaseException &e) {
2233 errorstream << "An error occured during migration: " << e.what() << std::endl;