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_):
59 // Initialize timer to random value to spread processing
60 float itv = abm->getTriggerInterval();
61 itv = MYMAX(0.001, itv); // No less than 1ms
62 int minval = MYMAX(-0.51*itv, -60); // Clamp to
63 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
64 timer = myrand_range(minval, maxval);
71 void LBMContentMapping::deleteContents()
73 for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
74 it != lbm_list.end(); ++it) {
79 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
81 // Add the lbm_def to the LBMContentMapping.
82 // Unknown names get added to the global NameIdMapping.
83 INodeDefManager *nodedef = gamedef->ndef();
85 lbm_list.push_back(lbm_def);
87 for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
88 it != lbm_def->trigger_contents.end(); ++it) {
89 std::set<content_t> c_ids;
90 bool found = nodedef->getIds(*it, c_ids);
92 content_t c_id = gamedef->allocateUnknownNodeId(*it);
93 if (c_id == CONTENT_IGNORE) {
94 // Seems it can't be allocated.
95 warningstream << "Could not internalize node name \"" << *it
96 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
102 for (std::set<content_t>::const_iterator iit =
103 c_ids.begin(); iit != c_ids.end(); ++iit) {
104 content_t c_id = *iit;
105 map[c_id].push_back(lbm_def);
110 const std::vector<LoadingBlockModifierDef *> *
111 LBMContentMapping::lookup(content_t c) const
113 container_map::const_iterator it = map.find(c);
116 // This first dereferences the iterator, returning
117 // a std::vector<LoadingBlockModifierDef *>
118 // reference, then we convert it to a pointer.
119 return &(it->second);
122 LBMManager::~LBMManager()
124 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
125 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
128 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
129 it != m_lbm_lookup.end(); ++it) {
130 (it->second).deleteContents();
134 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
136 // Precondition, in query mode the map isn't used anymore
137 FATAL_ERROR_IF(m_query_mode == true,
138 "attempted to modify LBMManager in query mode");
140 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
141 throw ModError("Error adding LBM \"" + lbm_def->name +
142 "\": Does not follow naming conventions: "
143 "Only characters [a-z0-9_:] are allowed.");
146 m_lbm_defs[lbm_def->name] = lbm_def;
149 void LBMManager::loadIntroductionTimes(const std::string ×,
150 IGameDef *gamedef, u32 now)
155 // Storing it in a map first instead of
156 // handling the stuff directly in the loop
157 // removes all duplicate entries.
158 // TODO make this std::unordered_map
159 std::map<std::string, u32> introduction_times;
162 The introduction times string consists of name~time entries,
163 with each entry terminated by a semicolon. The time is decimal.
168 while ((idx_new = times.find(";", idx)) != std::string::npos) {
169 std::string entry = times.substr(idx, idx_new - idx);
170 std::vector<std::string> components = str_split(entry, '~');
171 if (components.size() != 2)
172 throw SerializationError("Introduction times entry \""
173 + entry + "\" requires exactly one '~'!");
174 const std::string &name = components[0];
175 u32 time = from_string<u32>(components[1]);
176 introduction_times[name] = time;
180 // Put stuff from introduction_times into m_lbm_lookup
181 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
182 it != introduction_times.end(); ++it) {
183 const std::string &name = it->first;
184 u32 time = it->second;
186 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
187 m_lbm_defs.find(name);
188 if (def_it == m_lbm_defs.end()) {
189 // This seems to be an LBM entry for
190 // an LBM we haven't loaded. Discard it.
193 LoadingBlockModifierDef *lbm_def = def_it->second;
194 if (lbm_def->run_at_every_load) {
195 // This seems to be an LBM entry for
196 // an LBM that runs at every load.
197 // Don't add it just yet.
201 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
203 // Erase the entry so that we know later
204 // what elements didn't get put into m_lbm_lookup
205 m_lbm_defs.erase(name);
208 // Now also add the elements from m_lbm_defs to m_lbm_lookup
209 // that weren't added in the previous step.
210 // They are introduced first time to this world,
211 // or are run at every load (introducement time hardcoded to U32_MAX).
213 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
214 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
216 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
217 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
218 if (it->second->run_at_every_load) {
219 lbms_running_always.addLBM(it->second, gamedef);
221 lbms_we_introduce_now.addLBM(it->second, gamedef);
225 // Clear the list, so that we don't delete remaining elements
226 // twice in the destructor
230 std::string LBMManager::createIntroductionTimesString()
232 // Precondition, we must be in query mode
233 FATAL_ERROR_IF(m_query_mode == false,
234 "attempted to query on non fully set up LBMManager");
236 std::ostringstream oss;
237 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
238 it != m_lbm_lookup.end(); ++it) {
239 u32 time = it->first;
240 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
241 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
242 iit != lbm_list.end(); ++iit) {
243 // Don't add if the LBM runs at every load,
244 // then introducement time is hardcoded
245 // and doesn't need to be stored
246 if ((*iit)->run_at_every_load)
248 oss << (*iit)->name << "~" << time << ";";
254 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
256 // Precondition, we need m_lbm_lookup to be initialized
257 FATAL_ERROR_IF(m_query_mode == false,
258 "attempted to query on non fully set up LBMManager");
259 v3s16 pos_of_block = block->getPosRelative();
263 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
264 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
265 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
266 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
268 n = block->getNodeNoEx(pos);
270 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
271 iit != m_lbm_lookup.end(); ++iit) {
272 const std::vector<LoadingBlockModifierDef *> *lbm_list =
273 iit->second.lookup(c);
276 for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
277 lbm_list->begin(); iit != lbm_list->end(); ++iit) {
278 (*iit)->trigger(env, pos + pos_of_block, n);
288 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
291 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
292 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
293 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
296 if (p.getDistanceFrom(p0) <= r) {
303 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
305 std::set<v3s16> &blocks_removed,
306 std::set<v3s16> &blocks_added)
311 std::set<v3s16> newlist = m_forceloaded_list;
312 for(std::vector<v3s16>::iterator i = active_positions.begin();
313 i != active_positions.end(); ++i)
315 fillRadiusBlock(*i, radius, newlist);
319 Find out which blocks on the old list are not on the new list
321 // Go through old list
322 for(std::set<v3s16>::iterator i = m_list.begin();
323 i != m_list.end(); ++i)
326 // If not on new list, it's been removed
327 if(newlist.find(p) == newlist.end())
328 blocks_removed.insert(p);
332 Find out which blocks on the new list are not on the old list
334 // Go through new list
335 for(std::set<v3s16>::iterator i = newlist.begin();
336 i != newlist.end(); ++i)
339 // If not on old list, it's been added
340 if(m_list.find(p) == m_list.end())
341 blocks_added.insert(p);
348 for(std::set<v3s16>::iterator i = newlist.begin();
349 i != newlist.end(); ++i)
360 ServerEnvironment::ServerEnvironment(ServerMap *map,
361 ServerScripting *scriptIface, Server *server,
362 const std::string &path_world):
365 m_script(scriptIface),
367 m_path_world(path_world)
369 // Determine which database backend to use
370 std::string conf_path = path_world + DIR_DELIM + "world.mt";
372 bool succeeded = conf.readConfigFile(conf_path.c_str());
373 if (!succeeded || !conf.exists("player_backend")) {
374 // fall back to files
375 conf.set("player_backend", "files");
376 warningstream << "/!\\ You are using old player file backend. "
377 << "This backend is deprecated and will be removed in next release /!\\"
378 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
379 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
381 if (!conf.updateConfigFile(conf_path.c_str())) {
382 errorstream << "ServerEnvironment::ServerEnvironment(): "
383 << "Failed to update world.mt!" << std::endl;
387 std::string name = "";
388 conf.getNoEx("player_backend", name);
389 m_player_database = openPlayerDatabase(name, path_world, conf);
392 ServerEnvironment::~ServerEnvironment()
394 // Clear active block list.
395 // This makes the next one delete all active objects.
396 m_active_blocks.clear();
398 // Convert all objects to static and delete the active objects
399 deactivateFarObjects(true);
404 // Delete ActiveBlockModifiers
405 for (std::vector<ABMWithState>::iterator
406 i = m_abms.begin(); i != m_abms.end(); ++i){
410 // Deallocate players
411 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
412 i != m_players.end(); ++i) {
416 delete m_player_database;
419 Map & ServerEnvironment::getMap()
424 ServerMap & ServerEnvironment::getServerMap()
429 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
431 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
432 i != m_players.end(); ++i) {
433 RemotePlayer *player = *i;
434 if (player->peer_id == peer_id)
440 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
442 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
443 i != m_players.end(); ++i) {
444 RemotePlayer *player = *i;
445 if (strcmp(player->getName(), name) == 0)
451 void ServerEnvironment::addPlayer(RemotePlayer *player)
453 DSTACK(FUNCTION_NAME);
455 Check that peer_ids are unique.
456 Also check that names are unique.
457 Exception: there can be multiple players with peer_id=0
459 // If peer id is non-zero, it has to be unique.
460 if (player->peer_id != 0)
461 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
462 // Name has to be unique.
463 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
465 m_players.push_back(player);
468 void ServerEnvironment::removePlayer(RemotePlayer *player)
470 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
471 it != m_players.end(); ++it) {
472 if ((*it) == player) {
480 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
482 return m_player_database->removePlayer(name);
485 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
487 float distance = pos1.getDistanceFrom(pos2);
489 //calculate normalized direction vector
490 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
491 (pos2.Y - pos1.Y)/distance,
492 (pos2.Z - pos1.Z)/distance);
494 //find out if there's a node on path between pos1 and pos2
495 for (float i = 1; i < distance; i += stepsize) {
496 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
497 normalized_vector.Y * i,
498 normalized_vector.Z * i) +pos1,BS);
500 MapNode n = getMap().getNodeNoEx(pos);
502 if(n.param0 != CONTENT_AIR) {
512 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
513 const std::string &str_reason, bool reconnect)
515 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
516 it != m_players.end(); ++it) {
517 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
518 m_server->DenyAccessVerCompliant(player->peer_id,
519 player->protocol_version, reason, str_reason, reconnect);
523 void ServerEnvironment::saveLoadedPlayers()
525 std::string players_path = m_path_world + DIR_DELIM + "players";
526 fs::CreateDir(players_path);
528 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
529 it != m_players.end();
531 if ((*it)->checkModified() ||
532 ((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) {
534 m_player_database->savePlayer(*it);
535 } catch (DatabaseException &e) {
536 errorstream << "Failed to save player " << (*it)->getName() << " exception: "
537 << e.what() << std::endl;
544 void ServerEnvironment::savePlayer(RemotePlayer *player)
547 m_player_database->savePlayer(player);
548 } catch (DatabaseException &e) {
549 errorstream << "Failed to save player " << player->getName() << " exception: "
550 << e.what() << std::endl;
555 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
556 u16 peer_id, bool is_singleplayer)
558 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
559 // Create player if it doesn't exist
560 if (!m_player_database->loadPlayer(player, playersao)) {
562 // Set player position
563 infostream << "Server: Finding spawn place for player \""
564 << player->getName() << "\"" << std::endl;
565 playersao->setBasePosition(m_server->findSpawnPos());
567 // Make sure the player is saved
568 player->setModified(true);
570 // If the player exists, ensure that they respawn inside legal bounds
571 // This fixes an assert crash when the player can't be added
572 // to the environment
573 ServerMap &map = getServerMap();
574 if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) {
575 actionstream << "Respawn position for player \""
576 << player->getName() << "\" outside limits, resetting" << std::endl;
577 playersao->setBasePosition(m_server->findSpawnPos());
581 // Add player to environment
584 /* Clean up old HUD elements from previous sessions */
587 /* Add object to environment */
588 addActiveObject(playersao);
593 void ServerEnvironment::saveMeta()
595 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
597 // Open file and serialize
598 std::ostringstream ss(std::ios_base::binary);
601 args.setU64("game_time", m_game_time);
602 args.setU64("time_of_day", getTimeOfDay());
603 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
604 args.setU64("lbm_introduction_times_version", 1);
605 args.set("lbm_introduction_times",
606 m_lbm_mgr.createIntroductionTimesString());
607 args.setU64("day_count", m_day_count);
611 if(!fs::safeWriteToFile(path, ss.str()))
613 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
615 throw SerializationError("Couldn't save env meta");
619 void ServerEnvironment::loadMeta()
621 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
623 // Open file and deserialize
624 std::ifstream is(path.c_str(), std::ios_base::binary);
626 infostream << "ServerEnvironment::loadMeta(): Failed to open "
627 << path << std::endl;
628 throw SerializationError("Couldn't load env meta");
633 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
634 throw SerializationError("ServerEnvironment::loadMeta(): "
635 "EnvArgsEnd not found!");
639 m_game_time = args.getU64("game_time");
640 } catch (SettingNotFoundException &e) {
641 // Getting this is crucial, otherwise timestamps are useless
642 throw SerializationError("Couldn't load env meta game_time");
645 setTimeOfDay(args.exists("time_of_day") ?
646 // set day to morning by default
647 args.getU64("time_of_day") : 9000);
649 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
650 // If missing, do as if clearObjects was never called
651 args.getU64("last_clear_objects_time") : 0;
653 std::string lbm_introduction_times = "";
655 u64 ver = args.getU64("lbm_introduction_times_version");
657 lbm_introduction_times = args.get("lbm_introduction_times");
659 infostream << "ServerEnvironment::loadMeta(): Non-supported"
660 << " introduction time version " << ver << std::endl;
662 } catch (SettingNotFoundException &e) {
663 // No problem, this is expected. Just continue with an empty string
665 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
667 m_day_count = args.exists("day_count") ?
668 args.getU64("day_count") : 0;
671 void ServerEnvironment::loadDefaultMeta()
673 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
678 ActiveBlockModifier *abm;
680 std::set<content_t> required_neighbors;
686 ServerEnvironment *m_env;
687 std::vector<std::vector<ActiveABM> *> m_aabms;
689 ABMHandler(std::vector<ABMWithState> &abms,
690 float dtime_s, ServerEnvironment *env,
696 INodeDefManager *ndef = env->getGameDef()->ndef();
697 for(std::vector<ABMWithState>::iterator
698 i = abms.begin(); i != abms.end(); ++i) {
699 ActiveBlockModifier *abm = i->abm;
700 float trigger_interval = abm->getTriggerInterval();
701 if(trigger_interval < 0.001)
702 trigger_interval = 0.001;
703 float actual_interval = dtime_s;
706 if(i->timer < trigger_interval)
708 i->timer -= trigger_interval;
709 actual_interval = trigger_interval;
711 float chance = abm->getTriggerChance();
716 if (abm->getSimpleCatchUp()) {
717 float intervals = actual_interval / trigger_interval;
720 aabm.chance = chance / intervals;
724 aabm.chance = chance;
728 const std::set<std::string> &required_neighbors_s =
729 abm->getRequiredNeighbors();
730 for (std::set<std::string>::iterator rn = required_neighbors_s.begin();
731 rn != required_neighbors_s.end(); ++rn) {
732 ndef->getIds(*rn, aabm.required_neighbors);
736 const std::set<std::string> &contents_s = abm->getTriggerContents();
737 for (std::set<std::string>::iterator cs = contents_s.begin();
738 cs != contents_s.end(); ++cs) {
739 std::set<content_t> ids;
740 ndef->getIds(*cs, ids);
741 for (std::set<content_t>::const_iterator k = ids.begin();
742 k != ids.end(); ++k) {
744 if (c >= m_aabms.size())
745 m_aabms.resize(c + 256, NULL);
747 m_aabms[c] = new std::vector<ActiveABM>;
748 m_aabms[c]->push_back(aabm);
756 for (size_t i = 0; i < m_aabms.size(); i++)
760 // Find out how many objects the given block and its neighbours contain.
761 // Returns the number of objects in the block, and also in 'wider' the
762 // number of objects in the block and all its neighbours. The latter
763 // may an estimate if any neighbours are unloaded.
764 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
767 u32 wider_unknown_count = 0;
768 for(s16 x=-1; x<=1; x++)
769 for(s16 y=-1; y<=1; y++)
770 for(s16 z=-1; z<=1; z++)
772 MapBlock *block2 = map->getBlockNoCreateNoEx(
773 block->getPos() + v3s16(x,y,z));
775 wider_unknown_count++;
778 wider += block2->m_static_objects.m_active.size()
779 + block2->m_static_objects.m_stored.size();
782 u32 active_object_count = block->m_static_objects.m_active.size();
783 u32 wider_known_count = 3*3*3 - wider_unknown_count;
784 wider += wider_unknown_count * wider / wider_known_count;
785 return active_object_count;
788 void apply(MapBlock *block)
790 if(m_aabms.empty() || block->isDummy())
793 ServerMap *map = &m_env->getServerMap();
795 u32 active_object_count_wider;
796 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
797 m_env->m_added_objects = 0;
800 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
801 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
802 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
804 const MapNode &n = block->getNodeUnsafe(p0);
805 content_t c = n.getContent();
807 if (c >= m_aabms.size() || !m_aabms[c])
810 v3s16 p = p0 + block->getPosRelative();
811 for(std::vector<ActiveABM>::iterator
812 i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
813 if(myrand() % i->chance != 0)
817 if(!i->required_neighbors.empty())
820 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
821 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
822 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
827 if (block->isValidPosition(p1)) {
828 // if the neighbor is found on the same map block
829 // get it straight from there
830 const MapNode &n = block->getNodeUnsafe(p1);
833 // otherwise consult the map
834 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
837 std::set<content_t>::const_iterator k;
838 k = i->required_neighbors.find(c);
839 if(k != i->required_neighbors.end()){
843 // No required neighbor found
848 // Call all the trigger variations
849 i->abm->trigger(m_env, p, n);
850 i->abm->trigger(m_env, p, n,
851 active_object_count, active_object_count_wider);
853 // Count surrounding objects again if the abms added any
854 if(m_env->m_added_objects > 0) {
855 active_object_count = countObjects(block, map, active_object_count_wider);
856 m_env->m_added_objects = 0;
863 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
865 // Reset usage timer immediately, otherwise a block that becomes active
866 // again at around the same time as it would normally be unloaded will
867 // get unloaded incorrectly. (I think this still leaves a small possibility
868 // of a race condition between this and server::AsyncRunStep, which only
869 // some kind of synchronisation will fix, but it at least reduces the window
870 // of opportunity for it to break from seconds to nanoseconds)
871 block->resetUsageTimer();
873 // Get time difference
875 u32 stamp = block->getTimestamp();
876 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
877 dtime_s = m_game_time - stamp;
878 dtime_s += additional_dtime;
880 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
881 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
883 // Remove stored static objects if clearObjects was called since block's timestamp
884 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
885 block->m_static_objects.m_stored.clear();
886 // do not set changed flag to avoid unnecessary mapblock writes
889 // Set current time as timestamp
890 block->setTimestampNoChangedFlag(m_game_time);
892 /*infostream<<"ServerEnvironment::activateBlock(): block is "
893 <<dtime_s<<" seconds old."<<std::endl;*/
895 // Activate stored objects
896 activateObjects(block, dtime_s);
898 /* Handle LoadingBlockModifiers */
899 m_lbm_mgr.applyLBMs(this, block, stamp);
902 std::vector<NodeTimer> elapsed_timers =
903 block->m_node_timers.step((float)dtime_s);
904 if (!elapsed_timers.empty()) {
906 for (std::vector<NodeTimer>::iterator
907 i = elapsed_timers.begin();
908 i != elapsed_timers.end(); ++i){
909 n = block->getNodeNoEx(i->position);
910 v3s16 p = i->position + block->getPosRelative();
911 if (m_script->node_on_timer(p, n, i->elapsed))
912 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
916 /* Handle ActiveBlockModifiers */
917 ABMHandler abmhandler(m_abms, dtime_s, this, false);
918 abmhandler.apply(block);
921 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
923 m_abms.push_back(ABMWithState(abm));
926 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
928 m_lbm_mgr.addLBMDef(lbm);
931 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
933 INodeDefManager *ndef = m_server->ndef();
934 MapNode n_old = m_map->getNodeNoEx(p);
937 if (ndef->get(n_old).has_on_destruct)
938 m_script->node_on_destruct(p, n_old);
941 if (!m_map->addNodeWithEvent(p, n))
944 // Update active VoxelManipulator if a mapgen thread
945 m_map->updateVManip(p);
947 // Call post-destructor
948 if (ndef->get(n_old).has_after_destruct)
949 m_script->node_after_destruct(p, n_old);
952 if (ndef->get(n).has_on_construct)
953 m_script->node_on_construct(p, n);
958 bool ServerEnvironment::removeNode(v3s16 p)
960 INodeDefManager *ndef = m_server->ndef();
961 MapNode n_old = m_map->getNodeNoEx(p);
964 if (ndef->get(n_old).has_on_destruct)
965 m_script->node_on_destruct(p, n_old);
968 // This is slightly optimized compared to addNodeWithEvent(air)
969 if (!m_map->removeNodeWithEvent(p))
972 // Update active VoxelManipulator if a mapgen thread
973 m_map->updateVManip(p);
975 // Call post-destructor
976 if (ndef->get(n_old).has_after_destruct)
977 m_script->node_after_destruct(p, n_old);
979 // Air doesn't require constructor
983 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
985 if (!m_map->addNodeWithEvent(p, n, false))
988 // Update active VoxelManipulator if a mapgen thread
989 m_map->updateVManip(p);
994 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
997 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
998 i != m_active_objects.end(); ++i) {
999 ServerActiveObject* obj = i->second;
1001 v3f objectpos = obj->getBasePosition();
1002 if (objectpos.getDistanceFrom(pos) > radius)
1004 objects.push_back(id);
1008 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1010 infostream << "ServerEnvironment::clearObjects(): "
1011 << "Removing all active objects" << std::endl;
1012 std::vector<u16> objects_to_remove;
1013 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1014 i != m_active_objects.end(); ++i) {
1015 ServerActiveObject* obj = i->second;
1016 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1019 // Delete static object if block is loaded
1020 if (obj->m_static_exists) {
1021 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1023 block->m_static_objects.remove(id);
1024 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1025 MOD_REASON_CLEAR_ALL_OBJECTS);
1026 obj->m_static_exists = false;
1029 // If known by some client, don't delete immediately
1030 if (obj->m_known_by_count > 0) {
1031 obj->m_pending_deactivation = true;
1032 obj->m_removed = true;
1036 // Tell the object about removal
1037 obj->removingFromEnvironment();
1038 // Deregister in scripting api
1039 m_script->removeObjectReference(obj);
1041 // Delete active object
1042 if (obj->environmentDeletes())
1044 // Id to be removed from m_active_objects
1045 objects_to_remove.push_back(id);
1048 // Remove references from m_active_objects
1049 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1050 i != objects_to_remove.end(); ++i) {
1051 m_active_objects.erase(*i);
1054 // Get list of loaded blocks
1055 std::vector<v3s16> loaded_blocks;
1056 infostream << "ServerEnvironment::clearObjects(): "
1057 << "Listing all loaded blocks" << std::endl;
1058 m_map->listAllLoadedBlocks(loaded_blocks);
1059 infostream << "ServerEnvironment::clearObjects(): "
1060 << "Done listing all loaded blocks: "
1061 << loaded_blocks.size()<<std::endl;
1063 // Get list of loadable blocks
1064 std::vector<v3s16> loadable_blocks;
1065 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1066 infostream << "ServerEnvironment::clearObjects(): "
1067 << "Listing all loadable blocks" << std::endl;
1068 m_map->listAllLoadableBlocks(loadable_blocks);
1069 infostream << "ServerEnvironment::clearObjects(): "
1070 << "Done listing all loadable blocks: "
1071 << loadable_blocks.size() << std::endl;
1073 loadable_blocks = loaded_blocks;
1076 infostream << "ServerEnvironment::clearObjects(): "
1077 << "Now clearing objects in " << loadable_blocks.size()
1078 << " blocks" << std::endl;
1080 // Grab a reference on each loaded block to avoid unloading it
1081 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1082 i != loaded_blocks.end(); ++i) {
1084 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1085 assert(block != NULL);
1089 // Remove objects in all loadable blocks
1090 u32 unload_interval = U32_MAX;
1091 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1092 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1093 unload_interval = MYMAX(unload_interval, 1);
1095 u32 report_interval = loadable_blocks.size() / 10;
1096 u32 num_blocks_checked = 0;
1097 u32 num_blocks_cleared = 0;
1098 u32 num_objs_cleared = 0;
1099 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1100 i != loadable_blocks.end(); ++i) {
1102 MapBlock *block = m_map->emergeBlock(p, false);
1104 errorstream << "ServerEnvironment::clearObjects(): "
1105 << "Failed to emerge block " << PP(p) << std::endl;
1108 u32 num_stored = block->m_static_objects.m_stored.size();
1109 u32 num_active = block->m_static_objects.m_active.size();
1110 if (num_stored != 0 || num_active != 0) {
1111 block->m_static_objects.m_stored.clear();
1112 block->m_static_objects.m_active.clear();
1113 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1114 MOD_REASON_CLEAR_ALL_OBJECTS);
1115 num_objs_cleared += num_stored + num_active;
1116 num_blocks_cleared++;
1118 num_blocks_checked++;
1120 if (report_interval != 0 &&
1121 num_blocks_checked % report_interval == 0) {
1122 float percent = 100.0 * (float)num_blocks_checked /
1123 loadable_blocks.size();
1124 infostream << "ServerEnvironment::clearObjects(): "
1125 << "Cleared " << num_objs_cleared << " objects"
1126 << " in " << num_blocks_cleared << " blocks ("
1127 << percent << "%)" << std::endl;
1129 if (num_blocks_checked % unload_interval == 0) {
1130 m_map->unloadUnreferencedBlocks();
1133 m_map->unloadUnreferencedBlocks();
1135 // Drop references that were added above
1136 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1137 i != loaded_blocks.end(); ++i) {
1139 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1144 m_last_clear_objects_time = m_game_time;
1146 infostream << "ServerEnvironment::clearObjects(): "
1147 << "Finished: Cleared " << num_objs_cleared << " objects"
1148 << " in " << num_blocks_cleared << " blocks" << std::endl;
1151 void ServerEnvironment::step(float dtime)
1153 DSTACK(FUNCTION_NAME);
1155 //TimeTaker timer("ServerEnv step");
1157 /* Step time of day */
1158 stepTimeOfDay(dtime);
1161 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1162 // really matter that much.
1163 static thread_local const float server_step =
1164 g_settings->getFloat("dedicated_server_step");
1165 m_recommended_send_interval = server_step;
1171 m_game_time_fraction_counter += dtime;
1172 u32 inc_i = (u32)m_game_time_fraction_counter;
1173 m_game_time += inc_i;
1174 m_game_time_fraction_counter -= (float)inc_i;
1181 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1182 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1183 i != m_players.end(); ++i) {
1184 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1187 // Ignore disconnected players
1188 if(player->peer_id == 0)
1192 player->move(dtime, this, 100*BS);
1197 Manage active block list
1199 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1200 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1202 Get player block positions
1204 std::vector<v3s16> players_blockpos;
1205 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1206 i != m_players.end(); ++i) {
1207 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1210 // Ignore disconnected players
1211 if (player->peer_id == 0)
1214 PlayerSAO *playersao = player->getPlayerSAO();
1217 v3s16 blockpos = getNodeBlockPos(
1218 floatToInt(playersao->getBasePosition(), BS));
1219 players_blockpos.push_back(blockpos);
1223 Update list of active blocks, collecting changes
1225 static thread_local const s16 active_block_range =
1226 g_settings->getS16("active_block_range");
1227 std::set<v3s16> blocks_removed;
1228 std::set<v3s16> blocks_added;
1229 m_active_blocks.update(players_blockpos, active_block_range,
1230 blocks_removed, blocks_added);
1233 Handle removed blocks
1236 // Convert active objects that are no more in active blocks to static
1237 deactivateFarObjects(false);
1239 for(std::set<v3s16>::iterator
1240 i = blocks_removed.begin();
1241 i != blocks_removed.end(); ++i) {
1244 /* infostream<<"Server: Block " << PP(p)
1245 << " became inactive"<<std::endl; */
1247 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1251 // Set current time as timestamp (and let it set ChangedFlag)
1252 block->setTimestamp(m_game_time);
1259 for(std::set<v3s16>::iterator
1260 i = blocks_added.begin();
1261 i != blocks_added.end(); ++i)
1265 MapBlock *block = m_map->getBlockOrEmerge(p);
1267 m_active_blocks.m_list.erase(p);
1271 activateBlock(block);
1272 /* infostream<<"Server: Block " << PP(p)
1273 << " became active"<<std::endl; */
1278 Mess around in active blocks
1280 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1281 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1283 float dtime = m_cache_nodetimer_interval;
1285 for(std::set<v3s16>::iterator
1286 i = m_active_blocks.m_list.begin();
1287 i != m_active_blocks.m_list.end(); ++i)
1291 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1292 <<") being handled"<<std::endl;*/
1294 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1298 // Reset block usage timer
1299 block->resetUsageTimer();
1301 // Set current time as timestamp
1302 block->setTimestampNoChangedFlag(m_game_time);
1303 // If time has changed much from the one on disk,
1304 // set block to be saved when it is unloaded
1305 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1306 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1307 MOD_REASON_BLOCK_EXPIRED);
1310 std::vector<NodeTimer> elapsed_timers =
1311 block->m_node_timers.step((float)dtime);
1312 if (!elapsed_timers.empty()) {
1314 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1315 i != elapsed_timers.end(); ++i) {
1316 n = block->getNodeNoEx(i->position);
1317 p = i->position + block->getPosRelative();
1318 if (m_script->node_on_timer(p, n, i->elapsed)) {
1319 block->setNodeTimer(NodeTimer(
1320 i->timeout, 0, i->position));
1327 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1329 if(m_active_block_interval_overload_skip > 0){
1330 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1331 m_active_block_interval_overload_skip--;
1334 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1335 TimeTaker timer("modify in active blocks per interval");
1337 // Initialize handling of ActiveBlockModifiers
1338 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1340 for(std::set<v3s16>::iterator
1341 i = m_active_blocks.m_list.begin();
1342 i != m_active_blocks.m_list.end(); ++i)
1346 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1347 <<") being handled"<<std::endl;*/
1349 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1353 // Set current time as timestamp
1354 block->setTimestampNoChangedFlag(m_game_time);
1356 /* Handle ActiveBlockModifiers */
1357 abmhandler.apply(block);
1360 u32 time_ms = timer.stop(true);
1361 u32 max_time_ms = 200;
1362 if(time_ms > max_time_ms){
1363 warningstream<<"active block modifiers took "
1364 <<time_ms<<"ms (longer than "
1365 <<max_time_ms<<"ms)"<<std::endl;
1366 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1371 Step script environment (run global on_step())
1373 m_script->environment_Step(dtime);
1379 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1380 //TimeTaker timer("Step active objects");
1382 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1384 // This helps the objects to send data at the same time
1385 bool send_recommended = false;
1386 m_send_recommended_timer += dtime;
1387 if(m_send_recommended_timer > getSendRecommendedInterval())
1389 m_send_recommended_timer -= getSendRecommendedInterval();
1390 send_recommended = true;
1393 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1394 i != m_active_objects.end(); ++i) {
1395 ServerActiveObject* obj = i->second;
1396 // Don't step if is to be removed or stored statically
1397 if(obj->m_removed || obj->m_pending_deactivation)
1400 obj->step(dtime, send_recommended);
1401 // Read messages from object
1402 while(!obj->m_messages_out.empty())
1404 m_active_object_messages.push(
1405 obj->m_messages_out.front());
1406 obj->m_messages_out.pop();
1412 Manage active objects
1414 if(m_object_management_interval.step(dtime, 0.5))
1416 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1418 Remove objects that satisfy (m_removed && m_known_by_count==0)
1420 removeRemovedObjects();
1424 Manage particle spawner expiration
1426 if (m_particle_management_interval.step(dtime, 1.0)) {
1427 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1428 i != m_particle_spawners.end(); ) {
1429 //non expiring spawners
1430 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1436 if (i->second <= 0.f)
1437 m_particle_spawners.erase(i++);
1444 u32 ServerEnvironment::addParticleSpawner(float exptime)
1446 // Timers with lifetime 0 do not expire
1447 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1450 for (;;) { // look for unused particlespawner id
1452 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1453 if (f == m_particle_spawners.end()) {
1454 m_particle_spawners[id] = time;
1461 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1463 u32 id = addParticleSpawner(exptime);
1464 m_particle_spawner_attachments[id] = attached_id;
1465 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1466 obj->attachParticleSpawner(id);
1471 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1473 m_particle_spawners.erase(id);
1474 std::unordered_map<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1475 if (it != m_particle_spawner_attachments.end()) {
1476 u16 obj_id = (*it).second;
1477 ServerActiveObject *sao = getActiveObject(obj_id);
1478 if (sao != NULL && remove_from_object) {
1479 sao->detachParticleSpawner(id);
1481 m_particle_spawner_attachments.erase(id);
1485 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1487 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1488 return (n != m_active_objects.end() ? n->second : NULL);
1491 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1496 return objects.find(id) == objects.end();
1499 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1501 //try to reuse id's as late as possible
1502 static u16 last_used_id = 0;
1503 u16 startid = last_used_id;
1507 if(isFreeServerActiveObjectId(last_used_id, objects))
1508 return last_used_id;
1510 if(last_used_id == startid)
1515 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1517 assert(object); // Pre-condition
1519 u16 id = addActiveObjectRaw(object, true, 0);
1524 Finds out what new objects have been added to
1525 inside a radius around a position
1527 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1529 std::set<u16> ¤t_objects,
1530 std::queue<u16> &added_objects)
1532 f32 radius_f = radius * BS;
1533 f32 player_radius_f = player_radius * BS;
1535 if (player_radius_f < 0)
1536 player_radius_f = 0;
1538 Go through the object list,
1539 - discard m_removed objects,
1540 - discard objects that are too far away,
1541 - discard objects that are found in current_objects.
1542 - add remaining objects to added_objects
1544 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1545 i != m_active_objects.end(); ++i) {
1549 ServerActiveObject *object = i->second;
1553 // Discard if removed or deactivating
1554 if(object->m_removed || object->m_pending_deactivation)
1557 f32 distance_f = object->getBasePosition().
1558 getDistanceFrom(playersao->getBasePosition());
1559 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1560 // Discard if too far
1561 if (distance_f > player_radius_f && player_radius_f != 0)
1563 } else if (distance_f > radius_f)
1566 // Discard if already on current_objects
1567 std::set<u16>::iterator n;
1568 n = current_objects.find(id);
1569 if(n != current_objects.end())
1571 // Add to added_objects
1572 added_objects.push(id);
1577 Finds out what objects have been removed from
1578 inside a radius around a position
1580 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1582 std::set<u16> ¤t_objects,
1583 std::queue<u16> &removed_objects)
1585 f32 radius_f = radius * BS;
1586 f32 player_radius_f = player_radius * BS;
1588 if (player_radius_f < 0)
1589 player_radius_f = 0;
1591 Go through current_objects; object is removed if:
1592 - object is not found in m_active_objects (this is actually an
1593 error condition; objects should be set m_removed=true and removed
1594 only after all clients have been informed about removal), or
1595 - object has m_removed=true, or
1596 - object is too far away
1598 for(std::set<u16>::iterator
1599 i = current_objects.begin();
1600 i != current_objects.end(); ++i)
1603 ServerActiveObject *object = getActiveObject(id);
1605 if (object == NULL) {
1606 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1607 << " object in current_objects is NULL" << std::endl;
1608 removed_objects.push(id);
1612 if (object->m_removed || object->m_pending_deactivation) {
1613 removed_objects.push(id);
1617 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1618 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1619 if (distance_f <= player_radius_f || player_radius_f == 0)
1621 } else if (distance_f <= radius_f)
1624 // Object is no longer visible
1625 removed_objects.push(id);
1629 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1630 v3s16 blockpos, bool static_exists, v3s16 static_block)
1632 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1636 for (std::map<u16, StaticObject>::iterator
1637 so_it = block->m_static_objects.m_active.begin();
1638 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1639 // Get the ServerActiveObject counterpart to this StaticObject
1640 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it->first);
1641 if (ao_it == m_active_objects.end()) {
1642 // If this ever happens, there must be some kind of nasty bug.
1643 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1644 "Object from MapBlock::m_static_objects::m_active not found "
1645 "in m_active_objects";
1649 ServerActiveObject *sao = ao_it->second;
1650 sao->m_static_exists = static_exists;
1651 sao->m_static_block = static_block;
1655 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1657 if(m_active_object_messages.empty())
1658 return ActiveObjectMessage(0);
1660 ActiveObjectMessage message = m_active_object_messages.front();
1661 m_active_object_messages.pop();
1666 ************ Private methods *************
1669 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1670 bool set_changed, u32 dtime_s)
1672 assert(object); // Pre-condition
1673 if(object->getId() == 0){
1674 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1677 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1678 <<"no free ids available"<<std::endl;
1679 if(object->environmentDeletes())
1683 object->setId(new_id);
1686 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1687 <<"supplied with id "<<object->getId()<<std::endl;
1690 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1691 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1692 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1693 if(object->environmentDeletes())
1698 if (objectpos_over_limit(object->getBasePosition())) {
1699 v3f p = object->getBasePosition();
1700 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1701 << "object position (" << p.X << "," << p.Y << "," << p.Z
1702 << ") outside maximum range" << std::endl;
1703 if (object->environmentDeletes())
1708 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1709 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1711 m_active_objects[object->getId()] = object;
1713 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1714 <<"Added id="<<object->getId()<<"; there are now "
1715 <<m_active_objects.size()<<" active objects."
1718 // Register reference in scripting api (must be done before post-init)
1719 m_script->addObjectReference(object);
1720 // Post-initialize object
1721 object->addedToEnvironment(dtime_s);
1723 // Add static data to block
1724 if(object->isStaticAllowed())
1726 // Add static object to active static list of the block
1727 v3f objectpos = object->getBasePosition();
1728 std::string staticdata = "";
1729 object->getStaticData(&staticdata);
1730 StaticObject s_obj(object->getType(), objectpos, staticdata);
1731 // Add to the block where the object is located in
1732 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1733 MapBlock *block = m_map->emergeBlock(blockpos);
1735 block->m_static_objects.m_active[object->getId()] = s_obj;
1736 object->m_static_exists = true;
1737 object->m_static_block = blockpos;
1740 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1741 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1743 v3s16 p = floatToInt(objectpos, BS);
1744 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1745 <<"could not emerge block for storing id="<<object->getId()
1746 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1750 return object->getId();
1754 Remove objects that satisfy (m_removed && m_known_by_count==0)
1756 void ServerEnvironment::removeRemovedObjects()
1758 std::vector<u16> objects_to_remove;
1759 for(ServerActiveObjectMap::iterator i = m_active_objects.begin();
1760 i != m_active_objects.end(); ++i) {
1762 ServerActiveObject* obj = i->second;
1763 // This shouldn't happen but check it
1766 infostream<<"NULL object found in ServerEnvironment"
1767 <<" while finding removed objects. id="<<id<<std::endl;
1768 // Id to be removed from m_active_objects
1769 objects_to_remove.push_back(id);
1774 We will delete objects that are marked as removed or thatare
1775 waiting for deletion after deactivation
1777 if (!obj->m_removed && !obj->m_pending_deactivation)
1781 Delete static data from block if is marked as removed
1783 if(obj->m_static_exists && obj->m_removed)
1785 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1787 block->m_static_objects.remove(id);
1788 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1789 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1790 obj->m_static_exists = false;
1792 infostream<<"Failed to emerge block from which an object to "
1793 <<"be removed was loaded from. id="<<id<<std::endl;
1797 // If m_known_by_count > 0, don't actually remove. On some future
1798 // invocation this will be 0, which is when removal will continue.
1799 if(obj->m_known_by_count > 0)
1803 Move static data from active to stored if not marked as removed
1805 if(obj->m_static_exists && !obj->m_removed){
1806 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1808 std::map<u16, StaticObject>::iterator i =
1809 block->m_static_objects.m_active.find(id);
1810 if(i != block->m_static_objects.m_active.end()){
1811 block->m_static_objects.m_stored.push_back(i->second);
1812 block->m_static_objects.m_active.erase(id);
1813 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1814 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1817 infostream<<"Failed to emerge block from which an object to "
1818 <<"be deactivated was loaded from. id="<<id<<std::endl;
1822 // Tell the object about removal
1823 obj->removingFromEnvironment();
1824 // Deregister in scripting api
1825 m_script->removeObjectReference(obj);
1828 if(obj->environmentDeletes())
1831 // Id to be removed from m_active_objects
1832 objects_to_remove.push_back(id);
1834 // Remove references from m_active_objects
1835 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1836 i != objects_to_remove.end(); ++i) {
1837 m_active_objects.erase(*i);
1841 static void print_hexdump(std::ostream &o, const std::string &data)
1843 const int linelength = 16;
1844 for(int l=0; ; l++){
1845 int i0 = linelength * l;
1846 bool at_end = false;
1847 int thislinelength = linelength;
1848 if(i0 + thislinelength > (int)data.size()){
1849 thislinelength = data.size() - i0;
1852 for(int di=0; di<linelength; di++){
1855 if(di<thislinelength)
1856 snprintf(buf, 4, "%.2x ", data[i]);
1858 snprintf(buf, 4, " ");
1862 for(int di=0; di<thislinelength; di++){
1876 Convert stored objects from blocks near the players to active.
1878 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1883 // Ignore if no stored objects (to not set changed flag)
1884 if(block->m_static_objects.m_stored.empty())
1887 verbosestream<<"ServerEnvironment::activateObjects(): "
1888 <<"activating objects of block "<<PP(block->getPos())
1889 <<" ("<<block->m_static_objects.m_stored.size()
1890 <<" objects)"<<std::endl;
1891 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1893 errorstream<<"suspiciously large amount of objects detected: "
1894 <<block->m_static_objects.m_stored.size()<<" in "
1895 <<PP(block->getPos())
1896 <<"; removing all of them."<<std::endl;
1897 // Clear stored list
1898 block->m_static_objects.m_stored.clear();
1899 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1900 MOD_REASON_TOO_MANY_OBJECTS);
1904 // Activate stored objects
1905 std::vector<StaticObject> new_stored;
1906 for (std::vector<StaticObject>::iterator
1907 i = block->m_static_objects.m_stored.begin();
1908 i != block->m_static_objects.m_stored.end(); ++i) {
1909 StaticObject &s_obj = *i;
1911 // Create an active object from the data
1912 ServerActiveObject *obj = ServerActiveObject::create
1913 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1914 // If couldn't create object, store static data back.
1916 errorstream<<"ServerEnvironment::activateObjects(): "
1917 <<"failed to create active object from static object "
1918 <<"in block "<<PP(s_obj.pos/BS)
1919 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1920 print_hexdump(verbosestream, s_obj.data);
1922 new_stored.push_back(s_obj);
1925 verbosestream<<"ServerEnvironment::activateObjects(): "
1926 <<"activated static object pos="<<PP(s_obj.pos/BS)
1927 <<" type="<<(int)s_obj.type<<std::endl;
1928 // This will also add the object to the active static list
1929 addActiveObjectRaw(obj, false, dtime_s);
1931 // Clear stored list
1932 block->m_static_objects.m_stored.clear();
1933 // Add leftover failed stuff to stored list
1934 for(std::vector<StaticObject>::iterator
1935 i = new_stored.begin();
1936 i != new_stored.end(); ++i) {
1937 StaticObject &s_obj = *i;
1938 block->m_static_objects.m_stored.push_back(s_obj);
1941 // Turn the active counterparts of activated objects not pending for
1943 for(std::map<u16, StaticObject>::iterator
1944 i = block->m_static_objects.m_active.begin();
1945 i != block->m_static_objects.m_active.end(); ++i)
1948 ServerActiveObject *object = getActiveObject(id);
1950 object->m_pending_deactivation = false;
1954 Note: Block hasn't really been modified here.
1955 The objects have just been activated and moved from the stored
1956 static list to the active static list.
1957 As such, the block is essentially the same.
1958 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1959 Otherwise there would be a huge amount of unnecessary I/O.
1964 Convert objects that are not standing inside active blocks to static.
1966 If m_known_by_count != 0, active object is not deleted, but static
1967 data is still updated.
1969 If force_delete is set, active object is deleted nevertheless. It
1970 shall only be set so in the destructor of the environment.
1972 If block wasn't generated (not in memory or on disk),
1974 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1976 std::vector<u16> objects_to_remove;
1977 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1978 i != m_active_objects.end(); ++i) {
1979 // force_delete might be overriden per object
1980 bool force_delete = _force_delete;
1982 ServerActiveObject* obj = i->second;
1985 // Do not deactivate if static data creation not allowed
1986 if(!force_delete && !obj->isStaticAllowed())
1989 // If pending deactivation, let removeRemovedObjects() do it
1990 if(!force_delete && obj->m_pending_deactivation)
1994 v3f objectpos = obj->getBasePosition();
1996 // The block in which the object resides in
1997 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1999 // If object's static data is stored in a deactivated block and object
2000 // is actually located in an active block, re-save to the block in
2001 // which the object is actually located in.
2003 obj->m_static_exists &&
2004 !m_active_blocks.contains(obj->m_static_block) &&
2005 m_active_blocks.contains(blockpos_o))
2007 v3s16 old_static_block = obj->m_static_block;
2009 // Save to block where object is located
2010 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2012 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2013 <<"Could not save object id="<<id
2014 <<" to it's current block "<<PP(blockpos_o)
2018 std::string staticdata_new = "";
2019 obj->getStaticData(&staticdata_new);
2020 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2021 block->m_static_objects.insert(id, s_obj);
2022 obj->m_static_block = blockpos_o;
2023 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2024 MOD_REASON_STATIC_DATA_ADDED);
2026 // Delete from block where object was located
2027 block = m_map->emergeBlock(old_static_block, false);
2029 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2030 <<"Could not delete object id="<<id
2031 <<" from it's previous block "<<PP(old_static_block)
2035 block->m_static_objects.remove(id);
2036 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2037 MOD_REASON_STATIC_DATA_REMOVED);
2041 // If block is active, don't remove
2042 if(!force_delete && m_active_blocks.contains(blockpos_o))
2045 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2046 <<"deactivating object id="<<id<<" on inactive block "
2047 <<PP(blockpos_o)<<std::endl;
2049 // If known by some client, don't immediately delete.
2050 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2053 Update the static data
2056 if(obj->isStaticAllowed())
2058 // Create new static object
2059 std::string staticdata_new = "";
2060 obj->getStaticData(&staticdata_new);
2061 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2063 bool stays_in_same_block = false;
2064 bool data_changed = true;
2066 if (obj->m_static_exists) {
2067 if (obj->m_static_block == blockpos_o)
2068 stays_in_same_block = true;
2070 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2073 std::map<u16, StaticObject>::iterator n =
2074 block->m_static_objects.m_active.find(id);
2075 if (n != block->m_static_objects.m_active.end()) {
2076 StaticObject static_old = n->second;
2078 float save_movem = obj->getMinimumSavedMovement();
2080 if (static_old.data == staticdata_new &&
2081 (static_old.pos - objectpos).getLength() < save_movem)
2082 data_changed = false;
2084 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2085 <<"id="<<id<<" m_static_exists=true but "
2086 <<"static data doesn't actually exist in "
2087 <<PP(obj->m_static_block)<<std::endl;
2092 bool shall_be_written = (!stays_in_same_block || data_changed);
2094 // Delete old static object
2095 if(obj->m_static_exists)
2097 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2100 block->m_static_objects.remove(id);
2101 obj->m_static_exists = false;
2102 // Only mark block as modified if data changed considerably
2103 if(shall_be_written)
2104 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2105 MOD_REASON_STATIC_DATA_CHANGED);
2109 // Add to the block where the object is located in
2110 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2111 // Get or generate the block
2112 MapBlock *block = NULL;
2114 block = m_map->emergeBlock(blockpos);
2115 } catch(InvalidPositionException &e){
2116 // Handled via NULL pointer
2117 // NOTE: emergeBlock's failure is usually determined by it
2118 // actually returning NULL
2123 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2124 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2125 << " statically but block " << PP(blockpos)
2126 << " already contains "
2127 << block->m_static_objects.m_stored.size()
2129 << " Forcing delete." << std::endl;
2130 force_delete = true;
2132 // If static counterpart already exists in target block,
2134 // This shouldn't happen because the object is removed from
2135 // the previous block before this according to
2136 // obj->m_static_block, but happens rarely for some unknown
2137 // reason. Unsuccessful attempts have been made to find
2139 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2140 warningstream<<"ServerEnv: Performing hack #83274"
2142 block->m_static_objects.remove(id);
2144 // Store static data
2145 u16 store_id = pending_delete ? id : 0;
2146 block->m_static_objects.insert(store_id, s_obj);
2148 // Only mark block as modified if data changed considerably
2149 if(shall_be_written)
2150 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2151 MOD_REASON_STATIC_DATA_CHANGED);
2153 obj->m_static_exists = true;
2154 obj->m_static_block = block->getPos();
2159 v3s16 p = floatToInt(objectpos, BS);
2160 errorstream<<"ServerEnv: Could not find or generate "
2161 <<"a block for storing id="<<obj->getId()
2162 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2169 If known by some client, set pending deactivation.
2170 Otherwise delete it immediately.
2173 if(pending_delete && !force_delete)
2175 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2176 <<"object id="<<id<<" is known by clients"
2177 <<"; not deleting yet"<<std::endl;
2179 obj->m_pending_deactivation = true;
2183 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2184 <<"object id="<<id<<" is not known by clients"
2185 <<"; deleting"<<std::endl;
2187 // Tell the object about removal
2188 obj->removingFromEnvironment();
2189 // Deregister in scripting api
2190 m_script->removeObjectReference(obj);
2192 // Delete active object
2193 if(obj->environmentDeletes())
2195 // Id to be removed from m_active_objects
2196 objects_to_remove.push_back(id);
2199 // Remove references from m_active_objects
2200 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2201 i != objects_to_remove.end(); ++i) {
2202 m_active_objects.erase(*i);
2206 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2207 const std::string &savedir, const Settings &conf)
2210 if (name == "sqlite3")
2211 return new PlayerDatabaseSQLite3(savedir);
2212 else if (name == "dummy")
2213 return new Database_Dummy();
2215 else if (name == "postgresql") {
2216 std::string connect_string = "";
2217 conf.getNoEx("pgsql_player_connection", connect_string);
2218 return new PlayerDatabasePostgreSQL(connect_string);
2221 else if (name == "files")
2222 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2224 throw BaseException(std::string("Database backend ") + name + " not supported.");
2227 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2228 const Settings &cmd_args)
2230 std::string migrate_to = cmd_args.get("migrate-players");
2232 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2233 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2234 errorstream << "Cannot read world.mt!" << std::endl;
2238 if (!world_mt.exists("player_backend")) {
2239 errorstream << "Please specify your current backend in world.mt:"
2241 << " player_backend = {files|sqlite3|postgresql}"
2246 std::string backend = world_mt.get("player_backend");
2247 if (backend == migrate_to) {
2248 errorstream << "Cannot migrate: new backend is same"
2249 << " as the old one" << std::endl;
2253 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2256 if (backend == "files") {
2257 // Create backup directory
2258 fs::CreateDir(players_backup_path);
2262 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2263 game_params.world_path, world_mt);
2264 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2265 game_params.world_path, world_mt);
2267 std::vector<std::string> player_list;
2268 srcdb->listPlayers(player_list);
2269 for (std::vector<std::string>::const_iterator it = player_list.begin();
2270 it != player_list.end(); ++it) {
2271 actionstream << "Migrating player " << it->c_str() << std::endl;
2272 RemotePlayer player(it->c_str(), NULL);
2273 PlayerSAO playerSAO(NULL, &player, 15000, false);
2275 srcdb->loadPlayer(&player, &playerSAO);
2277 playerSAO.finalize(&player, std::set<std::string>());
2278 player.setPlayerSAO(&playerSAO);
2280 dstdb->savePlayer(&player);
2282 // For files source, move player files to backup dir
2283 if (backend == "files") {
2285 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2286 players_backup_path + DIR_DELIM + (*it));
2290 actionstream << "Successfully migrated " << player_list.size() << " players"
2292 world_mt.set("player_backend", migrate_to);
2293 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2294 errorstream << "Failed to update world.mt!" << std::endl;
2296 actionstream << "world.mt updated" << std::endl;
2298 // When migration is finished from file backend, remove players directory if empty
2299 if (backend == "files") {
2300 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2307 } catch (BaseException &e) {
2308 errorstream << "An error occured during migration: " << e.what() << std::endl;