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)
428 Check that peer_ids are unique.
429 Also check that names are unique.
430 Exception: there can be multiple players with peer_id=0
432 // If peer id is non-zero, it has to be unique.
433 if (player->peer_id != 0)
434 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
435 // Name has to be unique.
436 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
438 m_players.push_back(player);
441 void ServerEnvironment::removePlayer(RemotePlayer *player)
443 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
444 it != m_players.end(); ++it) {
445 if ((*it) == player) {
453 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
455 return m_player_database->removePlayer(name);
458 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
460 float distance = pos1.getDistanceFrom(pos2);
462 //calculate normalized direction vector
463 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
464 (pos2.Y - pos1.Y)/distance,
465 (pos2.Z - pos1.Z)/distance);
467 //find out if there's a node on path between pos1 and pos2
468 for (float i = 1; i < distance; i += stepsize) {
469 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
470 normalized_vector.Y * i,
471 normalized_vector.Z * i) +pos1,BS);
473 MapNode n = getMap().getNodeNoEx(pos);
475 if(n.param0 != CONTENT_AIR) {
485 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
486 const std::string &str_reason, bool reconnect)
488 for (RemotePlayer *player : m_players) {
489 m_server->DenyAccessVerCompliant(player->peer_id,
490 player->protocol_version, reason, str_reason, reconnect);
494 void ServerEnvironment::saveLoadedPlayers()
496 std::string players_path = m_path_world + DIR_DELIM + "players";
497 fs::CreateDir(players_path);
499 for (RemotePlayer *player : m_players) {
500 if (player->checkModified() || (player->getPlayerSAO() &&
501 player->getPlayerSAO()->extendedAttributesModified())) {
503 m_player_database->savePlayer(player);
504 } catch (DatabaseException &e) {
505 errorstream << "Failed to save player " << player->getName() << " exception: "
506 << e.what() << std::endl;
513 void ServerEnvironment::savePlayer(RemotePlayer *player)
516 m_player_database->savePlayer(player);
517 } catch (DatabaseException &e) {
518 errorstream << "Failed to save player " << player->getName() << " exception: "
519 << e.what() << std::endl;
524 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
525 u16 peer_id, bool is_singleplayer)
527 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
528 // Create player if it doesn't exist
529 if (!m_player_database->loadPlayer(player, playersao)) {
531 // Set player position
532 infostream << "Server: Finding spawn place for player \""
533 << player->getName() << "\"" << std::endl;
534 playersao->setBasePosition(m_server->findSpawnPos());
536 // Make sure the player is saved
537 player->setModified(true);
539 // If the player exists, ensure that they respawn inside legal bounds
540 // This fixes an assert crash when the player can't be added
541 // to the environment
542 ServerMap &map = getServerMap();
543 if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) {
544 actionstream << "Respawn position for player \""
545 << player->getName() << "\" outside limits, resetting" << std::endl;
546 playersao->setBasePosition(m_server->findSpawnPos());
550 // Add player to environment
553 /* Clean up old HUD elements from previous sessions */
556 /* Add object to environment */
557 addActiveObject(playersao);
562 void ServerEnvironment::saveMeta()
564 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
566 // Open file and serialize
567 std::ostringstream ss(std::ios_base::binary);
570 args.setU64("game_time", m_game_time);
571 args.setU64("time_of_day", getTimeOfDay());
572 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
573 args.setU64("lbm_introduction_times_version", 1);
574 args.set("lbm_introduction_times",
575 m_lbm_mgr.createIntroductionTimesString());
576 args.setU64("day_count", m_day_count);
580 if(!fs::safeWriteToFile(path, ss.str()))
582 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
584 throw SerializationError("Couldn't save env meta");
588 void ServerEnvironment::loadMeta()
590 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
592 // Open file and deserialize
593 std::ifstream is(path.c_str(), std::ios_base::binary);
595 infostream << "ServerEnvironment::loadMeta(): Failed to open "
596 << path << std::endl;
597 throw SerializationError("Couldn't load env meta");
602 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
603 throw SerializationError("ServerEnvironment::loadMeta(): "
604 "EnvArgsEnd not found!");
608 m_game_time = args.getU64("game_time");
609 } catch (SettingNotFoundException &e) {
610 // Getting this is crucial, otherwise timestamps are useless
611 throw SerializationError("Couldn't load env meta game_time");
614 setTimeOfDay(args.exists("time_of_day") ?
615 // set day to early morning by default
616 args.getU64("time_of_day") : 5250);
618 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
619 // If missing, do as if clearObjects was never called
620 args.getU64("last_clear_objects_time") : 0;
622 std::string lbm_introduction_times;
624 u64 ver = args.getU64("lbm_introduction_times_version");
626 lbm_introduction_times = args.get("lbm_introduction_times");
628 infostream << "ServerEnvironment::loadMeta(): Non-supported"
629 << " introduction time version " << ver << std::endl;
631 } catch (SettingNotFoundException &e) {
632 // No problem, this is expected. Just continue with an empty string
634 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
636 m_day_count = args.exists("day_count") ?
637 args.getU64("day_count") : 0;
640 void ServerEnvironment::loadDefaultMeta()
642 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
647 ActiveBlockModifier *abm;
649 std::set<content_t> required_neighbors;
655 ServerEnvironment *m_env;
656 std::vector<std::vector<ActiveABM> *> m_aabms;
658 ABMHandler(std::vector<ABMWithState> &abms,
659 float dtime_s, ServerEnvironment *env,
665 INodeDefManager *ndef = env->getGameDef()->ndef();
666 for (ABMWithState &abmws : abms) {
667 ActiveBlockModifier *abm = abmws.abm;
668 float trigger_interval = abm->getTriggerInterval();
669 if(trigger_interval < 0.001)
670 trigger_interval = 0.001;
671 float actual_interval = dtime_s;
673 abmws.timer += dtime_s;
674 if(abmws.timer < trigger_interval)
676 abmws.timer -= trigger_interval;
677 actual_interval = trigger_interval;
679 float chance = abm->getTriggerChance();
684 if (abm->getSimpleCatchUp()) {
685 float intervals = actual_interval / trigger_interval;
688 aabm.chance = chance / intervals;
692 aabm.chance = chance;
696 const std::set<std::string> &required_neighbors_s =
697 abm->getRequiredNeighbors();
698 for (const std::string &required_neighbor_s : required_neighbors_s) {
699 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
703 const std::set<std::string> &contents_s = abm->getTriggerContents();
704 for (const std::string &content_s : contents_s) {
705 std::set<content_t> ids;
706 ndef->getIds(content_s, ids);
707 for (content_t c : ids) {
708 if (c >= m_aabms.size())
709 m_aabms.resize(c + 256, NULL);
711 m_aabms[c] = new std::vector<ActiveABM>;
712 m_aabms[c]->push_back(aabm);
720 for (auto &aabms : m_aabms)
724 // Find out how many objects the given block and its neighbours contain.
725 // Returns the number of objects in the block, and also in 'wider' the
726 // number of objects in the block and all its neighbours. The latter
727 // may an estimate if any neighbours are unloaded.
728 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
731 u32 wider_unknown_count = 0;
732 for(s16 x=-1; x<=1; x++)
733 for(s16 y=-1; y<=1; y++)
734 for(s16 z=-1; z<=1; z++)
736 MapBlock *block2 = map->getBlockNoCreateNoEx(
737 block->getPos() + v3s16(x,y,z));
739 wider_unknown_count++;
742 wider += block2->m_static_objects.m_active.size()
743 + block2->m_static_objects.m_stored.size();
746 u32 active_object_count = block->m_static_objects.m_active.size();
747 u32 wider_known_count = 3*3*3 - wider_unknown_count;
748 wider += wider_unknown_count * wider / wider_known_count;
749 return active_object_count;
752 void apply(MapBlock *block)
754 if(m_aabms.empty() || block->isDummy())
757 ServerMap *map = &m_env->getServerMap();
759 u32 active_object_count_wider;
760 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
761 m_env->m_added_objects = 0;
764 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
765 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
766 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
768 const MapNode &n = block->getNodeUnsafe(p0);
769 content_t c = n.getContent();
771 if (c >= m_aabms.size() || !m_aabms[c])
774 v3s16 p = p0 + block->getPosRelative();
775 for (ActiveABM &aabm : *m_aabms[c]) {
776 if (myrand() % aabm.chance != 0)
780 if (!aabm.required_neighbors.empty()) {
782 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
783 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
784 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
789 if (block->isValidPosition(p1)) {
790 // if the neighbor is found on the same map block
791 // get it straight from there
792 const MapNode &n = block->getNodeUnsafe(p1);
795 // otherwise consult the map
796 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
799 std::set<content_t>::const_iterator k;
800 k = aabm.required_neighbors.find(c);
801 if(k != aabm.required_neighbors.end()){
805 // No required neighbor found
810 // Call all the trigger variations
811 aabm.abm->trigger(m_env, p, n);
812 aabm.abm->trigger(m_env, p, n,
813 active_object_count, active_object_count_wider);
815 // Count surrounding objects again if the abms added any
816 if(m_env->m_added_objects > 0) {
817 active_object_count = countObjects(block, map, active_object_count_wider);
818 m_env->m_added_objects = 0;
825 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
827 // Reset usage timer immediately, otherwise a block that becomes active
828 // again at around the same time as it would normally be unloaded will
829 // get unloaded incorrectly. (I think this still leaves a small possibility
830 // of a race condition between this and server::AsyncRunStep, which only
831 // some kind of synchronisation will fix, but it at least reduces the window
832 // of opportunity for it to break from seconds to nanoseconds)
833 block->resetUsageTimer();
835 // Get time difference
837 u32 stamp = block->getTimestamp();
838 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
839 dtime_s = m_game_time - stamp;
840 dtime_s += additional_dtime;
842 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
843 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
845 // Remove stored static objects if clearObjects was called since block's timestamp
846 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
847 block->m_static_objects.m_stored.clear();
848 // do not set changed flag to avoid unnecessary mapblock writes
851 // Set current time as timestamp
852 block->setTimestampNoChangedFlag(m_game_time);
854 /*infostream<<"ServerEnvironment::activateBlock(): block is "
855 <<dtime_s<<" seconds old."<<std::endl;*/
857 // Activate stored objects
858 activateObjects(block, dtime_s);
860 /* Handle LoadingBlockModifiers */
861 m_lbm_mgr.applyLBMs(this, block, stamp);
864 std::vector<NodeTimer> elapsed_timers =
865 block->m_node_timers.step((float)dtime_s);
866 if (!elapsed_timers.empty()) {
868 for (const NodeTimer &elapsed_timer : elapsed_timers) {
869 n = block->getNodeNoEx(elapsed_timer.position);
870 v3s16 p = elapsed_timer.position + block->getPosRelative();
871 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
872 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
873 elapsed_timer.position));
877 /* Handle ActiveBlockModifiers */
878 ABMHandler abmhandler(m_abms, dtime_s, this, false);
879 abmhandler.apply(block);
882 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
884 m_abms.emplace_back(abm);
887 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
889 m_lbm_mgr.addLBMDef(lbm);
892 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
894 INodeDefManager *ndef = m_server->ndef();
895 MapNode n_old = m_map->getNodeNoEx(p);
898 if (ndef->get(n_old).has_on_destruct)
899 m_script->node_on_destruct(p, n_old);
902 if (!m_map->addNodeWithEvent(p, n))
905 // Update active VoxelManipulator if a mapgen thread
906 m_map->updateVManip(p);
908 // Call post-destructor
909 if (ndef->get(n_old).has_after_destruct)
910 m_script->node_after_destruct(p, n_old);
913 if (ndef->get(n).has_on_construct)
914 m_script->node_on_construct(p, n);
919 bool ServerEnvironment::removeNode(v3s16 p)
921 INodeDefManager *ndef = m_server->ndef();
922 MapNode n_old = m_map->getNodeNoEx(p);
925 if (ndef->get(n_old).has_on_destruct)
926 m_script->node_on_destruct(p, n_old);
929 // This is slightly optimized compared to addNodeWithEvent(air)
930 if (!m_map->removeNodeWithEvent(p))
933 // Update active VoxelManipulator if a mapgen thread
934 m_map->updateVManip(p);
936 // Call post-destructor
937 if (ndef->get(n_old).has_after_destruct)
938 m_script->node_after_destruct(p, n_old);
940 // Air doesn't require constructor
944 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
946 if (!m_map->addNodeWithEvent(p, n, false))
949 // Update active VoxelManipulator if a mapgen thread
950 m_map->updateVManip(p);
955 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
958 for (auto &activeObject : m_active_objects) {
959 ServerActiveObject* obj = activeObject.second;
960 u16 id = activeObject.first;
961 v3f objectpos = obj->getBasePosition();
962 if (objectpos.getDistanceFrom(pos) > radius)
964 objects.push_back(id);
968 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
970 infostream << "ServerEnvironment::clearObjects(): "
971 << "Removing all active objects" << std::endl;
972 std::vector<u16> objects_to_remove;
973 for (auto &it : m_active_objects) {
974 ServerActiveObject* obj = it.second;
975 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
978 // Delete static object if block is loaded
979 if (obj->m_static_exists) {
980 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
982 block->m_static_objects.remove(id);
983 block->raiseModified(MOD_STATE_WRITE_NEEDED,
984 MOD_REASON_CLEAR_ALL_OBJECTS);
985 obj->m_static_exists = false;
988 // If known by some client, don't delete immediately
989 if (obj->m_known_by_count > 0) {
990 obj->m_pending_deactivation = true;
991 obj->m_removed = true;
995 // Tell the object about removal
996 obj->removingFromEnvironment();
997 // Deregister in scripting api
998 m_script->removeObjectReference(obj);
1000 // Delete active object
1001 if (obj->environmentDeletes())
1003 // Id to be removed from m_active_objects
1004 objects_to_remove.push_back(id);
1007 // Remove references from m_active_objects
1008 for (u16 i : objects_to_remove) {
1009 m_active_objects.erase(i);
1012 // Get list of loaded blocks
1013 std::vector<v3s16> loaded_blocks;
1014 infostream << "ServerEnvironment::clearObjects(): "
1015 << "Listing all loaded blocks" << std::endl;
1016 m_map->listAllLoadedBlocks(loaded_blocks);
1017 infostream << "ServerEnvironment::clearObjects(): "
1018 << "Done listing all loaded blocks: "
1019 << loaded_blocks.size()<<std::endl;
1021 // Get list of loadable blocks
1022 std::vector<v3s16> loadable_blocks;
1023 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1024 infostream << "ServerEnvironment::clearObjects(): "
1025 << "Listing all loadable blocks" << std::endl;
1026 m_map->listAllLoadableBlocks(loadable_blocks);
1027 infostream << "ServerEnvironment::clearObjects(): "
1028 << "Done listing all loadable blocks: "
1029 << loadable_blocks.size() << std::endl;
1031 loadable_blocks = loaded_blocks;
1034 infostream << "ServerEnvironment::clearObjects(): "
1035 << "Now clearing objects in " << loadable_blocks.size()
1036 << " blocks" << std::endl;
1038 // Grab a reference on each loaded block to avoid unloading it
1039 for (v3s16 p : loaded_blocks) {
1040 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1041 assert(block != NULL);
1045 // Remove objects in all loadable blocks
1046 u32 unload_interval = U32_MAX;
1047 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1048 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1049 unload_interval = MYMAX(unload_interval, 1);
1051 u32 report_interval = loadable_blocks.size() / 10;
1052 u32 num_blocks_checked = 0;
1053 u32 num_blocks_cleared = 0;
1054 u32 num_objs_cleared = 0;
1055 for (auto i = loadable_blocks.begin();
1056 i != loadable_blocks.end(); ++i) {
1058 MapBlock *block = m_map->emergeBlock(p, false);
1060 errorstream << "ServerEnvironment::clearObjects(): "
1061 << "Failed to emerge block " << PP(p) << std::endl;
1064 u32 num_stored = block->m_static_objects.m_stored.size();
1065 u32 num_active = block->m_static_objects.m_active.size();
1066 if (num_stored != 0 || num_active != 0) {
1067 block->m_static_objects.m_stored.clear();
1068 block->m_static_objects.m_active.clear();
1069 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1070 MOD_REASON_CLEAR_ALL_OBJECTS);
1071 num_objs_cleared += num_stored + num_active;
1072 num_blocks_cleared++;
1074 num_blocks_checked++;
1076 if (report_interval != 0 &&
1077 num_blocks_checked % report_interval == 0) {
1078 float percent = 100.0 * (float)num_blocks_checked /
1079 loadable_blocks.size();
1080 infostream << "ServerEnvironment::clearObjects(): "
1081 << "Cleared " << num_objs_cleared << " objects"
1082 << " in " << num_blocks_cleared << " blocks ("
1083 << percent << "%)" << std::endl;
1085 if (num_blocks_checked % unload_interval == 0) {
1086 m_map->unloadUnreferencedBlocks();
1089 m_map->unloadUnreferencedBlocks();
1091 // Drop references that were added above
1092 for (v3s16 p : loaded_blocks) {
1093 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1098 m_last_clear_objects_time = m_game_time;
1100 infostream << "ServerEnvironment::clearObjects(): "
1101 << "Finished: Cleared " << num_objs_cleared << " objects"
1102 << " in " << num_blocks_cleared << " blocks" << std::endl;
1105 void ServerEnvironment::step(float dtime)
1107 /* Step time of day */
1108 stepTimeOfDay(dtime);
1111 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1112 // really matter that much.
1113 static thread_local const float server_step =
1114 g_settings->getFloat("dedicated_server_step");
1115 m_recommended_send_interval = server_step;
1121 m_game_time_fraction_counter += dtime;
1122 u32 inc_i = (u32)m_game_time_fraction_counter;
1123 m_game_time += inc_i;
1124 m_game_time_fraction_counter -= (float)inc_i;
1131 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1132 for (RemotePlayer *player : m_players) {
1133 // Ignore disconnected players
1134 if (player->peer_id == 0)
1138 player->move(dtime, this, 100 * BS);
1143 Manage active block list
1145 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1146 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1148 Get player block positions
1150 std::vector<v3s16> players_blockpos;
1151 for (RemotePlayer *player: m_players) {
1152 // Ignore disconnected players
1153 if (player->peer_id == 0)
1156 PlayerSAO *playersao = player->getPlayerSAO();
1159 players_blockpos.push_back(
1160 getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS)));
1164 Update list of active blocks, collecting changes
1166 static thread_local const s16 active_block_range =
1167 g_settings->getS16("active_block_range");
1168 std::set<v3s16> blocks_removed;
1169 std::set<v3s16> blocks_added;
1170 m_active_blocks.update(players_blockpos, active_block_range,
1171 blocks_removed, blocks_added);
1174 Handle removed blocks
1177 // Convert active objects that are no more in active blocks to static
1178 deactivateFarObjects(false);
1180 for (const v3s16 &p: blocks_removed) {
1181 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1185 // Set current time as timestamp (and let it set ChangedFlag)
1186 block->setTimestamp(m_game_time);
1193 for (const v3s16 &p: blocks_added) {
1194 MapBlock *block = m_map->getBlockOrEmerge(p);
1196 m_active_blocks.m_list.erase(p);
1200 activateBlock(block);
1205 Mess around in active blocks
1207 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1208 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1210 float dtime = m_cache_nodetimer_interval;
1212 for (const v3s16 &p: m_active_blocks.m_list) {
1213 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1217 // Reset block usage timer
1218 block->resetUsageTimer();
1220 // Set current time as timestamp
1221 block->setTimestampNoChangedFlag(m_game_time);
1222 // If time has changed much from the one on disk,
1223 // set block to be saved when it is unloaded
1224 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1225 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1226 MOD_REASON_BLOCK_EXPIRED);
1229 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1230 if (!elapsed_timers.empty()) {
1233 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1234 n = block->getNodeNoEx(elapsed_timer.position);
1235 p2 = elapsed_timer.position + block->getPosRelative();
1236 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1237 block->setNodeTimer(NodeTimer(
1238 elapsed_timer.timeout, 0, elapsed_timer.position));
1245 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1247 if (m_active_block_interval_overload_skip > 0) {
1248 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1249 m_active_block_interval_overload_skip--;
1252 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1253 TimeTaker timer("modify in active blocks per interval");
1255 // Initialize handling of ActiveBlockModifiers
1256 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1258 for (const v3s16 &p : m_active_blocks.m_list) {
1259 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1263 // Set current time as timestamp
1264 block->setTimestampNoChangedFlag(m_game_time);
1266 /* Handle ActiveBlockModifiers */
1267 abmhandler.apply(block);
1270 u32 time_ms = timer.stop(true);
1271 u32 max_time_ms = 200;
1272 if (time_ms > max_time_ms) {
1273 warningstream<<"active block modifiers took "
1274 <<time_ms<<"ms (longer than "
1275 <<max_time_ms<<"ms)"<<std::endl;
1276 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1281 Step script environment (run global on_step())
1283 m_script->environment_Step(dtime);
1289 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1290 //TimeTaker timer("Step active objects");
1292 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1294 // This helps the objects to send data at the same time
1295 bool send_recommended = false;
1296 m_send_recommended_timer += dtime;
1297 if(m_send_recommended_timer > getSendRecommendedInterval())
1299 m_send_recommended_timer -= getSendRecommendedInterval();
1300 send_recommended = true;
1303 for (auto &ao_it : m_active_objects) {
1304 ServerActiveObject* obj = ao_it.second;
1305 // Don't step if is to be removed or stored statically
1306 if(obj->m_removed || obj->m_pending_deactivation)
1309 obj->step(dtime, send_recommended);
1310 // Read messages from object
1311 while(!obj->m_messages_out.empty())
1313 m_active_object_messages.push(
1314 obj->m_messages_out.front());
1315 obj->m_messages_out.pop();
1321 Manage active objects
1323 if (m_object_management_interval.step(dtime, 0.5)) {
1324 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1326 Remove objects that satisfy (m_removed && m_known_by_count==0)
1328 removeRemovedObjects();
1332 Manage particle spawner expiration
1334 if (m_particle_management_interval.step(dtime, 1.0)) {
1335 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1336 i != m_particle_spawners.end(); ) {
1337 //non expiring spawners
1338 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1344 if (i->second <= 0.f)
1345 m_particle_spawners.erase(i++);
1352 u32 ServerEnvironment::addParticleSpawner(float exptime)
1354 // Timers with lifetime 0 do not expire
1355 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1358 for (;;) { // look for unused particlespawner id
1360 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1361 if (f == m_particle_spawners.end()) {
1362 m_particle_spawners[id] = time;
1369 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1371 u32 id = addParticleSpawner(exptime);
1372 m_particle_spawner_attachments[id] = attached_id;
1373 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1374 obj->attachParticleSpawner(id);
1379 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1381 m_particle_spawners.erase(id);
1382 const auto &it = m_particle_spawner_attachments.find(id);
1383 if (it != m_particle_spawner_attachments.end()) {
1384 u16 obj_id = it->second;
1385 ServerActiveObject *sao = getActiveObject(obj_id);
1386 if (sao != NULL && remove_from_object) {
1387 sao->detachParticleSpawner(id);
1389 m_particle_spawner_attachments.erase(id);
1393 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1395 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1396 return (n != m_active_objects.end() ? n->second : NULL);
1399 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1404 return objects.find(id) == objects.end();
1407 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1409 //try to reuse id's as late as possible
1410 static u16 last_used_id = 0;
1411 u16 startid = last_used_id;
1415 if(isFreeServerActiveObjectId(last_used_id, objects))
1416 return last_used_id;
1418 if(last_used_id == startid)
1423 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1425 assert(object); // Pre-condition
1427 u16 id = addActiveObjectRaw(object, true, 0);
1432 Finds out what new objects have been added to
1433 inside a radius around a position
1435 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1437 std::set<u16> ¤t_objects,
1438 std::queue<u16> &added_objects)
1440 f32 radius_f = radius * BS;
1441 f32 player_radius_f = player_radius * BS;
1443 if (player_radius_f < 0)
1444 player_radius_f = 0;
1446 Go through the object list,
1447 - discard m_removed objects,
1448 - discard objects that are too far away,
1449 - discard objects that are found in current_objects.
1450 - add remaining objects to added_objects
1452 for (auto &ao_it : m_active_objects) {
1453 u16 id = ao_it.first;
1456 ServerActiveObject *object = ao_it.second;
1460 // Discard if removed or deactivating
1461 if(object->m_removed || object->m_pending_deactivation)
1464 f32 distance_f = object->getBasePosition().
1465 getDistanceFrom(playersao->getBasePosition());
1466 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1467 // Discard if too far
1468 if (distance_f > player_radius_f && player_radius_f != 0)
1470 } else if (distance_f > radius_f)
1473 // Discard if already on current_objects
1474 std::set<u16>::iterator n;
1475 n = current_objects.find(id);
1476 if(n != current_objects.end())
1478 // Add to added_objects
1479 added_objects.push(id);
1484 Finds out what objects have been removed from
1485 inside a radius around a position
1487 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1489 std::set<u16> ¤t_objects,
1490 std::queue<u16> &removed_objects)
1492 f32 radius_f = radius * BS;
1493 f32 player_radius_f = player_radius * BS;
1495 if (player_radius_f < 0)
1496 player_radius_f = 0;
1498 Go through current_objects; object is removed if:
1499 - object is not found in m_active_objects (this is actually an
1500 error condition; objects should be set m_removed=true and removed
1501 only after all clients have been informed about removal), or
1502 - object has m_removed=true, or
1503 - object is too far away
1505 for (u16 id : current_objects) {
1506 ServerActiveObject *object = getActiveObject(id);
1508 if (object == NULL) {
1509 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1510 << " object in current_objects is NULL" << std::endl;
1511 removed_objects.push(id);
1515 if (object->m_removed || object->m_pending_deactivation) {
1516 removed_objects.push(id);
1520 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1521 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1522 if (distance_f <= player_radius_f || player_radius_f == 0)
1524 } else if (distance_f <= radius_f)
1527 // Object is no longer visible
1528 removed_objects.push(id);
1532 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1533 v3s16 blockpos, bool static_exists, v3s16 static_block)
1535 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1539 for (auto &so_it : block->m_static_objects.m_active) {
1540 // Get the ServerActiveObject counterpart to this StaticObject
1541 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it.first);
1542 if (ao_it == m_active_objects.end()) {
1543 // If this ever happens, there must be some kind of nasty bug.
1544 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1545 "Object from MapBlock::m_static_objects::m_active not found "
1546 "in m_active_objects";
1550 ServerActiveObject *sao = ao_it->second;
1551 sao->m_static_exists = static_exists;
1552 sao->m_static_block = static_block;
1556 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1558 if(m_active_object_messages.empty())
1559 return ActiveObjectMessage(0);
1561 ActiveObjectMessage message = m_active_object_messages.front();
1562 m_active_object_messages.pop();
1566 void ServerEnvironment::getSelectedActiveObjects(
1567 const core::line3d<f32> &shootline_on_map,
1568 std::vector<PointedThing> &objects)
1570 std::vector<u16> objectIds;
1571 getObjectsInsideRadius(objectIds, shootline_on_map.start,
1572 shootline_on_map.getLength() + 10.0f);
1573 const v3f line_vector = shootline_on_map.getVector();
1575 for (u16 objectId : objectIds) {
1576 ServerActiveObject* obj = getActiveObject(objectId);
1578 aabb3f selection_box;
1579 if (!obj->getSelectionBox(&selection_box))
1582 v3f pos = obj->getBasePosition();
1584 aabb3f offsetted_box(selection_box.MinEdge + pos,
1585 selection_box.MaxEdge + pos);
1587 v3f current_intersection;
1588 v3s16 current_normal;
1589 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1590 ¤t_intersection, ¤t_normal)) {
1591 objects.emplace_back(
1592 (s16) objectId, current_intersection, current_normal,
1593 (current_intersection - shootline_on_map.start).getLengthSQ());
1599 ************ Private methods *************
1602 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1603 bool set_changed, u32 dtime_s)
1605 assert(object); // Pre-condition
1606 if(object->getId() == 0){
1607 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1610 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1611 <<"no free ids available"<<std::endl;
1612 if(object->environmentDeletes())
1616 object->setId(new_id);
1619 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1620 <<"supplied with id "<<object->getId()<<std::endl;
1623 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1624 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1625 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1626 if(object->environmentDeletes())
1631 if (objectpos_over_limit(object->getBasePosition())) {
1632 v3f p = object->getBasePosition();
1633 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1634 << "object position (" << p.X << "," << p.Y << "," << p.Z
1635 << ") outside maximum range" << std::endl;
1636 if (object->environmentDeletes())
1641 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1642 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1644 m_active_objects[object->getId()] = object;
1646 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1647 <<"Added id="<<object->getId()<<"; there are now "
1648 <<m_active_objects.size()<<" active objects."
1651 // Register reference in scripting api (must be done before post-init)
1652 m_script->addObjectReference(object);
1653 // Post-initialize object
1654 object->addedToEnvironment(dtime_s);
1656 // Add static data to block
1657 if(object->isStaticAllowed())
1659 // Add static object to active static list of the block
1660 v3f objectpos = object->getBasePosition();
1661 std::string staticdata;
1662 object->getStaticData(&staticdata);
1663 StaticObject s_obj(object->getType(), objectpos, staticdata);
1664 // Add to the block where the object is located in
1665 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1666 MapBlock *block = m_map->emergeBlock(blockpos);
1668 block->m_static_objects.m_active[object->getId()] = s_obj;
1669 object->m_static_exists = true;
1670 object->m_static_block = blockpos;
1673 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1674 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1676 v3s16 p = floatToInt(objectpos, BS);
1677 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1678 <<"could not emerge block for storing id="<<object->getId()
1679 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1683 return object->getId();
1687 Remove objects that satisfy (m_removed && m_known_by_count==0)
1689 void ServerEnvironment::removeRemovedObjects()
1691 std::vector<u16> objects_to_remove;
1692 for (auto &ao_it : m_active_objects) {
1693 u16 id = ao_it.first;
1694 ServerActiveObject* obj = ao_it.second;
1695 // This shouldn't happen but check it
1698 infostream<<"NULL object found in ServerEnvironment"
1699 <<" while finding removed objects. id="<<id<<std::endl;
1700 // Id to be removed from m_active_objects
1701 objects_to_remove.push_back(id);
1706 We will delete objects that are marked as removed or thatare
1707 waiting for deletion after deactivation
1709 if (!obj->m_removed && !obj->m_pending_deactivation)
1713 Delete static data from block if is marked as removed
1715 if(obj->m_static_exists && obj->m_removed)
1717 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1719 block->m_static_objects.remove(id);
1720 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1721 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1722 obj->m_static_exists = false;
1724 infostream<<"Failed to emerge block from which an object to "
1725 <<"be removed was loaded from. id="<<id<<std::endl;
1729 // If m_known_by_count > 0, don't actually remove. On some future
1730 // invocation this will be 0, which is when removal will continue.
1731 if(obj->m_known_by_count > 0)
1735 Move static data from active to stored if not marked as removed
1737 if(obj->m_static_exists && !obj->m_removed){
1738 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1740 std::map<u16, StaticObject>::iterator i =
1741 block->m_static_objects.m_active.find(id);
1742 if(i != block->m_static_objects.m_active.end()){
1743 block->m_static_objects.m_stored.push_back(i->second);
1744 block->m_static_objects.m_active.erase(id);
1745 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1746 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1749 infostream<<"Failed to emerge block from which an object to "
1750 <<"be deactivated was loaded from. id="<<id<<std::endl;
1754 // Tell the object about removal
1755 obj->removingFromEnvironment();
1756 // Deregister in scripting api
1757 m_script->removeObjectReference(obj);
1760 if(obj->environmentDeletes())
1763 // Id to be removed from m_active_objects
1764 objects_to_remove.push_back(id);
1766 // Remove references from m_active_objects
1767 for (u16 i : objects_to_remove) {
1768 m_active_objects.erase(i);
1772 static void print_hexdump(std::ostream &o, const std::string &data)
1774 const int linelength = 16;
1775 for(int l=0; ; l++){
1776 int i0 = linelength * l;
1777 bool at_end = false;
1778 int thislinelength = linelength;
1779 if(i0 + thislinelength > (int)data.size()){
1780 thislinelength = data.size() - i0;
1783 for(int di=0; di<linelength; di++){
1786 if(di<thislinelength)
1787 snprintf(buf, 4, "%.2x ", data[i]);
1789 snprintf(buf, 4, " ");
1793 for(int di=0; di<thislinelength; di++){
1807 Convert stored objects from blocks near the players to active.
1809 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1814 // Ignore if no stored objects (to not set changed flag)
1815 if(block->m_static_objects.m_stored.empty())
1818 verbosestream<<"ServerEnvironment::activateObjects(): "
1819 <<"activating objects of block "<<PP(block->getPos())
1820 <<" ("<<block->m_static_objects.m_stored.size()
1821 <<" objects)"<<std::endl;
1822 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1824 errorstream<<"suspiciously large amount of objects detected: "
1825 <<block->m_static_objects.m_stored.size()<<" in "
1826 <<PP(block->getPos())
1827 <<"; removing all of them."<<std::endl;
1828 // Clear stored list
1829 block->m_static_objects.m_stored.clear();
1830 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1831 MOD_REASON_TOO_MANY_OBJECTS);
1835 // Activate stored objects
1836 std::vector<StaticObject> new_stored;
1837 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1838 // Create an active object from the data
1839 ServerActiveObject *obj = ServerActiveObject::create
1840 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1841 // If couldn't create object, store static data back.
1843 errorstream<<"ServerEnvironment::activateObjects(): "
1844 <<"failed to create active object from static object "
1845 <<"in block "<<PP(s_obj.pos/BS)
1846 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1847 print_hexdump(verbosestream, s_obj.data);
1849 new_stored.push_back(s_obj);
1852 verbosestream<<"ServerEnvironment::activateObjects(): "
1853 <<"activated static object pos="<<PP(s_obj.pos/BS)
1854 <<" type="<<(int)s_obj.type<<std::endl;
1855 // This will also add the object to the active static list
1856 addActiveObjectRaw(obj, false, dtime_s);
1858 // Clear stored list
1859 block->m_static_objects.m_stored.clear();
1860 // Add leftover failed stuff to stored list
1861 for (const StaticObject &s_obj : new_stored) {
1862 block->m_static_objects.m_stored.push_back(s_obj);
1865 // Turn the active counterparts of activated objects not pending for
1867 for (auto &i : block->m_static_objects.m_active) {
1869 ServerActiveObject *object = getActiveObject(id);
1871 object->m_pending_deactivation = false;
1875 Note: Block hasn't really been modified here.
1876 The objects have just been activated and moved from the stored
1877 static list to the active static list.
1878 As such, the block is essentially the same.
1879 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1880 Otherwise there would be a huge amount of unnecessary I/O.
1885 Convert objects that are not standing inside active blocks to static.
1887 If m_known_by_count != 0, active object is not deleted, but static
1888 data is still updated.
1890 If force_delete is set, active object is deleted nevertheless. It
1891 shall only be set so in the destructor of the environment.
1893 If block wasn't generated (not in memory or on disk),
1895 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1897 std::vector<u16> objects_to_remove;
1898 for (auto &ao_it : m_active_objects) {
1899 // force_delete might be overriden per object
1900 bool force_delete = _force_delete;
1902 ServerActiveObject* obj = ao_it.second;
1905 // Do not deactivate if static data creation not allowed
1906 if(!force_delete && !obj->isStaticAllowed())
1909 // If pending deactivation, let removeRemovedObjects() do it
1910 if(!force_delete && obj->m_pending_deactivation)
1913 u16 id = ao_it.first;
1914 v3f objectpos = obj->getBasePosition();
1916 // The block in which the object resides in
1917 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1919 // If object's static data is stored in a deactivated block and object
1920 // is actually located in an active block, re-save to the block in
1921 // which the object is actually located in.
1923 obj->m_static_exists &&
1924 !m_active_blocks.contains(obj->m_static_block) &&
1925 m_active_blocks.contains(blockpos_o))
1927 v3s16 old_static_block = obj->m_static_block;
1929 // Save to block where object is located
1930 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1932 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1933 <<"Could not save object id="<<id
1934 <<" to it's current block "<<PP(blockpos_o)
1938 std::string staticdata_new;
1939 obj->getStaticData(&staticdata_new);
1940 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1941 block->m_static_objects.insert(id, s_obj);
1942 obj->m_static_block = blockpos_o;
1943 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1944 MOD_REASON_STATIC_DATA_ADDED);
1946 // Delete from block where object was located
1947 block = m_map->emergeBlock(old_static_block, false);
1949 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1950 <<"Could not delete object id="<<id
1951 <<" from it's previous block "<<PP(old_static_block)
1955 block->m_static_objects.remove(id);
1956 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1957 MOD_REASON_STATIC_DATA_REMOVED);
1961 // If block is active, don't remove
1962 if(!force_delete && m_active_blocks.contains(blockpos_o))
1965 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1966 <<"deactivating object id="<<id<<" on inactive block "
1967 <<PP(blockpos_o)<<std::endl;
1969 // If known by some client, don't immediately delete.
1970 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1973 Update the static data
1976 if(obj->isStaticAllowed())
1978 // Create new static object
1979 std::string staticdata_new;
1980 obj->getStaticData(&staticdata_new);
1981 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1983 bool stays_in_same_block = false;
1984 bool data_changed = true;
1986 if (obj->m_static_exists) {
1987 if (obj->m_static_block == blockpos_o)
1988 stays_in_same_block = true;
1990 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1993 std::map<u16, StaticObject>::iterator n =
1994 block->m_static_objects.m_active.find(id);
1995 if (n != block->m_static_objects.m_active.end()) {
1996 StaticObject static_old = n->second;
1998 float save_movem = obj->getMinimumSavedMovement();
2000 if (static_old.data == staticdata_new &&
2001 (static_old.pos - objectpos).getLength() < save_movem)
2002 data_changed = false;
2004 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2005 <<"id="<<id<<" m_static_exists=true but "
2006 <<"static data doesn't actually exist in "
2007 <<PP(obj->m_static_block)<<std::endl;
2012 bool shall_be_written = (!stays_in_same_block || data_changed);
2014 // Delete old static object
2015 if(obj->m_static_exists)
2017 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2020 block->m_static_objects.remove(id);
2021 obj->m_static_exists = false;
2022 // Only mark block as modified if data changed considerably
2023 if(shall_be_written)
2024 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2025 MOD_REASON_STATIC_DATA_CHANGED);
2029 // Add to the block where the object is located in
2030 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2031 // Get or generate the block
2032 MapBlock *block = NULL;
2034 block = m_map->emergeBlock(blockpos);
2035 } catch(InvalidPositionException &e){
2036 // Handled via NULL pointer
2037 // NOTE: emergeBlock's failure is usually determined by it
2038 // actually returning NULL
2043 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2044 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2045 << " statically but block " << PP(blockpos)
2046 << " already contains "
2047 << block->m_static_objects.m_stored.size()
2049 << " Forcing delete." << std::endl;
2050 force_delete = true;
2052 // If static counterpart already exists in target block,
2054 // This shouldn't happen because the object is removed from
2055 // the previous block before this according to
2056 // obj->m_static_block, but happens rarely for some unknown
2057 // reason. Unsuccessful attempts have been made to find
2059 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2060 warningstream<<"ServerEnv: Performing hack #83274"
2062 block->m_static_objects.remove(id);
2064 // Store static data
2065 u16 store_id = pending_delete ? id : 0;
2066 block->m_static_objects.insert(store_id, s_obj);
2068 // Only mark block as modified if data changed considerably
2069 if(shall_be_written)
2070 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2071 MOD_REASON_STATIC_DATA_CHANGED);
2073 obj->m_static_exists = true;
2074 obj->m_static_block = block->getPos();
2079 v3s16 p = floatToInt(objectpos, BS);
2080 errorstream<<"ServerEnv: Could not find or generate "
2081 <<"a block for storing id="<<obj->getId()
2082 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2089 If known by some client, set pending deactivation.
2090 Otherwise delete it immediately.
2093 if(pending_delete && !force_delete)
2095 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2096 <<"object id="<<id<<" is known by clients"
2097 <<"; not deleting yet"<<std::endl;
2099 obj->m_pending_deactivation = true;
2103 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2104 <<"object id="<<id<<" is not known by clients"
2105 <<"; deleting"<<std::endl;
2107 // Tell the object about removal
2108 obj->removingFromEnvironment();
2109 // Deregister in scripting api
2110 m_script->removeObjectReference(obj);
2112 // Delete active object
2113 if(obj->environmentDeletes())
2115 // Id to be removed from m_active_objects
2116 objects_to_remove.push_back(id);
2119 // Remove references from m_active_objects
2120 for (u16 i : objects_to_remove) {
2121 m_active_objects.erase(i);
2125 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2126 const std::string &savedir, const Settings &conf)
2129 if (name == "sqlite3")
2130 return new PlayerDatabaseSQLite3(savedir);
2132 if (name == "dummy")
2133 return new Database_Dummy();
2135 if (name == "postgresql") {
2136 std::string connect_string;
2137 conf.getNoEx("pgsql_player_connection", connect_string);
2138 return new PlayerDatabasePostgreSQL(connect_string);
2141 if (name == "files")
2142 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2144 throw BaseException(std::string("Database backend ") + name + " not supported.");
2147 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2148 const Settings &cmd_args)
2150 std::string migrate_to = cmd_args.get("migrate-players");
2152 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2153 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2154 errorstream << "Cannot read world.mt!" << std::endl;
2158 if (!world_mt.exists("player_backend")) {
2159 errorstream << "Please specify your current backend in world.mt:"
2161 << " player_backend = {files|sqlite3|postgresql}"
2166 std::string backend = world_mt.get("player_backend");
2167 if (backend == migrate_to) {
2168 errorstream << "Cannot migrate: new backend is same"
2169 << " as the old one" << std::endl;
2173 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2176 if (backend == "files") {
2177 // Create backup directory
2178 fs::CreateDir(players_backup_path);
2182 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2183 game_params.world_path, world_mt);
2184 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2185 game_params.world_path, world_mt);
2187 std::vector<std::string> player_list;
2188 srcdb->listPlayers(player_list);
2189 for (std::vector<std::string>::const_iterator it = player_list.begin();
2190 it != player_list.end(); ++it) {
2191 actionstream << "Migrating player " << it->c_str() << std::endl;
2192 RemotePlayer player(it->c_str(), NULL);
2193 PlayerSAO playerSAO(NULL, &player, 15000, false);
2195 srcdb->loadPlayer(&player, &playerSAO);
2197 playerSAO.finalize(&player, std::set<std::string>());
2198 player.setPlayerSAO(&playerSAO);
2200 dstdb->savePlayer(&player);
2202 // For files source, move player files to backup dir
2203 if (backend == "files") {
2205 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2206 players_backup_path + DIR_DELIM + (*it));
2210 actionstream << "Successfully migrated " << player_list.size() << " players"
2212 world_mt.set("player_backend", migrate_to);
2213 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2214 errorstream << "Failed to update world.mt!" << std::endl;
2216 actionstream << "world.mt updated" << std::endl;
2218 // When migration is finished from file backend, remove players directory if empty
2219 if (backend == "files") {
2220 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2227 } catch (BaseException &e) {
2228 errorstream << "An error occured during migration: " << e.what() << std::endl;