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 "util/serialize.h"
34 #include "util/basic_macros.h"
35 #include "util/pointedthing.h"
36 #include "threading/mutex_auto_lock.h"
38 #include "gameparams.h"
39 #include "database-dummy.h"
40 #include "database-files.h"
41 #include "database-sqlite3.h"
43 #include "database-postgresql.h"
46 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
48 // A number that is much smaller than the timeout for particle spawners should/could ever be
49 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
55 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
58 // Initialize timer to random value to spread processing
59 float itv = abm->getTriggerInterval();
60 itv = MYMAX(0.001, itv); // No less than 1ms
61 int minval = MYMAX(-0.51*itv, -60); // Clamp to
62 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
63 timer = myrand_range(minval, maxval);
70 void LBMContentMapping::deleteContents()
72 for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
73 it != lbm_list.end(); ++it) {
78 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
80 // Add the lbm_def to the LBMContentMapping.
81 // Unknown names get added to the global NameIdMapping.
82 INodeDefManager *nodedef = gamedef->ndef();
84 lbm_list.push_back(lbm_def);
86 for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
87 it != lbm_def->trigger_contents.end(); ++it) {
88 std::set<content_t> c_ids;
89 bool found = nodedef->getIds(*it, c_ids);
91 content_t c_id = gamedef->allocateUnknownNodeId(*it);
92 if (c_id == CONTENT_IGNORE) {
93 // Seems it can't be allocated.
94 warningstream << "Could not internalize node name \"" << *it
95 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
101 for (std::set<content_t>::const_iterator iit =
102 c_ids.begin(); iit != c_ids.end(); ++iit) {
103 content_t c_id = *iit;
104 map[c_id].push_back(lbm_def);
109 const std::vector<LoadingBlockModifierDef *> *
110 LBMContentMapping::lookup(content_t c) const
112 container_map::const_iterator it = map.find(c);
115 // This first dereferences the iterator, returning
116 // a std::vector<LoadingBlockModifierDef *>
117 // reference, then we convert it to a pointer.
118 return &(it->second);
121 LBMManager::~LBMManager()
123 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
124 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
127 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
128 it != m_lbm_lookup.end(); ++it) {
129 (it->second).deleteContents();
133 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
135 // Precondition, in query mode the map isn't used anymore
136 FATAL_ERROR_IF(m_query_mode == true,
137 "attempted to modify LBMManager in query mode");
139 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
140 throw ModError("Error adding LBM \"" + lbm_def->name +
141 "\": Does not follow naming conventions: "
142 "Only characters [a-z0-9_:] are allowed.");
145 m_lbm_defs[lbm_def->name] = lbm_def;
148 void LBMManager::loadIntroductionTimes(const std::string ×,
149 IGameDef *gamedef, u32 now)
154 // Storing it in a map first instead of
155 // handling the stuff directly in the loop
156 // removes all duplicate entries.
157 // TODO make this std::unordered_map
158 std::map<std::string, u32> introduction_times;
161 The introduction times string consists of name~time entries,
162 with each entry terminated by a semicolon. The time is decimal.
167 while ((idx_new = times.find(";", idx)) != std::string::npos) {
168 std::string entry = times.substr(idx, idx_new - idx);
169 std::vector<std::string> components = str_split(entry, '~');
170 if (components.size() != 2)
171 throw SerializationError("Introduction times entry \""
172 + entry + "\" requires exactly one '~'!");
173 const std::string &name = components[0];
174 u32 time = from_string<u32>(components[1]);
175 introduction_times[name] = time;
179 // Put stuff from introduction_times into m_lbm_lookup
180 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
181 it != introduction_times.end(); ++it) {
182 const std::string &name = it->first;
183 u32 time = it->second;
185 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
186 m_lbm_defs.find(name);
187 if (def_it == m_lbm_defs.end()) {
188 // This seems to be an LBM entry for
189 // an LBM we haven't loaded. Discard it.
192 LoadingBlockModifierDef *lbm_def = def_it->second;
193 if (lbm_def->run_at_every_load) {
194 // This seems to be an LBM entry for
195 // an LBM that runs at every load.
196 // Don't add it just yet.
200 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
202 // Erase the entry so that we know later
203 // what elements didn't get put into m_lbm_lookup
204 m_lbm_defs.erase(name);
207 // Now also add the elements from m_lbm_defs to m_lbm_lookup
208 // that weren't added in the previous step.
209 // They are introduced first time to this world,
210 // or are run at every load (introducement time hardcoded to U32_MAX).
212 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
213 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
215 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
216 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
217 if (it->second->run_at_every_load) {
218 lbms_running_always.addLBM(it->second, gamedef);
220 lbms_we_introduce_now.addLBM(it->second, gamedef);
224 // Clear the list, so that we don't delete remaining elements
225 // twice in the destructor
229 std::string LBMManager::createIntroductionTimesString()
231 // Precondition, we must be in query mode
232 FATAL_ERROR_IF(m_query_mode == false,
233 "attempted to query on non fully set up LBMManager");
235 std::ostringstream oss;
236 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
237 it != m_lbm_lookup.end(); ++it) {
238 u32 time = it->first;
239 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
240 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
241 iit != lbm_list.end(); ++iit) {
242 // Don't add if the LBM runs at every load,
243 // then introducement time is hardcoded
244 // and doesn't need to be stored
245 if ((*iit)->run_at_every_load)
247 oss << (*iit)->name << "~" << time << ";";
253 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
255 // Precondition, we need m_lbm_lookup to be initialized
256 FATAL_ERROR_IF(m_query_mode == false,
257 "attempted to query on non fully set up LBMManager");
258 v3s16 pos_of_block = block->getPosRelative();
262 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
263 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
264 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
265 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
267 n = block->getNodeNoEx(pos);
269 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
270 iit != m_lbm_lookup.end(); ++iit) {
271 const std::vector<LoadingBlockModifierDef *> *lbm_list =
272 iit->second.lookup(c);
275 for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
276 lbm_list->begin(); iit != lbm_list->end(); ++iit) {
277 (*iit)->trigger(env, pos + pos_of_block, n);
287 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
290 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
291 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
292 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
295 if (p.getDistanceFrom(p0) <= r) {
302 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
304 std::set<v3s16> &blocks_removed,
305 std::set<v3s16> &blocks_added)
310 std::set<v3s16> newlist = m_forceloaded_list;
311 for(std::vector<v3s16>::iterator i = active_positions.begin();
312 i != active_positions.end(); ++i)
314 fillRadiusBlock(*i, radius, newlist);
318 Find out which blocks on the old list are not on the new list
320 // Go through old list
321 for(std::set<v3s16>::iterator i = m_list.begin();
322 i != m_list.end(); ++i)
325 // If not on new list, it's been removed
326 if(newlist.find(p) == newlist.end())
327 blocks_removed.insert(p);
331 Find out which blocks on the new list are not on the old list
333 // Go through new list
334 for(std::set<v3s16>::iterator i = newlist.begin();
335 i != newlist.end(); ++i)
338 // If not on old list, it's been added
339 if(m_list.find(p) == m_list.end())
340 blocks_added.insert(p);
347 for(std::set<v3s16>::iterator i = newlist.begin();
348 i != newlist.end(); ++i)
359 ServerEnvironment::ServerEnvironment(ServerMap *map,
360 ServerScripting *scriptIface, Server *server,
361 const std::string &path_world):
364 m_script(scriptIface),
366 m_path_world(path_world)
368 // Determine which database backend to use
369 std::string conf_path = path_world + DIR_DELIM + "world.mt";
371 bool succeeded = conf.readConfigFile(conf_path.c_str());
372 if (!succeeded || !conf.exists("player_backend")) {
373 // fall back to files
374 conf.set("player_backend", "files");
375 warningstream << "/!\\ You are using old player file backend. "
376 << "This backend is deprecated and will be removed in next release /!\\"
377 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
378 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
380 if (!conf.updateConfigFile(conf_path.c_str())) {
381 errorstream << "ServerEnvironment::ServerEnvironment(): "
382 << "Failed to update world.mt!" << std::endl;
386 std::string name = "";
387 conf.getNoEx("player_backend", name);
388 m_player_database = openPlayerDatabase(name, path_world, conf);
391 ServerEnvironment::~ServerEnvironment()
393 // Clear active block list.
394 // This makes the next one delete all active objects.
395 m_active_blocks.clear();
397 // Convert all objects to static and delete the active objects
398 deactivateFarObjects(true);
403 // Delete ActiveBlockModifiers
404 for (std::vector<ABMWithState>::iterator
405 i = m_abms.begin(); i != m_abms.end(); ++i){
409 // Deallocate players
410 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
411 i != m_players.end(); ++i) {
415 delete m_player_database;
418 Map & ServerEnvironment::getMap()
423 ServerMap & ServerEnvironment::getServerMap()
428 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
430 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
431 i != m_players.end(); ++i) {
432 RemotePlayer *player = *i;
433 if (player->peer_id == peer_id)
439 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
441 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
442 i != m_players.end(); ++i) {
443 RemotePlayer *player = *i;
444 if (strcmp(player->getName(), name) == 0)
450 void ServerEnvironment::addPlayer(RemotePlayer *player)
452 DSTACK(FUNCTION_NAME);
454 Check that peer_ids are unique.
455 Also check that names are unique.
456 Exception: there can be multiple players with peer_id=0
458 // If peer id is non-zero, it has to be unique.
459 if (player->peer_id != 0)
460 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
461 // Name has to be unique.
462 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
464 m_players.push_back(player);
467 void ServerEnvironment::removePlayer(RemotePlayer *player)
469 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
470 it != m_players.end(); ++it) {
471 if ((*it) == player) {
479 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
481 return m_player_database->removePlayer(name);
484 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
486 float distance = pos1.getDistanceFrom(pos2);
488 //calculate normalized direction vector
489 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
490 (pos2.Y - pos1.Y)/distance,
491 (pos2.Z - pos1.Z)/distance);
493 //find out if there's a node on path between pos1 and pos2
494 for (float i = 1; i < distance; i += stepsize) {
495 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
496 normalized_vector.Y * i,
497 normalized_vector.Z * i) +pos1,BS);
499 MapNode n = getMap().getNodeNoEx(pos);
501 if(n.param0 != CONTENT_AIR) {
511 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
512 const std::string &str_reason, bool reconnect)
514 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
515 it != m_players.end(); ++it) {
516 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
517 m_server->DenyAccessVerCompliant(player->peer_id,
518 player->protocol_version, reason, str_reason, reconnect);
522 void ServerEnvironment::saveLoadedPlayers()
524 std::string players_path = m_path_world + DIR_DELIM + "players";
525 fs::CreateDir(players_path);
527 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
528 it != m_players.end();
530 if ((*it)->checkModified() ||
531 ((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) {
533 m_player_database->savePlayer(*it);
534 } catch (DatabaseException &e) {
535 errorstream << "Failed to save player " << (*it)->getName() << " exception: "
536 << e.what() << std::endl;
543 void ServerEnvironment::savePlayer(RemotePlayer *player)
546 m_player_database->savePlayer(player);
547 } catch (DatabaseException &e) {
548 errorstream << "Failed to save player " << player->getName() << " exception: "
549 << e.what() << std::endl;
554 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
555 u16 peer_id, bool is_singleplayer)
557 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
558 // Create player if it doesn't exist
559 if (!m_player_database->loadPlayer(player, playersao)) {
561 // Set player position
562 infostream << "Server: Finding spawn place for player \""
563 << player->getName() << "\"" << std::endl;
564 playersao->setBasePosition(m_server->findSpawnPos());
566 // Make sure the player is saved
567 player->setModified(true);
569 // If the player exists, ensure that they respawn inside legal bounds
570 // This fixes an assert crash when the player can't be added
571 // to the environment
572 ServerMap &map = getServerMap();
573 if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) {
574 actionstream << "Respawn position for player \""
575 << player->getName() << "\" outside limits, resetting" << std::endl;
576 playersao->setBasePosition(m_server->findSpawnPos());
580 // Add player to environment
583 /* Clean up old HUD elements from previous sessions */
586 /* Add object to environment */
587 addActiveObject(playersao);
592 void ServerEnvironment::saveMeta()
594 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
596 // Open file and serialize
597 std::ostringstream ss(std::ios_base::binary);
600 args.setU64("game_time", m_game_time);
601 args.setU64("time_of_day", getTimeOfDay());
602 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
603 args.setU64("lbm_introduction_times_version", 1);
604 args.set("lbm_introduction_times",
605 m_lbm_mgr.createIntroductionTimesString());
606 args.setU64("day_count", m_day_count);
610 if(!fs::safeWriteToFile(path, ss.str()))
612 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
614 throw SerializationError("Couldn't save env meta");
618 void ServerEnvironment::loadMeta()
620 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
622 // Open file and deserialize
623 std::ifstream is(path.c_str(), std::ios_base::binary);
625 infostream << "ServerEnvironment::loadMeta(): Failed to open "
626 << path << std::endl;
627 throw SerializationError("Couldn't load env meta");
632 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
633 throw SerializationError("ServerEnvironment::loadMeta(): "
634 "EnvArgsEnd not found!");
638 m_game_time = args.getU64("game_time");
639 } catch (SettingNotFoundException &e) {
640 // Getting this is crucial, otherwise timestamps are useless
641 throw SerializationError("Couldn't load env meta game_time");
644 setTimeOfDay(args.exists("time_of_day") ?
645 // set day to morning by default
646 args.getU64("time_of_day") : 9000);
648 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
649 // If missing, do as if clearObjects was never called
650 args.getU64("last_clear_objects_time") : 0;
652 std::string lbm_introduction_times = "";
654 u64 ver = args.getU64("lbm_introduction_times_version");
656 lbm_introduction_times = args.get("lbm_introduction_times");
658 infostream << "ServerEnvironment::loadMeta(): Non-supported"
659 << " introduction time version " << ver << std::endl;
661 } catch (SettingNotFoundException &e) {
662 // No problem, this is expected. Just continue with an empty string
664 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
666 m_day_count = args.exists("day_count") ?
667 args.getU64("day_count") : 0;
670 void ServerEnvironment::loadDefaultMeta()
672 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
677 ActiveBlockModifier *abm;
679 std::set<content_t> required_neighbors;
685 ServerEnvironment *m_env;
686 std::vector<std::vector<ActiveABM> *> m_aabms;
688 ABMHandler(std::vector<ABMWithState> &abms,
689 float dtime_s, ServerEnvironment *env,
695 INodeDefManager *ndef = env->getGameDef()->ndef();
696 for(std::vector<ABMWithState>::iterator
697 i = abms.begin(); i != abms.end(); ++i) {
698 ActiveBlockModifier *abm = i->abm;
699 float trigger_interval = abm->getTriggerInterval();
700 if(trigger_interval < 0.001)
701 trigger_interval = 0.001;
702 float actual_interval = dtime_s;
705 if(i->timer < trigger_interval)
707 i->timer -= trigger_interval;
708 actual_interval = trigger_interval;
710 float chance = abm->getTriggerChance();
715 if (abm->getSimpleCatchUp()) {
716 float intervals = actual_interval / trigger_interval;
719 aabm.chance = chance / intervals;
723 aabm.chance = chance;
727 const std::set<std::string> &required_neighbors_s =
728 abm->getRequiredNeighbors();
729 for (std::set<std::string>::iterator rn = required_neighbors_s.begin();
730 rn != required_neighbors_s.end(); ++rn) {
731 ndef->getIds(*rn, aabm.required_neighbors);
735 const std::set<std::string> &contents_s = abm->getTriggerContents();
736 for (std::set<std::string>::iterator cs = contents_s.begin();
737 cs != contents_s.end(); ++cs) {
738 std::set<content_t> ids;
739 ndef->getIds(*cs, ids);
740 for (std::set<content_t>::const_iterator k = ids.begin();
741 k != ids.end(); ++k) {
743 if (c >= m_aabms.size())
744 m_aabms.resize(c + 256, NULL);
746 m_aabms[c] = new std::vector<ActiveABM>;
747 m_aabms[c]->push_back(aabm);
755 for (size_t i = 0; i < m_aabms.size(); i++)
759 // Find out how many objects the given block and its neighbours contain.
760 // Returns the number of objects in the block, and also in 'wider' the
761 // number of objects in the block and all its neighbours. The latter
762 // may an estimate if any neighbours are unloaded.
763 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
766 u32 wider_unknown_count = 0;
767 for(s16 x=-1; x<=1; x++)
768 for(s16 y=-1; y<=1; y++)
769 for(s16 z=-1; z<=1; z++)
771 MapBlock *block2 = map->getBlockNoCreateNoEx(
772 block->getPos() + v3s16(x,y,z));
774 wider_unknown_count++;
777 wider += block2->m_static_objects.m_active.size()
778 + block2->m_static_objects.m_stored.size();
781 u32 active_object_count = block->m_static_objects.m_active.size();
782 u32 wider_known_count = 3*3*3 - wider_unknown_count;
783 wider += wider_unknown_count * wider / wider_known_count;
784 return active_object_count;
787 void apply(MapBlock *block)
789 if(m_aabms.empty() || block->isDummy())
792 ServerMap *map = &m_env->getServerMap();
794 u32 active_object_count_wider;
795 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
796 m_env->m_added_objects = 0;
799 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
800 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
801 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
803 const MapNode &n = block->getNodeUnsafe(p0);
804 content_t c = n.getContent();
806 if (c >= m_aabms.size() || !m_aabms[c])
809 v3s16 p = p0 + block->getPosRelative();
810 for(std::vector<ActiveABM>::iterator
811 i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
812 if(myrand() % i->chance != 0)
816 if(!i->required_neighbors.empty())
819 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
820 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
821 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
826 if (block->isValidPosition(p1)) {
827 // if the neighbor is found on the same map block
828 // get it straight from there
829 const MapNode &n = block->getNodeUnsafe(p1);
832 // otherwise consult the map
833 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
836 std::set<content_t>::const_iterator k;
837 k = i->required_neighbors.find(c);
838 if(k != i->required_neighbors.end()){
842 // No required neighbor found
847 // Call all the trigger variations
848 i->abm->trigger(m_env, p, n);
849 i->abm->trigger(m_env, p, n,
850 active_object_count, active_object_count_wider);
852 // Count surrounding objects again if the abms added any
853 if(m_env->m_added_objects > 0) {
854 active_object_count = countObjects(block, map, active_object_count_wider);
855 m_env->m_added_objects = 0;
862 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
864 // Reset usage timer immediately, otherwise a block that becomes active
865 // again at around the same time as it would normally be unloaded will
866 // get unloaded incorrectly. (I think this still leaves a small possibility
867 // of a race condition between this and server::AsyncRunStep, which only
868 // some kind of synchronisation will fix, but it at least reduces the window
869 // of opportunity for it to break from seconds to nanoseconds)
870 block->resetUsageTimer();
872 // Get time difference
874 u32 stamp = block->getTimestamp();
875 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
876 dtime_s = m_game_time - stamp;
877 dtime_s += additional_dtime;
879 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
880 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
882 // Remove stored static objects if clearObjects was called since block's timestamp
883 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
884 block->m_static_objects.m_stored.clear();
885 // do not set changed flag to avoid unnecessary mapblock writes
888 // Set current time as timestamp
889 block->setTimestampNoChangedFlag(m_game_time);
891 /*infostream<<"ServerEnvironment::activateBlock(): block is "
892 <<dtime_s<<" seconds old."<<std::endl;*/
894 // Activate stored objects
895 activateObjects(block, dtime_s);
897 /* Handle LoadingBlockModifiers */
898 m_lbm_mgr.applyLBMs(this, block, stamp);
901 std::vector<NodeTimer> elapsed_timers =
902 block->m_node_timers.step((float)dtime_s);
903 if (!elapsed_timers.empty()) {
905 for (std::vector<NodeTimer>::iterator
906 i = elapsed_timers.begin();
907 i != elapsed_timers.end(); ++i){
908 n = block->getNodeNoEx(i->position);
909 v3s16 p = i->position + block->getPosRelative();
910 if (m_script->node_on_timer(p, n, i->elapsed))
911 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
915 /* Handle ActiveBlockModifiers */
916 ABMHandler abmhandler(m_abms, dtime_s, this, false);
917 abmhandler.apply(block);
920 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
922 m_abms.push_back(ABMWithState(abm));
925 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
927 m_lbm_mgr.addLBMDef(lbm);
930 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
932 INodeDefManager *ndef = m_server->ndef();
933 MapNode n_old = m_map->getNodeNoEx(p);
936 if (ndef->get(n_old).has_on_destruct)
937 m_script->node_on_destruct(p, n_old);
940 if (!m_map->addNodeWithEvent(p, n))
943 // Update active VoxelManipulator if a mapgen thread
944 m_map->updateVManip(p);
946 // Call post-destructor
947 if (ndef->get(n_old).has_after_destruct)
948 m_script->node_after_destruct(p, n_old);
951 if (ndef->get(n).has_on_construct)
952 m_script->node_on_construct(p, n);
957 bool ServerEnvironment::removeNode(v3s16 p)
959 INodeDefManager *ndef = m_server->ndef();
960 MapNode n_old = m_map->getNodeNoEx(p);
963 if (ndef->get(n_old).has_on_destruct)
964 m_script->node_on_destruct(p, n_old);
967 // This is slightly optimized compared to addNodeWithEvent(air)
968 if (!m_map->removeNodeWithEvent(p))
971 // Update active VoxelManipulator if a mapgen thread
972 m_map->updateVManip(p);
974 // Call post-destructor
975 if (ndef->get(n_old).has_after_destruct)
976 m_script->node_after_destruct(p, n_old);
978 // Air doesn't require constructor
982 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
984 if (!m_map->addNodeWithEvent(p, n, false))
987 // Update active VoxelManipulator if a mapgen thread
988 m_map->updateVManip(p);
993 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
996 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
997 i != m_active_objects.end(); ++i) {
998 ServerActiveObject* obj = i->second;
1000 v3f objectpos = obj->getBasePosition();
1001 if (objectpos.getDistanceFrom(pos) > radius)
1003 objects.push_back(id);
1007 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1009 infostream << "ServerEnvironment::clearObjects(): "
1010 << "Removing all active objects" << std::endl;
1011 std::vector<u16> objects_to_remove;
1012 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1013 i != m_active_objects.end(); ++i) {
1014 ServerActiveObject* obj = i->second;
1015 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1018 // Delete static object if block is loaded
1019 if (obj->m_static_exists) {
1020 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1022 block->m_static_objects.remove(id);
1023 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1024 MOD_REASON_CLEAR_ALL_OBJECTS);
1025 obj->m_static_exists = false;
1028 // If known by some client, don't delete immediately
1029 if (obj->m_known_by_count > 0) {
1030 obj->m_pending_deactivation = true;
1031 obj->m_removed = true;
1035 // Tell the object about removal
1036 obj->removingFromEnvironment();
1037 // Deregister in scripting api
1038 m_script->removeObjectReference(obj);
1040 // Delete active object
1041 if (obj->environmentDeletes())
1043 // Id to be removed from m_active_objects
1044 objects_to_remove.push_back(id);
1047 // Remove references from m_active_objects
1048 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1049 i != objects_to_remove.end(); ++i) {
1050 m_active_objects.erase(*i);
1053 // Get list of loaded blocks
1054 std::vector<v3s16> loaded_blocks;
1055 infostream << "ServerEnvironment::clearObjects(): "
1056 << "Listing all loaded blocks" << std::endl;
1057 m_map->listAllLoadedBlocks(loaded_blocks);
1058 infostream << "ServerEnvironment::clearObjects(): "
1059 << "Done listing all loaded blocks: "
1060 << loaded_blocks.size()<<std::endl;
1062 // Get list of loadable blocks
1063 std::vector<v3s16> loadable_blocks;
1064 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1065 infostream << "ServerEnvironment::clearObjects(): "
1066 << "Listing all loadable blocks" << std::endl;
1067 m_map->listAllLoadableBlocks(loadable_blocks);
1068 infostream << "ServerEnvironment::clearObjects(): "
1069 << "Done listing all loadable blocks: "
1070 << loadable_blocks.size() << std::endl;
1072 loadable_blocks = loaded_blocks;
1075 infostream << "ServerEnvironment::clearObjects(): "
1076 << "Now clearing objects in " << loadable_blocks.size()
1077 << " blocks" << std::endl;
1079 // Grab a reference on each loaded block to avoid unloading it
1080 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1081 i != loaded_blocks.end(); ++i) {
1083 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1084 assert(block != NULL);
1088 // Remove objects in all loadable blocks
1089 u32 unload_interval = U32_MAX;
1090 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1091 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1092 unload_interval = MYMAX(unload_interval, 1);
1094 u32 report_interval = loadable_blocks.size() / 10;
1095 u32 num_blocks_checked = 0;
1096 u32 num_blocks_cleared = 0;
1097 u32 num_objs_cleared = 0;
1098 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1099 i != loadable_blocks.end(); ++i) {
1101 MapBlock *block = m_map->emergeBlock(p, false);
1103 errorstream << "ServerEnvironment::clearObjects(): "
1104 << "Failed to emerge block " << PP(p) << std::endl;
1107 u32 num_stored = block->m_static_objects.m_stored.size();
1108 u32 num_active = block->m_static_objects.m_active.size();
1109 if (num_stored != 0 || num_active != 0) {
1110 block->m_static_objects.m_stored.clear();
1111 block->m_static_objects.m_active.clear();
1112 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1113 MOD_REASON_CLEAR_ALL_OBJECTS);
1114 num_objs_cleared += num_stored + num_active;
1115 num_blocks_cleared++;
1117 num_blocks_checked++;
1119 if (report_interval != 0 &&
1120 num_blocks_checked % report_interval == 0) {
1121 float percent = 100.0 * (float)num_blocks_checked /
1122 loadable_blocks.size();
1123 infostream << "ServerEnvironment::clearObjects(): "
1124 << "Cleared " << num_objs_cleared << " objects"
1125 << " in " << num_blocks_cleared << " blocks ("
1126 << percent << "%)" << std::endl;
1128 if (num_blocks_checked % unload_interval == 0) {
1129 m_map->unloadUnreferencedBlocks();
1132 m_map->unloadUnreferencedBlocks();
1134 // Drop references that were added above
1135 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1136 i != loaded_blocks.end(); ++i) {
1138 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1143 m_last_clear_objects_time = m_game_time;
1145 infostream << "ServerEnvironment::clearObjects(): "
1146 << "Finished: Cleared " << num_objs_cleared << " objects"
1147 << " in " << num_blocks_cleared << " blocks" << std::endl;
1150 void ServerEnvironment::step(float dtime)
1152 DSTACK(FUNCTION_NAME);
1154 //TimeTaker timer("ServerEnv step");
1156 /* Step time of day */
1157 stepTimeOfDay(dtime);
1160 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1161 // really matter that much.
1162 static thread_local const float server_step =
1163 g_settings->getFloat("dedicated_server_step");
1164 m_recommended_send_interval = server_step;
1170 m_game_time_fraction_counter += dtime;
1171 u32 inc_i = (u32)m_game_time_fraction_counter;
1172 m_game_time += inc_i;
1173 m_game_time_fraction_counter -= (float)inc_i;
1180 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1181 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1182 i != m_players.end(); ++i) {
1183 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1186 // Ignore disconnected players
1187 if(player->peer_id == 0)
1191 player->move(dtime, this, 100*BS);
1196 Manage active block list
1198 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1199 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1201 Get player block positions
1203 std::vector<v3s16> players_blockpos;
1204 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1205 i != m_players.end(); ++i) {
1206 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1209 // Ignore disconnected players
1210 if (player->peer_id == 0)
1213 PlayerSAO *playersao = player->getPlayerSAO();
1216 v3s16 blockpos = getNodeBlockPos(
1217 floatToInt(playersao->getBasePosition(), BS));
1218 players_blockpos.push_back(blockpos);
1222 Update list of active blocks, collecting changes
1224 static thread_local const s16 active_block_range =
1225 g_settings->getS16("active_block_range");
1226 std::set<v3s16> blocks_removed;
1227 std::set<v3s16> blocks_added;
1228 m_active_blocks.update(players_blockpos, active_block_range,
1229 blocks_removed, blocks_added);
1232 Handle removed blocks
1235 // Convert active objects that are no more in active blocks to static
1236 deactivateFarObjects(false);
1238 for(std::set<v3s16>::iterator
1239 i = blocks_removed.begin();
1240 i != blocks_removed.end(); ++i) {
1243 /* infostream<<"Server: Block " << PP(p)
1244 << " became inactive"<<std::endl; */
1246 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1250 // Set current time as timestamp (and let it set ChangedFlag)
1251 block->setTimestamp(m_game_time);
1258 for(std::set<v3s16>::iterator
1259 i = blocks_added.begin();
1260 i != blocks_added.end(); ++i)
1264 MapBlock *block = m_map->getBlockOrEmerge(p);
1266 m_active_blocks.m_list.erase(p);
1270 activateBlock(block);
1271 /* infostream<<"Server: Block " << PP(p)
1272 << " became active"<<std::endl; */
1277 Mess around in active blocks
1279 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1280 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1282 float dtime = m_cache_nodetimer_interval;
1284 for(std::set<v3s16>::iterator
1285 i = m_active_blocks.m_list.begin();
1286 i != m_active_blocks.m_list.end(); ++i)
1290 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1291 <<") being handled"<<std::endl;*/
1293 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1297 // Reset block usage timer
1298 block->resetUsageTimer();
1300 // Set current time as timestamp
1301 block->setTimestampNoChangedFlag(m_game_time);
1302 // If time has changed much from the one on disk,
1303 // set block to be saved when it is unloaded
1304 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1305 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1306 MOD_REASON_BLOCK_EXPIRED);
1309 std::vector<NodeTimer> elapsed_timers =
1310 block->m_node_timers.step((float)dtime);
1311 if (!elapsed_timers.empty()) {
1313 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1314 i != elapsed_timers.end(); ++i) {
1315 n = block->getNodeNoEx(i->position);
1316 p = i->position + block->getPosRelative();
1317 if (m_script->node_on_timer(p, n, i->elapsed)) {
1318 block->setNodeTimer(NodeTimer(
1319 i->timeout, 0, i->position));
1326 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1328 if(m_active_block_interval_overload_skip > 0){
1329 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1330 m_active_block_interval_overload_skip--;
1333 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1334 TimeTaker timer("modify in active blocks per interval");
1336 // Initialize handling of ActiveBlockModifiers
1337 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1339 for(std::set<v3s16>::iterator
1340 i = m_active_blocks.m_list.begin();
1341 i != m_active_blocks.m_list.end(); ++i)
1345 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1346 <<") being handled"<<std::endl;*/
1348 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1352 // Set current time as timestamp
1353 block->setTimestampNoChangedFlag(m_game_time);
1355 /* Handle ActiveBlockModifiers */
1356 abmhandler.apply(block);
1359 u32 time_ms = timer.stop(true);
1360 u32 max_time_ms = 200;
1361 if(time_ms > max_time_ms){
1362 warningstream<<"active block modifiers took "
1363 <<time_ms<<"ms (longer than "
1364 <<max_time_ms<<"ms)"<<std::endl;
1365 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1370 Step script environment (run global on_step())
1372 m_script->environment_Step(dtime);
1378 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1379 //TimeTaker timer("Step active objects");
1381 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1383 // This helps the objects to send data at the same time
1384 bool send_recommended = false;
1385 m_send_recommended_timer += dtime;
1386 if(m_send_recommended_timer > getSendRecommendedInterval())
1388 m_send_recommended_timer -= getSendRecommendedInterval();
1389 send_recommended = true;
1392 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1393 i != m_active_objects.end(); ++i) {
1394 ServerActiveObject* obj = i->second;
1395 // Don't step if is to be removed or stored statically
1396 if(obj->m_removed || obj->m_pending_deactivation)
1399 obj->step(dtime, send_recommended);
1400 // Read messages from object
1401 while(!obj->m_messages_out.empty())
1403 m_active_object_messages.push(
1404 obj->m_messages_out.front());
1405 obj->m_messages_out.pop();
1411 Manage active objects
1413 if(m_object_management_interval.step(dtime, 0.5))
1415 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1417 Remove objects that satisfy (m_removed && m_known_by_count==0)
1419 removeRemovedObjects();
1423 Manage particle spawner expiration
1425 if (m_particle_management_interval.step(dtime, 1.0)) {
1426 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1427 i != m_particle_spawners.end(); ) {
1428 //non expiring spawners
1429 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1435 if (i->second <= 0.f)
1436 m_particle_spawners.erase(i++);
1443 u32 ServerEnvironment::addParticleSpawner(float exptime)
1445 // Timers with lifetime 0 do not expire
1446 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1449 for (;;) { // look for unused particlespawner id
1451 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1452 if (f == m_particle_spawners.end()) {
1453 m_particle_spawners[id] = time;
1460 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1462 u32 id = addParticleSpawner(exptime);
1463 m_particle_spawner_attachments[id] = attached_id;
1464 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1465 obj->attachParticleSpawner(id);
1470 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1472 m_particle_spawners.erase(id);
1473 std::unordered_map<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1474 if (it != m_particle_spawner_attachments.end()) {
1475 u16 obj_id = (*it).second;
1476 ServerActiveObject *sao = getActiveObject(obj_id);
1477 if (sao != NULL && remove_from_object) {
1478 sao->detachParticleSpawner(id);
1480 m_particle_spawner_attachments.erase(id);
1484 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1486 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1487 return (n != m_active_objects.end() ? n->second : NULL);
1490 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1495 return objects.find(id) == objects.end();
1498 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1500 //try to reuse id's as late as possible
1501 static u16 last_used_id = 0;
1502 u16 startid = last_used_id;
1506 if(isFreeServerActiveObjectId(last_used_id, objects))
1507 return last_used_id;
1509 if(last_used_id == startid)
1514 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1516 assert(object); // Pre-condition
1518 u16 id = addActiveObjectRaw(object, true, 0);
1523 Finds out what new objects have been added to
1524 inside a radius around a position
1526 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1528 std::set<u16> ¤t_objects,
1529 std::queue<u16> &added_objects)
1531 f32 radius_f = radius * BS;
1532 f32 player_radius_f = player_radius * BS;
1534 if (player_radius_f < 0)
1535 player_radius_f = 0;
1537 Go through the object list,
1538 - discard m_removed objects,
1539 - discard objects that are too far away,
1540 - discard objects that are found in current_objects.
1541 - add remaining objects to added_objects
1543 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1544 i != m_active_objects.end(); ++i) {
1548 ServerActiveObject *object = i->second;
1552 // Discard if removed or deactivating
1553 if(object->m_removed || object->m_pending_deactivation)
1556 f32 distance_f = object->getBasePosition().
1557 getDistanceFrom(playersao->getBasePosition());
1558 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1559 // Discard if too far
1560 if (distance_f > player_radius_f && player_radius_f != 0)
1562 } else if (distance_f > radius_f)
1565 // Discard if already on current_objects
1566 std::set<u16>::iterator n;
1567 n = current_objects.find(id);
1568 if(n != current_objects.end())
1570 // Add to added_objects
1571 added_objects.push(id);
1576 Finds out what objects have been removed from
1577 inside a radius around a position
1579 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1581 std::set<u16> ¤t_objects,
1582 std::queue<u16> &removed_objects)
1584 f32 radius_f = radius * BS;
1585 f32 player_radius_f = player_radius * BS;
1587 if (player_radius_f < 0)
1588 player_radius_f = 0;
1590 Go through current_objects; object is removed if:
1591 - object is not found in m_active_objects (this is actually an
1592 error condition; objects should be set m_removed=true and removed
1593 only after all clients have been informed about removal), or
1594 - object has m_removed=true, or
1595 - object is too far away
1597 for(std::set<u16>::iterator
1598 i = current_objects.begin();
1599 i != current_objects.end(); ++i)
1602 ServerActiveObject *object = getActiveObject(id);
1604 if (object == NULL) {
1605 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1606 << " object in current_objects is NULL" << std::endl;
1607 removed_objects.push(id);
1611 if (object->m_removed || object->m_pending_deactivation) {
1612 removed_objects.push(id);
1616 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1617 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1618 if (distance_f <= player_radius_f || player_radius_f == 0)
1620 } else if (distance_f <= radius_f)
1623 // Object is no longer visible
1624 removed_objects.push(id);
1628 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1629 v3s16 blockpos, bool static_exists, v3s16 static_block)
1631 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1635 for (std::map<u16, StaticObject>::iterator
1636 so_it = block->m_static_objects.m_active.begin();
1637 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1638 // Get the ServerActiveObject counterpart to this StaticObject
1639 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it->first);
1640 if (ao_it == m_active_objects.end()) {
1641 // If this ever happens, there must be some kind of nasty bug.
1642 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1643 "Object from MapBlock::m_static_objects::m_active not found "
1644 "in m_active_objects";
1648 ServerActiveObject *sao = ao_it->second;
1649 sao->m_static_exists = static_exists;
1650 sao->m_static_block = static_block;
1654 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1656 if(m_active_object_messages.empty())
1657 return ActiveObjectMessage(0);
1659 ActiveObjectMessage message = m_active_object_messages.front();
1660 m_active_object_messages.pop();
1664 void ServerEnvironment::getSelectedActiveObjects(
1665 const core::line3d<f32> &shootline_on_map,
1666 std::vector<PointedThing> &objects)
1668 std::vector<u16> objectIds;
1669 getObjectsInsideRadius(objectIds, shootline_on_map.start,
1670 shootline_on_map.getLength() + 10.0f);
1671 const v3f line_vector = shootline_on_map.getVector();
1673 for (u32 i = 0; i < objectIds.size(); i++) {
1674 ServerActiveObject* obj = getActiveObject(objectIds[i]);
1676 aabb3f selection_box;
1677 if (!obj->getSelectionBox(&selection_box))
1680 v3f pos = obj->getBasePosition();
1682 aabb3f offsetted_box(selection_box.MinEdge + pos,
1683 selection_box.MaxEdge + pos);
1685 v3f current_intersection;
1686 v3s16 current_normal;
1687 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1688 ¤t_intersection, ¤t_normal)) {
1689 objects.push_back(PointedThing(
1690 (s16) objectIds[i], current_intersection, current_normal,
1691 (current_intersection - shootline_on_map.start).getLengthSQ()));
1697 ************ Private methods *************
1700 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1701 bool set_changed, u32 dtime_s)
1703 assert(object); // Pre-condition
1704 if(object->getId() == 0){
1705 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1708 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1709 <<"no free ids available"<<std::endl;
1710 if(object->environmentDeletes())
1714 object->setId(new_id);
1717 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1718 <<"supplied with id "<<object->getId()<<std::endl;
1721 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1722 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1723 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1724 if(object->environmentDeletes())
1729 if (objectpos_over_limit(object->getBasePosition())) {
1730 v3f p = object->getBasePosition();
1731 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1732 << "object position (" << p.X << "," << p.Y << "," << p.Z
1733 << ") outside maximum range" << std::endl;
1734 if (object->environmentDeletes())
1739 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1740 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1742 m_active_objects[object->getId()] = object;
1744 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1745 <<"Added id="<<object->getId()<<"; there are now "
1746 <<m_active_objects.size()<<" active objects."
1749 // Register reference in scripting api (must be done before post-init)
1750 m_script->addObjectReference(object);
1751 // Post-initialize object
1752 object->addedToEnvironment(dtime_s);
1754 // Add static data to block
1755 if(object->isStaticAllowed())
1757 // Add static object to active static list of the block
1758 v3f objectpos = object->getBasePosition();
1759 std::string staticdata = "";
1760 object->getStaticData(&staticdata);
1761 StaticObject s_obj(object->getType(), objectpos, staticdata);
1762 // Add to the block where the object is located in
1763 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1764 MapBlock *block = m_map->emergeBlock(blockpos);
1766 block->m_static_objects.m_active[object->getId()] = s_obj;
1767 object->m_static_exists = true;
1768 object->m_static_block = blockpos;
1771 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1772 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1774 v3s16 p = floatToInt(objectpos, BS);
1775 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1776 <<"could not emerge block for storing id="<<object->getId()
1777 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1781 return object->getId();
1785 Remove objects that satisfy (m_removed && m_known_by_count==0)
1787 void ServerEnvironment::removeRemovedObjects()
1789 std::vector<u16> objects_to_remove;
1790 for(ServerActiveObjectMap::iterator i = m_active_objects.begin();
1791 i != m_active_objects.end(); ++i) {
1793 ServerActiveObject* obj = i->second;
1794 // This shouldn't happen but check it
1797 infostream<<"NULL object found in ServerEnvironment"
1798 <<" while finding removed objects. id="<<id<<std::endl;
1799 // Id to be removed from m_active_objects
1800 objects_to_remove.push_back(id);
1805 We will delete objects that are marked as removed or thatare
1806 waiting for deletion after deactivation
1808 if (!obj->m_removed && !obj->m_pending_deactivation)
1812 Delete static data from block if is marked as removed
1814 if(obj->m_static_exists && obj->m_removed)
1816 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1818 block->m_static_objects.remove(id);
1819 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1820 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1821 obj->m_static_exists = false;
1823 infostream<<"Failed to emerge block from which an object to "
1824 <<"be removed was loaded from. id="<<id<<std::endl;
1828 // If m_known_by_count > 0, don't actually remove. On some future
1829 // invocation this will be 0, which is when removal will continue.
1830 if(obj->m_known_by_count > 0)
1834 Move static data from active to stored if not marked as removed
1836 if(obj->m_static_exists && !obj->m_removed){
1837 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1839 std::map<u16, StaticObject>::iterator i =
1840 block->m_static_objects.m_active.find(id);
1841 if(i != block->m_static_objects.m_active.end()){
1842 block->m_static_objects.m_stored.push_back(i->second);
1843 block->m_static_objects.m_active.erase(id);
1844 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1845 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1848 infostream<<"Failed to emerge block from which an object to "
1849 <<"be deactivated was loaded from. id="<<id<<std::endl;
1853 // Tell the object about removal
1854 obj->removingFromEnvironment();
1855 // Deregister in scripting api
1856 m_script->removeObjectReference(obj);
1859 if(obj->environmentDeletes())
1862 // Id to be removed from m_active_objects
1863 objects_to_remove.push_back(id);
1865 // Remove references from m_active_objects
1866 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1867 i != objects_to_remove.end(); ++i) {
1868 m_active_objects.erase(*i);
1872 static void print_hexdump(std::ostream &o, const std::string &data)
1874 const int linelength = 16;
1875 for(int l=0; ; l++){
1876 int i0 = linelength * l;
1877 bool at_end = false;
1878 int thislinelength = linelength;
1879 if(i0 + thislinelength > (int)data.size()){
1880 thislinelength = data.size() - i0;
1883 for(int di=0; di<linelength; di++){
1886 if(di<thislinelength)
1887 snprintf(buf, 4, "%.2x ", data[i]);
1889 snprintf(buf, 4, " ");
1893 for(int di=0; di<thislinelength; di++){
1907 Convert stored objects from blocks near the players to active.
1909 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1914 // Ignore if no stored objects (to not set changed flag)
1915 if(block->m_static_objects.m_stored.empty())
1918 verbosestream<<"ServerEnvironment::activateObjects(): "
1919 <<"activating objects of block "<<PP(block->getPos())
1920 <<" ("<<block->m_static_objects.m_stored.size()
1921 <<" objects)"<<std::endl;
1922 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1924 errorstream<<"suspiciously large amount of objects detected: "
1925 <<block->m_static_objects.m_stored.size()<<" in "
1926 <<PP(block->getPos())
1927 <<"; removing all of them."<<std::endl;
1928 // Clear stored list
1929 block->m_static_objects.m_stored.clear();
1930 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1931 MOD_REASON_TOO_MANY_OBJECTS);
1935 // Activate stored objects
1936 std::vector<StaticObject> new_stored;
1937 for (std::vector<StaticObject>::iterator
1938 i = block->m_static_objects.m_stored.begin();
1939 i != block->m_static_objects.m_stored.end(); ++i) {
1940 StaticObject &s_obj = *i;
1942 // Create an active object from the data
1943 ServerActiveObject *obj = ServerActiveObject::create
1944 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1945 // If couldn't create object, store static data back.
1947 errorstream<<"ServerEnvironment::activateObjects(): "
1948 <<"failed to create active object from static object "
1949 <<"in block "<<PP(s_obj.pos/BS)
1950 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1951 print_hexdump(verbosestream, s_obj.data);
1953 new_stored.push_back(s_obj);
1956 verbosestream<<"ServerEnvironment::activateObjects(): "
1957 <<"activated static object pos="<<PP(s_obj.pos/BS)
1958 <<" type="<<(int)s_obj.type<<std::endl;
1959 // This will also add the object to the active static list
1960 addActiveObjectRaw(obj, false, dtime_s);
1962 // Clear stored list
1963 block->m_static_objects.m_stored.clear();
1964 // Add leftover failed stuff to stored list
1965 for(std::vector<StaticObject>::iterator
1966 i = new_stored.begin();
1967 i != new_stored.end(); ++i) {
1968 StaticObject &s_obj = *i;
1969 block->m_static_objects.m_stored.push_back(s_obj);
1972 // Turn the active counterparts of activated objects not pending for
1974 for(std::map<u16, StaticObject>::iterator
1975 i = block->m_static_objects.m_active.begin();
1976 i != block->m_static_objects.m_active.end(); ++i)
1979 ServerActiveObject *object = getActiveObject(id);
1981 object->m_pending_deactivation = false;
1985 Note: Block hasn't really been modified here.
1986 The objects have just been activated and moved from the stored
1987 static list to the active static list.
1988 As such, the block is essentially the same.
1989 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1990 Otherwise there would be a huge amount of unnecessary I/O.
1995 Convert objects that are not standing inside active blocks to static.
1997 If m_known_by_count != 0, active object is not deleted, but static
1998 data is still updated.
2000 If force_delete is set, active object is deleted nevertheless. It
2001 shall only be set so in the destructor of the environment.
2003 If block wasn't generated (not in memory or on disk),
2005 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
2007 std::vector<u16> objects_to_remove;
2008 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
2009 i != m_active_objects.end(); ++i) {
2010 // force_delete might be overriden per object
2011 bool force_delete = _force_delete;
2013 ServerActiveObject* obj = i->second;
2016 // Do not deactivate if static data creation not allowed
2017 if(!force_delete && !obj->isStaticAllowed())
2020 // If pending deactivation, let removeRemovedObjects() do it
2021 if(!force_delete && obj->m_pending_deactivation)
2025 v3f objectpos = obj->getBasePosition();
2027 // The block in which the object resides in
2028 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2030 // If object's static data is stored in a deactivated block and object
2031 // is actually located in an active block, re-save to the block in
2032 // which the object is actually located in.
2034 obj->m_static_exists &&
2035 !m_active_blocks.contains(obj->m_static_block) &&
2036 m_active_blocks.contains(blockpos_o))
2038 v3s16 old_static_block = obj->m_static_block;
2040 // Save to block where object is located
2041 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2043 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2044 <<"Could not save object id="<<id
2045 <<" to it's current block "<<PP(blockpos_o)
2049 std::string staticdata_new = "";
2050 obj->getStaticData(&staticdata_new);
2051 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2052 block->m_static_objects.insert(id, s_obj);
2053 obj->m_static_block = blockpos_o;
2054 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2055 MOD_REASON_STATIC_DATA_ADDED);
2057 // Delete from block where object was located
2058 block = m_map->emergeBlock(old_static_block, false);
2060 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2061 <<"Could not delete object id="<<id
2062 <<" from it's previous block "<<PP(old_static_block)
2066 block->m_static_objects.remove(id);
2067 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2068 MOD_REASON_STATIC_DATA_REMOVED);
2072 // If block is active, don't remove
2073 if(!force_delete && m_active_blocks.contains(blockpos_o))
2076 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2077 <<"deactivating object id="<<id<<" on inactive block "
2078 <<PP(blockpos_o)<<std::endl;
2080 // If known by some client, don't immediately delete.
2081 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2084 Update the static data
2087 if(obj->isStaticAllowed())
2089 // Create new static object
2090 std::string staticdata_new = "";
2091 obj->getStaticData(&staticdata_new);
2092 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2094 bool stays_in_same_block = false;
2095 bool data_changed = true;
2097 if (obj->m_static_exists) {
2098 if (obj->m_static_block == blockpos_o)
2099 stays_in_same_block = true;
2101 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2104 std::map<u16, StaticObject>::iterator n =
2105 block->m_static_objects.m_active.find(id);
2106 if (n != block->m_static_objects.m_active.end()) {
2107 StaticObject static_old = n->second;
2109 float save_movem = obj->getMinimumSavedMovement();
2111 if (static_old.data == staticdata_new &&
2112 (static_old.pos - objectpos).getLength() < save_movem)
2113 data_changed = false;
2115 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2116 <<"id="<<id<<" m_static_exists=true but "
2117 <<"static data doesn't actually exist in "
2118 <<PP(obj->m_static_block)<<std::endl;
2123 bool shall_be_written = (!stays_in_same_block || data_changed);
2125 // Delete old static object
2126 if(obj->m_static_exists)
2128 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2131 block->m_static_objects.remove(id);
2132 obj->m_static_exists = false;
2133 // Only mark block as modified if data changed considerably
2134 if(shall_be_written)
2135 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2136 MOD_REASON_STATIC_DATA_CHANGED);
2140 // Add to the block where the object is located in
2141 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2142 // Get or generate the block
2143 MapBlock *block = NULL;
2145 block = m_map->emergeBlock(blockpos);
2146 } catch(InvalidPositionException &e){
2147 // Handled via NULL pointer
2148 // NOTE: emergeBlock's failure is usually determined by it
2149 // actually returning NULL
2154 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2155 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2156 << " statically but block " << PP(blockpos)
2157 << " already contains "
2158 << block->m_static_objects.m_stored.size()
2160 << " Forcing delete." << std::endl;
2161 force_delete = true;
2163 // If static counterpart already exists in target block,
2165 // This shouldn't happen because the object is removed from
2166 // the previous block before this according to
2167 // obj->m_static_block, but happens rarely for some unknown
2168 // reason. Unsuccessful attempts have been made to find
2170 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2171 warningstream<<"ServerEnv: Performing hack #83274"
2173 block->m_static_objects.remove(id);
2175 // Store static data
2176 u16 store_id = pending_delete ? id : 0;
2177 block->m_static_objects.insert(store_id, s_obj);
2179 // Only mark block as modified if data changed considerably
2180 if(shall_be_written)
2181 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2182 MOD_REASON_STATIC_DATA_CHANGED);
2184 obj->m_static_exists = true;
2185 obj->m_static_block = block->getPos();
2190 v3s16 p = floatToInt(objectpos, BS);
2191 errorstream<<"ServerEnv: Could not find or generate "
2192 <<"a block for storing id="<<obj->getId()
2193 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2200 If known by some client, set pending deactivation.
2201 Otherwise delete it immediately.
2204 if(pending_delete && !force_delete)
2206 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2207 <<"object id="<<id<<" is known by clients"
2208 <<"; not deleting yet"<<std::endl;
2210 obj->m_pending_deactivation = true;
2214 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2215 <<"object id="<<id<<" is not known by clients"
2216 <<"; deleting"<<std::endl;
2218 // Tell the object about removal
2219 obj->removingFromEnvironment();
2220 // Deregister in scripting api
2221 m_script->removeObjectReference(obj);
2223 // Delete active object
2224 if(obj->environmentDeletes())
2226 // Id to be removed from m_active_objects
2227 objects_to_remove.push_back(id);
2230 // Remove references from m_active_objects
2231 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2232 i != objects_to_remove.end(); ++i) {
2233 m_active_objects.erase(*i);
2237 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2238 const std::string &savedir, const Settings &conf)
2241 if (name == "sqlite3")
2242 return new PlayerDatabaseSQLite3(savedir);
2243 else if (name == "dummy")
2244 return new Database_Dummy();
2246 else if (name == "postgresql") {
2247 std::string connect_string = "";
2248 conf.getNoEx("pgsql_player_connection", connect_string);
2249 return new PlayerDatabasePostgreSQL(connect_string);
2252 else if (name == "files")
2253 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2255 throw BaseException(std::string("Database backend ") + name + " not supported.");
2258 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2259 const Settings &cmd_args)
2261 std::string migrate_to = cmd_args.get("migrate-players");
2263 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2264 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2265 errorstream << "Cannot read world.mt!" << std::endl;
2269 if (!world_mt.exists("player_backend")) {
2270 errorstream << "Please specify your current backend in world.mt:"
2272 << " player_backend = {files|sqlite3|postgresql}"
2277 std::string backend = world_mt.get("player_backend");
2278 if (backend == migrate_to) {
2279 errorstream << "Cannot migrate: new backend is same"
2280 << " as the old one" << std::endl;
2284 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2287 if (backend == "files") {
2288 // Create backup directory
2289 fs::CreateDir(players_backup_path);
2293 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2294 game_params.world_path, world_mt);
2295 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2296 game_params.world_path, world_mt);
2298 std::vector<std::string> player_list;
2299 srcdb->listPlayers(player_list);
2300 for (std::vector<std::string>::const_iterator it = player_list.begin();
2301 it != player_list.end(); ++it) {
2302 actionstream << "Migrating player " << it->c_str() << std::endl;
2303 RemotePlayer player(it->c_str(), NULL);
2304 PlayerSAO playerSAO(NULL, &player, 15000, false);
2306 srcdb->loadPlayer(&player, &playerSAO);
2308 playerSAO.finalize(&player, std::set<std::string>());
2309 player.setPlayerSAO(&playerSAO);
2311 dstdb->savePlayer(&player);
2313 // For files source, move player files to backup dir
2314 if (backend == "files") {
2316 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2317 players_backup_path + DIR_DELIM + (*it));
2321 actionstream << "Successfully migrated " << player_list.size() << " players"
2323 world_mt.set("player_backend", migrate_to);
2324 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2325 errorstream << "Failed to update world.mt!" << std::endl;
2327 actionstream << "world.mt updated" << std::endl;
2329 // When migration is finished from file backend, remove players directory if empty
2330 if (backend == "files") {
2331 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2338 } catch (BaseException &e) {
2339 errorstream << "An error occured during migration: " << e.what() << std::endl;