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 == true,
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 == false,
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 == false,
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(std::vector<v3s16>::iterator i = active_positions.begin();
304 i != active_positions.end(); ++i)
306 fillRadiusBlock(*i, radius, newlist);
310 Find out which blocks on the old list are not on the new list
312 // Go through old list
313 for(std::set<v3s16>::iterator i = m_list.begin();
314 i != m_list.end(); ++i)
317 // If not on new list, it's been removed
318 if(newlist.find(p) == newlist.end())
319 blocks_removed.insert(p);
323 Find out which blocks on the new list are not on the old list
325 // Go through new list
326 for(std::set<v3s16>::iterator i = newlist.begin();
327 i != newlist.end(); ++i)
330 // If not on old list, it's been added
331 if(m_list.find(p) == m_list.end())
332 blocks_added.insert(p);
339 for(std::set<v3s16>::iterator i = newlist.begin();
340 i != newlist.end(); ++i)
351 ServerEnvironment::ServerEnvironment(ServerMap *map,
352 ServerScripting *scriptIface, Server *server,
353 const std::string &path_world):
356 m_script(scriptIface),
358 m_path_world(path_world)
360 // Determine which database backend to use
361 std::string conf_path = path_world + DIR_DELIM + "world.mt";
363 bool succeeded = conf.readConfigFile(conf_path.c_str());
364 if (!succeeded || !conf.exists("player_backend")) {
365 // fall back to files
366 conf.set("player_backend", "files");
367 warningstream << "/!\\ You are using old player file backend. "
368 << "This backend is deprecated and will be removed in next release /!\\"
369 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
370 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
372 if (!conf.updateConfigFile(conf_path.c_str())) {
373 errorstream << "ServerEnvironment::ServerEnvironment(): "
374 << "Failed to update world.mt!" << std::endl;
378 std::string name = "";
379 conf.getNoEx("player_backend", name);
380 m_player_database = openPlayerDatabase(name, path_world, conf);
383 ServerEnvironment::~ServerEnvironment()
385 // Clear active block list.
386 // This makes the next one delete all active objects.
387 m_active_blocks.clear();
389 // Convert all objects to static and delete the active objects
390 deactivateFarObjects(true);
395 // Delete ActiveBlockModifiers
396 for (std::vector<ABMWithState>::iterator
397 i = m_abms.begin(); i != m_abms.end(); ++i){
401 // Deallocate players
402 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
403 i != m_players.end(); ++i) {
407 delete m_player_database;
410 Map & ServerEnvironment::getMap()
415 ServerMap & ServerEnvironment::getServerMap()
420 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
422 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
423 i != m_players.end(); ++i) {
424 RemotePlayer *player = *i;
425 if (player->peer_id == peer_id)
431 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
433 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
434 i != m_players.end(); ++i) {
435 RemotePlayer *player = *i;
436 if (strcmp(player->getName(), name) == 0)
442 void ServerEnvironment::addPlayer(RemotePlayer *player)
444 DSTACK(FUNCTION_NAME);
446 Check that peer_ids are unique.
447 Also check that names are unique.
448 Exception: there can be multiple players with peer_id=0
450 // If peer id is non-zero, it has to be unique.
451 if (player->peer_id != 0)
452 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
453 // Name has to be unique.
454 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
456 m_players.push_back(player);
459 void ServerEnvironment::removePlayer(RemotePlayer *player)
461 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
462 it != m_players.end(); ++it) {
463 if ((*it) == player) {
471 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
473 return m_player_database->removePlayer(name);
476 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
478 float distance = pos1.getDistanceFrom(pos2);
480 //calculate normalized direction vector
481 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
482 (pos2.Y - pos1.Y)/distance,
483 (pos2.Z - pos1.Z)/distance);
485 //find out if there's a node on path between pos1 and pos2
486 for (float i = 1; i < distance; i += stepsize) {
487 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
488 normalized_vector.Y * i,
489 normalized_vector.Z * i) +pos1,BS);
491 MapNode n = getMap().getNodeNoEx(pos);
493 if(n.param0 != CONTENT_AIR) {
503 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
504 const std::string &str_reason, bool reconnect)
506 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
507 it != m_players.end(); ++it) {
508 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
509 m_server->DenyAccessVerCompliant(player->peer_id,
510 player->protocol_version, reason, str_reason, reconnect);
514 void ServerEnvironment::saveLoadedPlayers()
516 std::string players_path = m_path_world + DIR_DELIM + "players";
517 fs::CreateDir(players_path);
519 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
520 it != m_players.end();
522 if ((*it)->checkModified() ||
523 ((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) {
525 m_player_database->savePlayer(*it);
526 } catch (DatabaseException &e) {
527 errorstream << "Failed to save player " << (*it)->getName() << " exception: "
528 << e.what() << std::endl;
535 void ServerEnvironment::savePlayer(RemotePlayer *player)
538 m_player_database->savePlayer(player);
539 } catch (DatabaseException &e) {
540 errorstream << "Failed to save player " << player->getName() << " exception: "
541 << e.what() << std::endl;
546 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
547 u16 peer_id, bool is_singleplayer)
549 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
550 // Create player if it doesn't exist
551 if (!m_player_database->loadPlayer(player, playersao)) {
553 // Set player position
554 infostream << "Server: Finding spawn place for player \""
555 << player->getName() << "\"" << std::endl;
556 playersao->setBasePosition(m_server->findSpawnPos());
558 // Make sure the player is saved
559 player->setModified(true);
561 // If the player exists, ensure that they respawn inside legal bounds
562 // This fixes an assert crash when the player can't be added
563 // to the environment
564 ServerMap &map = getServerMap();
565 if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) {
566 actionstream << "Respawn position for player \""
567 << player->getName() << "\" outside limits, resetting" << std::endl;
568 playersao->setBasePosition(m_server->findSpawnPos());
572 // Add player to environment
575 /* Clean up old HUD elements from previous sessions */
578 /* Add object to environment */
579 addActiveObject(playersao);
584 void ServerEnvironment::saveMeta()
586 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
588 // Open file and serialize
589 std::ostringstream ss(std::ios_base::binary);
592 args.setU64("game_time", m_game_time);
593 args.setU64("time_of_day", getTimeOfDay());
594 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
595 args.setU64("lbm_introduction_times_version", 1);
596 args.set("lbm_introduction_times",
597 m_lbm_mgr.createIntroductionTimesString());
598 args.setU64("day_count", m_day_count);
602 if(!fs::safeWriteToFile(path, ss.str()))
604 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
606 throw SerializationError("Couldn't save env meta");
610 void ServerEnvironment::loadMeta()
612 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
614 // Open file and deserialize
615 std::ifstream is(path.c_str(), std::ios_base::binary);
617 infostream << "ServerEnvironment::loadMeta(): Failed to open "
618 << path << std::endl;
619 throw SerializationError("Couldn't load env meta");
624 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
625 throw SerializationError("ServerEnvironment::loadMeta(): "
626 "EnvArgsEnd not found!");
630 m_game_time = args.getU64("game_time");
631 } catch (SettingNotFoundException &e) {
632 // Getting this is crucial, otherwise timestamps are useless
633 throw SerializationError("Couldn't load env meta game_time");
636 setTimeOfDay(args.exists("time_of_day") ?
637 // set day to early morning by default
638 args.getU64("time_of_day") : 5250);
640 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
641 // If missing, do as if clearObjects was never called
642 args.getU64("last_clear_objects_time") : 0;
644 std::string lbm_introduction_times = "";
646 u64 ver = args.getU64("lbm_introduction_times_version");
648 lbm_introduction_times = args.get("lbm_introduction_times");
650 infostream << "ServerEnvironment::loadMeta(): Non-supported"
651 << " introduction time version " << ver << std::endl;
653 } catch (SettingNotFoundException &e) {
654 // No problem, this is expected. Just continue with an empty string
656 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
658 m_day_count = args.exists("day_count") ?
659 args.getU64("day_count") : 0;
662 void ServerEnvironment::loadDefaultMeta()
664 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
669 ActiveBlockModifier *abm;
671 std::set<content_t> required_neighbors;
677 ServerEnvironment *m_env;
678 std::vector<std::vector<ActiveABM> *> m_aabms;
680 ABMHandler(std::vector<ABMWithState> &abms,
681 float dtime_s, ServerEnvironment *env,
687 INodeDefManager *ndef = env->getGameDef()->ndef();
688 for(std::vector<ABMWithState>::iterator
689 i = abms.begin(); i != abms.end(); ++i) {
690 ActiveBlockModifier *abm = i->abm;
691 float trigger_interval = abm->getTriggerInterval();
692 if(trigger_interval < 0.001)
693 trigger_interval = 0.001;
694 float actual_interval = dtime_s;
697 if(i->timer < trigger_interval)
699 i->timer -= trigger_interval;
700 actual_interval = trigger_interval;
702 float chance = abm->getTriggerChance();
707 if (abm->getSimpleCatchUp()) {
708 float intervals = actual_interval / trigger_interval;
711 aabm.chance = chance / intervals;
715 aabm.chance = chance;
719 const std::set<std::string> &required_neighbors_s =
720 abm->getRequiredNeighbors();
721 for (std::set<std::string>::iterator rn = required_neighbors_s.begin();
722 rn != required_neighbors_s.end(); ++rn) {
723 ndef->getIds(*rn, aabm.required_neighbors);
727 const std::set<std::string> &contents_s = abm->getTriggerContents();
728 for (std::set<std::string>::iterator cs = contents_s.begin();
729 cs != contents_s.end(); ++cs) {
730 std::set<content_t> ids;
731 ndef->getIds(*cs, ids);
732 for (std::set<content_t>::const_iterator k = ids.begin();
733 k != ids.end(); ++k) {
735 if (c >= m_aabms.size())
736 m_aabms.resize(c + 256, NULL);
738 m_aabms[c] = new std::vector<ActiveABM>;
739 m_aabms[c]->push_back(aabm);
747 for (size_t i = 0; i < m_aabms.size(); i++)
751 // Find out how many objects the given block and its neighbours contain.
752 // Returns the number of objects in the block, and also in 'wider' the
753 // number of objects in the block and all its neighbours. The latter
754 // may an estimate if any neighbours are unloaded.
755 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
758 u32 wider_unknown_count = 0;
759 for(s16 x=-1; x<=1; x++)
760 for(s16 y=-1; y<=1; y++)
761 for(s16 z=-1; z<=1; z++)
763 MapBlock *block2 = map->getBlockNoCreateNoEx(
764 block->getPos() + v3s16(x,y,z));
766 wider_unknown_count++;
769 wider += block2->m_static_objects.m_active.size()
770 + block2->m_static_objects.m_stored.size();
773 u32 active_object_count = block->m_static_objects.m_active.size();
774 u32 wider_known_count = 3*3*3 - wider_unknown_count;
775 wider += wider_unknown_count * wider / wider_known_count;
776 return active_object_count;
779 void apply(MapBlock *block)
781 if(m_aabms.empty() || block->isDummy())
784 ServerMap *map = &m_env->getServerMap();
786 u32 active_object_count_wider;
787 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
788 m_env->m_added_objects = 0;
791 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
792 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
793 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
795 const MapNode &n = block->getNodeUnsafe(p0);
796 content_t c = n.getContent();
798 if (c >= m_aabms.size() || !m_aabms[c])
801 v3s16 p = p0 + block->getPosRelative();
802 for(std::vector<ActiveABM>::iterator
803 i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
804 if(myrand() % i->chance != 0)
808 if(!i->required_neighbors.empty())
811 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
812 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
813 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
818 if (block->isValidPosition(p1)) {
819 // if the neighbor is found on the same map block
820 // get it straight from there
821 const MapNode &n = block->getNodeUnsafe(p1);
824 // otherwise consult the map
825 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
828 std::set<content_t>::const_iterator k;
829 k = i->required_neighbors.find(c);
830 if(k != i->required_neighbors.end()){
834 // No required neighbor found
839 // Call all the trigger variations
840 i->abm->trigger(m_env, p, n);
841 i->abm->trigger(m_env, p, n,
842 active_object_count, active_object_count_wider);
844 // Count surrounding objects again if the abms added any
845 if(m_env->m_added_objects > 0) {
846 active_object_count = countObjects(block, map, active_object_count_wider);
847 m_env->m_added_objects = 0;
854 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
856 // Reset usage timer immediately, otherwise a block that becomes active
857 // again at around the same time as it would normally be unloaded will
858 // get unloaded incorrectly. (I think this still leaves a small possibility
859 // of a race condition between this and server::AsyncRunStep, which only
860 // some kind of synchronisation will fix, but it at least reduces the window
861 // of opportunity for it to break from seconds to nanoseconds)
862 block->resetUsageTimer();
864 // Get time difference
866 u32 stamp = block->getTimestamp();
867 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
868 dtime_s = m_game_time - stamp;
869 dtime_s += additional_dtime;
871 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
872 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
874 // Remove stored static objects if clearObjects was called since block's timestamp
875 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
876 block->m_static_objects.m_stored.clear();
877 // do not set changed flag to avoid unnecessary mapblock writes
880 // Set current time as timestamp
881 block->setTimestampNoChangedFlag(m_game_time);
883 /*infostream<<"ServerEnvironment::activateBlock(): block is "
884 <<dtime_s<<" seconds old."<<std::endl;*/
886 // Activate stored objects
887 activateObjects(block, dtime_s);
889 /* Handle LoadingBlockModifiers */
890 m_lbm_mgr.applyLBMs(this, block, stamp);
893 std::vector<NodeTimer> elapsed_timers =
894 block->m_node_timers.step((float)dtime_s);
895 if (!elapsed_timers.empty()) {
897 for (std::vector<NodeTimer>::iterator
898 i = elapsed_timers.begin();
899 i != elapsed_timers.end(); ++i){
900 n = block->getNodeNoEx(i->position);
901 v3s16 p = i->position + block->getPosRelative();
902 if (m_script->node_on_timer(p, n, i->elapsed))
903 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
907 /* Handle ActiveBlockModifiers */
908 ABMHandler abmhandler(m_abms, dtime_s, this, false);
909 abmhandler.apply(block);
912 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
914 m_abms.push_back(ABMWithState(abm));
917 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
919 m_lbm_mgr.addLBMDef(lbm);
922 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
924 INodeDefManager *ndef = m_server->ndef();
925 MapNode n_old = m_map->getNodeNoEx(p);
928 if (ndef->get(n_old).has_on_destruct)
929 m_script->node_on_destruct(p, n_old);
932 if (!m_map->addNodeWithEvent(p, n))
935 // Update active VoxelManipulator if a mapgen thread
936 m_map->updateVManip(p);
938 // Call post-destructor
939 if (ndef->get(n_old).has_after_destruct)
940 m_script->node_after_destruct(p, n_old);
943 if (ndef->get(n).has_on_construct)
944 m_script->node_on_construct(p, n);
949 bool ServerEnvironment::removeNode(v3s16 p)
951 INodeDefManager *ndef = m_server->ndef();
952 MapNode n_old = m_map->getNodeNoEx(p);
955 if (ndef->get(n_old).has_on_destruct)
956 m_script->node_on_destruct(p, n_old);
959 // This is slightly optimized compared to addNodeWithEvent(air)
960 if (!m_map->removeNodeWithEvent(p))
963 // Update active VoxelManipulator if a mapgen thread
964 m_map->updateVManip(p);
966 // Call post-destructor
967 if (ndef->get(n_old).has_after_destruct)
968 m_script->node_after_destruct(p, n_old);
970 // Air doesn't require constructor
974 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
976 if (!m_map->addNodeWithEvent(p, n, false))
979 // Update active VoxelManipulator if a mapgen thread
980 m_map->updateVManip(p);
985 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
988 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
989 i != m_active_objects.end(); ++i) {
990 ServerActiveObject* obj = i->second;
992 v3f objectpos = obj->getBasePosition();
993 if (objectpos.getDistanceFrom(pos) > radius)
995 objects.push_back(id);
999 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1001 infostream << "ServerEnvironment::clearObjects(): "
1002 << "Removing all active objects" << std::endl;
1003 std::vector<u16> objects_to_remove;
1004 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1005 i != m_active_objects.end(); ++i) {
1006 ServerActiveObject* obj = i->second;
1007 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1010 // Delete static object if block is loaded
1011 if (obj->m_static_exists) {
1012 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1014 block->m_static_objects.remove(id);
1015 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1016 MOD_REASON_CLEAR_ALL_OBJECTS);
1017 obj->m_static_exists = false;
1020 // If known by some client, don't delete immediately
1021 if (obj->m_known_by_count > 0) {
1022 obj->m_pending_deactivation = true;
1023 obj->m_removed = true;
1027 // Tell the object about removal
1028 obj->removingFromEnvironment();
1029 // Deregister in scripting api
1030 m_script->removeObjectReference(obj);
1032 // Delete active object
1033 if (obj->environmentDeletes())
1035 // Id to be removed from m_active_objects
1036 objects_to_remove.push_back(id);
1039 // Remove references from m_active_objects
1040 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1041 i != objects_to_remove.end(); ++i) {
1042 m_active_objects.erase(*i);
1045 // Get list of loaded blocks
1046 std::vector<v3s16> loaded_blocks;
1047 infostream << "ServerEnvironment::clearObjects(): "
1048 << "Listing all loaded blocks" << std::endl;
1049 m_map->listAllLoadedBlocks(loaded_blocks);
1050 infostream << "ServerEnvironment::clearObjects(): "
1051 << "Done listing all loaded blocks: "
1052 << loaded_blocks.size()<<std::endl;
1054 // Get list of loadable blocks
1055 std::vector<v3s16> loadable_blocks;
1056 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1057 infostream << "ServerEnvironment::clearObjects(): "
1058 << "Listing all loadable blocks" << std::endl;
1059 m_map->listAllLoadableBlocks(loadable_blocks);
1060 infostream << "ServerEnvironment::clearObjects(): "
1061 << "Done listing all loadable blocks: "
1062 << loadable_blocks.size() << std::endl;
1064 loadable_blocks = loaded_blocks;
1067 infostream << "ServerEnvironment::clearObjects(): "
1068 << "Now clearing objects in " << loadable_blocks.size()
1069 << " blocks" << std::endl;
1071 // Grab a reference on each loaded block to avoid unloading it
1072 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1073 i != loaded_blocks.end(); ++i) {
1075 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1076 assert(block != NULL);
1080 // Remove objects in all loadable blocks
1081 u32 unload_interval = U32_MAX;
1082 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1083 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1084 unload_interval = MYMAX(unload_interval, 1);
1086 u32 report_interval = loadable_blocks.size() / 10;
1087 u32 num_blocks_checked = 0;
1088 u32 num_blocks_cleared = 0;
1089 u32 num_objs_cleared = 0;
1090 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1091 i != loadable_blocks.end(); ++i) {
1093 MapBlock *block = m_map->emergeBlock(p, false);
1095 errorstream << "ServerEnvironment::clearObjects(): "
1096 << "Failed to emerge block " << PP(p) << std::endl;
1099 u32 num_stored = block->m_static_objects.m_stored.size();
1100 u32 num_active = block->m_static_objects.m_active.size();
1101 if (num_stored != 0 || num_active != 0) {
1102 block->m_static_objects.m_stored.clear();
1103 block->m_static_objects.m_active.clear();
1104 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1105 MOD_REASON_CLEAR_ALL_OBJECTS);
1106 num_objs_cleared += num_stored + num_active;
1107 num_blocks_cleared++;
1109 num_blocks_checked++;
1111 if (report_interval != 0 &&
1112 num_blocks_checked % report_interval == 0) {
1113 float percent = 100.0 * (float)num_blocks_checked /
1114 loadable_blocks.size();
1115 infostream << "ServerEnvironment::clearObjects(): "
1116 << "Cleared " << num_objs_cleared << " objects"
1117 << " in " << num_blocks_cleared << " blocks ("
1118 << percent << "%)" << std::endl;
1120 if (num_blocks_checked % unload_interval == 0) {
1121 m_map->unloadUnreferencedBlocks();
1124 m_map->unloadUnreferencedBlocks();
1126 // Drop references that were added above
1127 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1128 i != loaded_blocks.end(); ++i) {
1130 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1135 m_last_clear_objects_time = m_game_time;
1137 infostream << "ServerEnvironment::clearObjects(): "
1138 << "Finished: Cleared " << num_objs_cleared << " objects"
1139 << " in " << num_blocks_cleared << " blocks" << std::endl;
1142 void ServerEnvironment::step(float dtime)
1144 DSTACK(FUNCTION_NAME);
1146 //TimeTaker timer("ServerEnv step");
1148 /* Step time of day */
1149 stepTimeOfDay(dtime);
1152 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1153 // really matter that much.
1154 static thread_local const float server_step =
1155 g_settings->getFloat("dedicated_server_step");
1156 m_recommended_send_interval = server_step;
1162 m_game_time_fraction_counter += dtime;
1163 u32 inc_i = (u32)m_game_time_fraction_counter;
1164 m_game_time += inc_i;
1165 m_game_time_fraction_counter -= (float)inc_i;
1172 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1173 for (RemotePlayer *player : m_players) {
1174 // Ignore disconnected players
1175 if (player->peer_id == 0)
1179 player->move(dtime, this, 100 * BS);
1184 Manage active block list
1186 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1187 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1189 Get player block positions
1191 std::vector<v3s16> players_blockpos;
1192 for (RemotePlayer *player: m_players) {
1193 // Ignore disconnected players
1194 if (player->peer_id == 0)
1197 PlayerSAO *playersao = player->getPlayerSAO();
1200 players_blockpos.push_back(
1201 getNodeBlockPos(floatToInt(playersao->getBasePosition(), BS)));
1205 Update list of active blocks, collecting changes
1207 static thread_local const s16 active_block_range =
1208 g_settings->getS16("active_block_range");
1209 std::set<v3s16> blocks_removed;
1210 std::set<v3s16> blocks_added;
1211 m_active_blocks.update(players_blockpos, active_block_range,
1212 blocks_removed, blocks_added);
1215 Handle removed blocks
1218 // Convert active objects that are no more in active blocks to static
1219 deactivateFarObjects(false);
1221 for (const v3s16 &p: blocks_removed) {
1222 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1226 // Set current time as timestamp (and let it set ChangedFlag)
1227 block->setTimestamp(m_game_time);
1234 for (const v3s16 &p: blocks_added) {
1235 MapBlock *block = m_map->getBlockOrEmerge(p);
1237 m_active_blocks.m_list.erase(p);
1241 activateBlock(block);
1246 Mess around in active blocks
1248 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1249 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1251 float dtime = m_cache_nodetimer_interval;
1253 for (const v3s16 &p: m_active_blocks.m_list) {
1254 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1258 // Reset block usage timer
1259 block->resetUsageTimer();
1261 // Set current time as timestamp
1262 block->setTimestampNoChangedFlag(m_game_time);
1263 // If time has changed much from the one on disk,
1264 // set block to be saved when it is unloaded
1265 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1266 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1267 MOD_REASON_BLOCK_EXPIRED);
1270 std::vector<NodeTimer> elapsed_timers = block->m_node_timers.step(dtime);
1271 if (!elapsed_timers.empty()) {
1274 for (const NodeTimer &elapsed_timer: elapsed_timers) {
1275 n = block->getNodeNoEx(elapsed_timer.position);
1276 p2 = elapsed_timer.position + block->getPosRelative();
1277 if (m_script->node_on_timer(p2, n, elapsed_timer.elapsed)) {
1278 block->setNodeTimer(NodeTimer(
1279 elapsed_timer.timeout, 0, elapsed_timer.position));
1286 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1288 if (m_active_block_interval_overload_skip > 0) {
1289 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1290 m_active_block_interval_overload_skip--;
1293 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1294 TimeTaker timer("modify in active blocks per interval");
1296 // Initialize handling of ActiveBlockModifiers
1297 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1299 for (const v3s16 &p : m_active_blocks.m_list) {
1300 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1304 // Set current time as timestamp
1305 block->setTimestampNoChangedFlag(m_game_time);
1307 /* Handle ActiveBlockModifiers */
1308 abmhandler.apply(block);
1311 u32 time_ms = timer.stop(true);
1312 u32 max_time_ms = 200;
1313 if (time_ms > max_time_ms) {
1314 warningstream<<"active block modifiers took "
1315 <<time_ms<<"ms (longer than "
1316 <<max_time_ms<<"ms)"<<std::endl;
1317 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1322 Step script environment (run global on_step())
1324 m_script->environment_Step(dtime);
1330 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1331 //TimeTaker timer("Step active objects");
1333 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1335 // This helps the objects to send data at the same time
1336 bool send_recommended = false;
1337 m_send_recommended_timer += dtime;
1338 if(m_send_recommended_timer > getSendRecommendedInterval())
1340 m_send_recommended_timer -= getSendRecommendedInterval();
1341 send_recommended = true;
1344 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1345 i != m_active_objects.end(); ++i) {
1346 ServerActiveObject* obj = i->second;
1347 // Don't step if is to be removed or stored statically
1348 if(obj->m_removed || obj->m_pending_deactivation)
1351 obj->step(dtime, send_recommended);
1352 // Read messages from object
1353 while(!obj->m_messages_out.empty())
1355 m_active_object_messages.push(
1356 obj->m_messages_out.front());
1357 obj->m_messages_out.pop();
1363 Manage active objects
1365 if (m_object_management_interval.step(dtime, 0.5)) {
1366 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1368 Remove objects that satisfy (m_removed && m_known_by_count==0)
1370 removeRemovedObjects();
1374 Manage particle spawner expiration
1376 if (m_particle_management_interval.step(dtime, 1.0)) {
1377 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1378 i != m_particle_spawners.end(); ) {
1379 //non expiring spawners
1380 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1386 if (i->second <= 0.f)
1387 m_particle_spawners.erase(i++);
1394 u32 ServerEnvironment::addParticleSpawner(float exptime)
1396 // Timers with lifetime 0 do not expire
1397 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1400 for (;;) { // look for unused particlespawner id
1402 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1403 if (f == m_particle_spawners.end()) {
1404 m_particle_spawners[id] = time;
1411 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1413 u32 id = addParticleSpawner(exptime);
1414 m_particle_spawner_attachments[id] = attached_id;
1415 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1416 obj->attachParticleSpawner(id);
1421 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1423 m_particle_spawners.erase(id);
1424 const auto &it = m_particle_spawner_attachments.find(id);
1425 if (it != m_particle_spawner_attachments.end()) {
1426 u16 obj_id = it->second;
1427 ServerActiveObject *sao = getActiveObject(obj_id);
1428 if (sao != NULL && remove_from_object) {
1429 sao->detachParticleSpawner(id);
1431 m_particle_spawner_attachments.erase(id);
1435 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1437 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1438 return (n != m_active_objects.end() ? n->second : NULL);
1441 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1446 return objects.find(id) == objects.end();
1449 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1451 //try to reuse id's as late as possible
1452 static u16 last_used_id = 0;
1453 u16 startid = last_used_id;
1457 if(isFreeServerActiveObjectId(last_used_id, objects))
1458 return last_used_id;
1460 if(last_used_id == startid)
1465 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1467 assert(object); // Pre-condition
1469 u16 id = addActiveObjectRaw(object, true, 0);
1474 Finds out what new objects have been added to
1475 inside a radius around a position
1477 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1479 std::set<u16> ¤t_objects,
1480 std::queue<u16> &added_objects)
1482 f32 radius_f = radius * BS;
1483 f32 player_radius_f = player_radius * BS;
1485 if (player_radius_f < 0)
1486 player_radius_f = 0;
1488 Go through the object list,
1489 - discard m_removed objects,
1490 - discard objects that are too far away,
1491 - discard objects that are found in current_objects.
1492 - add remaining objects to added_objects
1494 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1495 i != m_active_objects.end(); ++i) {
1499 ServerActiveObject *object = i->second;
1503 // Discard if removed or deactivating
1504 if(object->m_removed || object->m_pending_deactivation)
1507 f32 distance_f = object->getBasePosition().
1508 getDistanceFrom(playersao->getBasePosition());
1509 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1510 // Discard if too far
1511 if (distance_f > player_radius_f && player_radius_f != 0)
1513 } else if (distance_f > radius_f)
1516 // Discard if already on current_objects
1517 std::set<u16>::iterator n;
1518 n = current_objects.find(id);
1519 if(n != current_objects.end())
1521 // Add to added_objects
1522 added_objects.push(id);
1527 Finds out what objects have been removed from
1528 inside a radius around a position
1530 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1532 std::set<u16> ¤t_objects,
1533 std::queue<u16> &removed_objects)
1535 f32 radius_f = radius * BS;
1536 f32 player_radius_f = player_radius * BS;
1538 if (player_radius_f < 0)
1539 player_radius_f = 0;
1541 Go through current_objects; object is removed if:
1542 - object is not found in m_active_objects (this is actually an
1543 error condition; objects should be set m_removed=true and removed
1544 only after all clients have been informed about removal), or
1545 - object has m_removed=true, or
1546 - object is too far away
1548 for(std::set<u16>::iterator
1549 i = current_objects.begin();
1550 i != current_objects.end(); ++i)
1553 ServerActiveObject *object = getActiveObject(id);
1555 if (object == NULL) {
1556 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1557 << " object in current_objects is NULL" << std::endl;
1558 removed_objects.push(id);
1562 if (object->m_removed || object->m_pending_deactivation) {
1563 removed_objects.push(id);
1567 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1568 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1569 if (distance_f <= player_radius_f || player_radius_f == 0)
1571 } else if (distance_f <= radius_f)
1574 // Object is no longer visible
1575 removed_objects.push(id);
1579 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1580 v3s16 blockpos, bool static_exists, v3s16 static_block)
1582 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1586 for (std::map<u16, StaticObject>::iterator
1587 so_it = block->m_static_objects.m_active.begin();
1588 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1589 // Get the ServerActiveObject counterpart to this StaticObject
1590 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it->first);
1591 if (ao_it == m_active_objects.end()) {
1592 // If this ever happens, there must be some kind of nasty bug.
1593 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1594 "Object from MapBlock::m_static_objects::m_active not found "
1595 "in m_active_objects";
1599 ServerActiveObject *sao = ao_it->second;
1600 sao->m_static_exists = static_exists;
1601 sao->m_static_block = static_block;
1605 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1607 if(m_active_object_messages.empty())
1608 return ActiveObjectMessage(0);
1610 ActiveObjectMessage message = m_active_object_messages.front();
1611 m_active_object_messages.pop();
1615 void ServerEnvironment::getSelectedActiveObjects(
1616 const core::line3d<f32> &shootline_on_map,
1617 std::vector<PointedThing> &objects)
1619 std::vector<u16> objectIds;
1620 getObjectsInsideRadius(objectIds, shootline_on_map.start,
1621 shootline_on_map.getLength() + 10.0f);
1622 const v3f line_vector = shootline_on_map.getVector();
1624 for (u32 i = 0; i < objectIds.size(); i++) {
1625 ServerActiveObject* obj = getActiveObject(objectIds[i]);
1627 aabb3f selection_box;
1628 if (!obj->getSelectionBox(&selection_box))
1631 v3f pos = obj->getBasePosition();
1633 aabb3f offsetted_box(selection_box.MinEdge + pos,
1634 selection_box.MaxEdge + pos);
1636 v3f current_intersection;
1637 v3s16 current_normal;
1638 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1639 ¤t_intersection, ¤t_normal)) {
1640 objects.push_back(PointedThing(
1641 (s16) objectIds[i], current_intersection, current_normal,
1642 (current_intersection - shootline_on_map.start).getLengthSQ()));
1648 ************ Private methods *************
1651 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1652 bool set_changed, u32 dtime_s)
1654 assert(object); // Pre-condition
1655 if(object->getId() == 0){
1656 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1659 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1660 <<"no free ids available"<<std::endl;
1661 if(object->environmentDeletes())
1665 object->setId(new_id);
1668 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1669 <<"supplied with id "<<object->getId()<<std::endl;
1672 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1673 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1674 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1675 if(object->environmentDeletes())
1680 if (objectpos_over_limit(object->getBasePosition())) {
1681 v3f p = object->getBasePosition();
1682 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1683 << "object position (" << p.X << "," << p.Y << "," << p.Z
1684 << ") outside maximum range" << std::endl;
1685 if (object->environmentDeletes())
1690 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1691 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1693 m_active_objects[object->getId()] = object;
1695 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1696 <<"Added id="<<object->getId()<<"; there are now "
1697 <<m_active_objects.size()<<" active objects."
1700 // Register reference in scripting api (must be done before post-init)
1701 m_script->addObjectReference(object);
1702 // Post-initialize object
1703 object->addedToEnvironment(dtime_s);
1705 // Add static data to block
1706 if(object->isStaticAllowed())
1708 // Add static object to active static list of the block
1709 v3f objectpos = object->getBasePosition();
1710 std::string staticdata = "";
1711 object->getStaticData(&staticdata);
1712 StaticObject s_obj(object->getType(), objectpos, staticdata);
1713 // Add to the block where the object is located in
1714 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1715 MapBlock *block = m_map->emergeBlock(blockpos);
1717 block->m_static_objects.m_active[object->getId()] = s_obj;
1718 object->m_static_exists = true;
1719 object->m_static_block = blockpos;
1722 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1723 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1725 v3s16 p = floatToInt(objectpos, BS);
1726 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1727 <<"could not emerge block for storing id="<<object->getId()
1728 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1732 return object->getId();
1736 Remove objects that satisfy (m_removed && m_known_by_count==0)
1738 void ServerEnvironment::removeRemovedObjects()
1740 std::vector<u16> objects_to_remove;
1741 for(ServerActiveObjectMap::iterator i = m_active_objects.begin();
1742 i != m_active_objects.end(); ++i) {
1744 ServerActiveObject* obj = i->second;
1745 // This shouldn't happen but check it
1748 infostream<<"NULL object found in ServerEnvironment"
1749 <<" while finding removed objects. id="<<id<<std::endl;
1750 // Id to be removed from m_active_objects
1751 objects_to_remove.push_back(id);
1756 We will delete objects that are marked as removed or thatare
1757 waiting for deletion after deactivation
1759 if (!obj->m_removed && !obj->m_pending_deactivation)
1763 Delete static data from block if is marked as removed
1765 if(obj->m_static_exists && obj->m_removed)
1767 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1769 block->m_static_objects.remove(id);
1770 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1771 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1772 obj->m_static_exists = false;
1774 infostream<<"Failed to emerge block from which an object to "
1775 <<"be removed was loaded from. id="<<id<<std::endl;
1779 // If m_known_by_count > 0, don't actually remove. On some future
1780 // invocation this will be 0, which is when removal will continue.
1781 if(obj->m_known_by_count > 0)
1785 Move static data from active to stored if not marked as removed
1787 if(obj->m_static_exists && !obj->m_removed){
1788 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1790 std::map<u16, StaticObject>::iterator i =
1791 block->m_static_objects.m_active.find(id);
1792 if(i != block->m_static_objects.m_active.end()){
1793 block->m_static_objects.m_stored.push_back(i->second);
1794 block->m_static_objects.m_active.erase(id);
1795 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1796 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1799 infostream<<"Failed to emerge block from which an object to "
1800 <<"be deactivated was loaded from. id="<<id<<std::endl;
1804 // Tell the object about removal
1805 obj->removingFromEnvironment();
1806 // Deregister in scripting api
1807 m_script->removeObjectReference(obj);
1810 if(obj->environmentDeletes())
1813 // Id to be removed from m_active_objects
1814 objects_to_remove.push_back(id);
1816 // Remove references from m_active_objects
1817 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1818 i != objects_to_remove.end(); ++i) {
1819 m_active_objects.erase(*i);
1823 static void print_hexdump(std::ostream &o, const std::string &data)
1825 const int linelength = 16;
1826 for(int l=0; ; l++){
1827 int i0 = linelength * l;
1828 bool at_end = false;
1829 int thislinelength = linelength;
1830 if(i0 + thislinelength > (int)data.size()){
1831 thislinelength = data.size() - i0;
1834 for(int di=0; di<linelength; di++){
1837 if(di<thislinelength)
1838 snprintf(buf, 4, "%.2x ", data[i]);
1840 snprintf(buf, 4, " ");
1844 for(int di=0; di<thislinelength; di++){
1858 Convert stored objects from blocks near the players to active.
1860 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1865 // Ignore if no stored objects (to not set changed flag)
1866 if(block->m_static_objects.m_stored.empty())
1869 verbosestream<<"ServerEnvironment::activateObjects(): "
1870 <<"activating objects of block "<<PP(block->getPos())
1871 <<" ("<<block->m_static_objects.m_stored.size()
1872 <<" objects)"<<std::endl;
1873 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1875 errorstream<<"suspiciously large amount of objects detected: "
1876 <<block->m_static_objects.m_stored.size()<<" in "
1877 <<PP(block->getPos())
1878 <<"; removing all of them."<<std::endl;
1879 // Clear stored list
1880 block->m_static_objects.m_stored.clear();
1881 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1882 MOD_REASON_TOO_MANY_OBJECTS);
1886 // Activate stored objects
1887 std::vector<StaticObject> new_stored;
1888 for (std::vector<StaticObject>::iterator
1889 i = block->m_static_objects.m_stored.begin();
1890 i != block->m_static_objects.m_stored.end(); ++i) {
1891 StaticObject &s_obj = *i;
1893 // Create an active object from the data
1894 ServerActiveObject *obj = ServerActiveObject::create
1895 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1896 // If couldn't create object, store static data back.
1898 errorstream<<"ServerEnvironment::activateObjects(): "
1899 <<"failed to create active object from static object "
1900 <<"in block "<<PP(s_obj.pos/BS)
1901 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1902 print_hexdump(verbosestream, s_obj.data);
1904 new_stored.push_back(s_obj);
1907 verbosestream<<"ServerEnvironment::activateObjects(): "
1908 <<"activated static object pos="<<PP(s_obj.pos/BS)
1909 <<" type="<<(int)s_obj.type<<std::endl;
1910 // This will also add the object to the active static list
1911 addActiveObjectRaw(obj, false, dtime_s);
1913 // Clear stored list
1914 block->m_static_objects.m_stored.clear();
1915 // Add leftover failed stuff to stored list
1916 for(std::vector<StaticObject>::iterator
1917 i = new_stored.begin();
1918 i != new_stored.end(); ++i) {
1919 StaticObject &s_obj = *i;
1920 block->m_static_objects.m_stored.push_back(s_obj);
1923 // Turn the active counterparts of activated objects not pending for
1925 for(std::map<u16, StaticObject>::iterator
1926 i = block->m_static_objects.m_active.begin();
1927 i != block->m_static_objects.m_active.end(); ++i)
1930 ServerActiveObject *object = getActiveObject(id);
1932 object->m_pending_deactivation = false;
1936 Note: Block hasn't really been modified here.
1937 The objects have just been activated and moved from the stored
1938 static list to the active static list.
1939 As such, the block is essentially the same.
1940 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1941 Otherwise there would be a huge amount of unnecessary I/O.
1946 Convert objects that are not standing inside active blocks to static.
1948 If m_known_by_count != 0, active object is not deleted, but static
1949 data is still updated.
1951 If force_delete is set, active object is deleted nevertheless. It
1952 shall only be set so in the destructor of the environment.
1954 If block wasn't generated (not in memory or on disk),
1956 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1958 std::vector<u16> objects_to_remove;
1959 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1960 i != m_active_objects.end(); ++i) {
1961 // force_delete might be overriden per object
1962 bool force_delete = _force_delete;
1964 ServerActiveObject* obj = i->second;
1967 // Do not deactivate if static data creation not allowed
1968 if(!force_delete && !obj->isStaticAllowed())
1971 // If pending deactivation, let removeRemovedObjects() do it
1972 if(!force_delete && obj->m_pending_deactivation)
1976 v3f objectpos = obj->getBasePosition();
1978 // The block in which the object resides in
1979 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1981 // If object's static data is stored in a deactivated block and object
1982 // is actually located in an active block, re-save to the block in
1983 // which the object is actually located in.
1985 obj->m_static_exists &&
1986 !m_active_blocks.contains(obj->m_static_block) &&
1987 m_active_blocks.contains(blockpos_o))
1989 v3s16 old_static_block = obj->m_static_block;
1991 // Save to block where object is located
1992 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1994 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1995 <<"Could not save object id="<<id
1996 <<" to it's current block "<<PP(blockpos_o)
2000 std::string staticdata_new = "";
2001 obj->getStaticData(&staticdata_new);
2002 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2003 block->m_static_objects.insert(id, s_obj);
2004 obj->m_static_block = blockpos_o;
2005 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2006 MOD_REASON_STATIC_DATA_ADDED);
2008 // Delete from block where object was located
2009 block = m_map->emergeBlock(old_static_block, false);
2011 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2012 <<"Could not delete object id="<<id
2013 <<" from it's previous block "<<PP(old_static_block)
2017 block->m_static_objects.remove(id);
2018 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2019 MOD_REASON_STATIC_DATA_REMOVED);
2023 // If block is active, don't remove
2024 if(!force_delete && m_active_blocks.contains(blockpos_o))
2027 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2028 <<"deactivating object id="<<id<<" on inactive block "
2029 <<PP(blockpos_o)<<std::endl;
2031 // If known by some client, don't immediately delete.
2032 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2035 Update the static data
2038 if(obj->isStaticAllowed())
2040 // Create new static object
2041 std::string staticdata_new = "";
2042 obj->getStaticData(&staticdata_new);
2043 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2045 bool stays_in_same_block = false;
2046 bool data_changed = true;
2048 if (obj->m_static_exists) {
2049 if (obj->m_static_block == blockpos_o)
2050 stays_in_same_block = true;
2052 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2055 std::map<u16, StaticObject>::iterator n =
2056 block->m_static_objects.m_active.find(id);
2057 if (n != block->m_static_objects.m_active.end()) {
2058 StaticObject static_old = n->second;
2060 float save_movem = obj->getMinimumSavedMovement();
2062 if (static_old.data == staticdata_new &&
2063 (static_old.pos - objectpos).getLength() < save_movem)
2064 data_changed = false;
2066 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2067 <<"id="<<id<<" m_static_exists=true but "
2068 <<"static data doesn't actually exist in "
2069 <<PP(obj->m_static_block)<<std::endl;
2074 bool shall_be_written = (!stays_in_same_block || data_changed);
2076 // Delete old static object
2077 if(obj->m_static_exists)
2079 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2082 block->m_static_objects.remove(id);
2083 obj->m_static_exists = false;
2084 // Only mark block as modified if data changed considerably
2085 if(shall_be_written)
2086 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2087 MOD_REASON_STATIC_DATA_CHANGED);
2091 // Add to the block where the object is located in
2092 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2093 // Get or generate the block
2094 MapBlock *block = NULL;
2096 block = m_map->emergeBlock(blockpos);
2097 } catch(InvalidPositionException &e){
2098 // Handled via NULL pointer
2099 // NOTE: emergeBlock's failure is usually determined by it
2100 // actually returning NULL
2105 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2106 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2107 << " statically but block " << PP(blockpos)
2108 << " already contains "
2109 << block->m_static_objects.m_stored.size()
2111 << " Forcing delete." << std::endl;
2112 force_delete = true;
2114 // If static counterpart already exists in target block,
2116 // This shouldn't happen because the object is removed from
2117 // the previous block before this according to
2118 // obj->m_static_block, but happens rarely for some unknown
2119 // reason. Unsuccessful attempts have been made to find
2121 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2122 warningstream<<"ServerEnv: Performing hack #83274"
2124 block->m_static_objects.remove(id);
2126 // Store static data
2127 u16 store_id = pending_delete ? id : 0;
2128 block->m_static_objects.insert(store_id, s_obj);
2130 // Only mark block as modified if data changed considerably
2131 if(shall_be_written)
2132 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2133 MOD_REASON_STATIC_DATA_CHANGED);
2135 obj->m_static_exists = true;
2136 obj->m_static_block = block->getPos();
2141 v3s16 p = floatToInt(objectpos, BS);
2142 errorstream<<"ServerEnv: Could not find or generate "
2143 <<"a block for storing id="<<obj->getId()
2144 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2151 If known by some client, set pending deactivation.
2152 Otherwise delete it immediately.
2155 if(pending_delete && !force_delete)
2157 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2158 <<"object id="<<id<<" is known by clients"
2159 <<"; not deleting yet"<<std::endl;
2161 obj->m_pending_deactivation = true;
2165 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2166 <<"object id="<<id<<" is not known by clients"
2167 <<"; deleting"<<std::endl;
2169 // Tell the object about removal
2170 obj->removingFromEnvironment();
2171 // Deregister in scripting api
2172 m_script->removeObjectReference(obj);
2174 // Delete active object
2175 if(obj->environmentDeletes())
2177 // Id to be removed from m_active_objects
2178 objects_to_remove.push_back(id);
2181 // Remove references from m_active_objects
2182 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2183 i != objects_to_remove.end(); ++i) {
2184 m_active_objects.erase(*i);
2188 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2189 const std::string &savedir, const Settings &conf)
2192 if (name == "sqlite3")
2193 return new PlayerDatabaseSQLite3(savedir);
2194 else if (name == "dummy")
2195 return new Database_Dummy();
2197 else if (name == "postgresql") {
2198 std::string connect_string = "";
2199 conf.getNoEx("pgsql_player_connection", connect_string);
2200 return new PlayerDatabasePostgreSQL(connect_string);
2203 else if (name == "files")
2204 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2206 throw BaseException(std::string("Database backend ") + name + " not supported.");
2209 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2210 const Settings &cmd_args)
2212 std::string migrate_to = cmd_args.get("migrate-players");
2214 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2215 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2216 errorstream << "Cannot read world.mt!" << std::endl;
2220 if (!world_mt.exists("player_backend")) {
2221 errorstream << "Please specify your current backend in world.mt:"
2223 << " player_backend = {files|sqlite3|postgresql}"
2228 std::string backend = world_mt.get("player_backend");
2229 if (backend == migrate_to) {
2230 errorstream << "Cannot migrate: new backend is same"
2231 << " as the old one" << std::endl;
2235 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2238 if (backend == "files") {
2239 // Create backup directory
2240 fs::CreateDir(players_backup_path);
2244 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2245 game_params.world_path, world_mt);
2246 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2247 game_params.world_path, world_mt);
2249 std::vector<std::string> player_list;
2250 srcdb->listPlayers(player_list);
2251 for (std::vector<std::string>::const_iterator it = player_list.begin();
2252 it != player_list.end(); ++it) {
2253 actionstream << "Migrating player " << it->c_str() << std::endl;
2254 RemotePlayer player(it->c_str(), NULL);
2255 PlayerSAO playerSAO(NULL, &player, 15000, false);
2257 srcdb->loadPlayer(&player, &playerSAO);
2259 playerSAO.finalize(&player, std::set<std::string>());
2260 player.setPlayerSAO(&playerSAO);
2262 dstdb->savePlayer(&player);
2264 // For files source, move player files to backup dir
2265 if (backend == "files") {
2267 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2268 players_backup_path + DIR_DELIM + (*it));
2272 actionstream << "Successfully migrated " << player_list.size() << " players"
2274 world_mt.set("player_backend", migrate_to);
2275 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2276 errorstream << "Failed to update world.mt!" << std::endl;
2278 actionstream << "world.mt updated" << std::endl;
2280 // When migration is finished from file backend, remove players directory if empty
2281 if (backend == "files") {
2282 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2289 } catch (BaseException &e) {
2290 errorstream << "An error occured during migration: " << e.what() << std::endl;