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 if (objectpos_over_limit(playersao->getBasePosition())) {
583 actionstream << "Respawn position for player \""
584 << player->getName() << "\" outside limits, resetting" << std::endl;
585 playersao->setBasePosition(m_server->findSpawnPos());
589 // Add player to environment
592 /* Clean up old HUD elements from previous sessions */
595 /* Add object to environment */
596 addActiveObject(playersao);
601 void ServerEnvironment::saveMeta()
603 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
605 // Open file and serialize
606 std::ostringstream ss(std::ios_base::binary);
609 args.setU64("game_time", m_game_time);
610 args.setU64("time_of_day", getTimeOfDay());
611 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
612 args.setU64("lbm_introduction_times_version", 1);
613 args.set("lbm_introduction_times",
614 m_lbm_mgr.createIntroductionTimesString());
615 args.setU64("day_count", m_day_count);
619 if(!fs::safeWriteToFile(path, ss.str()))
621 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
623 throw SerializationError("Couldn't save env meta");
627 void ServerEnvironment::loadMeta()
629 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
631 // Open file and deserialize
632 std::ifstream is(path.c_str(), std::ios_base::binary);
634 infostream << "ServerEnvironment::loadMeta(): Failed to open "
635 << path << std::endl;
636 throw SerializationError("Couldn't load env meta");
641 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
642 throw SerializationError("ServerEnvironment::loadMeta(): "
643 "EnvArgsEnd not found!");
647 m_game_time = args.getU64("game_time");
648 } catch (SettingNotFoundException &e) {
649 // Getting this is crucial, otherwise timestamps are useless
650 throw SerializationError("Couldn't load env meta game_time");
653 setTimeOfDay(args.exists("time_of_day") ?
654 // set day to morning by default
655 args.getU64("time_of_day") : 9000);
657 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
658 // If missing, do as if clearObjects was never called
659 args.getU64("last_clear_objects_time") : 0;
661 std::string lbm_introduction_times = "";
663 u64 ver = args.getU64("lbm_introduction_times_version");
665 lbm_introduction_times = args.get("lbm_introduction_times");
667 infostream << "ServerEnvironment::loadMeta(): Non-supported"
668 << " introduction time version " << ver << std::endl;
670 } catch (SettingNotFoundException &e) {
671 // No problem, this is expected. Just continue with an empty string
673 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
675 m_day_count = args.exists("day_count") ?
676 args.getU64("day_count") : 0;
679 void ServerEnvironment::loadDefaultMeta()
681 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
686 ActiveBlockModifier *abm;
688 std::set<content_t> required_neighbors;
694 ServerEnvironment *m_env;
695 std::vector<std::vector<ActiveABM> *> m_aabms;
697 ABMHandler(std::vector<ABMWithState> &abms,
698 float dtime_s, ServerEnvironment *env,
704 INodeDefManager *ndef = env->getGameDef()->ndef();
705 for(std::vector<ABMWithState>::iterator
706 i = abms.begin(); i != abms.end(); ++i) {
707 ActiveBlockModifier *abm = i->abm;
708 float trigger_interval = abm->getTriggerInterval();
709 if(trigger_interval < 0.001)
710 trigger_interval = 0.001;
711 float actual_interval = dtime_s;
714 if(i->timer < trigger_interval)
716 i->timer -= trigger_interval;
717 actual_interval = trigger_interval;
719 float chance = abm->getTriggerChance();
724 if (abm->getSimpleCatchUp()) {
725 float intervals = actual_interval / trigger_interval;
728 aabm.chance = chance / intervals;
732 aabm.chance = chance;
736 const std::set<std::string> &required_neighbors_s =
737 abm->getRequiredNeighbors();
738 for (std::set<std::string>::iterator rn = required_neighbors_s.begin();
739 rn != required_neighbors_s.end(); ++rn) {
740 ndef->getIds(*rn, aabm.required_neighbors);
744 const std::set<std::string> &contents_s = abm->getTriggerContents();
745 for (std::set<std::string>::iterator cs = contents_s.begin();
746 cs != contents_s.end(); ++cs) {
747 std::set<content_t> ids;
748 ndef->getIds(*cs, ids);
749 for (std::set<content_t>::const_iterator k = ids.begin();
750 k != ids.end(); ++k) {
752 if (c >= m_aabms.size())
753 m_aabms.resize(c + 256, NULL);
755 m_aabms[c] = new std::vector<ActiveABM>;
756 m_aabms[c]->push_back(aabm);
764 for (size_t i = 0; i < m_aabms.size(); i++)
768 // Find out how many objects the given block and its neighbours contain.
769 // Returns the number of objects in the block, and also in 'wider' the
770 // number of objects in the block and all its neighbours. The latter
771 // may an estimate if any neighbours are unloaded.
772 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
775 u32 wider_unknown_count = 0;
776 for(s16 x=-1; x<=1; x++)
777 for(s16 y=-1; y<=1; y++)
778 for(s16 z=-1; z<=1; z++)
780 MapBlock *block2 = map->getBlockNoCreateNoEx(
781 block->getPos() + v3s16(x,y,z));
783 wider_unknown_count++;
786 wider += block2->m_static_objects.m_active.size()
787 + block2->m_static_objects.m_stored.size();
790 u32 active_object_count = block->m_static_objects.m_active.size();
791 u32 wider_known_count = 3*3*3 - wider_unknown_count;
792 wider += wider_unknown_count * wider / wider_known_count;
793 return active_object_count;
796 void apply(MapBlock *block)
798 if(m_aabms.empty() || block->isDummy())
801 ServerMap *map = &m_env->getServerMap();
803 u32 active_object_count_wider;
804 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
805 m_env->m_added_objects = 0;
808 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
809 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
810 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
812 const MapNode &n = block->getNodeUnsafe(p0);
813 content_t c = n.getContent();
815 if (c >= m_aabms.size() || !m_aabms[c])
818 v3s16 p = p0 + block->getPosRelative();
819 for(std::vector<ActiveABM>::iterator
820 i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
821 if(myrand() % i->chance != 0)
825 if(!i->required_neighbors.empty())
828 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
829 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
830 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
835 if (block->isValidPosition(p1)) {
836 // if the neighbor is found on the same map block
837 // get it straight from there
838 const MapNode &n = block->getNodeUnsafe(p1);
841 // otherwise consult the map
842 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
845 std::set<content_t>::const_iterator k;
846 k = i->required_neighbors.find(c);
847 if(k != i->required_neighbors.end()){
851 // No required neighbor found
856 // Call all the trigger variations
857 i->abm->trigger(m_env, p, n);
858 i->abm->trigger(m_env, p, n,
859 active_object_count, active_object_count_wider);
861 // Count surrounding objects again if the abms added any
862 if(m_env->m_added_objects > 0) {
863 active_object_count = countObjects(block, map, active_object_count_wider);
864 m_env->m_added_objects = 0;
871 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
873 // Reset usage timer immediately, otherwise a block that becomes active
874 // again at around the same time as it would normally be unloaded will
875 // get unloaded incorrectly. (I think this still leaves a small possibility
876 // of a race condition between this and server::AsyncRunStep, which only
877 // some kind of synchronisation will fix, but it at least reduces the window
878 // of opportunity for it to break from seconds to nanoseconds)
879 block->resetUsageTimer();
881 // Get time difference
883 u32 stamp = block->getTimestamp();
884 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
885 dtime_s = m_game_time - stamp;
886 dtime_s += additional_dtime;
888 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
889 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
891 // Remove stored static objects if clearObjects was called since block's timestamp
892 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
893 block->m_static_objects.m_stored.clear();
894 // do not set changed flag to avoid unnecessary mapblock writes
897 // Set current time as timestamp
898 block->setTimestampNoChangedFlag(m_game_time);
900 /*infostream<<"ServerEnvironment::activateBlock(): block is "
901 <<dtime_s<<" seconds old."<<std::endl;*/
903 // Activate stored objects
904 activateObjects(block, dtime_s);
906 /* Handle LoadingBlockModifiers */
907 m_lbm_mgr.applyLBMs(this, block, stamp);
910 std::vector<NodeTimer> elapsed_timers =
911 block->m_node_timers.step((float)dtime_s);
912 if (!elapsed_timers.empty()) {
914 for (std::vector<NodeTimer>::iterator
915 i = elapsed_timers.begin();
916 i != elapsed_timers.end(); ++i){
917 n = block->getNodeNoEx(i->position);
918 v3s16 p = i->position + block->getPosRelative();
919 if (m_script->node_on_timer(p, n, i->elapsed))
920 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
924 /* Handle ActiveBlockModifiers */
925 ABMHandler abmhandler(m_abms, dtime_s, this, false);
926 abmhandler.apply(block);
929 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
931 m_abms.push_back(ABMWithState(abm));
934 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
936 m_lbm_mgr.addLBMDef(lbm);
939 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
941 INodeDefManager *ndef = m_server->ndef();
942 MapNode n_old = m_map->getNodeNoEx(p);
945 if (ndef->get(n_old).has_on_destruct)
946 m_script->node_on_destruct(p, n_old);
949 if (!m_map->addNodeWithEvent(p, n))
952 // Update active VoxelManipulator if a mapgen thread
953 m_map->updateVManip(p);
955 // Call post-destructor
956 if (ndef->get(n_old).has_after_destruct)
957 m_script->node_after_destruct(p, n_old);
960 if (ndef->get(n).has_on_construct)
961 m_script->node_on_construct(p, n);
966 bool ServerEnvironment::removeNode(v3s16 p)
968 INodeDefManager *ndef = m_server->ndef();
969 MapNode n_old = m_map->getNodeNoEx(p);
972 if (ndef->get(n_old).has_on_destruct)
973 m_script->node_on_destruct(p, n_old);
976 // This is slightly optimized compared to addNodeWithEvent(air)
977 if (!m_map->removeNodeWithEvent(p))
980 // Update active VoxelManipulator if a mapgen thread
981 m_map->updateVManip(p);
983 // Call post-destructor
984 if (ndef->get(n_old).has_after_destruct)
985 m_script->node_after_destruct(p, n_old);
987 // Air doesn't require constructor
991 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
993 if (!m_map->addNodeWithEvent(p, n, false))
996 // Update active VoxelManipulator if a mapgen thread
997 m_map->updateVManip(p);
1002 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
1005 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1006 i != m_active_objects.end(); ++i) {
1007 ServerActiveObject* obj = i->second;
1009 v3f objectpos = obj->getBasePosition();
1010 if (objectpos.getDistanceFrom(pos) > radius)
1012 objects.push_back(id);
1016 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1018 infostream << "ServerEnvironment::clearObjects(): "
1019 << "Removing all active objects" << std::endl;
1020 std::vector<u16> objects_to_remove;
1021 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1022 i != m_active_objects.end(); ++i) {
1023 ServerActiveObject* obj = i->second;
1024 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1027 // Delete static object if block is loaded
1028 if (obj->m_static_exists) {
1029 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1031 block->m_static_objects.remove(id);
1032 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1033 MOD_REASON_CLEAR_ALL_OBJECTS);
1034 obj->m_static_exists = false;
1037 // If known by some client, don't delete immediately
1038 if (obj->m_known_by_count > 0) {
1039 obj->m_pending_deactivation = true;
1040 obj->m_removed = true;
1044 // Tell the object about removal
1045 obj->removingFromEnvironment();
1046 // Deregister in scripting api
1047 m_script->removeObjectReference(obj);
1049 // Delete active object
1050 if (obj->environmentDeletes())
1052 // Id to be removed from m_active_objects
1053 objects_to_remove.push_back(id);
1056 // Remove references from m_active_objects
1057 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1058 i != objects_to_remove.end(); ++i) {
1059 m_active_objects.erase(*i);
1062 // Get list of loaded blocks
1063 std::vector<v3s16> loaded_blocks;
1064 infostream << "ServerEnvironment::clearObjects(): "
1065 << "Listing all loaded blocks" << std::endl;
1066 m_map->listAllLoadedBlocks(loaded_blocks);
1067 infostream << "ServerEnvironment::clearObjects(): "
1068 << "Done listing all loaded blocks: "
1069 << loaded_blocks.size()<<std::endl;
1071 // Get list of loadable blocks
1072 std::vector<v3s16> loadable_blocks;
1073 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1074 infostream << "ServerEnvironment::clearObjects(): "
1075 << "Listing all loadable blocks" << std::endl;
1076 m_map->listAllLoadableBlocks(loadable_blocks);
1077 infostream << "ServerEnvironment::clearObjects(): "
1078 << "Done listing all loadable blocks: "
1079 << loadable_blocks.size() << std::endl;
1081 loadable_blocks = loaded_blocks;
1084 infostream << "ServerEnvironment::clearObjects(): "
1085 << "Now clearing objects in " << loadable_blocks.size()
1086 << " blocks" << std::endl;
1088 // Grab a reference on each loaded block to avoid unloading it
1089 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1090 i != loaded_blocks.end(); ++i) {
1092 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1093 assert(block != NULL);
1097 // Remove objects in all loadable blocks
1098 u32 unload_interval = U32_MAX;
1099 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1100 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1101 unload_interval = MYMAX(unload_interval, 1);
1103 u32 report_interval = loadable_blocks.size() / 10;
1104 u32 num_blocks_checked = 0;
1105 u32 num_blocks_cleared = 0;
1106 u32 num_objs_cleared = 0;
1107 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1108 i != loadable_blocks.end(); ++i) {
1110 MapBlock *block = m_map->emergeBlock(p, false);
1112 errorstream << "ServerEnvironment::clearObjects(): "
1113 << "Failed to emerge block " << PP(p) << std::endl;
1116 u32 num_stored = block->m_static_objects.m_stored.size();
1117 u32 num_active = block->m_static_objects.m_active.size();
1118 if (num_stored != 0 || num_active != 0) {
1119 block->m_static_objects.m_stored.clear();
1120 block->m_static_objects.m_active.clear();
1121 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1122 MOD_REASON_CLEAR_ALL_OBJECTS);
1123 num_objs_cleared += num_stored + num_active;
1124 num_blocks_cleared++;
1126 num_blocks_checked++;
1128 if (report_interval != 0 &&
1129 num_blocks_checked % report_interval == 0) {
1130 float percent = 100.0 * (float)num_blocks_checked /
1131 loadable_blocks.size();
1132 infostream << "ServerEnvironment::clearObjects(): "
1133 << "Cleared " << num_objs_cleared << " objects"
1134 << " in " << num_blocks_cleared << " blocks ("
1135 << percent << "%)" << std::endl;
1137 if (num_blocks_checked % unload_interval == 0) {
1138 m_map->unloadUnreferencedBlocks();
1141 m_map->unloadUnreferencedBlocks();
1143 // Drop references that were added above
1144 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1145 i != loaded_blocks.end(); ++i) {
1147 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1152 m_last_clear_objects_time = m_game_time;
1154 infostream << "ServerEnvironment::clearObjects(): "
1155 << "Finished: Cleared " << num_objs_cleared << " objects"
1156 << " in " << num_blocks_cleared << " blocks" << std::endl;
1159 void ServerEnvironment::step(float dtime)
1161 DSTACK(FUNCTION_NAME);
1163 //TimeTaker timer("ServerEnv step");
1165 /* Step time of day */
1166 stepTimeOfDay(dtime);
1169 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1170 // really matter that much.
1171 static const float server_step = g_settings->getFloat("dedicated_server_step");
1172 m_recommended_send_interval = server_step;
1178 m_game_time_fraction_counter += dtime;
1179 u32 inc_i = (u32)m_game_time_fraction_counter;
1180 m_game_time += inc_i;
1181 m_game_time_fraction_counter -= (float)inc_i;
1188 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1189 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1190 i != m_players.end(); ++i) {
1191 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1194 // Ignore disconnected players
1195 if(player->peer_id == 0)
1199 player->move(dtime, this, 100*BS);
1204 Manage active block list
1206 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1207 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1209 Get player block positions
1211 std::vector<v3s16> players_blockpos;
1212 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1213 i != m_players.end(); ++i) {
1214 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1217 // Ignore disconnected players
1218 if (player->peer_id == 0)
1221 PlayerSAO *playersao = player->getPlayerSAO();
1224 v3s16 blockpos = getNodeBlockPos(
1225 floatToInt(playersao->getBasePosition(), BS));
1226 players_blockpos.push_back(blockpos);
1230 Update list of active blocks, collecting changes
1232 static const s16 active_block_range = g_settings->getS16("active_block_range");
1233 std::set<v3s16> blocks_removed;
1234 std::set<v3s16> blocks_added;
1235 m_active_blocks.update(players_blockpos, active_block_range,
1236 blocks_removed, blocks_added);
1239 Handle removed blocks
1242 // Convert active objects that are no more in active blocks to static
1243 deactivateFarObjects(false);
1245 for(std::set<v3s16>::iterator
1246 i = blocks_removed.begin();
1247 i != blocks_removed.end(); ++i) {
1250 /* infostream<<"Server: Block " << PP(p)
1251 << " became inactive"<<std::endl; */
1253 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1257 // Set current time as timestamp (and let it set ChangedFlag)
1258 block->setTimestamp(m_game_time);
1265 for(std::set<v3s16>::iterator
1266 i = blocks_added.begin();
1267 i != blocks_added.end(); ++i)
1271 MapBlock *block = m_map->getBlockOrEmerge(p);
1273 m_active_blocks.m_list.erase(p);
1277 activateBlock(block);
1278 /* infostream<<"Server: Block " << PP(p)
1279 << " became active"<<std::endl; */
1284 Mess around in active blocks
1286 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1287 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1289 float dtime = m_cache_nodetimer_interval;
1291 for(std::set<v3s16>::iterator
1292 i = m_active_blocks.m_list.begin();
1293 i != m_active_blocks.m_list.end(); ++i)
1297 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1298 <<") being handled"<<std::endl;*/
1300 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1304 // Reset block usage timer
1305 block->resetUsageTimer();
1307 // Set current time as timestamp
1308 block->setTimestampNoChangedFlag(m_game_time);
1309 // If time has changed much from the one on disk,
1310 // set block to be saved when it is unloaded
1311 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1312 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1313 MOD_REASON_BLOCK_EXPIRED);
1316 std::vector<NodeTimer> elapsed_timers =
1317 block->m_node_timers.step((float)dtime);
1318 if (!elapsed_timers.empty()) {
1320 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1321 i != elapsed_timers.end(); ++i) {
1322 n = block->getNodeNoEx(i->position);
1323 p = i->position + block->getPosRelative();
1324 if (m_script->node_on_timer(p, n, i->elapsed)) {
1325 block->setNodeTimer(NodeTimer(
1326 i->timeout, 0, i->position));
1333 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1335 if(m_active_block_interval_overload_skip > 0){
1336 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1337 m_active_block_interval_overload_skip--;
1340 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1341 TimeTaker timer("modify in active blocks per interval");
1343 // Initialize handling of ActiveBlockModifiers
1344 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1346 for(std::set<v3s16>::iterator
1347 i = m_active_blocks.m_list.begin();
1348 i != m_active_blocks.m_list.end(); ++i)
1352 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1353 <<") being handled"<<std::endl;*/
1355 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1359 // Set current time as timestamp
1360 block->setTimestampNoChangedFlag(m_game_time);
1362 /* Handle ActiveBlockModifiers */
1363 abmhandler.apply(block);
1366 u32 time_ms = timer.stop(true);
1367 u32 max_time_ms = 200;
1368 if(time_ms > max_time_ms){
1369 warningstream<<"active block modifiers took "
1370 <<time_ms<<"ms (longer than "
1371 <<max_time_ms<<"ms)"<<std::endl;
1372 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1377 Step script environment (run global on_step())
1379 m_script->environment_Step(dtime);
1385 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1386 //TimeTaker timer("Step active objects");
1388 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1390 // This helps the objects to send data at the same time
1391 bool send_recommended = false;
1392 m_send_recommended_timer += dtime;
1393 if(m_send_recommended_timer > getSendRecommendedInterval())
1395 m_send_recommended_timer -= getSendRecommendedInterval();
1396 send_recommended = true;
1399 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1400 i != m_active_objects.end(); ++i) {
1401 ServerActiveObject* obj = i->second;
1402 // Don't step if is to be removed or stored statically
1403 if(obj->m_removed || obj->m_pending_deactivation)
1406 obj->step(dtime, send_recommended);
1407 // Read messages from object
1408 while(!obj->m_messages_out.empty())
1410 m_active_object_messages.push(
1411 obj->m_messages_out.front());
1412 obj->m_messages_out.pop();
1418 Manage active objects
1420 if(m_object_management_interval.step(dtime, 0.5))
1422 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1424 Remove objects that satisfy (m_removed && m_known_by_count==0)
1426 removeRemovedObjects();
1430 Manage particle spawner expiration
1432 if (m_particle_management_interval.step(dtime, 1.0)) {
1433 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1434 i != m_particle_spawners.end(); ) {
1435 //non expiring spawners
1436 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1442 if (i->second <= 0.f)
1443 m_particle_spawners.erase(i++);
1450 u32 ServerEnvironment::addParticleSpawner(float exptime)
1452 // Timers with lifetime 0 do not expire
1453 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1456 for (;;) { // look for unused particlespawner id
1458 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1459 if (f == m_particle_spawners.end()) {
1460 m_particle_spawners[id] = time;
1467 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1469 u32 id = addParticleSpawner(exptime);
1470 m_particle_spawner_attachments[id] = attached_id;
1471 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1472 obj->attachParticleSpawner(id);
1477 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1479 m_particle_spawners.erase(id);
1480 std::unordered_map<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1481 if (it != m_particle_spawner_attachments.end()) {
1482 u16 obj_id = (*it).second;
1483 ServerActiveObject *sao = getActiveObject(obj_id);
1484 if (sao != NULL && remove_from_object) {
1485 sao->detachParticleSpawner(id);
1487 m_particle_spawner_attachments.erase(id);
1491 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1493 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1494 return (n != m_active_objects.end() ? n->second : NULL);
1497 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1502 return objects.find(id) == objects.end();
1505 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1507 //try to reuse id's as late as possible
1508 static u16 last_used_id = 0;
1509 u16 startid = last_used_id;
1513 if(isFreeServerActiveObjectId(last_used_id, objects))
1514 return last_used_id;
1516 if(last_used_id == startid)
1521 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1523 assert(object); // Pre-condition
1525 u16 id = addActiveObjectRaw(object, true, 0);
1530 Finds out what new objects have been added to
1531 inside a radius around a position
1533 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1535 std::set<u16> ¤t_objects,
1536 std::queue<u16> &added_objects)
1538 f32 radius_f = radius * BS;
1539 f32 player_radius_f = player_radius * BS;
1541 if (player_radius_f < 0)
1542 player_radius_f = 0;
1544 Go through the object list,
1545 - discard m_removed objects,
1546 - discard objects that are too far away,
1547 - discard objects that are found in current_objects.
1548 - add remaining objects to added_objects
1550 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1551 i != m_active_objects.end(); ++i) {
1555 ServerActiveObject *object = i->second;
1559 // Discard if removed or deactivating
1560 if(object->m_removed || object->m_pending_deactivation)
1563 f32 distance_f = object->getBasePosition().
1564 getDistanceFrom(playersao->getBasePosition());
1565 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1566 // Discard if too far
1567 if (distance_f > player_radius_f && player_radius_f != 0)
1569 } else if (distance_f > radius_f)
1572 // Discard if already on current_objects
1573 std::set<u16>::iterator n;
1574 n = current_objects.find(id);
1575 if(n != current_objects.end())
1577 // Add to added_objects
1578 added_objects.push(id);
1583 Finds out what objects have been removed from
1584 inside a radius around a position
1586 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1588 std::set<u16> ¤t_objects,
1589 std::queue<u16> &removed_objects)
1591 f32 radius_f = radius * BS;
1592 f32 player_radius_f = player_radius * BS;
1594 if (player_radius_f < 0)
1595 player_radius_f = 0;
1597 Go through current_objects; object is removed if:
1598 - object is not found in m_active_objects (this is actually an
1599 error condition; objects should be set m_removed=true and removed
1600 only after all clients have been informed about removal), or
1601 - object has m_removed=true, or
1602 - object is too far away
1604 for(std::set<u16>::iterator
1605 i = current_objects.begin();
1606 i != current_objects.end(); ++i)
1609 ServerActiveObject *object = getActiveObject(id);
1611 if (object == NULL) {
1612 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1613 << " object in current_objects is NULL" << std::endl;
1614 removed_objects.push(id);
1618 if (object->m_removed || object->m_pending_deactivation) {
1619 removed_objects.push(id);
1623 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1624 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1625 if (distance_f <= player_radius_f || player_radius_f == 0)
1627 } else if (distance_f <= radius_f)
1630 // Object is no longer visible
1631 removed_objects.push(id);
1635 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1636 v3s16 blockpos, bool static_exists, v3s16 static_block)
1638 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1642 for (std::map<u16, StaticObject>::iterator
1643 so_it = block->m_static_objects.m_active.begin();
1644 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1645 // Get the ServerActiveObject counterpart to this StaticObject
1646 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it->first);
1647 if (ao_it == m_active_objects.end()) {
1648 // If this ever happens, there must be some kind of nasty bug.
1649 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1650 "Object from MapBlock::m_static_objects::m_active not found "
1651 "in m_active_objects";
1655 ServerActiveObject *sao = ao_it->second;
1656 sao->m_static_exists = static_exists;
1657 sao->m_static_block = static_block;
1661 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1663 if(m_active_object_messages.empty())
1664 return ActiveObjectMessage(0);
1666 ActiveObjectMessage message = m_active_object_messages.front();
1667 m_active_object_messages.pop();
1672 ************ Private methods *************
1675 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1676 bool set_changed, u32 dtime_s)
1678 assert(object); // Pre-condition
1679 if(object->getId() == 0){
1680 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1683 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1684 <<"no free ids available"<<std::endl;
1685 if(object->environmentDeletes())
1689 object->setId(new_id);
1692 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1693 <<"supplied with id "<<object->getId()<<std::endl;
1696 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1697 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1698 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1699 if(object->environmentDeletes())
1704 if (objectpos_over_limit(object->getBasePosition())) {
1705 v3f p = object->getBasePosition();
1706 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1707 << "object position (" << p.X << "," << p.Y << "," << p.Z
1708 << ") outside maximum range" << std::endl;
1709 if (object->environmentDeletes())
1714 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1715 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1717 m_active_objects[object->getId()] = object;
1719 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1720 <<"Added id="<<object->getId()<<"; there are now "
1721 <<m_active_objects.size()<<" active objects."
1724 // Register reference in scripting api (must be done before post-init)
1725 m_script->addObjectReference(object);
1726 // Post-initialize object
1727 object->addedToEnvironment(dtime_s);
1729 // Add static data to block
1730 if(object->isStaticAllowed())
1732 // Add static object to active static list of the block
1733 v3f objectpos = object->getBasePosition();
1734 std::string staticdata = "";
1735 object->getStaticData(&staticdata);
1736 StaticObject s_obj(object->getType(), objectpos, staticdata);
1737 // Add to the block where the object is located in
1738 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1739 MapBlock *block = m_map->emergeBlock(blockpos);
1741 block->m_static_objects.m_active[object->getId()] = s_obj;
1742 object->m_static_exists = true;
1743 object->m_static_block = blockpos;
1746 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1747 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1749 v3s16 p = floatToInt(objectpos, BS);
1750 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1751 <<"could not emerge block for storing id="<<object->getId()
1752 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1756 return object->getId();
1760 Remove objects that satisfy (m_removed && m_known_by_count==0)
1762 void ServerEnvironment::removeRemovedObjects()
1764 std::vector<u16> objects_to_remove;
1765 for(ServerActiveObjectMap::iterator i = m_active_objects.begin();
1766 i != m_active_objects.end(); ++i) {
1768 ServerActiveObject* obj = i->second;
1769 // This shouldn't happen but check it
1772 infostream<<"NULL object found in ServerEnvironment"
1773 <<" while finding removed objects. id="<<id<<std::endl;
1774 // Id to be removed from m_active_objects
1775 objects_to_remove.push_back(id);
1780 We will delete objects that are marked as removed or thatare
1781 waiting for deletion after deactivation
1783 if (!obj->m_removed && !obj->m_pending_deactivation)
1787 Delete static data from block if is marked as removed
1789 if(obj->m_static_exists && obj->m_removed)
1791 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1793 block->m_static_objects.remove(id);
1794 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1795 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1796 obj->m_static_exists = false;
1798 infostream<<"Failed to emerge block from which an object to "
1799 <<"be removed was loaded from. id="<<id<<std::endl;
1803 // If m_known_by_count > 0, don't actually remove. On some future
1804 // invocation this will be 0, which is when removal will continue.
1805 if(obj->m_known_by_count > 0)
1809 Move static data from active to stored if not marked as removed
1811 if(obj->m_static_exists && !obj->m_removed){
1812 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1814 std::map<u16, StaticObject>::iterator i =
1815 block->m_static_objects.m_active.find(id);
1816 if(i != block->m_static_objects.m_active.end()){
1817 block->m_static_objects.m_stored.push_back(i->second);
1818 block->m_static_objects.m_active.erase(id);
1819 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1820 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1823 infostream<<"Failed to emerge block from which an object to "
1824 <<"be deactivated was loaded from. id="<<id<<std::endl;
1828 // Tell the object about removal
1829 obj->removingFromEnvironment();
1830 // Deregister in scripting api
1831 m_script->removeObjectReference(obj);
1834 if(obj->environmentDeletes())
1837 // Id to be removed from m_active_objects
1838 objects_to_remove.push_back(id);
1840 // Remove references from m_active_objects
1841 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1842 i != objects_to_remove.end(); ++i) {
1843 m_active_objects.erase(*i);
1847 static void print_hexdump(std::ostream &o, const std::string &data)
1849 const int linelength = 16;
1850 for(int l=0; ; l++){
1851 int i0 = linelength * l;
1852 bool at_end = false;
1853 int thislinelength = linelength;
1854 if(i0 + thislinelength > (int)data.size()){
1855 thislinelength = data.size() - i0;
1858 for(int di=0; di<linelength; di++){
1861 if(di<thislinelength)
1862 snprintf(buf, 4, "%.2x ", data[i]);
1864 snprintf(buf, 4, " ");
1868 for(int di=0; di<thislinelength; di++){
1882 Convert stored objects from blocks near the players to active.
1884 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1889 // Ignore if no stored objects (to not set changed flag)
1890 if(block->m_static_objects.m_stored.empty())
1893 verbosestream<<"ServerEnvironment::activateObjects(): "
1894 <<"activating objects of block "<<PP(block->getPos())
1895 <<" ("<<block->m_static_objects.m_stored.size()
1896 <<" objects)"<<std::endl;
1897 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1899 errorstream<<"suspiciously large amount of objects detected: "
1900 <<block->m_static_objects.m_stored.size()<<" in "
1901 <<PP(block->getPos())
1902 <<"; removing all of them."<<std::endl;
1903 // Clear stored list
1904 block->m_static_objects.m_stored.clear();
1905 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1906 MOD_REASON_TOO_MANY_OBJECTS);
1910 // Activate stored objects
1911 std::vector<StaticObject> new_stored;
1912 for (std::vector<StaticObject>::iterator
1913 i = block->m_static_objects.m_stored.begin();
1914 i != block->m_static_objects.m_stored.end(); ++i) {
1915 StaticObject &s_obj = *i;
1917 // Create an active object from the data
1918 ServerActiveObject *obj = ServerActiveObject::create
1919 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1920 // If couldn't create object, store static data back.
1922 errorstream<<"ServerEnvironment::activateObjects(): "
1923 <<"failed to create active object from static object "
1924 <<"in block "<<PP(s_obj.pos/BS)
1925 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1926 print_hexdump(verbosestream, s_obj.data);
1928 new_stored.push_back(s_obj);
1931 verbosestream<<"ServerEnvironment::activateObjects(): "
1932 <<"activated static object pos="<<PP(s_obj.pos/BS)
1933 <<" type="<<(int)s_obj.type<<std::endl;
1934 // This will also add the object to the active static list
1935 addActiveObjectRaw(obj, false, dtime_s);
1937 // Clear stored list
1938 block->m_static_objects.m_stored.clear();
1939 // Add leftover failed stuff to stored list
1940 for(std::vector<StaticObject>::iterator
1941 i = new_stored.begin();
1942 i != new_stored.end(); ++i) {
1943 StaticObject &s_obj = *i;
1944 block->m_static_objects.m_stored.push_back(s_obj);
1947 // Turn the active counterparts of activated objects not pending for
1949 for(std::map<u16, StaticObject>::iterator
1950 i = block->m_static_objects.m_active.begin();
1951 i != block->m_static_objects.m_active.end(); ++i)
1954 ServerActiveObject *object = getActiveObject(id);
1956 object->m_pending_deactivation = false;
1960 Note: Block hasn't really been modified here.
1961 The objects have just been activated and moved from the stored
1962 static list to the active static list.
1963 As such, the block is essentially the same.
1964 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1965 Otherwise there would be a huge amount of unnecessary I/O.
1970 Convert objects that are not standing inside active blocks to static.
1972 If m_known_by_count != 0, active object is not deleted, but static
1973 data is still updated.
1975 If force_delete is set, active object is deleted nevertheless. It
1976 shall only be set so in the destructor of the environment.
1978 If block wasn't generated (not in memory or on disk),
1980 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1982 std::vector<u16> objects_to_remove;
1983 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1984 i != m_active_objects.end(); ++i) {
1985 // force_delete might be overriden per object
1986 bool force_delete = _force_delete;
1988 ServerActiveObject* obj = i->second;
1991 // Do not deactivate if static data creation not allowed
1992 if(!force_delete && !obj->isStaticAllowed())
1995 // If pending deactivation, let removeRemovedObjects() do it
1996 if(!force_delete && obj->m_pending_deactivation)
2000 v3f objectpos = obj->getBasePosition();
2002 // The block in which the object resides in
2003 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2005 // If object's static data is stored in a deactivated block and object
2006 // is actually located in an active block, re-save to the block in
2007 // which the object is actually located in.
2009 obj->m_static_exists &&
2010 !m_active_blocks.contains(obj->m_static_block) &&
2011 m_active_blocks.contains(blockpos_o))
2013 v3s16 old_static_block = obj->m_static_block;
2015 // Save to block where object is located
2016 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2018 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2019 <<"Could not save object id="<<id
2020 <<" to it's current block "<<PP(blockpos_o)
2024 std::string staticdata_new = "";
2025 obj->getStaticData(&staticdata_new);
2026 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2027 block->m_static_objects.insert(id, s_obj);
2028 obj->m_static_block = blockpos_o;
2029 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2030 MOD_REASON_STATIC_DATA_ADDED);
2032 // Delete from block where object was located
2033 block = m_map->emergeBlock(old_static_block, false);
2035 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2036 <<"Could not delete object id="<<id
2037 <<" from it's previous block "<<PP(old_static_block)
2041 block->m_static_objects.remove(id);
2042 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2043 MOD_REASON_STATIC_DATA_REMOVED);
2047 // If block is active, don't remove
2048 if(!force_delete && m_active_blocks.contains(blockpos_o))
2051 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2052 <<"deactivating object id="<<id<<" on inactive block "
2053 <<PP(blockpos_o)<<std::endl;
2055 // If known by some client, don't immediately delete.
2056 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2059 Update the static data
2062 if(obj->isStaticAllowed())
2064 // Create new static object
2065 std::string staticdata_new = "";
2066 obj->getStaticData(&staticdata_new);
2067 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2069 bool stays_in_same_block = false;
2070 bool data_changed = true;
2072 if (obj->m_static_exists) {
2073 if (obj->m_static_block == blockpos_o)
2074 stays_in_same_block = true;
2076 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2079 std::map<u16, StaticObject>::iterator n =
2080 block->m_static_objects.m_active.find(id);
2081 if (n != block->m_static_objects.m_active.end()) {
2082 StaticObject static_old = n->second;
2084 float save_movem = obj->getMinimumSavedMovement();
2086 if (static_old.data == staticdata_new &&
2087 (static_old.pos - objectpos).getLength() < save_movem)
2088 data_changed = false;
2090 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2091 <<"id="<<id<<" m_static_exists=true but "
2092 <<"static data doesn't actually exist in "
2093 <<PP(obj->m_static_block)<<std::endl;
2098 bool shall_be_written = (!stays_in_same_block || data_changed);
2100 // Delete old static object
2101 if(obj->m_static_exists)
2103 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2106 block->m_static_objects.remove(id);
2107 obj->m_static_exists = false;
2108 // Only mark block as modified if data changed considerably
2109 if(shall_be_written)
2110 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2111 MOD_REASON_STATIC_DATA_CHANGED);
2115 // Add to the block where the object is located in
2116 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2117 // Get or generate the block
2118 MapBlock *block = NULL;
2120 block = m_map->emergeBlock(blockpos);
2121 } catch(InvalidPositionException &e){
2122 // Handled via NULL pointer
2123 // NOTE: emergeBlock's failure is usually determined by it
2124 // actually returning NULL
2129 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2130 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2131 << " statically but block " << PP(blockpos)
2132 << " already contains "
2133 << block->m_static_objects.m_stored.size()
2135 << " Forcing delete." << std::endl;
2136 force_delete = true;
2138 // If static counterpart already exists in target block,
2140 // This shouldn't happen because the object is removed from
2141 // the previous block before this according to
2142 // obj->m_static_block, but happens rarely for some unknown
2143 // reason. Unsuccessful attempts have been made to find
2145 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2146 warningstream<<"ServerEnv: Performing hack #83274"
2148 block->m_static_objects.remove(id);
2150 // Store static data
2151 u16 store_id = pending_delete ? id : 0;
2152 block->m_static_objects.insert(store_id, s_obj);
2154 // Only mark block as modified if data changed considerably
2155 if(shall_be_written)
2156 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2157 MOD_REASON_STATIC_DATA_CHANGED);
2159 obj->m_static_exists = true;
2160 obj->m_static_block = block->getPos();
2165 v3s16 p = floatToInt(objectpos, BS);
2166 errorstream<<"ServerEnv: Could not find or generate "
2167 <<"a block for storing id="<<obj->getId()
2168 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2175 If known by some client, set pending deactivation.
2176 Otherwise delete it immediately.
2179 if(pending_delete && !force_delete)
2181 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2182 <<"object id="<<id<<" is known by clients"
2183 <<"; not deleting yet"<<std::endl;
2185 obj->m_pending_deactivation = true;
2189 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2190 <<"object id="<<id<<" is not known by clients"
2191 <<"; deleting"<<std::endl;
2193 // Tell the object about removal
2194 obj->removingFromEnvironment();
2195 // Deregister in scripting api
2196 m_script->removeObjectReference(obj);
2198 // Delete active object
2199 if(obj->environmentDeletes())
2201 // Id to be removed from m_active_objects
2202 objects_to_remove.push_back(id);
2205 // Remove references from m_active_objects
2206 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2207 i != objects_to_remove.end(); ++i) {
2208 m_active_objects.erase(*i);
2212 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2213 const std::string &savedir, const Settings &conf)
2216 if (name == "sqlite3")
2217 return new PlayerDatabaseSQLite3(savedir);
2218 else if (name == "dummy")
2219 return new Database_Dummy();
2221 else if (name == "postgresql") {
2222 std::string connect_string = "";
2223 conf.getNoEx("pgsql_player_connection", connect_string);
2224 return new PlayerDatabasePostgreSQL(connect_string);
2227 else if (name == "files")
2228 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2230 throw BaseException(std::string("Database backend ") + name + " not supported.");
2233 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2234 const Settings &cmd_args)
2236 std::string migrate_to = cmd_args.get("migrate-players");
2238 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2239 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2240 errorstream << "Cannot read world.mt!" << std::endl;
2244 if (!world_mt.exists("player_backend")) {
2245 errorstream << "Please specify your current backend in world.mt:"
2247 << " player_backend = {files|sqlite3|postgresql}"
2252 std::string backend = world_mt.get("player_backend");
2253 if (backend == migrate_to) {
2254 errorstream << "Cannot migrate: new backend is same"
2255 << " as the old one" << std::endl;
2259 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2262 if (backend == "files") {
2263 // Create backup directory
2264 fs::CreateDir(players_backup_path);
2268 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2269 game_params.world_path, world_mt);
2270 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2271 game_params.world_path, world_mt);
2273 std::vector<std::string> player_list;
2274 srcdb->listPlayers(player_list);
2275 for (std::vector<std::string>::const_iterator it = player_list.begin();
2276 it != player_list.end(); ++it) {
2277 actionstream << "Migrating player " << it->c_str() << std::endl;
2278 RemotePlayer player(it->c_str(), NULL);
2279 PlayerSAO playerSAO(NULL, &player, 15000, false);
2281 srcdb->loadPlayer(&player, &playerSAO);
2283 playerSAO.finalize(&player, std::set<std::string>());
2284 player.setPlayerSAO(&playerSAO);
2286 dstdb->savePlayer(&player);
2288 // For files source, move player files to backup dir
2289 if (backend == "files") {
2291 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2292 players_backup_path + DIR_DELIM + (*it));
2296 actionstream << "Successfully migrated " << player_list.size() << " players"
2298 world_mt.set("player_backend", migrate_to);
2299 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2300 errorstream << "Failed to update world.mt!" << std::endl;
2302 actionstream << "world.mt updated" << std::endl;
2304 // When migration is finished from file backend, remove players directory if empty
2305 if (backend == "files") {
2306 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2313 } catch (BaseException &e) {
2314 errorstream << "An error occured during migration: " << e.what() << std::endl;