3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "serverenvironment.h"
21 #include "content_sao.h"
26 #include "nodemetadata.h"
31 #include "remoteplayer.h"
32 #include "scripting_server.h"
34 #include "util/serialize.h"
35 #include "util/basic_macros.h"
36 #include "util/pointedthing.h"
37 #include "threading/mutex_auto_lock.h"
39 #include "gameparams.h"
40 #include "database/database-dummy.h"
41 #include "database/database-files.h"
42 #include "database/database-sqlite3.h"
44 #include "database/database-postgresql.h"
48 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
50 // A number that is much smaller than the timeout for particle spawners should/could ever be
51 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
57 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
60 // Initialize timer to random value to spread processing
61 float itv = abm->getTriggerInterval();
62 itv = MYMAX(0.001, itv); // No less than 1ms
63 int minval = MYMAX(-0.51*itv, -60); // Clamp to
64 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
65 timer = myrand_range(minval, maxval);
72 void LBMContentMapping::deleteContents()
74 for (auto &it : lbm_list) {
79 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
81 // Add the lbm_def to the LBMContentMapping.
82 // Unknown names get added to the global NameIdMapping.
83 const NodeDefManager *nodedef = gamedef->ndef();
85 lbm_list.push_back(lbm_def);
87 for (const std::string &nodeTrigger: lbm_def->trigger_contents) {
88 std::vector<content_t> c_ids;
89 bool found = nodedef->getIds(nodeTrigger, c_ids);
91 content_t c_id = gamedef->allocateUnknownNodeId(nodeTrigger);
92 if (c_id == CONTENT_IGNORE) {
93 // Seems it can't be allocated.
94 warningstream << "Could not internalize node name \"" << nodeTrigger
95 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
98 c_ids.push_back(c_id);
101 for (content_t c_id : c_ids) {
102 map[c_id].push_back(lbm_def);
107 const std::vector<LoadingBlockModifierDef *> *
108 LBMContentMapping::lookup(content_t c) const
110 lbm_map::const_iterator it = map.find(c);
113 // This first dereferences the iterator, returning
114 // a std::vector<LoadingBlockModifierDef *>
115 // reference, then we convert it to a pointer.
116 return &(it->second);
119 LBMManager::~LBMManager()
121 for (auto &m_lbm_def : m_lbm_defs) {
122 delete m_lbm_def.second;
125 for (auto &it : m_lbm_lookup) {
126 (it.second).deleteContents();
130 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
132 // Precondition, in query mode the map isn't used anymore
133 FATAL_ERROR_IF(m_query_mode,
134 "attempted to modify LBMManager in query mode");
136 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
137 throw ModError("Error adding LBM \"" + lbm_def->name +
138 "\": Does not follow naming conventions: "
139 "Only characters [a-z0-9_:] are allowed.");
142 m_lbm_defs[lbm_def->name] = lbm_def;
145 void LBMManager::loadIntroductionTimes(const std::string ×,
146 IGameDef *gamedef, u32 now)
151 // Storing it in a map first instead of
152 // handling the stuff directly in the loop
153 // removes all duplicate entries.
154 // TODO make this std::unordered_map
155 std::map<std::string, u32> introduction_times;
158 The introduction times string consists of name~time entries,
159 with each entry terminated by a semicolon. The time is decimal.
164 while ((idx_new = times.find(';', idx)) != std::string::npos) {
165 std::string entry = times.substr(idx, idx_new - idx);
166 std::vector<std::string> components = str_split(entry, '~');
167 if (components.size() != 2)
168 throw SerializationError("Introduction times entry \""
169 + entry + "\" requires exactly one '~'!");
170 const std::string &name = components[0];
171 u32 time = from_string<u32>(components[1]);
172 introduction_times[name] = time;
176 // Put stuff from introduction_times into m_lbm_lookup
177 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
178 it != introduction_times.end(); ++it) {
179 const std::string &name = it->first;
180 u32 time = it->second;
182 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
183 m_lbm_defs.find(name);
184 if (def_it == m_lbm_defs.end()) {
185 // This seems to be an LBM entry for
186 // an LBM we haven't loaded. Discard it.
189 LoadingBlockModifierDef *lbm_def = def_it->second;
190 if (lbm_def->run_at_every_load) {
191 // This seems to be an LBM entry for
192 // an LBM that runs at every load.
193 // Don't add it just yet.
197 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
199 // Erase the entry so that we know later
200 // what elements didn't get put into m_lbm_lookup
201 m_lbm_defs.erase(name);
204 // Now also add the elements from m_lbm_defs to m_lbm_lookup
205 // that weren't added in the previous step.
206 // They are introduced first time to this world,
207 // or are run at every load (introducement time hardcoded to U32_MAX).
209 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
210 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
212 for (auto &m_lbm_def : m_lbm_defs) {
213 if (m_lbm_def.second->run_at_every_load) {
214 lbms_running_always.addLBM(m_lbm_def.second, gamedef);
216 lbms_we_introduce_now.addLBM(m_lbm_def.second, gamedef);
220 // Clear the list, so that we don't delete remaining elements
221 // twice in the destructor
225 std::string LBMManager::createIntroductionTimesString()
227 // Precondition, we must be in query mode
228 FATAL_ERROR_IF(!m_query_mode,
229 "attempted to query on non fully set up LBMManager");
231 std::ostringstream oss;
232 for (const auto &it : m_lbm_lookup) {
234 const std::vector<LoadingBlockModifierDef *> &lbm_list = it.second.lbm_list;
235 for (const auto &lbm_def : lbm_list) {
236 // Don't add if the LBM runs at every load,
237 // then introducement time is hardcoded
238 // and doesn't need to be stored
239 if (lbm_def->run_at_every_load)
241 oss << lbm_def->name << "~" << time << ";";
247 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
249 // Precondition, we need m_lbm_lookup to be initialized
250 FATAL_ERROR_IF(!m_query_mode,
251 "attempted to query on non fully set up LBMManager");
252 v3s16 pos_of_block = block->getPosRelative();
256 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
257 for (; it != m_lbm_lookup.end(); ++it) {
258 // Cache previous version to speedup lookup which has a very high performance
259 // penalty on each call
260 content_t previous_c{};
261 std::vector<LoadingBlockModifierDef *> *lbm_list = nullptr;
263 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
264 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
265 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) {
266 n = block->getNodeNoEx(pos);
269 // If content_t are not matching perform an LBM lookup
270 if (previous_c != c) {
271 lbm_list = (std::vector<LoadingBlockModifierDef *> *)
272 it->second.lookup(c);
278 for (auto lbmdef : *lbm_list) {
279 lbmdef->trigger(env, pos + pos_of_block, n);
289 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
292 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
293 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
294 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
297 if (p.getDistanceFrom(p0) <= r) {
304 void fillViewConeBlock(v3s16 p0,
306 const v3f camera_pos,
307 const v3f camera_dir,
308 const float camera_fov,
309 std::set<v3s16> &list)
312 const s16 r_nodes = r * BS * MAP_BLOCKSIZE;
313 for (p.X = p0.X - r; p.X <= p0.X+r; p.X++)
314 for (p.Y = p0.Y - r; p.Y <= p0.Y+r; p.Y++)
315 for (p.Z = p0.Z - r; p.Z <= p0.Z+r; p.Z++) {
316 if (isBlockInSight(p, camera_pos, camera_dir, camera_fov, r_nodes)) {
322 void ActiveBlockList::update(std::vector<PlayerSAO*> &active_players,
323 s16 active_block_range,
324 s16 active_object_range,
325 std::set<v3s16> &blocks_removed,
326 std::set<v3s16> &blocks_added)
331 std::set<v3s16> newlist = m_forceloaded_list;
332 m_abm_list = m_forceloaded_list;
333 for (const PlayerSAO *playersao : active_players) {
334 v3s16 pos = getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS));
335 fillRadiusBlock(pos, active_block_range, m_abm_list);
336 fillRadiusBlock(pos, active_block_range, newlist);
338 s16 player_ao_range = std::min(active_object_range, playersao->getWantedRange());
339 // only do this if this would add blocks
340 if (player_ao_range > active_block_range) {
341 v3f camera_dir = v3f(0,0,1);
342 camera_dir.rotateYZBy(playersao->getPitch());
343 camera_dir.rotateXZBy(playersao->getYaw());
344 fillViewConeBlock(pos,
346 playersao->getEyePosition(),
354 Find out which blocks on the old list are not on the new list
356 // Go through old list
357 for (v3s16 p : m_list) {
358 // If not on new list, it's been removed
359 if (newlist.find(p) == newlist.end())
360 blocks_removed.insert(p);
364 Find out which blocks on the new list are not on the old list
366 // Go through new list
367 for (v3s16 p : newlist) {
368 // If not on old list, it's been added
369 if(m_list.find(p) == m_list.end())
370 blocks_added.insert(p);
377 for (v3s16 p : newlist) {
386 ServerEnvironment::ServerEnvironment(ServerMap *map,
387 ServerScripting *scriptIface, Server *server,
388 const std::string &path_world):
391 m_script(scriptIface),
393 m_path_world(path_world)
395 // Determine which database backend to use
396 std::string conf_path = path_world + DIR_DELIM + "world.mt";
398 bool succeeded = conf.readConfigFile(conf_path.c_str());
399 if (!succeeded || !conf.exists("player_backend")) {
400 // fall back to files
401 conf.set("player_backend", "files");
402 warningstream << "/!\\ You are using old player file backend. "
403 << "This backend is deprecated and will be removed in next release /!\\"
404 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
405 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
407 if (!conf.updateConfigFile(conf_path.c_str())) {
408 errorstream << "ServerEnvironment::ServerEnvironment(): "
409 << "Failed to update world.mt!" << std::endl;
414 conf.getNoEx("player_backend", name);
415 m_player_database = openPlayerDatabase(name, path_world, conf);
418 ServerEnvironment::~ServerEnvironment()
420 // Clear active block list.
421 // This makes the next one delete all active objects.
422 m_active_blocks.clear();
424 // Convert all objects to static and delete the active objects
425 deactivateFarObjects(true);
430 // Delete ActiveBlockModifiers
431 for (ABMWithState &m_abm : m_abms) {
435 // Deallocate players
436 for (RemotePlayer *m_player : m_players) {
440 delete m_player_database;
443 Map & ServerEnvironment::getMap()
448 ServerMap & ServerEnvironment::getServerMap()
453 RemotePlayer *ServerEnvironment::getPlayer(const session_t peer_id)
455 for (RemotePlayer *player : m_players) {
456 if (player->getPeerId() == peer_id)
462 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
464 for (RemotePlayer *player : m_players) {
465 if (strcmp(player->getName(), name) == 0)
471 void ServerEnvironment::addPlayer(RemotePlayer *player)
474 Check that peer_ids are unique.
475 Also check that names are unique.
476 Exception: there can be multiple players with peer_id=0
478 // If peer id is non-zero, it has to be unique.
479 if (player->getPeerId() != PEER_ID_INEXISTENT)
480 FATAL_ERROR_IF(getPlayer(player->getPeerId()) != NULL, "Peer id not unique");
481 // Name has to be unique.
482 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
484 m_players.push_back(player);
487 void ServerEnvironment::removePlayer(RemotePlayer *player)
489 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
490 it != m_players.end(); ++it) {
491 if ((*it) == player) {
499 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
501 return m_player_database->removePlayer(name);
504 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, v3s16 *p)
506 // Iterate trough nodes on the line
507 voxalgo::VoxelLineIterator iterator(pos1 / BS, (pos2 - pos1) / BS);
509 MapNode n = getMap().getNodeNoEx(iterator.m_current_node_pos);
512 if (n.param0 != CONTENT_AIR) {
514 *p = iterator.m_current_node_pos;
518 } while (iterator.m_current_index <= iterator.m_last_index);
522 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
523 const std::string &str_reason, bool reconnect)
525 for (RemotePlayer *player : m_players) {
526 m_server->DenyAccessVerCompliant(player->getPeerId(),
527 player->protocol_version, reason, str_reason, reconnect);
531 void ServerEnvironment::saveLoadedPlayers()
533 std::string players_path = m_path_world + DIR_DELIM + "players";
534 fs::CreateDir(players_path);
536 for (RemotePlayer *player : m_players) {
537 if (player->checkModified() || (player->getPlayerSAO() &&
538 player->getPlayerSAO()->getMeta().isModified())) {
540 m_player_database->savePlayer(player);
541 } catch (DatabaseException &e) {
542 errorstream << "Failed to save player " << player->getName() << " exception: "
543 << e.what() << std::endl;
550 void ServerEnvironment::savePlayer(RemotePlayer *player)
553 m_player_database->savePlayer(player);
554 } catch (DatabaseException &e) {
555 errorstream << "Failed to save player " << player->getName() << " exception: "
556 << e.what() << std::endl;
561 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
562 session_t peer_id, bool is_singleplayer)
564 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
565 // Create player if it doesn't exist
566 if (!m_player_database->loadPlayer(player, playersao)) {
568 // Set player position
569 infostream << "Server: Finding spawn place for player \""
570 << player->getName() << "\"" << std::endl;
571 playersao->setBasePosition(m_server->findSpawnPos());
573 // Make sure the player is saved
574 player->setModified(true);
576 // If the player exists, ensure that they respawn inside legal bounds
577 // This fixes an assert crash when the player can't be added
578 // to the environment
579 if (objectpos_over_limit(playersao->getBasePosition())) {
580 actionstream << "Respawn position for player \""
581 << player->getName() << "\" outside limits, resetting" << std::endl;
582 playersao->setBasePosition(m_server->findSpawnPos());
586 // Add player to environment
589 /* Clean up old HUD elements from previous sessions */
592 /* Add object to environment */
593 addActiveObject(playersao);
598 void ServerEnvironment::saveMeta()
600 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
602 // Open file and serialize
603 std::ostringstream ss(std::ios_base::binary);
606 args.setU64("game_time", m_game_time);
607 args.setU64("time_of_day", getTimeOfDay());
608 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
609 args.setU64("lbm_introduction_times_version", 1);
610 args.set("lbm_introduction_times",
611 m_lbm_mgr.createIntroductionTimesString());
612 args.setU64("day_count", m_day_count);
616 if(!fs::safeWriteToFile(path, ss.str()))
618 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
620 throw SerializationError("Couldn't save env meta");
624 void ServerEnvironment::loadMeta()
626 // If file doesn't exist, load default environment metadata
627 if (!fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
628 infostream << "ServerEnvironment: Loading default environment metadata"
634 infostream << "ServerEnvironment: Loading environment metadata" << std::endl;
636 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
638 // Open file and deserialize
639 std::ifstream is(path.c_str(), std::ios_base::binary);
641 infostream << "ServerEnvironment::loadMeta(): Failed to open "
642 << path << std::endl;
643 throw SerializationError("Couldn't load env meta");
648 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
649 throw SerializationError("ServerEnvironment::loadMeta(): "
650 "EnvArgsEnd not found!");
654 m_game_time = args.getU64("game_time");
655 } catch (SettingNotFoundException &e) {
656 // Getting this is crucial, otherwise timestamps are useless
657 throw SerializationError("Couldn't load env meta game_time");
660 setTimeOfDay(args.exists("time_of_day") ?
661 // set day to early morning by default
662 args.getU64("time_of_day") : 5250);
664 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
665 // If missing, do as if clearObjects was never called
666 args.getU64("last_clear_objects_time") : 0;
668 std::string lbm_introduction_times;
670 u64 ver = args.getU64("lbm_introduction_times_version");
672 lbm_introduction_times = args.get("lbm_introduction_times");
674 infostream << "ServerEnvironment::loadMeta(): Non-supported"
675 << " introduction time version " << ver << std::endl;
677 } catch (SettingNotFoundException &e) {
678 // No problem, this is expected. Just continue with an empty string
680 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
682 m_day_count = args.exists("day_count") ?
683 args.getU64("day_count") : 0;
687 * called if env_meta.txt doesn't exist (e.g. new world)
689 void ServerEnvironment::loadDefaultMeta()
691 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
696 ActiveBlockModifier *abm;
698 std::vector<content_t> required_neighbors;
699 bool check_required_neighbors; // false if required_neighbors is known to be empty
705 ServerEnvironment *m_env;
706 std::vector<std::vector<ActiveABM> *> m_aabms;
708 ABMHandler(std::vector<ABMWithState> &abms,
709 float dtime_s, ServerEnvironment *env,
715 const NodeDefManager *ndef = env->getGameDef()->ndef();
716 for (ABMWithState &abmws : abms) {
717 ActiveBlockModifier *abm = abmws.abm;
718 float trigger_interval = abm->getTriggerInterval();
719 if(trigger_interval < 0.001)
720 trigger_interval = 0.001;
721 float actual_interval = dtime_s;
723 abmws.timer += dtime_s;
724 if(abmws.timer < trigger_interval)
726 abmws.timer -= trigger_interval;
727 actual_interval = trigger_interval;
729 float chance = abm->getTriggerChance();
734 if (abm->getSimpleCatchUp()) {
735 float intervals = actual_interval / trigger_interval;
738 aabm.chance = chance / intervals;
742 aabm.chance = chance;
746 const std::vector<std::string> &required_neighbors_s =
747 abm->getRequiredNeighbors();
748 for (const std::string &required_neighbor_s : required_neighbors_s) {
749 ndef->getIds(required_neighbor_s, aabm.required_neighbors);
751 aabm.check_required_neighbors = !required_neighbors_s.empty();
754 const std::vector<std::string> &contents_s = abm->getTriggerContents();
755 for (const std::string &content_s : contents_s) {
756 std::vector<content_t> ids;
757 ndef->getIds(content_s, ids);
758 for (content_t c : ids) {
759 if (c >= m_aabms.size())
760 m_aabms.resize(c + 256, NULL);
762 m_aabms[c] = new std::vector<ActiveABM>;
763 m_aabms[c]->push_back(aabm);
771 for (auto &aabms : m_aabms)
775 // Find out how many objects the given block and its neighbours contain.
776 // Returns the number of objects in the block, and also in 'wider' the
777 // number of objects in the block and all its neighbours. The latter
778 // may an estimate if any neighbours are unloaded.
779 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
782 u32 wider_unknown_count = 0;
783 for(s16 x=-1; x<=1; x++)
784 for(s16 y=-1; y<=1; y++)
785 for(s16 z=-1; z<=1; z++)
787 MapBlock *block2 = map->getBlockNoCreateNoEx(
788 block->getPos() + v3s16(x,y,z));
790 wider_unknown_count++;
793 wider += block2->m_static_objects.m_active.size()
794 + block2->m_static_objects.m_stored.size();
797 u32 active_object_count = block->m_static_objects.m_active.size();
798 u32 wider_known_count = 3*3*3 - wider_unknown_count;
799 wider += wider_unknown_count * wider / wider_known_count;
800 return active_object_count;
803 void apply(MapBlock *block, int &blocks_scanned, int &abms_run, int &blocks_cached)
805 if(m_aabms.empty() || block->isDummy())
808 // Check the content type cache first
809 // to see whether there are any ABMs
810 // to be run at all for this block.
811 if (block->contents_cached) {
813 bool run_abms = false;
814 for (content_t c : block->contents) {
815 if (c < m_aabms.size() && m_aabms[c]) {
824 block->contents.clear();
828 ServerMap *map = &m_env->getServerMap();
830 u32 active_object_count_wider;
831 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
832 m_env->m_added_objects = 0;
835 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
836 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
837 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
839 const MapNode &n = block->getNodeUnsafe(p0);
840 content_t c = n.getContent();
841 // Cache content types as we go
842 if (!block->contents_cached && !block->do_not_cache_contents) {
843 block->contents.insert(c);
844 if (block->contents.size() > 64) {
845 // Too many different nodes... don't try to cache
846 block->do_not_cache_contents = true;
847 block->contents.clear();
851 if (c >= m_aabms.size() || !m_aabms[c])
854 v3s16 p = p0 + block->getPosRelative();
855 for (ActiveABM &aabm : *m_aabms[c]) {
856 if (myrand() % aabm.chance != 0)
860 if (aabm.check_required_neighbors) {
862 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
863 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
864 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
869 if (block->isValidPosition(p1)) {
870 // if the neighbor is found on the same map block
871 // get it straight from there
872 const MapNode &n = block->getNodeUnsafe(p1);
875 // otherwise consult the map
876 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
879 if (CONTAINS(aabm.required_neighbors, c))
882 // No required neighbor found
888 // Call all the trigger variations
889 aabm.abm->trigger(m_env, p, n);
890 aabm.abm->trigger(m_env, p, n,
891 active_object_count, active_object_count_wider);
893 // Count surrounding objects again if the abms added any
894 if(m_env->m_added_objects > 0) {
895 active_object_count = countObjects(block, map, active_object_count_wider);
896 m_env->m_added_objects = 0;
900 block->contents_cached = !block->do_not_cache_contents;
904 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
906 // Reset usage timer immediately, otherwise a block that becomes active
907 // again at around the same time as it would normally be unloaded will
908 // get unloaded incorrectly. (I think this still leaves a small possibility
909 // of a race condition between this and server::AsyncRunStep, which only
910 // some kind of synchronisation will fix, but it at least reduces the window
911 // of opportunity for it to break from seconds to nanoseconds)
912 block->resetUsageTimer();
914 // Get time difference
916 u32 stamp = block->getTimestamp();
917 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
918 dtime_s = m_game_time - stamp;
919 dtime_s += additional_dtime;
921 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
922 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
924 // Remove stored static objects if clearObjects was called since block's timestamp
925 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
926 block->m_static_objects.m_stored.clear();
927 // do not set changed flag to avoid unnecessary mapblock writes
930 // Set current time as timestamp
931 block->setTimestampNoChangedFlag(m_game_time);
933 /*infostream<<"ServerEnvironment::activateBlock(): block is "
934 <<dtime_s<<" seconds old."<<std::endl;*/
936 // Activate stored objects
937 activateObjects(block, dtime_s);
939 /* Handle LoadingBlockModifiers */
940 m_lbm_mgr.applyLBMs(this, block, stamp);
943 std::vector<NodeTimer> elapsed_timers =
944 block->m_node_timers.step((float)dtime_s);
945 if (!elapsed_timers.empty()) {
947 for (const NodeTimer &elapsed_timer : elapsed_timers) {
948 n = block->getNodeNoEx(elapsed_timer.position);
949 v3s16 p = elapsed_timer.position + block->getPosRelative();
950 if (m_script->node_on_timer(p, n, elapsed_timer.elapsed))
951 block->setNodeTimer(NodeTimer(elapsed_timer.timeout, 0,
952 elapsed_timer.position));
957 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
959 m_abms.emplace_back(abm);
962 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
964 m_lbm_mgr.addLBMDef(lbm);
967 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
969 const NodeDefManager *ndef = m_server->ndef();
970 MapNode n_old = m_map->getNodeNoEx(p);
972 const ContentFeatures &cf_old = ndef->get(n_old);
975 if (cf_old.has_on_destruct)
976 m_script->node_on_destruct(p, n_old);
979 if (!m_map->addNodeWithEvent(p, n))
982 // Update active VoxelManipulator if a mapgen thread
983 m_map->updateVManip(p);
985 // Call post-destructor
986 if (cf_old.has_after_destruct)
987 m_script->node_after_destruct(p, n_old);
989 // Retrieve node content features
990 // if new node is same as old, reuse old definition to prevent a lookup
991 const ContentFeatures &cf_new = n_old == n ? cf_old : ndef->get(n);
994 if (cf_new.has_on_construct)
995 m_script->node_on_construct(p, n);
1000 bool ServerEnvironment::removeNode(v3s16 p)
1002 const NodeDefManager *ndef = m_server->ndef();
1003 MapNode n_old = m_map->getNodeNoEx(p);
1006 if (ndef->get(n_old).has_on_destruct)
1007 m_script->node_on_destruct(p, n_old);
1010 // This is slightly optimized compared to addNodeWithEvent(air)
1011 if (!m_map->removeNodeWithEvent(p))
1014 // Update active VoxelManipulator if a mapgen thread
1015 m_map->updateVManip(p);
1017 // Call post-destructor
1018 if (ndef->get(n_old).has_after_destruct)
1019 m_script->node_after_destruct(p, n_old);
1021 // Air doesn't require constructor
1025 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1027 if (!m_map->addNodeWithEvent(p, n, false))
1030 // Update active VoxelManipulator if a mapgen thread
1031 m_map->updateVManip(p);
1036 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
1039 for (auto &activeObject : m_active_objects) {
1040 ServerActiveObject* obj = activeObject.second;
1041 u16 id = activeObject.first;
1042 v3f objectpos = obj->getBasePosition();
1043 if (objectpos.getDistanceFrom(pos) > radius)
1045 objects.push_back(id);
1049 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1051 infostream << "ServerEnvironment::clearObjects(): "
1052 << "Removing all active objects" << std::endl;
1053 std::vector<u16> objects_to_remove;
1054 for (auto &it : m_active_objects) {
1056 ServerActiveObject* obj = it.second;
1057 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1060 // Delete static object if block is loaded
1061 deleteStaticFromBlock(obj, id, MOD_REASON_CLEAR_ALL_OBJECTS, true);
1063 // If known by some client, don't delete immediately
1064 if (obj->m_known_by_count > 0) {
1065 obj->m_pending_removal = true;
1069 // Tell the object about removal
1070 obj->removingFromEnvironment();
1071 // Deregister in scripting api
1072 m_script->removeObjectReference(obj);
1074 // Delete active object
1075 if (obj->environmentDeletes())
1077 // Id to be removed from m_active_objects
1078 objects_to_remove.push_back(id);
1081 // Remove references from m_active_objects
1082 for (u16 i : objects_to_remove) {
1083 m_active_objects.erase(i);
1086 // Get list of loaded blocks
1087 std::vector<v3s16> loaded_blocks;
1088 infostream << "ServerEnvironment::clearObjects(): "
1089 << "Listing all loaded blocks" << std::endl;
1090 m_map->listAllLoadedBlocks(loaded_blocks);
1091 infostream << "ServerEnvironment::clearObjects(): "
1092 << "Done listing all loaded blocks: "
1093 << loaded_blocks.size()<<std::endl;
1095 // Get list of loadable blocks
1096 std::vector<v3s16> loadable_blocks;
1097 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1098 infostream << "ServerEnvironment::clearObjects(): "
1099 << "Listing all loadable blocks" << std::endl;
1100 m_map->listAllLoadableBlocks(loadable_blocks);
1101 infostream << "ServerEnvironment::clearObjects(): "
1102 << "Done listing all loadable blocks: "
1103 << loadable_blocks.size() << std::endl;
1105 loadable_blocks = loaded_blocks;
1108 actionstream << "ServerEnvironment::clearObjects(): "
1109 << "Now clearing objects in " << loadable_blocks.size()
1110 << " blocks" << std::endl;
1112 // Grab a reference on each loaded block to avoid unloading it
1113 for (v3s16 p : loaded_blocks) {
1114 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1115 assert(block != NULL);
1119 // Remove objects in all loadable blocks
1120 u32 unload_interval = U32_MAX;
1121 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1122 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1123 unload_interval = MYMAX(unload_interval, 1);
1125 u32 report_interval = loadable_blocks.size() / 10;
1126 u32 num_blocks_checked = 0;
1127 u32 num_blocks_cleared = 0;
1128 u32 num_objs_cleared = 0;
1129 for (auto i = loadable_blocks.begin();
1130 i != loadable_blocks.end(); ++i) {
1132 MapBlock *block = m_map->emergeBlock(p, false);
1134 errorstream << "ServerEnvironment::clearObjects(): "
1135 << "Failed to emerge block " << PP(p) << std::endl;
1138 u32 num_stored = block->m_static_objects.m_stored.size();
1139 u32 num_active = block->m_static_objects.m_active.size();
1140 if (num_stored != 0 || num_active != 0) {
1141 block->m_static_objects.m_stored.clear();
1142 block->m_static_objects.m_active.clear();
1143 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1144 MOD_REASON_CLEAR_ALL_OBJECTS);
1145 num_objs_cleared += num_stored + num_active;
1146 num_blocks_cleared++;
1148 num_blocks_checked++;
1150 if (report_interval != 0 &&
1151 num_blocks_checked % report_interval == 0) {
1152 float percent = 100.0 * (float)num_blocks_checked /
1153 loadable_blocks.size();
1154 actionstream << "ServerEnvironment::clearObjects(): "
1155 << "Cleared " << num_objs_cleared << " objects"
1156 << " in " << num_blocks_cleared << " blocks ("
1157 << percent << "%)" << std::endl;
1159 if (num_blocks_checked % unload_interval == 0) {
1160 m_map->unloadUnreferencedBlocks();
1163 m_map->unloadUnreferencedBlocks();
1165 // Drop references that were added above
1166 for (v3s16 p : loaded_blocks) {
1167 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1172 m_last_clear_objects_time = m_game_time;
1174 actionstream << "ServerEnvironment::clearObjects(): "
1175 << "Finished: Cleared " << num_objs_cleared << " objects"
1176 << " in " << num_blocks_cleared << " blocks" << std::endl;
1179 void ServerEnvironment::step(float dtime)
1181 /* Step time of day */
1182 stepTimeOfDay(dtime);
1185 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1186 // really matter that much.
1187 static thread_local const float server_step =
1188 g_settings->getFloat("dedicated_server_step");
1189 m_recommended_send_interval = server_step;
1195 m_game_time_fraction_counter += dtime;
1196 u32 inc_i = (u32)m_game_time_fraction_counter;
1197 m_game_time += inc_i;
1198 m_game_time_fraction_counter -= (float)inc_i;
1205 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1206 for (RemotePlayer *player : m_players) {
1207 // Ignore disconnected players
1208 if (player->getPeerId() == PEER_ID_INEXISTENT)
1212 player->move(dtime, this, 100 * BS);
1217 Manage active block list
1219 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1220 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1222 Get player block positions
1224 std::vector<PlayerSAO*> players;
1225 for (RemotePlayer *player: m_players) {
1226 // Ignore disconnected players
1227 if (player->getPeerId() == PEER_ID_INEXISTENT)
1230 PlayerSAO *playersao = player->getPlayerSAO();
1233 players.push_back(playersao);
1237 Update list of active blocks, collecting changes
1239 // use active_object_send_range_blocks since that is max distance
1240 // for active objects sent the client anyway
1241 static thread_local const s16 active_object_range =
1242 g_settings->getS16("active_object_send_range_blocks");
1243 static thread_local const s16 active_block_range =
1244 g_settings->getS16("active_block_range");
1245 std::set<v3s16> blocks_removed;
1246 std::set<v3s16> blocks_added;
1247 m_active_blocks.update(players, active_block_range, active_object_range,
1248 blocks_removed, blocks_added);
1251 Handle removed blocks
1254 // Convert active objects that are no more in active blocks to static
1255 deactivateFarObjects(false);
1257 for (const v3s16 &p: blocks_removed) {
1258 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1262 // Set current time as timestamp (and let it set ChangedFlag)
1263 block->setTimestamp(m_game_time);
1270 for (const v3s16 &p: blocks_added) {
1271 MapBlock *block = m_map->getBlockOrEmerge(p);
1273 m_active_blocks.m_list.erase(p);
1274 m_active_blocks.m_abm_list.erase(p);
1278 activateBlock(block);
1283 Mess around in active blocks
1285 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1286 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1288 float dtime = m_cache_nodetimer_interval;
1290 for (const v3s16 &p: m_active_blocks.m_list) {
1291 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1295 // Reset block usage timer
1296 block->resetUsageTimer();
1298 // Set current time as timestamp
1299 block->setTimestampNoChangedFlag(m_game_time);
1300 // If time has changed much from the one on disk,
1301 // set block to be saved when it is unloaded
1302 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1303 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1304 MOD_REASON_BLOCK_EXPIRED);
1307 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1308 if (!elapsed_timers.empty()) {
1311 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1312 n = block->getNodeNoEx(elapsed_timer.position);
1313 p2 = elapsed_timer.position + block->getPosRelative();
1314 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1315 block->setNodeTimer(NodeTimer(
1316 elapsed_timer.timeout, 0, elapsed_timer.position));
1323 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1325 if (m_active_block_interval_overload_skip > 0) {
1326 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1327 m_active_block_interval_overload_skip--;
1330 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1331 TimeTaker timer("modify in active blocks per interval");
1333 // Initialize handling of ActiveBlockModifiers
1334 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1336 int blocks_scanned = 0;
1338 int blocks_cached = 0;
1339 for (const v3s16 &p : m_active_blocks.m_abm_list) {
1340 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1344 // Set current time as timestamp
1345 block->setTimestampNoChangedFlag(m_game_time);
1347 /* Handle ActiveBlockModifiers */
1348 abmhandler.apply(block, blocks_scanned, abms_run, blocks_cached);
1350 g_profiler->avg("SEnv: active blocks", m_active_blocks.m_abm_list.size());
1351 g_profiler->avg("SEnv: active blocks cached", blocks_cached);
1352 g_profiler->avg("SEnv: active blocks scanned for ABMs", blocks_scanned);
1353 g_profiler->avg("SEnv: ABMs run", abms_run);
1355 u32 time_ms = timer.stop(true);
1356 u32 max_time_ms = 200;
1357 if (time_ms > max_time_ms) {
1358 warningstream<<"active block modifiers took "
1359 <<time_ms<<"ms (longer than "
1360 <<max_time_ms<<"ms)"<<std::endl;
1361 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1366 Step script environment (run global on_step())
1368 m_script->environment_Step(dtime);
1374 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1375 //TimeTaker timer("Step active objects");
1377 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1379 // This helps the objects to send data at the same time
1380 bool send_recommended = false;
1381 m_send_recommended_timer += dtime;
1382 if(m_send_recommended_timer > getSendRecommendedInterval())
1384 m_send_recommended_timer -= getSendRecommendedInterval();
1385 send_recommended = true;
1388 for (auto &ao_it : m_active_objects) {
1389 ServerActiveObject* obj = ao_it.second;
1394 obj->step(dtime, send_recommended);
1395 // Read messages from object
1396 while (!obj->m_messages_out.empty()) {
1397 m_active_object_messages.push(obj->m_messages_out.front());
1398 obj->m_messages_out.pop();
1404 Manage active objects
1406 if (m_object_management_interval.step(dtime, 0.5)) {
1407 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1408 removeRemovedObjects();
1412 Manage particle spawner expiration
1414 if (m_particle_management_interval.step(dtime, 1.0)) {
1415 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1416 i != m_particle_spawners.end(); ) {
1417 //non expiring spawners
1418 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1424 if (i->second <= 0.f)
1425 m_particle_spawners.erase(i++);
1432 u32 ServerEnvironment::addParticleSpawner(float exptime)
1434 // Timers with lifetime 0 do not expire
1435 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1438 for (;;) { // look for unused particlespawner id
1440 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1441 if (f == m_particle_spawners.end()) {
1442 m_particle_spawners[id] = time;
1449 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1451 u32 id = addParticleSpawner(exptime);
1452 m_particle_spawner_attachments[id] = attached_id;
1453 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1454 obj->attachParticleSpawner(id);
1459 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1461 m_particle_spawners.erase(id);
1462 const auto &it = m_particle_spawner_attachments.find(id);
1463 if (it != m_particle_spawner_attachments.end()) {
1464 u16 obj_id = it->second;
1465 ServerActiveObject *sao = getActiveObject(obj_id);
1466 if (sao != NULL && remove_from_object) {
1467 sao->detachParticleSpawner(id);
1469 m_particle_spawner_attachments.erase(id);
1473 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1475 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1476 return (n != m_active_objects.end() ? n->second : NULL);
1480 * Verify if id is a free active object id
1482 * @return true if slot is free
1484 bool ServerEnvironment::isFreeServerActiveObjectId(u16 id) const
1489 return m_active_objects.find(id) == m_active_objects.end();
1493 * Retrieve the first free ActiveObject ID
1494 * @return free activeobject ID or 0 if none was found
1496 u16 ServerEnvironment::getFreeServerActiveObjectId()
1498 // try to reuse id's as late as possible
1499 static u16 last_used_id = 0;
1500 u16 startid = last_used_id;
1503 if (isFreeServerActiveObjectId(last_used_id))
1504 return last_used_id;
1506 if (last_used_id == startid)
1511 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1513 assert(object); // Pre-condition
1515 u16 id = addActiveObjectRaw(object, true, 0);
1520 Finds out what new objects have been added to
1521 inside a radius around a position
1523 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1525 std::set<u16> ¤t_objects,
1526 std::queue<u16> &added_objects)
1528 f32 radius_f = radius * BS;
1529 f32 player_radius_f = player_radius * BS;
1531 if (player_radius_f < 0)
1532 player_radius_f = 0;
1534 Go through the object list,
1535 - discard removed/deactivated objects,
1536 - discard objects that are too far away,
1537 - discard objects that are found in current_objects.
1538 - add remaining objects to added_objects
1540 for (auto &ao_it : m_active_objects) {
1541 u16 id = ao_it.first;
1544 ServerActiveObject *object = ao_it.second;
1548 if (object->isGone())
1551 f32 distance_f = object->getBasePosition().
1552 getDistanceFrom(playersao->getBasePosition());
1553 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1554 // Discard if too far
1555 if (distance_f > player_radius_f && player_radius_f != 0)
1557 } else if (distance_f > radius_f)
1560 // Discard if already on current_objects
1561 std::set<u16>::iterator n;
1562 n = current_objects.find(id);
1563 if(n != current_objects.end())
1565 // Add to added_objects
1566 added_objects.push(id);
1571 Finds out what objects have been removed from
1572 inside a radius around a position
1574 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1576 std::set<u16> ¤t_objects,
1577 std::queue<u16> &removed_objects)
1579 f32 radius_f = radius * BS;
1580 f32 player_radius_f = player_radius * BS;
1582 if (player_radius_f < 0)
1583 player_radius_f = 0;
1585 Go through current_objects; object is removed if:
1586 - object is not found in m_active_objects (this is actually an
1587 error condition; objects should be removed only after all clients
1588 have been informed about removal), or
1589 - object is to be removed or deactivated, or
1590 - object is too far away
1592 for (u16 id : current_objects) {
1593 ServerActiveObject *object = getActiveObject(id);
1595 if (object == NULL) {
1596 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1597 << " object in current_objects is NULL" << std::endl;
1598 removed_objects.push(id);
1602 if (object->isGone()) {
1603 removed_objects.push(id);
1607 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1608 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1609 if (distance_f <= player_radius_f || player_radius_f == 0)
1611 } else if (distance_f <= radius_f)
1614 // Object is no longer visible
1615 removed_objects.push(id);
1619 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1620 v3s16 blockpos, bool static_exists, v3s16 static_block)
1622 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1626 for (auto &so_it : block->m_static_objects.m_active) {
1627 // Get the ServerActiveObject counterpart to this StaticObject
1628 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it.first);
1629 if (ao_it == m_active_objects.end()) {
1630 // If this ever happens, there must be some kind of nasty bug.
1631 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1632 "Object from MapBlock::m_static_objects::m_active not found "
1633 "in m_active_objects";
1637 ServerActiveObject *sao = ao_it->second;
1638 sao->m_static_exists = static_exists;
1639 sao->m_static_block = static_block;
1643 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1645 if(m_active_object_messages.empty())
1646 return ActiveObjectMessage(0);
1648 ActiveObjectMessage message = m_active_object_messages.front();
1649 m_active_object_messages.pop();
1653 void ServerEnvironment::getSelectedActiveObjects(
1654 const core::line3d<f32> &shootline_on_map,
1655 std::vector<PointedThing> &objects)
1657 std::vector<u16> objectIds;
1658 getObjectsInsideRadius(objectIds, shootline_on_map.start,
1659 shootline_on_map.getLength() + 10.0f);
1660 const v3f line_vector = shootline_on_map.getVector();
1662 for (u16 objectId : objectIds) {
1663 ServerActiveObject* obj = getActiveObject(objectId);
1665 aabb3f selection_box;
1666 if (!obj->getSelectionBox(&selection_box))
1669 v3f pos = obj->getBasePosition();
1671 aabb3f offsetted_box(selection_box.MinEdge + pos,
1672 selection_box.MaxEdge + pos);
1674 v3f current_intersection;
1675 v3s16 current_normal;
1676 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1677 ¤t_intersection, ¤t_normal)) {
1678 objects.emplace_back(
1679 (s16) objectId, current_intersection, current_normal,
1680 (current_intersection - shootline_on_map.start).getLengthSQ());
1686 ************ Private methods *************
1689 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1690 bool set_changed, u32 dtime_s)
1692 assert(object); // Pre-condition
1693 if(object->getId() == 0){
1694 u16 new_id = getFreeServerActiveObjectId();
1697 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1698 <<"no free ids available"<<std::endl;
1699 if(object->environmentDeletes())
1703 object->setId(new_id);
1706 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1707 <<"supplied with id "<<object->getId()<<std::endl;
1710 if(!isFreeServerActiveObjectId(object->getId())) {
1711 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1712 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1713 if(object->environmentDeletes())
1718 if (objectpos_over_limit(object->getBasePosition())) {
1719 v3f p = object->getBasePosition();
1720 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1721 << "object position (" << p.X << "," << p.Y << "," << p.Z
1722 << ") outside maximum range" << std::endl;
1723 if (object->environmentDeletes())
1728 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1729 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1731 m_active_objects[object->getId()] = object;
1733 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1734 <<"Added id="<<object->getId()<<"; there are now "
1735 <<m_active_objects.size()<<" active objects."
1738 // Register reference in scripting api (must be done before post-init)
1739 m_script->addObjectReference(object);
1740 // Post-initialize object
1741 object->addedToEnvironment(dtime_s);
1743 // Add static data to block
1744 if(object->isStaticAllowed())
1746 // Add static object to active static list of the block
1747 v3f objectpos = object->getBasePosition();
1748 StaticObject s_obj(object, objectpos);
1749 // Add to the block where the object is located in
1750 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1751 MapBlock *block = m_map->emergeBlock(blockpos);
1753 block->m_static_objects.m_active[object->getId()] = s_obj;
1754 object->m_static_exists = true;
1755 object->m_static_block = blockpos;
1758 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1759 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1761 v3s16 p = floatToInt(objectpos, BS);
1762 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1763 <<"could not emerge block for storing id="<<object->getId()
1764 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1768 return object->getId();
1772 Remove objects that satisfy (isGone() && m_known_by_count==0)
1774 void ServerEnvironment::removeRemovedObjects()
1776 std::vector<u16> objects_to_remove;
1777 for (auto &ao_it : m_active_objects) {
1778 u16 id = ao_it.first;
1779 ServerActiveObject* obj = ao_it.second;
1781 // This shouldn't happen but check it
1783 errorstream << "ServerEnvironment::removeRemovedObjects(): "
1784 << "NULL object found. id=" << id << std::endl;
1785 objects_to_remove.push_back(id);
1790 We will handle objects marked for removal or deactivation
1796 Delete static data from block if removed
1798 if (obj->m_pending_removal)
1799 deleteStaticFromBlock(obj, id, MOD_REASON_REMOVE_OBJECTS_REMOVE, false);
1801 // If still known by clients, don't actually remove. On some future
1802 // invocation this will be 0, which is when removal will continue.
1803 if(obj->m_known_by_count > 0)
1807 Move static data from active to stored if deactivated
1809 if (!obj->m_pending_removal && obj->m_static_exists) {
1810 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1812 std::map<u16, StaticObject>::iterator i =
1813 block->m_static_objects.m_active.find(id);
1814 if (i != block->m_static_objects.m_active.end()) {
1815 block->m_static_objects.m_stored.push_back(i->second);
1816 block->m_static_objects.m_active.erase(id);
1817 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1818 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1820 warningstream << "ServerEnvironment::removeRemovedObjects(): "
1821 << "id=" << id << " m_static_exists=true but "
1822 << "static data doesn't actually exist in "
1823 << PP(obj->m_static_block) << std::endl;
1826 infostream << "Failed to emerge block from which an object to "
1827 << "be deactivated was loaded from. id=" << id << std::endl;
1831 // Tell the object about removal
1832 obj->removingFromEnvironment();
1833 // Deregister in scripting api
1834 m_script->removeObjectReference(obj);
1837 if(obj->environmentDeletes())
1840 objects_to_remove.push_back(id);
1842 // Remove references from m_active_objects
1843 for (u16 i : objects_to_remove) {
1844 m_active_objects.erase(i);
1848 static void print_hexdump(std::ostream &o, const std::string &data)
1850 const int linelength = 16;
1851 for(int l=0; ; l++){
1852 int i0 = linelength * l;
1853 bool at_end = false;
1854 int thislinelength = linelength;
1855 if(i0 + thislinelength > (int)data.size()){
1856 thislinelength = data.size() - i0;
1859 for(int di=0; di<linelength; di++){
1862 if(di<thislinelength)
1863 snprintf(buf, 4, "%.2x ", data[i]);
1865 snprintf(buf, 4, " ");
1869 for(int di=0; di<thislinelength; di++){
1883 Convert stored objects from blocks near the players to active.
1885 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1890 // Ignore if no stored objects (to not set changed flag)
1891 if(block->m_static_objects.m_stored.empty())
1894 verbosestream<<"ServerEnvironment::activateObjects(): "
1895 <<"activating objects of block "<<PP(block->getPos())
1896 <<" ("<<block->m_static_objects.m_stored.size()
1897 <<" objects)"<<std::endl;
1898 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1900 errorstream<<"suspiciously large amount of objects detected: "
1901 <<block->m_static_objects.m_stored.size()<<" in "
1902 <<PP(block->getPos())
1903 <<"; removing all of them."<<std::endl;
1904 // Clear stored list
1905 block->m_static_objects.m_stored.clear();
1906 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1907 MOD_REASON_TOO_MANY_OBJECTS);
1911 // Activate stored objects
1912 std::vector<StaticObject> new_stored;
1913 for (const StaticObject &s_obj : block->m_static_objects.m_stored) {
1914 // Create an active object from the data
1915 ServerActiveObject *obj = ServerActiveObject::create
1916 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1917 // If couldn't create object, store static data back.
1919 errorstream<<"ServerEnvironment::activateObjects(): "
1920 <<"failed to create active object from static object "
1921 <<"in block "<<PP(s_obj.pos/BS)
1922 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1923 print_hexdump(verbosestream, s_obj.data);
1925 new_stored.push_back(s_obj);
1928 verbosestream<<"ServerEnvironment::activateObjects(): "
1929 <<"activated static object pos="<<PP(s_obj.pos/BS)
1930 <<" type="<<(int)s_obj.type<<std::endl;
1931 // This will also add the object to the active static list
1932 addActiveObjectRaw(obj, false, dtime_s);
1935 // Clear stored list
1936 block->m_static_objects.m_stored.clear();
1937 // Add leftover failed stuff to stored list
1938 for (const StaticObject &s_obj : new_stored) {
1939 block->m_static_objects.m_stored.push_back(s_obj);
1943 Note: Block hasn't really been modified here.
1944 The objects have just been activated and moved from the stored
1945 static list to the active static list.
1946 As such, the block is essentially the same.
1947 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1948 Otherwise there would be a huge amount of unnecessary I/O.
1953 Convert objects that are not standing inside active blocks to static.
1955 If m_known_by_count != 0, active object is not deleted, but static
1956 data is still updated.
1958 If force_delete is set, active object is deleted nevertheless. It
1959 shall only be set so in the destructor of the environment.
1961 If block wasn't generated (not in memory or on disk),
1963 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1965 std::vector<u16> objects_to_remove;
1966 for (auto &ao_it : m_active_objects) {
1967 // force_delete might be overriden per object
1968 bool force_delete = _force_delete;
1970 ServerActiveObject* obj = ao_it.second;
1973 // Do not deactivate if static data creation not allowed
1974 if(!force_delete && !obj->isStaticAllowed())
1977 // removeRemovedObjects() is responsible for these
1978 if(!force_delete && obj->isGone())
1981 u16 id = ao_it.first;
1982 v3f objectpos = obj->getBasePosition();
1984 // The block in which the object resides in
1985 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1987 // If object's static data is stored in a deactivated block and object
1988 // is actually located in an active block, re-save to the block in
1989 // which the object is actually located in.
1991 obj->m_static_exists &&
1992 !m_active_blocks.contains(obj->m_static_block) &&
1993 m_active_blocks.contains(blockpos_o))
1995 // Delete from block where object was located
1996 deleteStaticFromBlock(obj, id, MOD_REASON_STATIC_DATA_REMOVED, false);
1998 StaticObject s_obj(obj, objectpos);
1999 // Save to block where object is located
2000 saveStaticToBlock(blockpos_o, id, obj, s_obj, MOD_REASON_STATIC_DATA_ADDED);
2005 // If block is still active, don't remove
2006 if(!force_delete && m_active_blocks.contains(blockpos_o))
2009 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2010 << "deactivating object id=" << id << " on inactive block "
2011 << PP(blockpos_o) << std::endl;
2013 // If known by some client, don't immediately delete.
2014 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2017 Update the static data
2019 if (obj->isStaticAllowed()) {
2020 // Create new static object
2021 StaticObject s_obj(obj, objectpos);
2023 bool stays_in_same_block = false;
2024 bool data_changed = true;
2026 // Check if static data has changed considerably
2027 if (obj->m_static_exists) {
2028 if (obj->m_static_block == blockpos_o)
2029 stays_in_same_block = true;
2031 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2034 std::map<u16, StaticObject>::iterator n =
2035 block->m_static_objects.m_active.find(id);
2036 if (n != block->m_static_objects.m_active.end()) {
2037 StaticObject static_old = n->second;
2039 float save_movem = obj->getMinimumSavedMovement();
2041 if (static_old.data == s_obj.data &&
2042 (static_old.pos - objectpos).getLength() < save_movem)
2043 data_changed = false;
2045 warningstream << "ServerEnvironment::deactivateFarObjects(): "
2046 << "id=" << id << " m_static_exists=true but "
2047 << "static data doesn't actually exist in "
2048 << PP(obj->m_static_block) << std::endl;
2054 While changes are always saved, blocks are only marked as modified
2055 if the object has moved or different staticdata. (see above)
2057 bool shall_be_written = (!stays_in_same_block || data_changed);
2058 u32 reason = shall_be_written ? MOD_REASON_STATIC_DATA_CHANGED : MOD_REASON_UNKNOWN;
2060 // Delete old static object
2061 deleteStaticFromBlock(obj, id, reason, false);
2063 // Add to the block where the object is located in
2064 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2065 u16 store_id = pending_delete ? id : 0;
2066 if (!saveStaticToBlock(blockpos, store_id, obj, s_obj, reason))
2067 force_delete = true;
2071 If known by some client, set pending deactivation.
2072 Otherwise delete it immediately.
2074 if(pending_delete && !force_delete)
2076 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2077 << "object id=" << id << " is known by clients"
2078 << "; not deleting yet" << std::endl;
2080 obj->m_pending_deactivation = true;
2083 verbosestream << "ServerEnvironment::deactivateFarObjects(): "
2084 << "object id=" << id << " is not known by clients"
2085 << "; deleting" << std::endl;
2087 // Tell the object about removal
2088 obj->removingFromEnvironment();
2089 // Deregister in scripting api
2090 m_script->removeObjectReference(obj);
2092 // Delete active object
2093 if(obj->environmentDeletes())
2095 // Id to be removed from m_active_objects
2096 objects_to_remove.push_back(id);
2099 // Remove references from m_active_objects
2100 for (u16 i : objects_to_remove) {
2101 m_active_objects.erase(i);
2105 void ServerEnvironment::deleteStaticFromBlock(
2106 ServerActiveObject *obj, u16 id, u32 mod_reason, bool no_emerge)
2108 if (!obj->m_static_exists)
2113 block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
2115 block = m_map->emergeBlock(obj->m_static_block, false);
2118 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2119 << " when deleting static data of object from it. id=" << id << std::endl;
2123 block->m_static_objects.remove(id);
2124 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2125 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2127 obj->m_static_exists = false;
2130 bool ServerEnvironment::saveStaticToBlock(
2131 v3s16 blockpos, u16 store_id,
2132 ServerActiveObject *obj, const StaticObject &s_obj,
2135 MapBlock *block = nullptr;
2137 block = m_map->emergeBlock(blockpos);
2138 } catch (InvalidPositionException &e) {
2139 // Handled via NULL pointer
2140 // NOTE: emergeBlock's failure is usually determined by it
2141 // actually returning NULL
2145 errorstream << "ServerEnv: Failed to emerge block " << PP(obj->m_static_block)
2146 << " when saving static data of object to it. id=" << store_id << std::endl;
2149 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2150 warningstream << "ServerEnv: Trying to store id = " << store_id
2151 << " statically but block " << PP(blockpos)
2152 << " already contains "
2153 << block->m_static_objects.m_stored.size()
2154 << " objects." << std::endl;
2158 block->m_static_objects.insert(store_id, s_obj);
2159 if (mod_reason != MOD_REASON_UNKNOWN) // Do not mark as modified if requested
2160 block->raiseModified(MOD_STATE_WRITE_NEEDED, mod_reason);
2162 obj->m_static_exists = true;
2163 obj->m_static_block = blockpos;
2168 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2169 const std::string &savedir, const Settings &conf)
2172 if (name == "sqlite3")
2173 return new PlayerDatabaseSQLite3(savedir);
2175 if (name == "dummy")
2176 return new Database_Dummy();
2178 if (name == "postgresql") {
2179 std::string connect_string;
2180 conf.getNoEx("pgsql_player_connection", connect_string);
2181 return new PlayerDatabasePostgreSQL(connect_string);
2184 if (name == "files")
2185 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2187 throw BaseException(std::string("Database backend ") + name + " not supported.");
2190 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2191 const Settings &cmd_args)
2193 std::string migrate_to = cmd_args.get("migrate-players");
2195 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2196 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2197 errorstream << "Cannot read world.mt!" << std::endl;
2201 if (!world_mt.exists("player_backend")) {
2202 errorstream << "Please specify your current backend in world.mt:"
2204 << " player_backend = {files|sqlite3|postgresql}"
2209 std::string backend = world_mt.get("player_backend");
2210 if (backend == migrate_to) {
2211 errorstream << "Cannot migrate: new backend is same"
2212 << " as the old one" << std::endl;
2216 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2219 if (backend == "files") {
2220 // Create backup directory
2221 fs::CreateDir(players_backup_path);
2225 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2226 game_params.world_path, world_mt);
2227 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2228 game_params.world_path, world_mt);
2230 std::vector<std::string> player_list;
2231 srcdb->listPlayers(player_list);
2232 for (std::vector<std::string>::const_iterator it = player_list.begin();
2233 it != player_list.end(); ++it) {
2234 actionstream << "Migrating player " << it->c_str() << std::endl;
2235 RemotePlayer player(it->c_str(), NULL);
2236 PlayerSAO playerSAO(NULL, &player, 15000, false);
2238 srcdb->loadPlayer(&player, &playerSAO);
2240 playerSAO.finalize(&player, std::set<std::string>());
2241 player.setPlayerSAO(&playerSAO);
2243 dstdb->savePlayer(&player);
2245 // For files source, move player files to backup dir
2246 if (backend == "files") {
2248 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2249 players_backup_path + DIR_DELIM + (*it));
2253 actionstream << "Successfully migrated " << player_list.size() << " players"
2255 world_mt.set("player_backend", migrate_to);
2256 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2257 errorstream << "Failed to update world.mt!" << std::endl;
2259 actionstream << "world.mt updated" << std::endl;
2261 // When migration is finished from file backend, remove players directory if empty
2262 if (backend == "files") {
2263 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2270 } catch (BaseException &e) {
2271 errorstream << "An error occured during migration: " << e.what() << std::endl;