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, float radius)
1004 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1005 i != m_active_objects.end(); ++i) {
1006 ServerActiveObject* obj = i->second;
1008 v3f objectpos = obj->getBasePosition();
1009 if (objectpos.getDistanceFrom(pos) > radius)
1011 objects.push_back(id);
1015 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1017 infostream << "ServerEnvironment::clearObjects(): "
1018 << "Removing all active objects" << std::endl;
1019 std::vector<u16> objects_to_remove;
1020 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1021 i != m_active_objects.end(); ++i) {
1022 ServerActiveObject* obj = i->second;
1023 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1026 // Delete static object if block is loaded
1027 if (obj->m_static_exists) {
1028 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1030 block->m_static_objects.remove(id);
1031 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1032 MOD_REASON_CLEAR_ALL_OBJECTS);
1033 obj->m_static_exists = false;
1036 // If known by some client, don't delete immediately
1037 if (obj->m_known_by_count > 0) {
1038 obj->m_pending_deactivation = true;
1039 obj->m_removed = true;
1043 // Tell the object about removal
1044 obj->removingFromEnvironment();
1045 // Deregister in scripting api
1046 m_script->removeObjectReference(obj);
1048 // Delete active object
1049 if (obj->environmentDeletes())
1051 // Id to be removed from m_active_objects
1052 objects_to_remove.push_back(id);
1055 // Remove references from m_active_objects
1056 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1057 i != objects_to_remove.end(); ++i) {
1058 m_active_objects.erase(*i);
1061 // Get list of loaded blocks
1062 std::vector<v3s16> loaded_blocks;
1063 infostream << "ServerEnvironment::clearObjects(): "
1064 << "Listing all loaded blocks" << std::endl;
1065 m_map->listAllLoadedBlocks(loaded_blocks);
1066 infostream << "ServerEnvironment::clearObjects(): "
1067 << "Done listing all loaded blocks: "
1068 << loaded_blocks.size()<<std::endl;
1070 // Get list of loadable blocks
1071 std::vector<v3s16> loadable_blocks;
1072 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1073 infostream << "ServerEnvironment::clearObjects(): "
1074 << "Listing all loadable blocks" << std::endl;
1075 m_map->listAllLoadableBlocks(loadable_blocks);
1076 infostream << "ServerEnvironment::clearObjects(): "
1077 << "Done listing all loadable blocks: "
1078 << loadable_blocks.size() << std::endl;
1080 loadable_blocks = loaded_blocks;
1083 infostream << "ServerEnvironment::clearObjects(): "
1084 << "Now clearing objects in " << loadable_blocks.size()
1085 << " blocks" << std::endl;
1087 // Grab a reference on each loaded block to avoid unloading it
1088 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1089 i != loaded_blocks.end(); ++i) {
1091 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1092 assert(block != NULL);
1096 // Remove objects in all loadable blocks
1097 u32 unload_interval = U32_MAX;
1098 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1099 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1100 unload_interval = MYMAX(unload_interval, 1);
1102 u32 report_interval = loadable_blocks.size() / 10;
1103 u32 num_blocks_checked = 0;
1104 u32 num_blocks_cleared = 0;
1105 u32 num_objs_cleared = 0;
1106 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1107 i != loadable_blocks.end(); ++i) {
1109 MapBlock *block = m_map->emergeBlock(p, false);
1111 errorstream << "ServerEnvironment::clearObjects(): "
1112 << "Failed to emerge block " << PP(p) << std::endl;
1115 u32 num_stored = block->m_static_objects.m_stored.size();
1116 u32 num_active = block->m_static_objects.m_active.size();
1117 if (num_stored != 0 || num_active != 0) {
1118 block->m_static_objects.m_stored.clear();
1119 block->m_static_objects.m_active.clear();
1120 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1121 MOD_REASON_CLEAR_ALL_OBJECTS);
1122 num_objs_cleared += num_stored + num_active;
1123 num_blocks_cleared++;
1125 num_blocks_checked++;
1127 if (report_interval != 0 &&
1128 num_blocks_checked % report_interval == 0) {
1129 float percent = 100.0 * (float)num_blocks_checked /
1130 loadable_blocks.size();
1131 infostream << "ServerEnvironment::clearObjects(): "
1132 << "Cleared " << num_objs_cleared << " objects"
1133 << " in " << num_blocks_cleared << " blocks ("
1134 << percent << "%)" << std::endl;
1136 if (num_blocks_checked % unload_interval == 0) {
1137 m_map->unloadUnreferencedBlocks();
1140 m_map->unloadUnreferencedBlocks();
1142 // Drop references that were added above
1143 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1144 i != loaded_blocks.end(); ++i) {
1146 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1151 m_last_clear_objects_time = m_game_time;
1153 infostream << "ServerEnvironment::clearObjects(): "
1154 << "Finished: Cleared " << num_objs_cleared << " objects"
1155 << " in " << num_blocks_cleared << " blocks" << std::endl;
1158 void ServerEnvironment::step(float dtime)
1160 DSTACK(FUNCTION_NAME);
1162 //TimeTaker timer("ServerEnv step");
1164 /* Step time of day */
1165 stepTimeOfDay(dtime);
1168 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1169 // really matter that much.
1170 static const float server_step = g_settings->getFloat("dedicated_server_step");
1171 m_recommended_send_interval = server_step;
1177 m_game_time_fraction_counter += dtime;
1178 u32 inc_i = (u32)m_game_time_fraction_counter;
1179 m_game_time += inc_i;
1180 m_game_time_fraction_counter -= (float)inc_i;
1187 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1188 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1189 i != m_players.end(); ++i) {
1190 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1193 // Ignore disconnected players
1194 if(player->peer_id == 0)
1198 player->move(dtime, this, 100*BS);
1203 Manage active block list
1205 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1206 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1208 Get player block positions
1210 std::vector<v3s16> players_blockpos;
1211 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1212 i != m_players.end(); ++i) {
1213 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1216 // Ignore disconnected players
1217 if (player->peer_id == 0)
1220 PlayerSAO *playersao = player->getPlayerSAO();
1223 v3s16 blockpos = getNodeBlockPos(
1224 floatToInt(playersao->getBasePosition(), BS));
1225 players_blockpos.push_back(blockpos);
1229 Update list of active blocks, collecting changes
1231 static const s16 active_block_range = g_settings->getS16("active_block_range");
1232 std::set<v3s16> blocks_removed;
1233 std::set<v3s16> blocks_added;
1234 m_active_blocks.update(players_blockpos, active_block_range,
1235 blocks_removed, blocks_added);
1238 Handle removed blocks
1241 // Convert active objects that are no more in active blocks to static
1242 deactivateFarObjects(false);
1244 for(std::set<v3s16>::iterator
1245 i = blocks_removed.begin();
1246 i != blocks_removed.end(); ++i) {
1249 /* infostream<<"Server: Block " << PP(p)
1250 << " became inactive"<<std::endl; */
1252 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1256 // Set current time as timestamp (and let it set ChangedFlag)
1257 block->setTimestamp(m_game_time);
1264 for(std::set<v3s16>::iterator
1265 i = blocks_added.begin();
1266 i != blocks_added.end(); ++i)
1270 MapBlock *block = m_map->getBlockOrEmerge(p);
1272 m_active_blocks.m_list.erase(p);
1276 activateBlock(block);
1277 /* infostream<<"Server: Block " << PP(p)
1278 << " became active"<<std::endl; */
1283 Mess around in active blocks
1285 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1286 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1288 float dtime = m_cache_nodetimer_interval;
1290 for(std::set<v3s16>::iterator
1291 i = m_active_blocks.m_list.begin();
1292 i != m_active_blocks.m_list.end(); ++i)
1296 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1297 <<") being handled"<<std::endl;*/
1299 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1303 // Reset block usage timer
1304 block->resetUsageTimer();
1306 // Set current time as timestamp
1307 block->setTimestampNoChangedFlag(m_game_time);
1308 // If time has changed much from the one on disk,
1309 // set block to be saved when it is unloaded
1310 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1311 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1312 MOD_REASON_BLOCK_EXPIRED);
1315 std::vector<NodeTimer> elapsed_timers =
1316 block->m_node_timers.step((float)dtime);
1317 if (!elapsed_timers.empty()) {
1319 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1320 i != elapsed_timers.end(); ++i) {
1321 n = block->getNodeNoEx(i->position);
1322 p = i->position + block->getPosRelative();
1323 if (m_script->node_on_timer(p, n, i->elapsed)) {
1324 block->setNodeTimer(NodeTimer(
1325 i->timeout, 0, i->position));
1332 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1334 if(m_active_block_interval_overload_skip > 0){
1335 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1336 m_active_block_interval_overload_skip--;
1339 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1340 TimeTaker timer("modify in active blocks per interval");
1342 // Initialize handling of ActiveBlockModifiers
1343 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1345 for(std::set<v3s16>::iterator
1346 i = m_active_blocks.m_list.begin();
1347 i != m_active_blocks.m_list.end(); ++i)
1351 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1352 <<") being handled"<<std::endl;*/
1354 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1358 // Set current time as timestamp
1359 block->setTimestampNoChangedFlag(m_game_time);
1361 /* Handle ActiveBlockModifiers */
1362 abmhandler.apply(block);
1365 u32 time_ms = timer.stop(true);
1366 u32 max_time_ms = 200;
1367 if(time_ms > max_time_ms){
1368 warningstream<<"active block modifiers took "
1369 <<time_ms<<"ms (longer than "
1370 <<max_time_ms<<"ms)"<<std::endl;
1371 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1376 Step script environment (run global on_step())
1378 m_script->environment_Step(dtime);
1384 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1385 //TimeTaker timer("Step active objects");
1387 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1389 // This helps the objects to send data at the same time
1390 bool send_recommended = false;
1391 m_send_recommended_timer += dtime;
1392 if(m_send_recommended_timer > getSendRecommendedInterval())
1394 m_send_recommended_timer -= getSendRecommendedInterval();
1395 send_recommended = true;
1398 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1399 i != m_active_objects.end(); ++i) {
1400 ServerActiveObject* obj = i->second;
1401 // Don't step if is to be removed or stored statically
1402 if(obj->m_removed || obj->m_pending_deactivation)
1405 obj->step(dtime, send_recommended);
1406 // Read messages from object
1407 while(!obj->m_messages_out.empty())
1409 m_active_object_messages.push(
1410 obj->m_messages_out.front());
1411 obj->m_messages_out.pop();
1417 Manage active objects
1419 if(m_object_management_interval.step(dtime, 0.5))
1421 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1423 Remove objects that satisfy (m_removed && m_known_by_count==0)
1425 removeRemovedObjects();
1429 Manage particle spawner expiration
1431 if (m_particle_management_interval.step(dtime, 1.0)) {
1432 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1433 i != m_particle_spawners.end(); ) {
1434 //non expiring spawners
1435 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1441 if (i->second <= 0.f)
1442 m_particle_spawners.erase(i++);
1449 u32 ServerEnvironment::addParticleSpawner(float exptime)
1451 // Timers with lifetime 0 do not expire
1452 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1455 for (;;) { // look for unused particlespawner id
1457 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1458 if (f == m_particle_spawners.end()) {
1459 m_particle_spawners[id] = time;
1466 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1468 u32 id = addParticleSpawner(exptime);
1469 m_particle_spawner_attachments[id] = attached_id;
1470 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1471 obj->attachParticleSpawner(id);
1476 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1478 m_particle_spawners.erase(id);
1479 UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1480 if (it != m_particle_spawner_attachments.end()) {
1481 u16 obj_id = (*it).second;
1482 ServerActiveObject *sao = getActiveObject(obj_id);
1483 if (sao != NULL && remove_from_object) {
1484 sao->detachParticleSpawner(id);
1486 m_particle_spawner_attachments.erase(id);
1490 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1492 ActiveObjectMap::iterator n = m_active_objects.find(id);
1493 return (n != m_active_objects.end() ? n->second : NULL);
1496 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1501 return objects.find(id) == objects.end();
1504 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1506 //try to reuse id's as late as possible
1507 static u16 last_used_id = 0;
1508 u16 startid = last_used_id;
1512 if(isFreeServerActiveObjectId(last_used_id, objects))
1513 return last_used_id;
1515 if(last_used_id == startid)
1520 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1522 assert(object); // Pre-condition
1524 u16 id = addActiveObjectRaw(object, true, 0);
1529 Finds out what new objects have been added to
1530 inside a radius around a position
1532 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1534 std::set<u16> ¤t_objects,
1535 std::queue<u16> &added_objects)
1537 f32 radius_f = radius * BS;
1538 f32 player_radius_f = player_radius * BS;
1540 if (player_radius_f < 0)
1541 player_radius_f = 0;
1543 Go through the object list,
1544 - discard m_removed objects,
1545 - discard objects that are too far away,
1546 - discard objects that are found in current_objects.
1547 - add remaining objects to added_objects
1549 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1550 i != m_active_objects.end(); ++i) {
1554 ServerActiveObject *object = i->second;
1558 // Discard if removed or deactivating
1559 if(object->m_removed || object->m_pending_deactivation)
1562 f32 distance_f = object->getBasePosition().
1563 getDistanceFrom(playersao->getBasePosition());
1564 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1565 // Discard if too far
1566 if (distance_f > player_radius_f && player_radius_f != 0)
1568 } else if (distance_f > radius_f)
1571 // Discard if already on current_objects
1572 std::set<u16>::iterator n;
1573 n = current_objects.find(id);
1574 if(n != current_objects.end())
1576 // Add to added_objects
1577 added_objects.push(id);
1582 Finds out what objects have been removed from
1583 inside a radius around a position
1585 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1587 std::set<u16> ¤t_objects,
1588 std::queue<u16> &removed_objects)
1590 f32 radius_f = radius * BS;
1591 f32 player_radius_f = player_radius * BS;
1593 if (player_radius_f < 0)
1594 player_radius_f = 0;
1596 Go through current_objects; object is removed if:
1597 - object is not found in m_active_objects (this is actually an
1598 error condition; objects should be set m_removed=true and removed
1599 only after all clients have been informed about removal), or
1600 - object has m_removed=true, or
1601 - object is too far away
1603 for(std::set<u16>::iterator
1604 i = current_objects.begin();
1605 i != current_objects.end(); ++i)
1608 ServerActiveObject *object = getActiveObject(id);
1610 if (object == NULL) {
1611 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1612 << " object in current_objects is NULL" << std::endl;
1613 removed_objects.push(id);
1617 if (object->m_removed || object->m_pending_deactivation) {
1618 removed_objects.push(id);
1622 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1623 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1624 if (distance_f <= player_radius_f || player_radius_f == 0)
1626 } else if (distance_f <= radius_f)
1629 // Object is no longer visible
1630 removed_objects.push(id);
1634 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1635 v3s16 blockpos, bool static_exists, v3s16 static_block)
1637 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1641 for (std::map<u16, StaticObject>::iterator
1642 so_it = block->m_static_objects.m_active.begin();
1643 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1644 // Get the ServerActiveObject counterpart to this StaticObject
1645 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1646 if (ao_it == m_active_objects.end()) {
1647 // If this ever happens, there must be some kind of nasty bug.
1648 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1649 "Object from MapBlock::m_static_objects::m_active not found "
1650 "in m_active_objects";
1654 ServerActiveObject *sao = ao_it->second;
1655 sao->m_static_exists = static_exists;
1656 sao->m_static_block = static_block;
1660 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1662 if(m_active_object_messages.empty())
1663 return ActiveObjectMessage(0);
1665 ActiveObjectMessage message = m_active_object_messages.front();
1666 m_active_object_messages.pop();
1671 ************ Private methods *************
1674 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1675 bool set_changed, u32 dtime_s)
1677 assert(object); // Pre-condition
1678 if(object->getId() == 0){
1679 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1682 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1683 <<"no free ids available"<<std::endl;
1684 if(object->environmentDeletes())
1688 object->setId(new_id);
1691 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1692 <<"supplied with id "<<object->getId()<<std::endl;
1695 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1696 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1697 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1698 if(object->environmentDeletes())
1703 if (objectpos_over_limit(object->getBasePosition())) {
1704 v3f p = object->getBasePosition();
1705 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1706 << "object position (" << p.X << "," << p.Y << "," << p.Z
1707 << ") outside maximum range" << std::endl;
1708 if (object->environmentDeletes())
1713 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1714 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1716 m_active_objects[object->getId()] = object;
1718 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1719 <<"Added id="<<object->getId()<<"; there are now "
1720 <<m_active_objects.size()<<" active objects."
1723 // Register reference in scripting api (must be done before post-init)
1724 m_script->addObjectReference(object);
1725 // Post-initialize object
1726 object->addedToEnvironment(dtime_s);
1728 // Add static data to block
1729 if(object->isStaticAllowed())
1731 // Add static object to active static list of the block
1732 v3f objectpos = object->getBasePosition();
1733 std::string staticdata = "";
1734 object->getStaticData(&staticdata);
1735 StaticObject s_obj(object->getType(), objectpos, staticdata);
1736 // Add to the block where the object is located in
1737 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1738 MapBlock *block = m_map->emergeBlock(blockpos);
1740 block->m_static_objects.m_active[object->getId()] = s_obj;
1741 object->m_static_exists = true;
1742 object->m_static_block = blockpos;
1745 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1746 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1748 v3s16 p = floatToInt(objectpos, BS);
1749 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1750 <<"could not emerge block for storing id="<<object->getId()
1751 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1755 return object->getId();
1759 Remove objects that satisfy (m_removed && m_known_by_count==0)
1761 void ServerEnvironment::removeRemovedObjects()
1763 std::vector<u16> objects_to_remove;
1764 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1765 i != m_active_objects.end(); ++i) {
1767 ServerActiveObject* obj = i->second;
1768 // This shouldn't happen but check it
1771 infostream<<"NULL object found in ServerEnvironment"
1772 <<" while finding removed objects. id="<<id<<std::endl;
1773 // Id to be removed from m_active_objects
1774 objects_to_remove.push_back(id);
1779 We will delete objects that are marked as removed or thatare
1780 waiting for deletion after deactivation
1782 if (!obj->m_removed && !obj->m_pending_deactivation)
1786 Delete static data from block if is marked as removed
1788 if(obj->m_static_exists && obj->m_removed)
1790 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1792 block->m_static_objects.remove(id);
1793 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1794 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1795 obj->m_static_exists = false;
1797 infostream<<"Failed to emerge block from which an object to "
1798 <<"be removed was loaded from. id="<<id<<std::endl;
1802 // If m_known_by_count > 0, don't actually remove. On some future
1803 // invocation this will be 0, which is when removal will continue.
1804 if(obj->m_known_by_count > 0)
1808 Move static data from active to stored if not marked as removed
1810 if(obj->m_static_exists && !obj->m_removed){
1811 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1813 std::map<u16, StaticObject>::iterator i =
1814 block->m_static_objects.m_active.find(id);
1815 if(i != block->m_static_objects.m_active.end()){
1816 block->m_static_objects.m_stored.push_back(i->second);
1817 block->m_static_objects.m_active.erase(id);
1818 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1819 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1822 infostream<<"Failed to emerge block from which an object to "
1823 <<"be deactivated was loaded from. id="<<id<<std::endl;
1827 // Tell the object about removal
1828 obj->removingFromEnvironment();
1829 // Deregister in scripting api
1830 m_script->removeObjectReference(obj);
1833 if(obj->environmentDeletes())
1836 // Id to be removed from m_active_objects
1837 objects_to_remove.push_back(id);
1839 // Remove references from m_active_objects
1840 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1841 i != objects_to_remove.end(); ++i) {
1842 m_active_objects.erase(*i);
1846 static void print_hexdump(std::ostream &o, const std::string &data)
1848 const int linelength = 16;
1849 for(int l=0; ; l++){
1850 int i0 = linelength * l;
1851 bool at_end = false;
1852 int thislinelength = linelength;
1853 if(i0 + thislinelength > (int)data.size()){
1854 thislinelength = data.size() - i0;
1857 for(int di=0; di<linelength; di++){
1860 if(di<thislinelength)
1861 snprintf(buf, 4, "%.2x ", data[i]);
1863 snprintf(buf, 4, " ");
1867 for(int di=0; di<thislinelength; di++){
1881 Convert stored objects from blocks near the players to active.
1883 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1888 // Ignore if no stored objects (to not set changed flag)
1889 if(block->m_static_objects.m_stored.empty())
1892 verbosestream<<"ServerEnvironment::activateObjects(): "
1893 <<"activating objects of block "<<PP(block->getPos())
1894 <<" ("<<block->m_static_objects.m_stored.size()
1895 <<" objects)"<<std::endl;
1896 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1898 errorstream<<"suspiciously large amount of objects detected: "
1899 <<block->m_static_objects.m_stored.size()<<" in "
1900 <<PP(block->getPos())
1901 <<"; removing all of them."<<std::endl;
1902 // Clear stored list
1903 block->m_static_objects.m_stored.clear();
1904 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1905 MOD_REASON_TOO_MANY_OBJECTS);
1909 // Activate stored objects
1910 std::vector<StaticObject> new_stored;
1911 for (std::vector<StaticObject>::iterator
1912 i = block->m_static_objects.m_stored.begin();
1913 i != block->m_static_objects.m_stored.end(); ++i) {
1914 StaticObject &s_obj = *i;
1916 // Create an active object from the data
1917 ServerActiveObject *obj = ServerActiveObject::create
1918 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1919 // If couldn't create object, store static data back.
1921 errorstream<<"ServerEnvironment::activateObjects(): "
1922 <<"failed to create active object from static object "
1923 <<"in block "<<PP(s_obj.pos/BS)
1924 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1925 print_hexdump(verbosestream, s_obj.data);
1927 new_stored.push_back(s_obj);
1930 verbosestream<<"ServerEnvironment::activateObjects(): "
1931 <<"activated static object pos="<<PP(s_obj.pos/BS)
1932 <<" type="<<(int)s_obj.type<<std::endl;
1933 // This will also add the object to the active static list
1934 addActiveObjectRaw(obj, false, dtime_s);
1936 // Clear stored list
1937 block->m_static_objects.m_stored.clear();
1938 // Add leftover failed stuff to stored list
1939 for(std::vector<StaticObject>::iterator
1940 i = new_stored.begin();
1941 i != new_stored.end(); ++i) {
1942 StaticObject &s_obj = *i;
1943 block->m_static_objects.m_stored.push_back(s_obj);
1946 // Turn the active counterparts of activated objects not pending for
1948 for(std::map<u16, StaticObject>::iterator
1949 i = block->m_static_objects.m_active.begin();
1950 i != block->m_static_objects.m_active.end(); ++i)
1953 ServerActiveObject *object = getActiveObject(id);
1955 object->m_pending_deactivation = false;
1959 Note: Block hasn't really been modified here.
1960 The objects have just been activated and moved from the stored
1961 static list to the active static list.
1962 As such, the block is essentially the same.
1963 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1964 Otherwise there would be a huge amount of unnecessary I/O.
1969 Convert objects that are not standing inside active blocks to static.
1971 If m_known_by_count != 0, active object is not deleted, but static
1972 data is still updated.
1974 If force_delete is set, active object is deleted nevertheless. It
1975 shall only be set so in the destructor of the environment.
1977 If block wasn't generated (not in memory or on disk),
1979 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1981 std::vector<u16> objects_to_remove;
1982 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1983 i != m_active_objects.end(); ++i) {
1984 // force_delete might be overriden per object
1985 bool force_delete = _force_delete;
1987 ServerActiveObject* obj = i->second;
1990 // Do not deactivate if static data creation not allowed
1991 if(!force_delete && !obj->isStaticAllowed())
1994 // If pending deactivation, let removeRemovedObjects() do it
1995 if(!force_delete && obj->m_pending_deactivation)
1999 v3f objectpos = obj->getBasePosition();
2001 // The block in which the object resides in
2002 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2004 // If object's static data is stored in a deactivated block and object
2005 // is actually located in an active block, re-save to the block in
2006 // which the object is actually located in.
2008 obj->m_static_exists &&
2009 !m_active_blocks.contains(obj->m_static_block) &&
2010 m_active_blocks.contains(blockpos_o))
2012 v3s16 old_static_block = obj->m_static_block;
2014 // Save to block where object is located
2015 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2017 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2018 <<"Could not save object id="<<id
2019 <<" to it's current block "<<PP(blockpos_o)
2023 std::string staticdata_new = "";
2024 obj->getStaticData(&staticdata_new);
2025 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2026 block->m_static_objects.insert(id, s_obj);
2027 obj->m_static_block = blockpos_o;
2028 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2029 MOD_REASON_STATIC_DATA_ADDED);
2031 // Delete from block where object was located
2032 block = m_map->emergeBlock(old_static_block, false);
2034 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2035 <<"Could not delete object id="<<id
2036 <<" from it's previous block "<<PP(old_static_block)
2040 block->m_static_objects.remove(id);
2041 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2042 MOD_REASON_STATIC_DATA_REMOVED);
2046 // If block is active, don't remove
2047 if(!force_delete && m_active_blocks.contains(blockpos_o))
2050 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2051 <<"deactivating object id="<<id<<" on inactive block "
2052 <<PP(blockpos_o)<<std::endl;
2054 // If known by some client, don't immediately delete.
2055 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2058 Update the static data
2061 if(obj->isStaticAllowed())
2063 // Create new static object
2064 std::string staticdata_new = "";
2065 obj->getStaticData(&staticdata_new);
2066 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2068 bool stays_in_same_block = false;
2069 bool data_changed = true;
2071 if (obj->m_static_exists) {
2072 if (obj->m_static_block == blockpos_o)
2073 stays_in_same_block = true;
2075 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2078 std::map<u16, StaticObject>::iterator n =
2079 block->m_static_objects.m_active.find(id);
2080 if (n != block->m_static_objects.m_active.end()) {
2081 StaticObject static_old = n->second;
2083 float save_movem = obj->getMinimumSavedMovement();
2085 if (static_old.data == staticdata_new &&
2086 (static_old.pos - objectpos).getLength() < save_movem)
2087 data_changed = false;
2089 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2090 <<"id="<<id<<" m_static_exists=true but "
2091 <<"static data doesn't actually exist in "
2092 <<PP(obj->m_static_block)<<std::endl;
2097 bool shall_be_written = (!stays_in_same_block || data_changed);
2099 // Delete old static object
2100 if(obj->m_static_exists)
2102 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2105 block->m_static_objects.remove(id);
2106 obj->m_static_exists = false;
2107 // Only mark block as modified if data changed considerably
2108 if(shall_be_written)
2109 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2110 MOD_REASON_STATIC_DATA_CHANGED);
2114 // Add to the block where the object is located in
2115 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2116 // Get or generate the block
2117 MapBlock *block = NULL;
2119 block = m_map->emergeBlock(blockpos);
2120 } catch(InvalidPositionException &e){
2121 // Handled via NULL pointer
2122 // NOTE: emergeBlock's failure is usually determined by it
2123 // actually returning NULL
2128 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2129 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2130 << " statically but block " << PP(blockpos)
2131 << " already contains "
2132 << block->m_static_objects.m_stored.size()
2134 << " Forcing delete." << std::endl;
2135 force_delete = true;
2137 // If static counterpart already exists in target block,
2139 // This shouldn't happen because the object is removed from
2140 // the previous block before this according to
2141 // obj->m_static_block, but happens rarely for some unknown
2142 // reason. Unsuccessful attempts have been made to find
2144 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2145 warningstream<<"ServerEnv: Performing hack #83274"
2147 block->m_static_objects.remove(id);
2149 // Store static data
2150 u16 store_id = pending_delete ? id : 0;
2151 block->m_static_objects.insert(store_id, s_obj);
2153 // Only mark block as modified if data changed considerably
2154 if(shall_be_written)
2155 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2156 MOD_REASON_STATIC_DATA_CHANGED);
2158 obj->m_static_exists = true;
2159 obj->m_static_block = block->getPos();
2164 v3s16 p = floatToInt(objectpos, BS);
2165 errorstream<<"ServerEnv: Could not find or generate "
2166 <<"a block for storing id="<<obj->getId()
2167 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2174 If known by some client, set pending deactivation.
2175 Otherwise delete it immediately.
2178 if(pending_delete && !force_delete)
2180 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2181 <<"object id="<<id<<" is known by clients"
2182 <<"; not deleting yet"<<std::endl;
2184 obj->m_pending_deactivation = true;
2188 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2189 <<"object id="<<id<<" is not known by clients"
2190 <<"; deleting"<<std::endl;
2192 // Tell the object about removal
2193 obj->removingFromEnvironment();
2194 // Deregister in scripting api
2195 m_script->removeObjectReference(obj);
2197 // Delete active object
2198 if(obj->environmentDeletes())
2200 // Id to be removed from m_active_objects
2201 objects_to_remove.push_back(id);
2204 // Remove references from m_active_objects
2205 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2206 i != objects_to_remove.end(); ++i) {
2207 m_active_objects.erase(*i);
2211 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2212 const std::string &savedir, const Settings &conf)
2215 if (name == "sqlite3")
2216 return new PlayerDatabaseSQLite3(savedir);
2217 else if (name == "dummy")
2218 return new Database_Dummy();
2220 else if (name == "postgresql") {
2221 std::string connect_string = "";
2222 conf.getNoEx("pgsql_player_connection", connect_string);
2223 return new PlayerDatabasePostgreSQL(connect_string);
2226 else if (name == "files")
2227 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2229 throw BaseException(std::string("Database backend ") + name + " not supported.");
2232 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2233 const Settings &cmd_args)
2235 std::string migrate_to = cmd_args.get("migrate-players");
2237 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2238 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2239 errorstream << "Cannot read world.mt!" << std::endl;
2243 if (!world_mt.exists("player_backend")) {
2244 errorstream << "Please specify your current backend in world.mt:"
2246 << " player_backend = {files|sqlite3|postgresql}"
2251 std::string backend = world_mt.get("player_backend");
2252 if (backend == migrate_to) {
2253 errorstream << "Cannot migrate: new backend is same"
2254 << " as the old one" << std::endl;
2258 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2261 if (backend == "files") {
2262 // Create backup directory
2263 fs::CreateDir(players_backup_path);
2267 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2268 game_params.world_path, world_mt);
2269 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2270 game_params.world_path, world_mt);
2272 std::vector<std::string> player_list;
2273 srcdb->listPlayers(player_list);
2274 for (std::vector<std::string>::const_iterator it = player_list.begin();
2275 it != player_list.end(); ++it) {
2276 actionstream << "Migrating player " << it->c_str() << std::endl;
2277 RemotePlayer player(it->c_str(), NULL);
2278 PlayerSAO playerSAO(NULL, &player, 15000, false);
2280 srcdb->loadPlayer(&player, &playerSAO);
2282 playerSAO.finalize(&player, std::set<std::string>());
2283 player.setPlayerSAO(&playerSAO);
2285 dstdb->savePlayer(&player);
2287 // For files source, move player files to backup dir
2288 if (backend == "files") {
2290 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2291 players_backup_path + DIR_DELIM + (*it));
2295 actionstream << "Successfully migrated " << player_list.size() << " players"
2297 world_mt.set("player_backend", migrate_to);
2298 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2299 errorstream << "Failed to update world.mt!" << std::endl;
2301 actionstream << "world.mt updated" << std::endl;
2303 // When migration is finished from file backend, remove players directory if empty
2304 if (backend == "files") {
2305 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2312 } catch (BaseException &e) {
2313 errorstream << "An error occured during migration: " << e.what() << std::endl;