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"
25 #include "nodemetadata.h"
30 #include "remoteplayer.h"
31 #include "scripting_server.h"
33 #include "voxelalgorithms.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_):
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 (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
75 it != lbm_list.end(); ++it) {
80 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
82 // Add the lbm_def to the LBMContentMapping.
83 // Unknown names get added to the global NameIdMapping.
84 INodeDefManager *nodedef = gamedef->ndef();
86 lbm_list.push_back(lbm_def);
88 for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
89 it != lbm_def->trigger_contents.end(); ++it) {
90 std::set<content_t> c_ids;
91 bool found = nodedef->getIds(*it, c_ids);
93 content_t c_id = gamedef->allocateUnknownNodeId(*it);
94 if (c_id == CONTENT_IGNORE) {
95 // Seems it can't be allocated.
96 warningstream << "Could not internalize node name \"" << *it
97 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
103 for (std::set<content_t>::const_iterator iit =
104 c_ids.begin(); iit != c_ids.end(); ++iit) {
105 content_t c_id = *iit;
106 map[c_id].push_back(lbm_def);
111 const std::vector<LoadingBlockModifierDef *> *
112 LBMContentMapping::lookup(content_t c) const
114 container_map::const_iterator it = map.find(c);
117 // This first dereferences the iterator, returning
118 // a std::vector<LoadingBlockModifierDef *>
119 // reference, then we convert it to a pointer.
120 return &(it->second);
123 LBMManager::~LBMManager()
125 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
126 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
129 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
130 it != m_lbm_lookup.end(); ++it) {
131 (it->second).deleteContents();
135 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
137 // Precondition, in query mode the map isn't used anymore
138 FATAL_ERROR_IF(m_query_mode == true,
139 "attempted to modify LBMManager in query mode");
141 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
142 throw ModError("Error adding LBM \"" + lbm_def->name +
143 "\": Does not follow naming conventions: "
144 "Only characters [a-z0-9_:] are allowed.");
147 m_lbm_defs[lbm_def->name] = lbm_def;
150 void LBMManager::loadIntroductionTimes(const std::string ×,
151 IGameDef *gamedef, u32 now)
156 // Storing it in a map first instead of
157 // handling the stuff directly in the loop
158 // removes all duplicate entries.
159 // TODO make this std::unordered_map
160 std::map<std::string, u32> introduction_times;
163 The introduction times string consists of name~time entries,
164 with each entry terminated by a semicolon. The time is decimal.
169 while ((idx_new = times.find(";", idx)) != std::string::npos) {
170 std::string entry = times.substr(idx, idx_new - idx);
171 std::vector<std::string> components = str_split(entry, '~');
172 if (components.size() != 2)
173 throw SerializationError("Introduction times entry \""
174 + entry + "\" requires exactly one '~'!");
175 const std::string &name = components[0];
176 u32 time = from_string<u32>(components[1]);
177 introduction_times[name] = time;
181 // Put stuff from introduction_times into m_lbm_lookup
182 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
183 it != introduction_times.end(); ++it) {
184 const std::string &name = it->first;
185 u32 time = it->second;
187 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
188 m_lbm_defs.find(name);
189 if (def_it == m_lbm_defs.end()) {
190 // This seems to be an LBM entry for
191 // an LBM we haven't loaded. Discard it.
194 LoadingBlockModifierDef *lbm_def = def_it->second;
195 if (lbm_def->run_at_every_load) {
196 // This seems to be an LBM entry for
197 // an LBM that runs at every load.
198 // Don't add it just yet.
202 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
204 // Erase the entry so that we know later
205 // what elements didn't get put into m_lbm_lookup
206 m_lbm_defs.erase(name);
209 // Now also add the elements from m_lbm_defs to m_lbm_lookup
210 // that weren't added in the previous step.
211 // They are introduced first time to this world,
212 // or are run at every load (introducement time hardcoded to U32_MAX).
214 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
215 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
217 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
218 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
219 if (it->second->run_at_every_load) {
220 lbms_running_always.addLBM(it->second, gamedef);
222 lbms_we_introduce_now.addLBM(it->second, gamedef);
226 // Clear the list, so that we don't delete remaining elements
227 // twice in the destructor
231 std::string LBMManager::createIntroductionTimesString()
233 // Precondition, we must be in query mode
234 FATAL_ERROR_IF(m_query_mode == false,
235 "attempted to query on non fully set up LBMManager");
237 std::ostringstream oss;
238 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
239 it != m_lbm_lookup.end(); ++it) {
240 u32 time = it->first;
241 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
242 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
243 iit != lbm_list.end(); ++iit) {
244 // Don't add if the LBM runs at every load,
245 // then introducement time is hardcoded
246 // and doesn't need to be stored
247 if ((*iit)->run_at_every_load)
249 oss << (*iit)->name << "~" << time << ";";
255 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
257 // Precondition, we need m_lbm_lookup to be initialized
258 FATAL_ERROR_IF(m_query_mode == false,
259 "attempted to query on non fully set up LBMManager");
260 v3s16 pos_of_block = block->getPosRelative();
264 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
265 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
266 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
267 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
269 n = block->getNodeNoEx(pos);
271 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
272 iit != m_lbm_lookup.end(); ++iit) {
273 const std::vector<LoadingBlockModifierDef *> *lbm_list =
274 iit->second.lookup(c);
277 for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
278 lbm_list->begin(); iit != lbm_list->end(); ++iit) {
279 (*iit)->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 ActiveBlockList::update(std::vector<v3s16> &active_positions,
306 std::set<v3s16> &blocks_removed,
307 std::set<v3s16> &blocks_added)
312 std::set<v3s16> newlist = m_forceloaded_list;
313 for(std::vector<v3s16>::iterator i = active_positions.begin();
314 i != active_positions.end(); ++i)
316 fillRadiusBlock(*i, radius, newlist);
320 Find out which blocks on the old list are not on the new list
322 // Go through old list
323 for(std::set<v3s16>::iterator i = m_list.begin();
324 i != m_list.end(); ++i)
327 // If not on new list, it's been removed
328 if(newlist.find(p) == newlist.end())
329 blocks_removed.insert(p);
333 Find out which blocks on the new list are not on the old list
335 // Go through new list
336 for(std::set<v3s16>::iterator i = newlist.begin();
337 i != newlist.end(); ++i)
340 // If not on old list, it's been added
341 if(m_list.find(p) == m_list.end())
342 blocks_added.insert(p);
349 for(std::set<v3s16>::iterator i = newlist.begin();
350 i != newlist.end(); ++i)
361 ServerEnvironment::ServerEnvironment(ServerMap *map,
362 ServerScripting *scriptIface, Server *server,
363 const std::string &path_world):
366 m_script(scriptIface),
368 m_path_world(path_world),
369 m_send_recommended_timer(0),
370 m_active_block_interval_overload_skip(0),
372 m_game_time_fraction_counter(0),
373 m_last_clear_objects_time(0),
374 m_recommended_send_interval(0.1),
375 m_max_lag_estimate(0.1),
376 m_player_database(NULL)
378 // Determine which database backend to use
379 std::string conf_path = path_world + DIR_DELIM + "world.mt";
381 bool succeeded = conf.readConfigFile(conf_path.c_str());
382 if (!succeeded || !conf.exists("player_backend")) {
383 // fall back to files
384 conf.set("player_backend", "files");
385 warningstream << "/!\\ You are using old player file backend. "
386 << "This backend is deprecated and will be removed in next release /!\\"
387 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
388 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
390 if (!conf.updateConfigFile(conf_path.c_str())) {
391 errorstream << "ServerEnvironment::ServerEnvironment(): "
392 << "Failed to update world.mt!" << std::endl;
396 std::string name = "";
397 conf.getNoEx("player_backend", name);
398 m_player_database = openPlayerDatabase(name, path_world, conf);
401 ServerEnvironment::~ServerEnvironment()
403 // Clear active block list.
404 // This makes the next one delete all active objects.
405 m_active_blocks.clear();
407 // Convert all objects to static and delete the active objects
408 deactivateFarObjects(true);
413 // Delete ActiveBlockModifiers
414 for (std::vector<ABMWithState>::iterator
415 i = m_abms.begin(); i != m_abms.end(); ++i){
419 // Deallocate players
420 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
421 i != m_players.end(); ++i) {
425 delete m_player_database;
428 Map & ServerEnvironment::getMap()
433 ServerMap & ServerEnvironment::getServerMap()
438 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
440 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
441 i != m_players.end(); ++i) {
442 RemotePlayer *player = *i;
443 if (player->peer_id == peer_id)
449 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
451 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
452 i != m_players.end(); ++i) {
453 RemotePlayer *player = *i;
454 if (strcmp(player->getName(), name) == 0)
460 void ServerEnvironment::addPlayer(RemotePlayer *player)
462 DSTACK(FUNCTION_NAME);
464 Check that peer_ids are unique.
465 Also check that names are unique.
466 Exception: there can be multiple players with peer_id=0
468 // If peer id is non-zero, it has to be unique.
469 if (player->peer_id != 0)
470 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
471 // Name has to be unique.
472 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
474 m_players.push_back(player);
477 void ServerEnvironment::removePlayer(RemotePlayer *player)
479 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
480 it != m_players.end(); ++it) {
481 if ((*it) == player) {
489 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
491 return m_player_database->removePlayer(name);
494 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
496 float distance = pos1.getDistanceFrom(pos2);
498 //calculate normalized direction vector
499 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
500 (pos2.Y - pos1.Y)/distance,
501 (pos2.Z - pos1.Z)/distance);
503 //find out if there's a node on path between pos1 and pos2
504 for (float i = 1; i < distance; i += stepsize) {
505 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
506 normalized_vector.Y * i,
507 normalized_vector.Z * i) +pos1,BS);
509 MapNode n = getMap().getNodeNoEx(pos);
511 if(n.param0 != CONTENT_AIR) {
521 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
522 const std::string &str_reason, bool reconnect)
524 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
525 it != m_players.end(); ++it) {
526 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
527 m_server->DenyAccessVerCompliant(player->peer_id,
528 player->protocol_version, reason, str_reason, reconnect);
532 void ServerEnvironment::saveLoadedPlayers()
534 std::string players_path = m_path_world + DIR_DELIM + "players";
535 fs::CreateDir(players_path);
537 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
538 it != m_players.end();
540 if ((*it)->checkModified() ||
541 ((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) {
543 m_player_database->savePlayer(*it);
544 } catch (DatabaseException &e) {
545 errorstream << "Failed to save player " << (*it)->getName() << " exception: "
546 << e.what() << std::endl;
553 void ServerEnvironment::savePlayer(RemotePlayer *player)
556 m_player_database->savePlayer(player);
557 } catch (DatabaseException &e) {
558 errorstream << "Failed to save player " << player->getName() << " exception: "
559 << e.what() << std::endl;
564 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
565 u16 peer_id, bool is_singleplayer)
567 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
568 // Create player if it doesn't exist
569 if (!m_player_database->loadPlayer(player, playersao)) {
571 // Set player position
572 infostream << "Server: Finding spawn place for player \""
573 << player->getName() << "\"" << std::endl;
574 playersao->setBasePosition(m_server->findSpawnPos());
576 // Make sure the player is saved
577 player->setModified(true);
579 // If the player exists, ensure that they respawn inside legal bounds
580 // This fixes an assert crash when the player can't be added
581 // to the environment
582 ServerMap &map = getServerMap();
583 if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) {
584 actionstream << "Respawn position for player \""
585 << player->getName() << "\" outside limits, resetting" << std::endl;
586 playersao->setBasePosition(m_server->findSpawnPos());
590 // Add player to environment
593 /* Clean up old HUD elements from previous sessions */
596 /* Add object to environment */
597 addActiveObject(playersao);
602 void ServerEnvironment::saveMeta()
604 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
606 // Open file and serialize
607 std::ostringstream ss(std::ios_base::binary);
610 args.setU64("game_time", m_game_time);
611 args.setU64("time_of_day", getTimeOfDay());
612 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
613 args.setU64("lbm_introduction_times_version", 1);
614 args.set("lbm_introduction_times",
615 m_lbm_mgr.createIntroductionTimesString());
616 args.setU64("day_count", m_day_count);
620 if(!fs::safeWriteToFile(path, ss.str()))
622 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
624 throw SerializationError("Couldn't save env meta");
628 void ServerEnvironment::loadMeta()
630 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
632 // Open file and deserialize
633 std::ifstream is(path.c_str(), std::ios_base::binary);
635 infostream << "ServerEnvironment::loadMeta(): Failed to open "
636 << path << std::endl;
637 throw SerializationError("Couldn't load env meta");
642 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
643 throw SerializationError("ServerEnvironment::loadMeta(): "
644 "EnvArgsEnd not found!");
648 m_game_time = args.getU64("game_time");
649 } catch (SettingNotFoundException &e) {
650 // Getting this is crucial, otherwise timestamps are useless
651 throw SerializationError("Couldn't load env meta game_time");
654 setTimeOfDay(args.exists("time_of_day") ?
655 // set day to morning by default
656 args.getU64("time_of_day") : 9000);
658 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
659 // If missing, do as if clearObjects was never called
660 args.getU64("last_clear_objects_time") : 0;
662 std::string lbm_introduction_times = "";
664 u64 ver = args.getU64("lbm_introduction_times_version");
666 lbm_introduction_times = args.get("lbm_introduction_times");
668 infostream << "ServerEnvironment::loadMeta(): Non-supported"
669 << " introduction time version " << ver << std::endl;
671 } catch (SettingNotFoundException &e) {
672 // No problem, this is expected. Just continue with an empty string
674 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
676 m_day_count = args.exists("day_count") ?
677 args.getU64("day_count") : 0;
680 void ServerEnvironment::loadDefaultMeta()
682 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
687 ActiveBlockModifier *abm;
689 std::set<content_t> required_neighbors;
695 ServerEnvironment *m_env;
696 std::vector<std::vector<ActiveABM> *> m_aabms;
698 ABMHandler(std::vector<ABMWithState> &abms,
699 float dtime_s, ServerEnvironment *env,
705 INodeDefManager *ndef = env->getGameDef()->ndef();
706 for(std::vector<ABMWithState>::iterator
707 i = abms.begin(); i != abms.end(); ++i) {
708 ActiveBlockModifier *abm = i->abm;
709 float trigger_interval = abm->getTriggerInterval();
710 if(trigger_interval < 0.001)
711 trigger_interval = 0.001;
712 float actual_interval = dtime_s;
715 if(i->timer < trigger_interval)
717 i->timer -= trigger_interval;
718 actual_interval = trigger_interval;
720 float chance = abm->getTriggerChance();
725 if (abm->getSimpleCatchUp()) {
726 float intervals = actual_interval / trigger_interval;
729 aabm.chance = chance / intervals;
733 aabm.chance = chance;
737 const std::set<std::string> &required_neighbors_s =
738 abm->getRequiredNeighbors();
739 for (std::set<std::string>::iterator rn = required_neighbors_s.begin();
740 rn != required_neighbors_s.end(); ++rn) {
741 ndef->getIds(*rn, aabm.required_neighbors);
745 const std::set<std::string> &contents_s = abm->getTriggerContents();
746 for (std::set<std::string>::iterator cs = contents_s.begin();
747 cs != contents_s.end(); ++cs) {
748 std::set<content_t> ids;
749 ndef->getIds(*cs, ids);
750 for (std::set<content_t>::const_iterator k = ids.begin();
751 k != ids.end(); ++k) {
753 if (c >= m_aabms.size())
754 m_aabms.resize(c + 256, NULL);
756 m_aabms[c] = new std::vector<ActiveABM>;
757 m_aabms[c]->push_back(aabm);
765 for (size_t i = 0; i < m_aabms.size(); i++)
769 // Find out how many objects the given block and its neighbours contain.
770 // Returns the number of objects in the block, and also in 'wider' the
771 // number of objects in the block and all its neighbours. The latter
772 // may an estimate if any neighbours are unloaded.
773 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
776 u32 wider_unknown_count = 0;
777 for(s16 x=-1; x<=1; x++)
778 for(s16 y=-1; y<=1; y++)
779 for(s16 z=-1; z<=1; z++)
781 MapBlock *block2 = map->getBlockNoCreateNoEx(
782 block->getPos() + v3s16(x,y,z));
784 wider_unknown_count++;
787 wider += block2->m_static_objects.m_active.size()
788 + block2->m_static_objects.m_stored.size();
791 u32 active_object_count = block->m_static_objects.m_active.size();
792 u32 wider_known_count = 3*3*3 - wider_unknown_count;
793 wider += wider_unknown_count * wider / wider_known_count;
794 return active_object_count;
797 void apply(MapBlock *block)
799 if(m_aabms.empty() || block->isDummy())
802 ServerMap *map = &m_env->getServerMap();
804 u32 active_object_count_wider;
805 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
806 m_env->m_added_objects = 0;
809 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
810 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
811 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
813 const MapNode &n = block->getNodeUnsafe(p0);
814 content_t c = n.getContent();
816 if (c >= m_aabms.size() || !m_aabms[c])
819 v3s16 p = p0 + block->getPosRelative();
820 for(std::vector<ActiveABM>::iterator
821 i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
822 if(myrand() % i->chance != 0)
826 if(!i->required_neighbors.empty())
829 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
830 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
831 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
836 if (block->isValidPosition(p1)) {
837 // if the neighbor is found on the same map block
838 // get it straight from there
839 const MapNode &n = block->getNodeUnsafe(p1);
842 // otherwise consult the map
843 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
846 std::set<content_t>::const_iterator k;
847 k = i->required_neighbors.find(c);
848 if(k != i->required_neighbors.end()){
852 // No required neighbor found
857 // Call all the trigger variations
858 i->abm->trigger(m_env, p, n);
859 i->abm->trigger(m_env, p, n,
860 active_object_count, active_object_count_wider);
862 // Count surrounding objects again if the abms added any
863 if(m_env->m_added_objects > 0) {
864 active_object_count = countObjects(block, map, active_object_count_wider);
865 m_env->m_added_objects = 0;
872 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
874 // Reset usage timer immediately, otherwise a block that becomes active
875 // again at around the same time as it would normally be unloaded will
876 // get unloaded incorrectly. (I think this still leaves a small possibility
877 // of a race condition between this and server::AsyncRunStep, which only
878 // some kind of synchronisation will fix, but it at least reduces the window
879 // of opportunity for it to break from seconds to nanoseconds)
880 block->resetUsageTimer();
882 // Get time difference
884 u32 stamp = block->getTimestamp();
885 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
886 dtime_s = m_game_time - stamp;
887 dtime_s += additional_dtime;
889 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
890 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
892 // Remove stored static objects if clearObjects was called since block's timestamp
893 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
894 block->m_static_objects.m_stored.clear();
895 // do not set changed flag to avoid unnecessary mapblock writes
898 // Set current time as timestamp
899 block->setTimestampNoChangedFlag(m_game_time);
901 /*infostream<<"ServerEnvironment::activateBlock(): block is "
902 <<dtime_s<<" seconds old."<<std::endl;*/
904 // Activate stored objects
905 activateObjects(block, dtime_s);
907 /* Handle LoadingBlockModifiers */
908 m_lbm_mgr.applyLBMs(this, block, stamp);
911 std::vector<NodeTimer> elapsed_timers =
912 block->m_node_timers.step((float)dtime_s);
913 if (!elapsed_timers.empty()) {
915 for (std::vector<NodeTimer>::iterator
916 i = elapsed_timers.begin();
917 i != elapsed_timers.end(); ++i){
918 n = block->getNodeNoEx(i->position);
919 v3s16 p = i->position + block->getPosRelative();
920 if (m_script->node_on_timer(p, n, i->elapsed))
921 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
925 /* Handle ActiveBlockModifiers */
926 ABMHandler abmhandler(m_abms, dtime_s, this, false);
927 abmhandler.apply(block);
930 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
932 m_abms.push_back(ABMWithState(abm));
935 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
937 m_lbm_mgr.addLBMDef(lbm);
940 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
942 INodeDefManager *ndef = m_server->ndef();
943 MapNode n_old = m_map->getNodeNoEx(p);
946 if (ndef->get(n_old).has_on_destruct)
947 m_script->node_on_destruct(p, n_old);
950 if (!m_map->addNodeWithEvent(p, n))
953 // Update active VoxelManipulator if a mapgen thread
954 m_map->updateVManip(p);
956 // Call post-destructor
957 if (ndef->get(n_old).has_after_destruct)
958 m_script->node_after_destruct(p, n_old);
961 if (ndef->get(n).has_on_construct)
962 m_script->node_on_construct(p, n);
967 bool ServerEnvironment::removeNode(v3s16 p)
969 INodeDefManager *ndef = m_server->ndef();
970 MapNode n_old = m_map->getNodeNoEx(p);
973 if (ndef->get(n_old).has_on_destruct)
974 m_script->node_on_destruct(p, n_old);
977 // This is slightly optimized compared to addNodeWithEvent(air)
978 if (!m_map->removeNodeWithEvent(p))
981 // Update active VoxelManipulator if a mapgen thread
982 m_map->updateVManip(p);
984 // Call post-destructor
985 if (ndef->get(n_old).has_after_destruct)
986 m_script->node_after_destruct(p, n_old);
988 // Air doesn't require constructor
992 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
994 if (!m_map->addNodeWithEvent(p, n, false))
997 // Update active VoxelManipulator if a mapgen thread
998 m_map->updateVManip(p);
1003 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
1006 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1007 i != m_active_objects.end(); ++i) {
1008 ServerActiveObject* obj = i->second;
1010 v3f objectpos = obj->getBasePosition();
1011 if (objectpos.getDistanceFrom(pos) > radius)
1013 objects.push_back(id);
1017 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1019 infostream << "ServerEnvironment::clearObjects(): "
1020 << "Removing all active objects" << std::endl;
1021 std::vector<u16> objects_to_remove;
1022 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1023 i != m_active_objects.end(); ++i) {
1024 ServerActiveObject* obj = i->second;
1025 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1028 // Delete static object if block is loaded
1029 if (obj->m_static_exists) {
1030 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1032 block->m_static_objects.remove(id);
1033 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1034 MOD_REASON_CLEAR_ALL_OBJECTS);
1035 obj->m_static_exists = false;
1038 // If known by some client, don't delete immediately
1039 if (obj->m_known_by_count > 0) {
1040 obj->m_pending_deactivation = true;
1041 obj->m_removed = true;
1045 // Tell the object about removal
1046 obj->removingFromEnvironment();
1047 // Deregister in scripting api
1048 m_script->removeObjectReference(obj);
1050 // Delete active object
1051 if (obj->environmentDeletes())
1053 // Id to be removed from m_active_objects
1054 objects_to_remove.push_back(id);
1057 // Remove references from m_active_objects
1058 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1059 i != objects_to_remove.end(); ++i) {
1060 m_active_objects.erase(*i);
1063 // Get list of loaded blocks
1064 std::vector<v3s16> loaded_blocks;
1065 infostream << "ServerEnvironment::clearObjects(): "
1066 << "Listing all loaded blocks" << std::endl;
1067 m_map->listAllLoadedBlocks(loaded_blocks);
1068 infostream << "ServerEnvironment::clearObjects(): "
1069 << "Done listing all loaded blocks: "
1070 << loaded_blocks.size()<<std::endl;
1072 // Get list of loadable blocks
1073 std::vector<v3s16> loadable_blocks;
1074 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1075 infostream << "ServerEnvironment::clearObjects(): "
1076 << "Listing all loadable blocks" << std::endl;
1077 m_map->listAllLoadableBlocks(loadable_blocks);
1078 infostream << "ServerEnvironment::clearObjects(): "
1079 << "Done listing all loadable blocks: "
1080 << loadable_blocks.size() << std::endl;
1082 loadable_blocks = loaded_blocks;
1085 infostream << "ServerEnvironment::clearObjects(): "
1086 << "Now clearing objects in " << loadable_blocks.size()
1087 << " blocks" << std::endl;
1089 // Grab a reference on each loaded block to avoid unloading it
1090 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1091 i != loaded_blocks.end(); ++i) {
1093 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1094 assert(block != NULL);
1098 // Remove objects in all loadable blocks
1099 u32 unload_interval = U32_MAX;
1100 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1101 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1102 unload_interval = MYMAX(unload_interval, 1);
1104 u32 report_interval = loadable_blocks.size() / 10;
1105 u32 num_blocks_checked = 0;
1106 u32 num_blocks_cleared = 0;
1107 u32 num_objs_cleared = 0;
1108 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1109 i != loadable_blocks.end(); ++i) {
1111 MapBlock *block = m_map->emergeBlock(p, false);
1113 errorstream << "ServerEnvironment::clearObjects(): "
1114 << "Failed to emerge block " << PP(p) << std::endl;
1117 u32 num_stored = block->m_static_objects.m_stored.size();
1118 u32 num_active = block->m_static_objects.m_active.size();
1119 if (num_stored != 0 || num_active != 0) {
1120 block->m_static_objects.m_stored.clear();
1121 block->m_static_objects.m_active.clear();
1122 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1123 MOD_REASON_CLEAR_ALL_OBJECTS);
1124 num_objs_cleared += num_stored + num_active;
1125 num_blocks_cleared++;
1127 num_blocks_checked++;
1129 if (report_interval != 0 &&
1130 num_blocks_checked % report_interval == 0) {
1131 float percent = 100.0 * (float)num_blocks_checked /
1132 loadable_blocks.size();
1133 infostream << "ServerEnvironment::clearObjects(): "
1134 << "Cleared " << num_objs_cleared << " objects"
1135 << " in " << num_blocks_cleared << " blocks ("
1136 << percent << "%)" << std::endl;
1138 if (num_blocks_checked % unload_interval == 0) {
1139 m_map->unloadUnreferencedBlocks();
1142 m_map->unloadUnreferencedBlocks();
1144 // Drop references that were added above
1145 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1146 i != loaded_blocks.end(); ++i) {
1148 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1153 m_last_clear_objects_time = m_game_time;
1155 infostream << "ServerEnvironment::clearObjects(): "
1156 << "Finished: Cleared " << num_objs_cleared << " objects"
1157 << " in " << num_blocks_cleared << " blocks" << std::endl;
1160 void ServerEnvironment::step(float dtime)
1162 DSTACK(FUNCTION_NAME);
1164 //TimeTaker timer("ServerEnv step");
1166 /* Step time of day */
1167 stepTimeOfDay(dtime);
1170 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1171 // really matter that much.
1172 static thread_local const float server_step =
1173 g_settings->getFloat("dedicated_server_step");
1174 m_recommended_send_interval = server_step;
1180 m_game_time_fraction_counter += dtime;
1181 u32 inc_i = (u32)m_game_time_fraction_counter;
1182 m_game_time += inc_i;
1183 m_game_time_fraction_counter -= (float)inc_i;
1190 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1191 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1192 i != m_players.end(); ++i) {
1193 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1196 // Ignore disconnected players
1197 if(player->peer_id == 0)
1201 player->move(dtime, this, 100*BS);
1206 Manage active block list
1208 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1209 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1211 Get player block positions
1213 std::vector<v3s16> players_blockpos;
1214 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1215 i != m_players.end(); ++i) {
1216 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1219 // Ignore disconnected players
1220 if (player->peer_id == 0)
1223 PlayerSAO *playersao = player->getPlayerSAO();
1226 v3s16 blockpos = getNodeBlockPos(
1227 floatToInt(playersao->getBasePosition(), BS));
1228 players_blockpos.push_back(blockpos);
1232 Update list of active blocks, collecting changes
1234 static thread_local const s16 active_block_range =
1235 g_settings->getS16("active_block_range");
1236 std::set<v3s16> blocks_removed;
1237 std::set<v3s16> blocks_added;
1238 m_active_blocks.update(players_blockpos, active_block_range,
1239 blocks_removed, blocks_added);
1242 Handle removed blocks
1245 // Convert active objects that are no more in active blocks to static
1246 deactivateFarObjects(false);
1248 for(std::set<v3s16>::iterator
1249 i = blocks_removed.begin();
1250 i != blocks_removed.end(); ++i) {
1253 /* infostream<<"Server: Block " << PP(p)
1254 << " became inactive"<<std::endl; */
1256 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1260 // Set current time as timestamp (and let it set ChangedFlag)
1261 block->setTimestamp(m_game_time);
1268 for(std::set<v3s16>::iterator
1269 i = blocks_added.begin();
1270 i != blocks_added.end(); ++i)
1274 MapBlock *block = m_map->getBlockOrEmerge(p);
1276 m_active_blocks.m_list.erase(p);
1280 activateBlock(block);
1281 /* infostream<<"Server: Block " << PP(p)
1282 << " became active"<<std::endl; */
1287 Mess around in active blocks
1289 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1290 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1292 float dtime = m_cache_nodetimer_interval;
1294 for(std::set<v3s16>::iterator
1295 i = m_active_blocks.m_list.begin();
1296 i != m_active_blocks.m_list.end(); ++i)
1300 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1301 <<") being handled"<<std::endl;*/
1303 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1307 // Reset block usage timer
1308 block->resetUsageTimer();
1310 // Set current time as timestamp
1311 block->setTimestampNoChangedFlag(m_game_time);
1312 // If time has changed much from the one on disk,
1313 // set block to be saved when it is unloaded
1314 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1315 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1316 MOD_REASON_BLOCK_EXPIRED);
1319 std::vector<NodeTimer> elapsed_timers =
1320 block->m_node_timers.step((float)dtime);
1321 if (!elapsed_timers.empty()) {
1323 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1324 i != elapsed_timers.end(); ++i) {
1325 n = block->getNodeNoEx(i->position);
1326 p = i->position + block->getPosRelative();
1327 if (m_script->node_on_timer(p, n, i->elapsed)) {
1328 block->setNodeTimer(NodeTimer(
1329 i->timeout, 0, i->position));
1336 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1338 if(m_active_block_interval_overload_skip > 0){
1339 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1340 m_active_block_interval_overload_skip--;
1343 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1344 TimeTaker timer("modify in active blocks per interval");
1346 // Initialize handling of ActiveBlockModifiers
1347 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1349 for(std::set<v3s16>::iterator
1350 i = m_active_blocks.m_list.begin();
1351 i != m_active_blocks.m_list.end(); ++i)
1355 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1356 <<") being handled"<<std::endl;*/
1358 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1362 // Set current time as timestamp
1363 block->setTimestampNoChangedFlag(m_game_time);
1365 /* Handle ActiveBlockModifiers */
1366 abmhandler.apply(block);
1369 u32 time_ms = timer.stop(true);
1370 u32 max_time_ms = 200;
1371 if(time_ms > max_time_ms){
1372 warningstream<<"active block modifiers took "
1373 <<time_ms<<"ms (longer than "
1374 <<max_time_ms<<"ms)"<<std::endl;
1375 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1380 Step script environment (run global on_step())
1382 m_script->environment_Step(dtime);
1388 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1389 //TimeTaker timer("Step active objects");
1391 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1393 // This helps the objects to send data at the same time
1394 bool send_recommended = false;
1395 m_send_recommended_timer += dtime;
1396 if(m_send_recommended_timer > getSendRecommendedInterval())
1398 m_send_recommended_timer -= getSendRecommendedInterval();
1399 send_recommended = true;
1402 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1403 i != m_active_objects.end(); ++i) {
1404 ServerActiveObject* obj = i->second;
1405 // Don't step if is to be removed or stored statically
1406 if(obj->m_removed || obj->m_pending_deactivation)
1409 obj->step(dtime, send_recommended);
1410 // Read messages from object
1411 while(!obj->m_messages_out.empty())
1413 m_active_object_messages.push(
1414 obj->m_messages_out.front());
1415 obj->m_messages_out.pop();
1421 Manage active objects
1423 if(m_object_management_interval.step(dtime, 0.5))
1425 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1427 Remove objects that satisfy (m_removed && m_known_by_count==0)
1429 removeRemovedObjects();
1433 Manage particle spawner expiration
1435 if (m_particle_management_interval.step(dtime, 1.0)) {
1436 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1437 i != m_particle_spawners.end(); ) {
1438 //non expiring spawners
1439 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1445 if (i->second <= 0.f)
1446 m_particle_spawners.erase(i++);
1453 u32 ServerEnvironment::addParticleSpawner(float exptime)
1455 // Timers with lifetime 0 do not expire
1456 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1459 for (;;) { // look for unused particlespawner id
1461 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1462 if (f == m_particle_spawners.end()) {
1463 m_particle_spawners[id] = time;
1470 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1472 u32 id = addParticleSpawner(exptime);
1473 m_particle_spawner_attachments[id] = attached_id;
1474 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1475 obj->attachParticleSpawner(id);
1480 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1482 m_particle_spawners.erase(id);
1483 std::unordered_map<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1484 if (it != m_particle_spawner_attachments.end()) {
1485 u16 obj_id = (*it).second;
1486 ServerActiveObject *sao = getActiveObject(obj_id);
1487 if (sao != NULL && remove_from_object) {
1488 sao->detachParticleSpawner(id);
1490 m_particle_spawner_attachments.erase(id);
1494 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1496 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1497 return (n != m_active_objects.end() ? n->second : NULL);
1500 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1505 return objects.find(id) == objects.end();
1508 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1510 //try to reuse id's as late as possible
1511 static u16 last_used_id = 0;
1512 u16 startid = last_used_id;
1516 if(isFreeServerActiveObjectId(last_used_id, objects))
1517 return last_used_id;
1519 if(last_used_id == startid)
1524 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1526 assert(object); // Pre-condition
1528 u16 id = addActiveObjectRaw(object, true, 0);
1533 Finds out what new objects have been added to
1534 inside a radius around a position
1536 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1538 std::set<u16> ¤t_objects,
1539 std::queue<u16> &added_objects)
1541 f32 radius_f = radius * BS;
1542 f32 player_radius_f = player_radius * BS;
1544 if (player_radius_f < 0)
1545 player_radius_f = 0;
1547 Go through the object list,
1548 - discard m_removed objects,
1549 - discard objects that are too far away,
1550 - discard objects that are found in current_objects.
1551 - add remaining objects to added_objects
1553 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1554 i != m_active_objects.end(); ++i) {
1558 ServerActiveObject *object = i->second;
1562 // Discard if removed or deactivating
1563 if(object->m_removed || object->m_pending_deactivation)
1566 f32 distance_f = object->getBasePosition().
1567 getDistanceFrom(playersao->getBasePosition());
1568 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1569 // Discard if too far
1570 if (distance_f > player_radius_f && player_radius_f != 0)
1572 } else if (distance_f > radius_f)
1575 // Discard if already on current_objects
1576 std::set<u16>::iterator n;
1577 n = current_objects.find(id);
1578 if(n != current_objects.end())
1580 // Add to added_objects
1581 added_objects.push(id);
1586 Finds out what objects have been removed from
1587 inside a radius around a position
1589 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1591 std::set<u16> ¤t_objects,
1592 std::queue<u16> &removed_objects)
1594 f32 radius_f = radius * BS;
1595 f32 player_radius_f = player_radius * BS;
1597 if (player_radius_f < 0)
1598 player_radius_f = 0;
1600 Go through current_objects; object is removed if:
1601 - object is not found in m_active_objects (this is actually an
1602 error condition; objects should be set m_removed=true and removed
1603 only after all clients have been informed about removal), or
1604 - object has m_removed=true, or
1605 - object is too far away
1607 for(std::set<u16>::iterator
1608 i = current_objects.begin();
1609 i != current_objects.end(); ++i)
1612 ServerActiveObject *object = getActiveObject(id);
1614 if (object == NULL) {
1615 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1616 << " object in current_objects is NULL" << std::endl;
1617 removed_objects.push(id);
1621 if (object->m_removed || object->m_pending_deactivation) {
1622 removed_objects.push(id);
1626 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1627 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1628 if (distance_f <= player_radius_f || player_radius_f == 0)
1630 } else if (distance_f <= radius_f)
1633 // Object is no longer visible
1634 removed_objects.push(id);
1638 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1639 v3s16 blockpos, bool static_exists, v3s16 static_block)
1641 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1645 for (std::map<u16, StaticObject>::iterator
1646 so_it = block->m_static_objects.m_active.begin();
1647 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1648 // Get the ServerActiveObject counterpart to this StaticObject
1649 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it->first);
1650 if (ao_it == m_active_objects.end()) {
1651 // If this ever happens, there must be some kind of nasty bug.
1652 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1653 "Object from MapBlock::m_static_objects::m_active not found "
1654 "in m_active_objects";
1658 ServerActiveObject *sao = ao_it->second;
1659 sao->m_static_exists = static_exists;
1660 sao->m_static_block = static_block;
1664 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1666 if(m_active_object_messages.empty())
1667 return ActiveObjectMessage(0);
1669 ActiveObjectMessage message = m_active_object_messages.front();
1670 m_active_object_messages.pop();
1675 ************ Private methods *************
1678 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1679 bool set_changed, u32 dtime_s)
1681 assert(object); // Pre-condition
1682 if(object->getId() == 0){
1683 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1686 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1687 <<"no free ids available"<<std::endl;
1688 if(object->environmentDeletes())
1692 object->setId(new_id);
1695 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1696 <<"supplied with id "<<object->getId()<<std::endl;
1699 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1700 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1701 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1702 if(object->environmentDeletes())
1707 if (objectpos_over_limit(object->getBasePosition())) {
1708 v3f p = object->getBasePosition();
1709 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1710 << "object position (" << p.X << "," << p.Y << "," << p.Z
1711 << ") outside maximum range" << std::endl;
1712 if (object->environmentDeletes())
1717 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1718 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1720 m_active_objects[object->getId()] = object;
1722 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1723 <<"Added id="<<object->getId()<<"; there are now "
1724 <<m_active_objects.size()<<" active objects."
1727 // Register reference in scripting api (must be done before post-init)
1728 m_script->addObjectReference(object);
1729 // Post-initialize object
1730 object->addedToEnvironment(dtime_s);
1732 // Add static data to block
1733 if(object->isStaticAllowed())
1735 // Add static object to active static list of the block
1736 v3f objectpos = object->getBasePosition();
1737 std::string staticdata = "";
1738 object->getStaticData(&staticdata);
1739 StaticObject s_obj(object->getType(), objectpos, staticdata);
1740 // Add to the block where the object is located in
1741 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1742 MapBlock *block = m_map->emergeBlock(blockpos);
1744 block->m_static_objects.m_active[object->getId()] = s_obj;
1745 object->m_static_exists = true;
1746 object->m_static_block = blockpos;
1749 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1750 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1752 v3s16 p = floatToInt(objectpos, BS);
1753 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1754 <<"could not emerge block for storing id="<<object->getId()
1755 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1759 return object->getId();
1763 Remove objects that satisfy (m_removed && m_known_by_count==0)
1765 void ServerEnvironment::removeRemovedObjects()
1767 std::vector<u16> objects_to_remove;
1768 for(ServerActiveObjectMap::iterator i = m_active_objects.begin();
1769 i != m_active_objects.end(); ++i) {
1771 ServerActiveObject* obj = i->second;
1772 // This shouldn't happen but check it
1775 infostream<<"NULL object found in ServerEnvironment"
1776 <<" while finding removed objects. id="<<id<<std::endl;
1777 // Id to be removed from m_active_objects
1778 objects_to_remove.push_back(id);
1783 We will delete objects that are marked as removed or thatare
1784 waiting for deletion after deactivation
1786 if (!obj->m_removed && !obj->m_pending_deactivation)
1790 Delete static data from block if is marked as removed
1792 if(obj->m_static_exists && obj->m_removed)
1794 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1796 block->m_static_objects.remove(id);
1797 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1798 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1799 obj->m_static_exists = false;
1801 infostream<<"Failed to emerge block from which an object to "
1802 <<"be removed was loaded from. id="<<id<<std::endl;
1806 // If m_known_by_count > 0, don't actually remove. On some future
1807 // invocation this will be 0, which is when removal will continue.
1808 if(obj->m_known_by_count > 0)
1812 Move static data from active to stored if not marked as removed
1814 if(obj->m_static_exists && !obj->m_removed){
1815 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1817 std::map<u16, StaticObject>::iterator i =
1818 block->m_static_objects.m_active.find(id);
1819 if(i != block->m_static_objects.m_active.end()){
1820 block->m_static_objects.m_stored.push_back(i->second);
1821 block->m_static_objects.m_active.erase(id);
1822 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1823 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
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 // Id to be removed from m_active_objects
1841 objects_to_remove.push_back(id);
1843 // Remove references from m_active_objects
1844 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1845 i != objects_to_remove.end(); ++i) {
1846 m_active_objects.erase(*i);
1850 static void print_hexdump(std::ostream &o, const std::string &data)
1852 const int linelength = 16;
1853 for(int l=0; ; l++){
1854 int i0 = linelength * l;
1855 bool at_end = false;
1856 int thislinelength = linelength;
1857 if(i0 + thislinelength > (int)data.size()){
1858 thislinelength = data.size() - i0;
1861 for(int di=0; di<linelength; di++){
1864 if(di<thislinelength)
1865 snprintf(buf, 4, "%.2x ", data[i]);
1867 snprintf(buf, 4, " ");
1871 for(int di=0; di<thislinelength; di++){
1885 Convert stored objects from blocks near the players to active.
1887 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1892 // Ignore if no stored objects (to not set changed flag)
1893 if(block->m_static_objects.m_stored.empty())
1896 verbosestream<<"ServerEnvironment::activateObjects(): "
1897 <<"activating objects of block "<<PP(block->getPos())
1898 <<" ("<<block->m_static_objects.m_stored.size()
1899 <<" objects)"<<std::endl;
1900 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1902 errorstream<<"suspiciously large amount of objects detected: "
1903 <<block->m_static_objects.m_stored.size()<<" in "
1904 <<PP(block->getPos())
1905 <<"; removing all of them."<<std::endl;
1906 // Clear stored list
1907 block->m_static_objects.m_stored.clear();
1908 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1909 MOD_REASON_TOO_MANY_OBJECTS);
1913 // Activate stored objects
1914 std::vector<StaticObject> new_stored;
1915 for (std::vector<StaticObject>::iterator
1916 i = block->m_static_objects.m_stored.begin();
1917 i != block->m_static_objects.m_stored.end(); ++i) {
1918 StaticObject &s_obj = *i;
1920 // Create an active object from the data
1921 ServerActiveObject *obj = ServerActiveObject::create
1922 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1923 // If couldn't create object, store static data back.
1925 errorstream<<"ServerEnvironment::activateObjects(): "
1926 <<"failed to create active object from static object "
1927 <<"in block "<<PP(s_obj.pos/BS)
1928 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1929 print_hexdump(verbosestream, s_obj.data);
1931 new_stored.push_back(s_obj);
1934 verbosestream<<"ServerEnvironment::activateObjects(): "
1935 <<"activated static object pos="<<PP(s_obj.pos/BS)
1936 <<" type="<<(int)s_obj.type<<std::endl;
1937 // This will also add the object to the active static list
1938 addActiveObjectRaw(obj, false, dtime_s);
1940 // Clear stored list
1941 block->m_static_objects.m_stored.clear();
1942 // Add leftover failed stuff to stored list
1943 for(std::vector<StaticObject>::iterator
1944 i = new_stored.begin();
1945 i != new_stored.end(); ++i) {
1946 StaticObject &s_obj = *i;
1947 block->m_static_objects.m_stored.push_back(s_obj);
1950 // Turn the active counterparts of activated objects not pending for
1952 for(std::map<u16, StaticObject>::iterator
1953 i = block->m_static_objects.m_active.begin();
1954 i != block->m_static_objects.m_active.end(); ++i)
1957 ServerActiveObject *object = getActiveObject(id);
1959 object->m_pending_deactivation = false;
1963 Note: Block hasn't really been modified here.
1964 The objects have just been activated and moved from the stored
1965 static list to the active static list.
1966 As such, the block is essentially the same.
1967 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1968 Otherwise there would be a huge amount of unnecessary I/O.
1973 Convert objects that are not standing inside active blocks to static.
1975 If m_known_by_count != 0, active object is not deleted, but static
1976 data is still updated.
1978 If force_delete is set, active object is deleted nevertheless. It
1979 shall only be set so in the destructor of the environment.
1981 If block wasn't generated (not in memory or on disk),
1983 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1985 std::vector<u16> objects_to_remove;
1986 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1987 i != m_active_objects.end(); ++i) {
1988 // force_delete might be overriden per object
1989 bool force_delete = _force_delete;
1991 ServerActiveObject* obj = i->second;
1994 // Do not deactivate if static data creation not allowed
1995 if(!force_delete && !obj->isStaticAllowed())
1998 // If pending deactivation, let removeRemovedObjects() do it
1999 if(!force_delete && obj->m_pending_deactivation)
2003 v3f objectpos = obj->getBasePosition();
2005 // The block in which the object resides in
2006 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2008 // If object's static data is stored in a deactivated block and object
2009 // is actually located in an active block, re-save to the block in
2010 // which the object is actually located in.
2012 obj->m_static_exists &&
2013 !m_active_blocks.contains(obj->m_static_block) &&
2014 m_active_blocks.contains(blockpos_o))
2016 v3s16 old_static_block = obj->m_static_block;
2018 // Save to block where object is located
2019 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2021 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2022 <<"Could not save object id="<<id
2023 <<" to it's current block "<<PP(blockpos_o)
2027 std::string staticdata_new = "";
2028 obj->getStaticData(&staticdata_new);
2029 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2030 block->m_static_objects.insert(id, s_obj);
2031 obj->m_static_block = blockpos_o;
2032 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2033 MOD_REASON_STATIC_DATA_ADDED);
2035 // Delete from block where object was located
2036 block = m_map->emergeBlock(old_static_block, false);
2038 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2039 <<"Could not delete object id="<<id
2040 <<" from it's previous block "<<PP(old_static_block)
2044 block->m_static_objects.remove(id);
2045 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2046 MOD_REASON_STATIC_DATA_REMOVED);
2050 // If block is active, don't remove
2051 if(!force_delete && m_active_blocks.contains(blockpos_o))
2054 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2055 <<"deactivating object id="<<id<<" on inactive block "
2056 <<PP(blockpos_o)<<std::endl;
2058 // If known by some client, don't immediately delete.
2059 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2062 Update the static data
2065 if(obj->isStaticAllowed())
2067 // Create new static object
2068 std::string staticdata_new = "";
2069 obj->getStaticData(&staticdata_new);
2070 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2072 bool stays_in_same_block = false;
2073 bool data_changed = true;
2075 if (obj->m_static_exists) {
2076 if (obj->m_static_block == blockpos_o)
2077 stays_in_same_block = true;
2079 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2082 std::map<u16, StaticObject>::iterator n =
2083 block->m_static_objects.m_active.find(id);
2084 if (n != block->m_static_objects.m_active.end()) {
2085 StaticObject static_old = n->second;
2087 float save_movem = obj->getMinimumSavedMovement();
2089 if (static_old.data == staticdata_new &&
2090 (static_old.pos - objectpos).getLength() < save_movem)
2091 data_changed = false;
2093 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2094 <<"id="<<id<<" m_static_exists=true but "
2095 <<"static data doesn't actually exist in "
2096 <<PP(obj->m_static_block)<<std::endl;
2101 bool shall_be_written = (!stays_in_same_block || data_changed);
2103 // Delete old static object
2104 if(obj->m_static_exists)
2106 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2109 block->m_static_objects.remove(id);
2110 obj->m_static_exists = false;
2111 // Only mark block as modified if data changed considerably
2112 if(shall_be_written)
2113 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2114 MOD_REASON_STATIC_DATA_CHANGED);
2118 // Add to the block where the object is located in
2119 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2120 // Get or generate the block
2121 MapBlock *block = NULL;
2123 block = m_map->emergeBlock(blockpos);
2124 } catch(InvalidPositionException &e){
2125 // Handled via NULL pointer
2126 // NOTE: emergeBlock's failure is usually determined by it
2127 // actually returning NULL
2132 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2133 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2134 << " statically but block " << PP(blockpos)
2135 << " already contains "
2136 << block->m_static_objects.m_stored.size()
2138 << " Forcing delete." << std::endl;
2139 force_delete = true;
2141 // If static counterpart already exists in target block,
2143 // This shouldn't happen because the object is removed from
2144 // the previous block before this according to
2145 // obj->m_static_block, but happens rarely for some unknown
2146 // reason. Unsuccessful attempts have been made to find
2148 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2149 warningstream<<"ServerEnv: Performing hack #83274"
2151 block->m_static_objects.remove(id);
2153 // Store static data
2154 u16 store_id = pending_delete ? id : 0;
2155 block->m_static_objects.insert(store_id, s_obj);
2157 // Only mark block as modified if data changed considerably
2158 if(shall_be_written)
2159 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2160 MOD_REASON_STATIC_DATA_CHANGED);
2162 obj->m_static_exists = true;
2163 obj->m_static_block = block->getPos();
2168 v3s16 p = floatToInt(objectpos, BS);
2169 errorstream<<"ServerEnv: Could not find or generate "
2170 <<"a block for storing id="<<obj->getId()
2171 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2178 If known by some client, set pending deactivation.
2179 Otherwise delete it immediately.
2182 if(pending_delete && !force_delete)
2184 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2185 <<"object id="<<id<<" is known by clients"
2186 <<"; not deleting yet"<<std::endl;
2188 obj->m_pending_deactivation = true;
2192 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2193 <<"object id="<<id<<" is not known by clients"
2194 <<"; deleting"<<std::endl;
2196 // Tell the object about removal
2197 obj->removingFromEnvironment();
2198 // Deregister in scripting api
2199 m_script->removeObjectReference(obj);
2201 // Delete active object
2202 if(obj->environmentDeletes())
2204 // Id to be removed from m_active_objects
2205 objects_to_remove.push_back(id);
2208 // Remove references from m_active_objects
2209 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2210 i != objects_to_remove.end(); ++i) {
2211 m_active_objects.erase(*i);
2215 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2216 const std::string &savedir, const Settings &conf)
2219 if (name == "sqlite3")
2220 return new PlayerDatabaseSQLite3(savedir);
2221 else if (name == "dummy")
2222 return new Database_Dummy();
2224 else if (name == "postgresql") {
2225 std::string connect_string = "";
2226 conf.getNoEx("pgsql_player_connection", connect_string);
2227 return new PlayerDatabasePostgreSQL(connect_string);
2230 else if (name == "files")
2231 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2233 throw BaseException(std::string("Database backend ") + name + " not supported.");
2236 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2237 const Settings &cmd_args)
2239 std::string migrate_to = cmd_args.get("migrate-players");
2241 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2242 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2243 errorstream << "Cannot read world.mt!" << std::endl;
2247 if (!world_mt.exists("player_backend")) {
2248 errorstream << "Please specify your current backend in world.mt:"
2250 << " player_backend = {files|sqlite3|postgresql}"
2255 std::string backend = world_mt.get("player_backend");
2256 if (backend == migrate_to) {
2257 errorstream << "Cannot migrate: new backend is same"
2258 << " as the old one" << std::endl;
2262 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2265 if (backend == "files") {
2266 // Create backup directory
2267 fs::CreateDir(players_backup_path);
2271 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2272 game_params.world_path, world_mt);
2273 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2274 game_params.world_path, world_mt);
2276 std::vector<std::string> player_list;
2277 srcdb->listPlayers(player_list);
2278 for (std::vector<std::string>::const_iterator it = player_list.begin();
2279 it != player_list.end(); ++it) {
2280 actionstream << "Migrating player " << it->c_str() << std::endl;
2281 RemotePlayer player(it->c_str(), NULL);
2282 PlayerSAO playerSAO(NULL, &player, 15000, false);
2284 srcdb->loadPlayer(&player, &playerSAO);
2286 playerSAO.finalize(&player, std::set<std::string>());
2287 player.setPlayerSAO(&playerSAO);
2289 dstdb->savePlayer(&player);
2291 // For files source, move player files to backup dir
2292 if (backend == "files") {
2294 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2295 players_backup_path + DIR_DELIM + (*it));
2299 actionstream << "Successfully migrated " << player_list.size() << " players"
2301 world_mt.set("player_backend", migrate_to);
2302 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2303 errorstream << "Failed to update world.mt!" << std::endl;
2305 actionstream << "world.mt updated" << std::endl;
2307 // When migration is finished from file backend, remove players directory if empty
2308 if (backend == "files") {
2309 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2316 } catch (BaseException &e) {
2317 errorstream << "An error occured during migration: " << e.what() << std::endl;