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 (auto &it : lbm_list) {
77 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
79 // Add the lbm_def to the LBMContentMapping.
80 // Unknown names get added to the global NameIdMapping.
81 INodeDefManager *nodedef = gamedef->ndef();
83 lbm_list.push_back(lbm_def);
85 for (const std::string &nodeTrigger: lbm_def->trigger_contents) {
86 std::set<content_t> c_ids;
87 bool found = nodedef->getIds(nodeTrigger, c_ids);
89 content_t c_id = gamedef->allocateUnknownNodeId(nodeTrigger);
90 if (c_id == CONTENT_IGNORE) {
91 // Seems it can't be allocated.
92 warningstream << "Could not internalize node name \"" << nodeTrigger
93 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
99 for (content_t c_id : c_ids) {
100 map[c_id].push_back(lbm_def);
105 const std::vector<LoadingBlockModifierDef *> *
106 LBMContentMapping::lookup(content_t c) const
108 lbm_map::const_iterator it = map.find(c);
111 // This first dereferences the iterator, returning
112 // a std::vector<LoadingBlockModifierDef *>
113 // reference, then we convert it to a pointer.
114 return &(it->second);
117 LBMManager::~LBMManager()
119 for (auto &m_lbm_def : m_lbm_defs) {
120 delete m_lbm_def.second;
123 for (auto &it : m_lbm_lookup) {
124 (it.second).deleteContents();
128 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
130 // Precondition, in query mode the map isn't used anymore
131 FATAL_ERROR_IF(m_query_mode == true,
132 "attempted to modify LBMManager in query mode");
134 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
135 throw ModError("Error adding LBM \"" + lbm_def->name +
136 "\": Does not follow naming conventions: "
137 "Only characters [a-z0-9_:] are allowed.");
140 m_lbm_defs[lbm_def->name] = lbm_def;
143 void LBMManager::loadIntroductionTimes(const std::string ×,
144 IGameDef *gamedef, u32 now)
149 // Storing it in a map first instead of
150 // handling the stuff directly in the loop
151 // removes all duplicate entries.
152 // TODO make this std::unordered_map
153 std::map<std::string, u32> introduction_times;
156 The introduction times string consists of name~time entries,
157 with each entry terminated by a semicolon. The time is decimal.
162 while ((idx_new = times.find(";", idx)) != std::string::npos) {
163 std::string entry = times.substr(idx, idx_new - idx);
164 std::vector<std::string> components = str_split(entry, '~');
165 if (components.size() != 2)
166 throw SerializationError("Introduction times entry \""
167 + entry + "\" requires exactly one '~'!");
168 const std::string &name = components[0];
169 u32 time = from_string<u32>(components[1]);
170 introduction_times[name] = time;
174 // Put stuff from introduction_times into m_lbm_lookup
175 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
176 it != introduction_times.end(); ++it) {
177 const std::string &name = it->first;
178 u32 time = it->second;
180 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
181 m_lbm_defs.find(name);
182 if (def_it == m_lbm_defs.end()) {
183 // This seems to be an LBM entry for
184 // an LBM we haven't loaded. Discard it.
187 LoadingBlockModifierDef *lbm_def = def_it->second;
188 if (lbm_def->run_at_every_load) {
189 // This seems to be an LBM entry for
190 // an LBM that runs at every load.
191 // Don't add it just yet.
195 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
197 // Erase the entry so that we know later
198 // what elements didn't get put into m_lbm_lookup
199 m_lbm_defs.erase(name);
202 // Now also add the elements from m_lbm_defs to m_lbm_lookup
203 // that weren't added in the previous step.
204 // They are introduced first time to this world,
205 // or are run at every load (introducement time hardcoded to U32_MAX).
207 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
208 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
210 for (auto &m_lbm_def : m_lbm_defs) {
211 if (m_lbm_def.second->run_at_every_load) {
212 lbms_running_always.addLBM(m_lbm_def.second, gamedef);
214 lbms_we_introduce_now.addLBM(m_lbm_def.second, gamedef);
218 // Clear the list, so that we don't delete remaining elements
219 // twice in the destructor
223 std::string LBMManager::createIntroductionTimesString()
225 // Precondition, we must be in query mode
226 FATAL_ERROR_IF(m_query_mode == false,
227 "attempted to query on non fully set up LBMManager");
229 std::ostringstream oss;
230 for (const auto &it : m_lbm_lookup) {
232 const std::vector<LoadingBlockModifierDef *> &lbm_list = it.second.lbm_list;
233 for (const auto &lbm_def : lbm_list) {
234 // Don't add if the LBM runs at every load,
235 // then introducement time is hardcoded
236 // and doesn't need to be stored
237 if (lbm_def->run_at_every_load)
239 oss << lbm_def->name << "~" << time << ";";
245 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
247 // Precondition, we need m_lbm_lookup to be initialized
248 FATAL_ERROR_IF(m_query_mode == false,
249 "attempted to query on non fully set up LBMManager");
250 v3s16 pos_of_block = block->getPosRelative();
254 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
255 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
256 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
257 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
259 n = block->getNodeNoEx(pos);
261 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
262 iit != m_lbm_lookup.end(); ++iit) {
263 const std::vector<LoadingBlockModifierDef *> *lbm_list =
264 iit->second.lookup(c);
267 for (auto lbmdef : *lbm_list) {
268 lbmdef->trigger(env, pos + pos_of_block, n);
278 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
281 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
282 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
283 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
286 if (p.getDistanceFrom(p0) <= r) {
293 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
295 std::set<v3s16> &blocks_removed,
296 std::set<v3s16> &blocks_added)
301 std::set<v3s16> newlist = m_forceloaded_list;
302 for(std::vector<v3s16>::iterator i = active_positions.begin();
303 i != active_positions.end(); ++i)
305 fillRadiusBlock(*i, radius, newlist);
309 Find out which blocks on the old list are not on the new list
311 // Go through old list
312 for(std::set<v3s16>::iterator i = m_list.begin();
313 i != m_list.end(); ++i)
316 // If not on new list, it's been removed
317 if(newlist.find(p) == newlist.end())
318 blocks_removed.insert(p);
322 Find out which blocks on the new list are not on the old list
324 // Go through new list
325 for(std::set<v3s16>::iterator i = newlist.begin();
326 i != newlist.end(); ++i)
329 // If not on old list, it's been added
330 if(m_list.find(p) == m_list.end())
331 blocks_added.insert(p);
338 for(std::set<v3s16>::iterator i = newlist.begin();
339 i != newlist.end(); ++i)
350 ServerEnvironment::ServerEnvironment(ServerMap *map,
351 ServerScripting *scriptIface, Server *server,
352 const std::string &path_world):
355 m_script(scriptIface),
357 m_path_world(path_world)
359 // Determine which database backend to use
360 std::string conf_path = path_world + DIR_DELIM + "world.mt";
362 bool succeeded = conf.readConfigFile(conf_path.c_str());
363 if (!succeeded || !conf.exists("player_backend")) {
364 // fall back to files
365 conf.set("player_backend", "files");
366 warningstream << "/!\\ You are using old player file backend. "
367 << "This backend is deprecated and will be removed in next release /!\\"
368 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
369 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
371 if (!conf.updateConfigFile(conf_path.c_str())) {
372 errorstream << "ServerEnvironment::ServerEnvironment(): "
373 << "Failed to update world.mt!" << std::endl;
377 std::string name = "";
378 conf.getNoEx("player_backend", name);
379 m_player_database = openPlayerDatabase(name, path_world, conf);
382 ServerEnvironment::~ServerEnvironment()
384 // Clear active block list.
385 // This makes the next one delete all active objects.
386 m_active_blocks.clear();
388 // Convert all objects to static and delete the active objects
389 deactivateFarObjects(true);
394 // Delete ActiveBlockModifiers
395 for (std::vector<ABMWithState>::iterator
396 i = m_abms.begin(); i != m_abms.end(); ++i){
400 // Deallocate players
401 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
402 i != m_players.end(); ++i) {
406 delete m_player_database;
409 Map & ServerEnvironment::getMap()
414 ServerMap & ServerEnvironment::getServerMap()
419 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
421 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
422 i != m_players.end(); ++i) {
423 RemotePlayer *player = *i;
424 if (player->peer_id == peer_id)
430 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
432 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
433 i != m_players.end(); ++i) {
434 RemotePlayer *player = *i;
435 if (strcmp(player->getName(), name) == 0)
441 void ServerEnvironment::addPlayer(RemotePlayer *player)
443 DSTACK(FUNCTION_NAME);
445 Check that peer_ids are unique.
446 Also check that names are unique.
447 Exception: there can be multiple players with peer_id=0
449 // If peer id is non-zero, it has to be unique.
450 if (player->peer_id != 0)
451 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
452 // Name has to be unique.
453 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
455 m_players.push_back(player);
458 void ServerEnvironment::removePlayer(RemotePlayer *player)
460 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
461 it != m_players.end(); ++it) {
462 if ((*it) == player) {
470 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
472 return m_player_database->removePlayer(name);
475 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
477 float distance = pos1.getDistanceFrom(pos2);
479 //calculate normalized direction vector
480 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
481 (pos2.Y - pos1.Y)/distance,
482 (pos2.Z - pos1.Z)/distance);
484 //find out if there's a node on path between pos1 and pos2
485 for (float i = 1; i < distance; i += stepsize) {
486 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
487 normalized_vector.Y * i,
488 normalized_vector.Z * i) +pos1,BS);
490 MapNode n = getMap().getNodeNoEx(pos);
492 if(n.param0 != CONTENT_AIR) {
502 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
503 const std::string &str_reason, bool reconnect)
505 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
506 it != m_players.end(); ++it) {
507 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
508 m_server->DenyAccessVerCompliant(player->peer_id,
509 player->protocol_version, reason, str_reason, reconnect);
513 void ServerEnvironment::saveLoadedPlayers()
515 std::string players_path = m_path_world + DIR_DELIM + "players";
516 fs::CreateDir(players_path);
518 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
519 it != m_players.end();
521 if ((*it)->checkModified() ||
522 ((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) {
524 m_player_database->savePlayer(*it);
525 } catch (DatabaseException &e) {
526 errorstream << "Failed to save player " << (*it)->getName() << " exception: "
527 << e.what() << std::endl;
534 void ServerEnvironment::savePlayer(RemotePlayer *player)
537 m_player_database->savePlayer(player);
538 } catch (DatabaseException &e) {
539 errorstream << "Failed to save player " << player->getName() << " exception: "
540 << e.what() << std::endl;
545 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
546 u16 peer_id, bool is_singleplayer)
548 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
549 // Create player if it doesn't exist
550 if (!m_player_database->loadPlayer(player, playersao)) {
552 // Set player position
553 infostream << "Server: Finding spawn place for player \""
554 << player->getName() << "\"" << std::endl;
555 playersao->setBasePosition(m_server->findSpawnPos());
557 // Make sure the player is saved
558 player->setModified(true);
560 // If the player exists, ensure that they respawn inside legal bounds
561 // This fixes an assert crash when the player can't be added
562 // to the environment
563 ServerMap &map = getServerMap();
564 if (map.getMapgenParams()->saoPosOverLimit(playersao->getBasePosition())) {
565 actionstream << "Respawn position for player \""
566 << player->getName() << "\" outside limits, resetting" << std::endl;
567 playersao->setBasePosition(m_server->findSpawnPos());
571 // Add player to environment
574 /* Clean up old HUD elements from previous sessions */
577 /* Add object to environment */
578 addActiveObject(playersao);
583 void ServerEnvironment::saveMeta()
585 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
587 // Open file and serialize
588 std::ostringstream ss(std::ios_base::binary);
591 args.setU64("game_time", m_game_time);
592 args.setU64("time_of_day", getTimeOfDay());
593 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
594 args.setU64("lbm_introduction_times_version", 1);
595 args.set("lbm_introduction_times",
596 m_lbm_mgr.createIntroductionTimesString());
597 args.setU64("day_count", m_day_count);
601 if(!fs::safeWriteToFile(path, ss.str()))
603 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
605 throw SerializationError("Couldn't save env meta");
609 void ServerEnvironment::loadMeta()
611 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
613 // Open file and deserialize
614 std::ifstream is(path.c_str(), std::ios_base::binary);
616 infostream << "ServerEnvironment::loadMeta(): Failed to open "
617 << path << std::endl;
618 throw SerializationError("Couldn't load env meta");
623 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
624 throw SerializationError("ServerEnvironment::loadMeta(): "
625 "EnvArgsEnd not found!");
629 m_game_time = args.getU64("game_time");
630 } catch (SettingNotFoundException &e) {
631 // Getting this is crucial, otherwise timestamps are useless
632 throw SerializationError("Couldn't load env meta game_time");
635 setTimeOfDay(args.exists("time_of_day") ?
636 // set day to early morning by default
637 args.getU64("time_of_day") : 5250);
639 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
640 // If missing, do as if clearObjects was never called
641 args.getU64("last_clear_objects_time") : 0;
643 std::string lbm_introduction_times = "";
645 u64 ver = args.getU64("lbm_introduction_times_version");
647 lbm_introduction_times = args.get("lbm_introduction_times");
649 infostream << "ServerEnvironment::loadMeta(): Non-supported"
650 << " introduction time version " << ver << std::endl;
652 } catch (SettingNotFoundException &e) {
653 // No problem, this is expected. Just continue with an empty string
655 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
657 m_day_count = args.exists("day_count") ?
658 args.getU64("day_count") : 0;
661 void ServerEnvironment::loadDefaultMeta()
663 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
668 ActiveBlockModifier *abm;
670 std::set<content_t> required_neighbors;
676 ServerEnvironment *m_env;
677 std::vector<std::vector<ActiveABM> *> m_aabms;
679 ABMHandler(std::vector<ABMWithState> &abms,
680 float dtime_s, ServerEnvironment *env,
686 INodeDefManager *ndef = env->getGameDef()->ndef();
687 for(std::vector<ABMWithState>::iterator
688 i = abms.begin(); i != abms.end(); ++i) {
689 ActiveBlockModifier *abm = i->abm;
690 float trigger_interval = abm->getTriggerInterval();
691 if(trigger_interval < 0.001)
692 trigger_interval = 0.001;
693 float actual_interval = dtime_s;
696 if(i->timer < trigger_interval)
698 i->timer -= trigger_interval;
699 actual_interval = trigger_interval;
701 float chance = abm->getTriggerChance();
706 if (abm->getSimpleCatchUp()) {
707 float intervals = actual_interval / trigger_interval;
710 aabm.chance = chance / intervals;
714 aabm.chance = chance;
718 const std::set<std::string> &required_neighbors_s =
719 abm->getRequiredNeighbors();
720 for (std::set<std::string>::iterator rn = required_neighbors_s.begin();
721 rn != required_neighbors_s.end(); ++rn) {
722 ndef->getIds(*rn, aabm.required_neighbors);
726 const std::set<std::string> &contents_s = abm->getTriggerContents();
727 for (std::set<std::string>::iterator cs = contents_s.begin();
728 cs != contents_s.end(); ++cs) {
729 std::set<content_t> ids;
730 ndef->getIds(*cs, ids);
731 for (std::set<content_t>::const_iterator k = ids.begin();
732 k != ids.end(); ++k) {
734 if (c >= m_aabms.size())
735 m_aabms.resize(c + 256, NULL);
737 m_aabms[c] = new std::vector<ActiveABM>;
738 m_aabms[c]->push_back(aabm);
746 for (size_t i = 0; i < m_aabms.size(); i++)
750 // Find out how many objects the given block and its neighbours contain.
751 // Returns the number of objects in the block, and also in 'wider' the
752 // number of objects in the block and all its neighbours. The latter
753 // may an estimate if any neighbours are unloaded.
754 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
757 u32 wider_unknown_count = 0;
758 for(s16 x=-1; x<=1; x++)
759 for(s16 y=-1; y<=1; y++)
760 for(s16 z=-1; z<=1; z++)
762 MapBlock *block2 = map->getBlockNoCreateNoEx(
763 block->getPos() + v3s16(x,y,z));
765 wider_unknown_count++;
768 wider += block2->m_static_objects.m_active.size()
769 + block2->m_static_objects.m_stored.size();
772 u32 active_object_count = block->m_static_objects.m_active.size();
773 u32 wider_known_count = 3*3*3 - wider_unknown_count;
774 wider += wider_unknown_count * wider / wider_known_count;
775 return active_object_count;
778 void apply(MapBlock *block)
780 if(m_aabms.empty() || block->isDummy())
783 ServerMap *map = &m_env->getServerMap();
785 u32 active_object_count_wider;
786 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
787 m_env->m_added_objects = 0;
790 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
791 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
792 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
794 const MapNode &n = block->getNodeUnsafe(p0);
795 content_t c = n.getContent();
797 if (c >= m_aabms.size() || !m_aabms[c])
800 v3s16 p = p0 + block->getPosRelative();
801 for(std::vector<ActiveABM>::iterator
802 i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
803 if(myrand() % i->chance != 0)
807 if(!i->required_neighbors.empty())
810 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
811 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
812 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
817 if (block->isValidPosition(p1)) {
818 // if the neighbor is found on the same map block
819 // get it straight from there
820 const MapNode &n = block->getNodeUnsafe(p1);
823 // otherwise consult the map
824 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
827 std::set<content_t>::const_iterator k;
828 k = i->required_neighbors.find(c);
829 if(k != i->required_neighbors.end()){
833 // No required neighbor found
838 // Call all the trigger variations
839 i->abm->trigger(m_env, p, n);
840 i->abm->trigger(m_env, p, n,
841 active_object_count, active_object_count_wider);
843 // Count surrounding objects again if the abms added any
844 if(m_env->m_added_objects > 0) {
845 active_object_count = countObjects(block, map, active_object_count_wider);
846 m_env->m_added_objects = 0;
853 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
855 // Reset usage timer immediately, otherwise a block that becomes active
856 // again at around the same time as it would normally be unloaded will
857 // get unloaded incorrectly. (I think this still leaves a small possibility
858 // of a race condition between this and server::AsyncRunStep, which only
859 // some kind of synchronisation will fix, but it at least reduces the window
860 // of opportunity for it to break from seconds to nanoseconds)
861 block->resetUsageTimer();
863 // Get time difference
865 u32 stamp = block->getTimestamp();
866 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
867 dtime_s = m_game_time - stamp;
868 dtime_s += additional_dtime;
870 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
871 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
873 // Remove stored static objects if clearObjects was called since block's timestamp
874 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
875 block->m_static_objects.m_stored.clear();
876 // do not set changed flag to avoid unnecessary mapblock writes
879 // Set current time as timestamp
880 block->setTimestampNoChangedFlag(m_game_time);
882 /*infostream<<"ServerEnvironment::activateBlock(): block is "
883 <<dtime_s<<" seconds old."<<std::endl;*/
885 // Activate stored objects
886 activateObjects(block, dtime_s);
888 /* Handle LoadingBlockModifiers */
889 m_lbm_mgr.applyLBMs(this, block, stamp);
892 std::vector<NodeTimer> elapsed_timers =
893 block->m_node_timers.step((float)dtime_s);
894 if (!elapsed_timers.empty()) {
896 for (std::vector<NodeTimer>::iterator
897 i = elapsed_timers.begin();
898 i != elapsed_timers.end(); ++i){
899 n = block->getNodeNoEx(i->position);
900 v3s16 p = i->position + block->getPosRelative();
901 if (m_script->node_on_timer(p, n, i->elapsed))
902 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
906 /* Handle ActiveBlockModifiers */
907 ABMHandler abmhandler(m_abms, dtime_s, this, false);
908 abmhandler.apply(block);
911 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
913 m_abms.push_back(ABMWithState(abm));
916 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
918 m_lbm_mgr.addLBMDef(lbm);
921 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
923 INodeDefManager *ndef = m_server->ndef();
924 MapNode n_old = m_map->getNodeNoEx(p);
927 if (ndef->get(n_old).has_on_destruct)
928 m_script->node_on_destruct(p, n_old);
931 if (!m_map->addNodeWithEvent(p, n))
934 // Update active VoxelManipulator if a mapgen thread
935 m_map->updateVManip(p);
937 // Call post-destructor
938 if (ndef->get(n_old).has_after_destruct)
939 m_script->node_after_destruct(p, n_old);
942 if (ndef->get(n).has_on_construct)
943 m_script->node_on_construct(p, n);
948 bool ServerEnvironment::removeNode(v3s16 p)
950 INodeDefManager *ndef = m_server->ndef();
951 MapNode n_old = m_map->getNodeNoEx(p);
954 if (ndef->get(n_old).has_on_destruct)
955 m_script->node_on_destruct(p, n_old);
958 // This is slightly optimized compared to addNodeWithEvent(air)
959 if (!m_map->removeNodeWithEvent(p))
962 // Update active VoxelManipulator if a mapgen thread
963 m_map->updateVManip(p);
965 // Call post-destructor
966 if (ndef->get(n_old).has_after_destruct)
967 m_script->node_after_destruct(p, n_old);
969 // Air doesn't require constructor
973 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
975 if (!m_map->addNodeWithEvent(p, n, false))
978 // Update active VoxelManipulator if a mapgen thread
979 m_map->updateVManip(p);
984 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos,
987 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
988 i != m_active_objects.end(); ++i) {
989 ServerActiveObject* obj = i->second;
991 v3f objectpos = obj->getBasePosition();
992 if (objectpos.getDistanceFrom(pos) > radius)
994 objects.push_back(id);
998 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1000 infostream << "ServerEnvironment::clearObjects(): "
1001 << "Removing all active objects" << std::endl;
1002 std::vector<u16> objects_to_remove;
1003 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1004 i != m_active_objects.end(); ++i) {
1005 ServerActiveObject* obj = i->second;
1006 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1009 // Delete static object if block is loaded
1010 if (obj->m_static_exists) {
1011 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1013 block->m_static_objects.remove(id);
1014 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1015 MOD_REASON_CLEAR_ALL_OBJECTS);
1016 obj->m_static_exists = false;
1019 // If known by some client, don't delete immediately
1020 if (obj->m_known_by_count > 0) {
1021 obj->m_pending_deactivation = true;
1022 obj->m_removed = true;
1026 // Tell the object about removal
1027 obj->removingFromEnvironment();
1028 // Deregister in scripting api
1029 m_script->removeObjectReference(obj);
1031 // Delete active object
1032 if (obj->environmentDeletes())
1034 // Id to be removed from m_active_objects
1035 objects_to_remove.push_back(id);
1038 // Remove references from m_active_objects
1039 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1040 i != objects_to_remove.end(); ++i) {
1041 m_active_objects.erase(*i);
1044 // Get list of loaded blocks
1045 std::vector<v3s16> loaded_blocks;
1046 infostream << "ServerEnvironment::clearObjects(): "
1047 << "Listing all loaded blocks" << std::endl;
1048 m_map->listAllLoadedBlocks(loaded_blocks);
1049 infostream << "ServerEnvironment::clearObjects(): "
1050 << "Done listing all loaded blocks: "
1051 << loaded_blocks.size()<<std::endl;
1053 // Get list of loadable blocks
1054 std::vector<v3s16> loadable_blocks;
1055 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1056 infostream << "ServerEnvironment::clearObjects(): "
1057 << "Listing all loadable blocks" << std::endl;
1058 m_map->listAllLoadableBlocks(loadable_blocks);
1059 infostream << "ServerEnvironment::clearObjects(): "
1060 << "Done listing all loadable blocks: "
1061 << loadable_blocks.size() << std::endl;
1063 loadable_blocks = loaded_blocks;
1066 infostream << "ServerEnvironment::clearObjects(): "
1067 << "Now clearing objects in " << loadable_blocks.size()
1068 << " blocks" << std::endl;
1070 // Grab a reference on each loaded block to avoid unloading it
1071 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1072 i != loaded_blocks.end(); ++i) {
1074 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1075 assert(block != NULL);
1079 // Remove objects in all loadable blocks
1080 u32 unload_interval = U32_MAX;
1081 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1082 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1083 unload_interval = MYMAX(unload_interval, 1);
1085 u32 report_interval = loadable_blocks.size() / 10;
1086 u32 num_blocks_checked = 0;
1087 u32 num_blocks_cleared = 0;
1088 u32 num_objs_cleared = 0;
1089 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1090 i != loadable_blocks.end(); ++i) {
1092 MapBlock *block = m_map->emergeBlock(p, false);
1094 errorstream << "ServerEnvironment::clearObjects(): "
1095 << "Failed to emerge block " << PP(p) << std::endl;
1098 u32 num_stored = block->m_static_objects.m_stored.size();
1099 u32 num_active = block->m_static_objects.m_active.size();
1100 if (num_stored != 0 || num_active != 0) {
1101 block->m_static_objects.m_stored.clear();
1102 block->m_static_objects.m_active.clear();
1103 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1104 MOD_REASON_CLEAR_ALL_OBJECTS);
1105 num_objs_cleared += num_stored + num_active;
1106 num_blocks_cleared++;
1108 num_blocks_checked++;
1110 if (report_interval != 0 &&
1111 num_blocks_checked % report_interval == 0) {
1112 float percent = 100.0 * (float)num_blocks_checked /
1113 loadable_blocks.size();
1114 infostream << "ServerEnvironment::clearObjects(): "
1115 << "Cleared " << num_objs_cleared << " objects"
1116 << " in " << num_blocks_cleared << " blocks ("
1117 << percent << "%)" << std::endl;
1119 if (num_blocks_checked % unload_interval == 0) {
1120 m_map->unloadUnreferencedBlocks();
1123 m_map->unloadUnreferencedBlocks();
1125 // Drop references that were added above
1126 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1127 i != loaded_blocks.end(); ++i) {
1129 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1134 m_last_clear_objects_time = m_game_time;
1136 infostream << "ServerEnvironment::clearObjects(): "
1137 << "Finished: Cleared " << num_objs_cleared << " objects"
1138 << " in " << num_blocks_cleared << " blocks" << std::endl;
1141 void ServerEnvironment::step(float dtime)
1143 DSTACK(FUNCTION_NAME);
1145 //TimeTaker timer("ServerEnv step");
1147 /* Step time of day */
1148 stepTimeOfDay(dtime);
1151 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1152 // really matter that much.
1153 static thread_local const float server_step =
1154 g_settings->getFloat("dedicated_server_step");
1155 m_recommended_send_interval = server_step;
1161 m_game_time_fraction_counter += dtime;
1162 u32 inc_i = (u32)m_game_time_fraction_counter;
1163 m_game_time += inc_i;
1164 m_game_time_fraction_counter -= (float)inc_i;
1171 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1172 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1173 i != m_players.end(); ++i) {
1174 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1177 // Ignore disconnected players
1178 if(player->peer_id == 0)
1182 player->move(dtime, this, 100*BS);
1187 Manage active block list
1189 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1190 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1192 Get player block positions
1194 std::vector<v3s16> players_blockpos;
1195 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1196 i != m_players.end(); ++i) {
1197 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1200 // Ignore disconnected players
1201 if (player->peer_id == 0)
1204 PlayerSAO *playersao = player->getPlayerSAO();
1207 v3s16 blockpos = getNodeBlockPos(
1208 floatToInt(playersao->getBasePosition(), BS));
1209 players_blockpos.push_back(blockpos);
1213 Update list of active blocks, collecting changes
1215 static thread_local const s16 active_block_range =
1216 g_settings->getS16("active_block_range");
1217 std::set<v3s16> blocks_removed;
1218 std::set<v3s16> blocks_added;
1219 m_active_blocks.update(players_blockpos, active_block_range,
1220 blocks_removed, blocks_added);
1223 Handle removed blocks
1226 // Convert active objects that are no more in active blocks to static
1227 deactivateFarObjects(false);
1229 for(std::set<v3s16>::iterator
1230 i = blocks_removed.begin();
1231 i != blocks_removed.end(); ++i) {
1234 /* infostream<<"Server: Block " << PP(p)
1235 << " became inactive"<<std::endl; */
1237 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1241 // Set current time as timestamp (and let it set ChangedFlag)
1242 block->setTimestamp(m_game_time);
1249 for(std::set<v3s16>::iterator
1250 i = blocks_added.begin();
1251 i != blocks_added.end(); ++i)
1255 MapBlock *block = m_map->getBlockOrEmerge(p);
1257 m_active_blocks.m_list.erase(p);
1261 activateBlock(block);
1262 /* infostream<<"Server: Block " << PP(p)
1263 << " became active"<<std::endl; */
1268 Mess around in active blocks
1270 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1271 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1273 float dtime = m_cache_nodetimer_interval;
1275 for(std::set<v3s16>::iterator
1276 i = m_active_blocks.m_list.begin();
1277 i != m_active_blocks.m_list.end(); ++i)
1281 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1282 <<") being handled"<<std::endl;*/
1284 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1288 // Reset block usage timer
1289 block->resetUsageTimer();
1291 // Set current time as timestamp
1292 block->setTimestampNoChangedFlag(m_game_time);
1293 // If time has changed much from the one on disk,
1294 // set block to be saved when it is unloaded
1295 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1296 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1297 MOD_REASON_BLOCK_EXPIRED);
1300 std::vector<NodeTimer> elapsed_timers =
1301 block->m_node_timers.step((float)dtime);
1302 if (!elapsed_timers.empty()) {
1304 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1305 i != elapsed_timers.end(); ++i) {
1306 n = block->getNodeNoEx(i->position);
1307 p = i->position + block->getPosRelative();
1308 if (m_script->node_on_timer(p, n, i->elapsed)) {
1309 block->setNodeTimer(NodeTimer(
1310 i->timeout, 0, i->position));
1317 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1319 if(m_active_block_interval_overload_skip > 0){
1320 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1321 m_active_block_interval_overload_skip--;
1324 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1325 TimeTaker timer("modify in active blocks per interval");
1327 // Initialize handling of ActiveBlockModifiers
1328 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1330 for(std::set<v3s16>::iterator
1331 i = m_active_blocks.m_list.begin();
1332 i != m_active_blocks.m_list.end(); ++i)
1336 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1337 <<") being handled"<<std::endl;*/
1339 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1343 // Set current time as timestamp
1344 block->setTimestampNoChangedFlag(m_game_time);
1346 /* Handle ActiveBlockModifiers */
1347 abmhandler.apply(block);
1350 u32 time_ms = timer.stop(true);
1351 u32 max_time_ms = 200;
1352 if(time_ms > max_time_ms){
1353 warningstream<<"active block modifiers took "
1354 <<time_ms<<"ms (longer than "
1355 <<max_time_ms<<"ms)"<<std::endl;
1356 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1361 Step script environment (run global on_step())
1363 m_script->environment_Step(dtime);
1369 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1370 //TimeTaker timer("Step active objects");
1372 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1374 // This helps the objects to send data at the same time
1375 bool send_recommended = false;
1376 m_send_recommended_timer += dtime;
1377 if(m_send_recommended_timer > getSendRecommendedInterval())
1379 m_send_recommended_timer -= getSendRecommendedInterval();
1380 send_recommended = true;
1383 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1384 i != m_active_objects.end(); ++i) {
1385 ServerActiveObject* obj = i->second;
1386 // Don't step if is to be removed or stored statically
1387 if(obj->m_removed || obj->m_pending_deactivation)
1390 obj->step(dtime, send_recommended);
1391 // Read messages from object
1392 while(!obj->m_messages_out.empty())
1394 m_active_object_messages.push(
1395 obj->m_messages_out.front());
1396 obj->m_messages_out.pop();
1402 Manage active objects
1404 if(m_object_management_interval.step(dtime, 0.5))
1406 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1408 Remove objects that satisfy (m_removed && m_known_by_count==0)
1410 removeRemovedObjects();
1414 Manage particle spawner expiration
1416 if (m_particle_management_interval.step(dtime, 1.0)) {
1417 for (std::unordered_map<u32, float>::iterator i = m_particle_spawners.begin();
1418 i != m_particle_spawners.end(); ) {
1419 //non expiring spawners
1420 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1426 if (i->second <= 0.f)
1427 m_particle_spawners.erase(i++);
1434 u32 ServerEnvironment::addParticleSpawner(float exptime)
1436 // Timers with lifetime 0 do not expire
1437 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1440 for (;;) { // look for unused particlespawner id
1442 std::unordered_map<u32, float>::iterator f = m_particle_spawners.find(id);
1443 if (f == m_particle_spawners.end()) {
1444 m_particle_spawners[id] = time;
1451 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1453 u32 id = addParticleSpawner(exptime);
1454 m_particle_spawner_attachments[id] = attached_id;
1455 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1456 obj->attachParticleSpawner(id);
1461 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1463 m_particle_spawners.erase(id);
1464 std::unordered_map<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1465 if (it != m_particle_spawner_attachments.end()) {
1466 u16 obj_id = (*it).second;
1467 ServerActiveObject *sao = getActiveObject(obj_id);
1468 if (sao != NULL && remove_from_object) {
1469 sao->detachParticleSpawner(id);
1471 m_particle_spawner_attachments.erase(id);
1475 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1477 ServerActiveObjectMap::const_iterator n = m_active_objects.find(id);
1478 return (n != m_active_objects.end() ? n->second : NULL);
1481 bool isFreeServerActiveObjectId(u16 id, ServerActiveObjectMap &objects)
1486 return objects.find(id) == objects.end();
1489 u16 getFreeServerActiveObjectId(ServerActiveObjectMap &objects)
1491 //try to reuse id's as late as possible
1492 static u16 last_used_id = 0;
1493 u16 startid = last_used_id;
1497 if(isFreeServerActiveObjectId(last_used_id, objects))
1498 return last_used_id;
1500 if(last_used_id == startid)
1505 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1507 assert(object); // Pre-condition
1509 u16 id = addActiveObjectRaw(object, true, 0);
1514 Finds out what new objects have been added to
1515 inside a radius around a position
1517 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1519 std::set<u16> ¤t_objects,
1520 std::queue<u16> &added_objects)
1522 f32 radius_f = radius * BS;
1523 f32 player_radius_f = player_radius * BS;
1525 if (player_radius_f < 0)
1526 player_radius_f = 0;
1528 Go through the object list,
1529 - discard m_removed objects,
1530 - discard objects that are too far away,
1531 - discard objects that are found in current_objects.
1532 - add remaining objects to added_objects
1534 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
1535 i != m_active_objects.end(); ++i) {
1539 ServerActiveObject *object = i->second;
1543 // Discard if removed or deactivating
1544 if(object->m_removed || object->m_pending_deactivation)
1547 f32 distance_f = object->getBasePosition().
1548 getDistanceFrom(playersao->getBasePosition());
1549 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1550 // Discard if too far
1551 if (distance_f > player_radius_f && player_radius_f != 0)
1553 } else if (distance_f > radius_f)
1556 // Discard if already on current_objects
1557 std::set<u16>::iterator n;
1558 n = current_objects.find(id);
1559 if(n != current_objects.end())
1561 // Add to added_objects
1562 added_objects.push(id);
1567 Finds out what objects have been removed from
1568 inside a radius around a position
1570 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1572 std::set<u16> ¤t_objects,
1573 std::queue<u16> &removed_objects)
1575 f32 radius_f = radius * BS;
1576 f32 player_radius_f = player_radius * BS;
1578 if (player_radius_f < 0)
1579 player_radius_f = 0;
1581 Go through current_objects; object is removed if:
1582 - object is not found in m_active_objects (this is actually an
1583 error condition; objects should be set m_removed=true and removed
1584 only after all clients have been informed about removal), or
1585 - object has m_removed=true, or
1586 - object is too far away
1588 for(std::set<u16>::iterator
1589 i = current_objects.begin();
1590 i != current_objects.end(); ++i)
1593 ServerActiveObject *object = getActiveObject(id);
1595 if (object == NULL) {
1596 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1597 << " object in current_objects is NULL" << std::endl;
1598 removed_objects.push(id);
1602 if (object->m_removed || object->m_pending_deactivation) {
1603 removed_objects.push(id);
1607 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1608 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1609 if (distance_f <= player_radius_f || player_radius_f == 0)
1611 } else if (distance_f <= radius_f)
1614 // Object is no longer visible
1615 removed_objects.push(id);
1619 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1620 v3s16 blockpos, bool static_exists, v3s16 static_block)
1622 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1626 for (std::map<u16, StaticObject>::iterator
1627 so_it = block->m_static_objects.m_active.begin();
1628 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1629 // Get the ServerActiveObject counterpart to this StaticObject
1630 ServerActiveObjectMap::const_iterator ao_it = m_active_objects.find(so_it->first);
1631 if (ao_it == m_active_objects.end()) {
1632 // If this ever happens, there must be some kind of nasty bug.
1633 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1634 "Object from MapBlock::m_static_objects::m_active not found "
1635 "in m_active_objects";
1639 ServerActiveObject *sao = ao_it->second;
1640 sao->m_static_exists = static_exists;
1641 sao->m_static_block = static_block;
1645 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1647 if(m_active_object_messages.empty())
1648 return ActiveObjectMessage(0);
1650 ActiveObjectMessage message = m_active_object_messages.front();
1651 m_active_object_messages.pop();
1655 void ServerEnvironment::getSelectedActiveObjects(
1656 const core::line3d<f32> &shootline_on_map,
1657 std::vector<PointedThing> &objects)
1659 std::vector<u16> objectIds;
1660 getObjectsInsideRadius(objectIds, shootline_on_map.start,
1661 shootline_on_map.getLength() + 10.0f);
1662 const v3f line_vector = shootline_on_map.getVector();
1664 for (u32 i = 0; i < objectIds.size(); i++) {
1665 ServerActiveObject* obj = getActiveObject(objectIds[i]);
1667 aabb3f selection_box;
1668 if (!obj->getSelectionBox(&selection_box))
1671 v3f pos = obj->getBasePosition();
1673 aabb3f offsetted_box(selection_box.MinEdge + pos,
1674 selection_box.MaxEdge + pos);
1676 v3f current_intersection;
1677 v3s16 current_normal;
1678 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
1679 ¤t_intersection, ¤t_normal)) {
1680 objects.push_back(PointedThing(
1681 (s16) objectIds[i], current_intersection, current_normal,
1682 (current_intersection - shootline_on_map.start).getLengthSQ()));
1688 ************ Private methods *************
1691 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1692 bool set_changed, u32 dtime_s)
1694 assert(object); // Pre-condition
1695 if(object->getId() == 0){
1696 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1699 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1700 <<"no free ids available"<<std::endl;
1701 if(object->environmentDeletes())
1705 object->setId(new_id);
1708 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1709 <<"supplied with id "<<object->getId()<<std::endl;
1712 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1713 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1714 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1715 if(object->environmentDeletes())
1720 if (objectpos_over_limit(object->getBasePosition())) {
1721 v3f p = object->getBasePosition();
1722 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1723 << "object position (" << p.X << "," << p.Y << "," << p.Z
1724 << ") outside maximum range" << std::endl;
1725 if (object->environmentDeletes())
1730 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1731 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1733 m_active_objects[object->getId()] = object;
1735 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1736 <<"Added id="<<object->getId()<<"; there are now "
1737 <<m_active_objects.size()<<" active objects."
1740 // Register reference in scripting api (must be done before post-init)
1741 m_script->addObjectReference(object);
1742 // Post-initialize object
1743 object->addedToEnvironment(dtime_s);
1745 // Add static data to block
1746 if(object->isStaticAllowed())
1748 // Add static object to active static list of the block
1749 v3f objectpos = object->getBasePosition();
1750 std::string staticdata = "";
1751 object->getStaticData(&staticdata);
1752 StaticObject s_obj(object->getType(), objectpos, staticdata);
1753 // Add to the block where the object is located in
1754 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1755 MapBlock *block = m_map->emergeBlock(blockpos);
1757 block->m_static_objects.m_active[object->getId()] = s_obj;
1758 object->m_static_exists = true;
1759 object->m_static_block = blockpos;
1762 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1763 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1765 v3s16 p = floatToInt(objectpos, BS);
1766 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1767 <<"could not emerge block for storing id="<<object->getId()
1768 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1772 return object->getId();
1776 Remove objects that satisfy (m_removed && m_known_by_count==0)
1778 void ServerEnvironment::removeRemovedObjects()
1780 std::vector<u16> objects_to_remove;
1781 for(ServerActiveObjectMap::iterator i = m_active_objects.begin();
1782 i != m_active_objects.end(); ++i) {
1784 ServerActiveObject* obj = i->second;
1785 // This shouldn't happen but check it
1788 infostream<<"NULL object found in ServerEnvironment"
1789 <<" while finding removed objects. id="<<id<<std::endl;
1790 // Id to be removed from m_active_objects
1791 objects_to_remove.push_back(id);
1796 We will delete objects that are marked as removed or thatare
1797 waiting for deletion after deactivation
1799 if (!obj->m_removed && !obj->m_pending_deactivation)
1803 Delete static data from block if is marked as removed
1805 if(obj->m_static_exists && obj->m_removed)
1807 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1809 block->m_static_objects.remove(id);
1810 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1811 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1812 obj->m_static_exists = false;
1814 infostream<<"Failed to emerge block from which an object to "
1815 <<"be removed was loaded from. id="<<id<<std::endl;
1819 // If m_known_by_count > 0, don't actually remove. On some future
1820 // invocation this will be 0, which is when removal will continue.
1821 if(obj->m_known_by_count > 0)
1825 Move static data from active to stored if not marked as removed
1827 if(obj->m_static_exists && !obj->m_removed){
1828 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1830 std::map<u16, StaticObject>::iterator i =
1831 block->m_static_objects.m_active.find(id);
1832 if(i != block->m_static_objects.m_active.end()){
1833 block->m_static_objects.m_stored.push_back(i->second);
1834 block->m_static_objects.m_active.erase(id);
1835 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1836 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1839 infostream<<"Failed to emerge block from which an object to "
1840 <<"be deactivated was loaded from. id="<<id<<std::endl;
1844 // Tell the object about removal
1845 obj->removingFromEnvironment();
1846 // Deregister in scripting api
1847 m_script->removeObjectReference(obj);
1850 if(obj->environmentDeletes())
1853 // Id to be removed from m_active_objects
1854 objects_to_remove.push_back(id);
1856 // Remove references from m_active_objects
1857 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1858 i != objects_to_remove.end(); ++i) {
1859 m_active_objects.erase(*i);
1863 static void print_hexdump(std::ostream &o, const std::string &data)
1865 const int linelength = 16;
1866 for(int l=0; ; l++){
1867 int i0 = linelength * l;
1868 bool at_end = false;
1869 int thislinelength = linelength;
1870 if(i0 + thislinelength > (int)data.size()){
1871 thislinelength = data.size() - i0;
1874 for(int di=0; di<linelength; di++){
1877 if(di<thislinelength)
1878 snprintf(buf, 4, "%.2x ", data[i]);
1880 snprintf(buf, 4, " ");
1884 for(int di=0; di<thislinelength; di++){
1898 Convert stored objects from blocks near the players to active.
1900 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1905 // Ignore if no stored objects (to not set changed flag)
1906 if(block->m_static_objects.m_stored.empty())
1909 verbosestream<<"ServerEnvironment::activateObjects(): "
1910 <<"activating objects of block "<<PP(block->getPos())
1911 <<" ("<<block->m_static_objects.m_stored.size()
1912 <<" objects)"<<std::endl;
1913 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1915 errorstream<<"suspiciously large amount of objects detected: "
1916 <<block->m_static_objects.m_stored.size()<<" in "
1917 <<PP(block->getPos())
1918 <<"; removing all of them."<<std::endl;
1919 // Clear stored list
1920 block->m_static_objects.m_stored.clear();
1921 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1922 MOD_REASON_TOO_MANY_OBJECTS);
1926 // Activate stored objects
1927 std::vector<StaticObject> new_stored;
1928 for (std::vector<StaticObject>::iterator
1929 i = block->m_static_objects.m_stored.begin();
1930 i != block->m_static_objects.m_stored.end(); ++i) {
1931 StaticObject &s_obj = *i;
1933 // Create an active object from the data
1934 ServerActiveObject *obj = ServerActiveObject::create
1935 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1936 // If couldn't create object, store static data back.
1938 errorstream<<"ServerEnvironment::activateObjects(): "
1939 <<"failed to create active object from static object "
1940 <<"in block "<<PP(s_obj.pos/BS)
1941 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1942 print_hexdump(verbosestream, s_obj.data);
1944 new_stored.push_back(s_obj);
1947 verbosestream<<"ServerEnvironment::activateObjects(): "
1948 <<"activated static object pos="<<PP(s_obj.pos/BS)
1949 <<" type="<<(int)s_obj.type<<std::endl;
1950 // This will also add the object to the active static list
1951 addActiveObjectRaw(obj, false, dtime_s);
1953 // Clear stored list
1954 block->m_static_objects.m_stored.clear();
1955 // Add leftover failed stuff to stored list
1956 for(std::vector<StaticObject>::iterator
1957 i = new_stored.begin();
1958 i != new_stored.end(); ++i) {
1959 StaticObject &s_obj = *i;
1960 block->m_static_objects.m_stored.push_back(s_obj);
1963 // Turn the active counterparts of activated objects not pending for
1965 for(std::map<u16, StaticObject>::iterator
1966 i = block->m_static_objects.m_active.begin();
1967 i != block->m_static_objects.m_active.end(); ++i)
1970 ServerActiveObject *object = getActiveObject(id);
1972 object->m_pending_deactivation = false;
1976 Note: Block hasn't really been modified here.
1977 The objects have just been activated and moved from the stored
1978 static list to the active static list.
1979 As such, the block is essentially the same.
1980 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1981 Otherwise there would be a huge amount of unnecessary I/O.
1986 Convert objects that are not standing inside active blocks to static.
1988 If m_known_by_count != 0, active object is not deleted, but static
1989 data is still updated.
1991 If force_delete is set, active object is deleted nevertheless. It
1992 shall only be set so in the destructor of the environment.
1994 If block wasn't generated (not in memory or on disk),
1996 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1998 std::vector<u16> objects_to_remove;
1999 for (ServerActiveObjectMap::iterator i = m_active_objects.begin();
2000 i != m_active_objects.end(); ++i) {
2001 // force_delete might be overriden per object
2002 bool force_delete = _force_delete;
2004 ServerActiveObject* obj = i->second;
2007 // Do not deactivate if static data creation not allowed
2008 if(!force_delete && !obj->isStaticAllowed())
2011 // If pending deactivation, let removeRemovedObjects() do it
2012 if(!force_delete && obj->m_pending_deactivation)
2016 v3f objectpos = obj->getBasePosition();
2018 // The block in which the object resides in
2019 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2021 // If object's static data is stored in a deactivated block and object
2022 // is actually located in an active block, re-save to the block in
2023 // which the object is actually located in.
2025 obj->m_static_exists &&
2026 !m_active_blocks.contains(obj->m_static_block) &&
2027 m_active_blocks.contains(blockpos_o))
2029 v3s16 old_static_block = obj->m_static_block;
2031 // Save to block where object is located
2032 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2034 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2035 <<"Could not save object id="<<id
2036 <<" to it's current block "<<PP(blockpos_o)
2040 std::string staticdata_new = "";
2041 obj->getStaticData(&staticdata_new);
2042 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2043 block->m_static_objects.insert(id, s_obj);
2044 obj->m_static_block = blockpos_o;
2045 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2046 MOD_REASON_STATIC_DATA_ADDED);
2048 // Delete from block where object was located
2049 block = m_map->emergeBlock(old_static_block, false);
2051 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2052 <<"Could not delete object id="<<id
2053 <<" from it's previous block "<<PP(old_static_block)
2057 block->m_static_objects.remove(id);
2058 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2059 MOD_REASON_STATIC_DATA_REMOVED);
2063 // If block is active, don't remove
2064 if(!force_delete && m_active_blocks.contains(blockpos_o))
2067 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2068 <<"deactivating object id="<<id<<" on inactive block "
2069 <<PP(blockpos_o)<<std::endl;
2071 // If known by some client, don't immediately delete.
2072 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2075 Update the static data
2078 if(obj->isStaticAllowed())
2080 // Create new static object
2081 std::string staticdata_new = "";
2082 obj->getStaticData(&staticdata_new);
2083 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2085 bool stays_in_same_block = false;
2086 bool data_changed = true;
2088 if (obj->m_static_exists) {
2089 if (obj->m_static_block == blockpos_o)
2090 stays_in_same_block = true;
2092 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2095 std::map<u16, StaticObject>::iterator n =
2096 block->m_static_objects.m_active.find(id);
2097 if (n != block->m_static_objects.m_active.end()) {
2098 StaticObject static_old = n->second;
2100 float save_movem = obj->getMinimumSavedMovement();
2102 if (static_old.data == staticdata_new &&
2103 (static_old.pos - objectpos).getLength() < save_movem)
2104 data_changed = false;
2106 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2107 <<"id="<<id<<" m_static_exists=true but "
2108 <<"static data doesn't actually exist in "
2109 <<PP(obj->m_static_block)<<std::endl;
2114 bool shall_be_written = (!stays_in_same_block || data_changed);
2116 // Delete old static object
2117 if(obj->m_static_exists)
2119 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2122 block->m_static_objects.remove(id);
2123 obj->m_static_exists = false;
2124 // Only mark block as modified if data changed considerably
2125 if(shall_be_written)
2126 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2127 MOD_REASON_STATIC_DATA_CHANGED);
2131 // Add to the block where the object is located in
2132 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2133 // Get or generate the block
2134 MapBlock *block = NULL;
2136 block = m_map->emergeBlock(blockpos);
2137 } catch(InvalidPositionException &e){
2138 // Handled via NULL pointer
2139 // NOTE: emergeBlock's failure is usually determined by it
2140 // actually returning NULL
2145 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2146 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2147 << " statically but block " << PP(blockpos)
2148 << " already contains "
2149 << block->m_static_objects.m_stored.size()
2151 << " Forcing delete." << std::endl;
2152 force_delete = true;
2154 // If static counterpart already exists in target block,
2156 // This shouldn't happen because the object is removed from
2157 // the previous block before this according to
2158 // obj->m_static_block, but happens rarely for some unknown
2159 // reason. Unsuccessful attempts have been made to find
2161 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2162 warningstream<<"ServerEnv: Performing hack #83274"
2164 block->m_static_objects.remove(id);
2166 // Store static data
2167 u16 store_id = pending_delete ? id : 0;
2168 block->m_static_objects.insert(store_id, s_obj);
2170 // Only mark block as modified if data changed considerably
2171 if(shall_be_written)
2172 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2173 MOD_REASON_STATIC_DATA_CHANGED);
2175 obj->m_static_exists = true;
2176 obj->m_static_block = block->getPos();
2181 v3s16 p = floatToInt(objectpos, BS);
2182 errorstream<<"ServerEnv: Could not find or generate "
2183 <<"a block for storing id="<<obj->getId()
2184 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2191 If known by some client, set pending deactivation.
2192 Otherwise delete it immediately.
2195 if(pending_delete && !force_delete)
2197 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2198 <<"object id="<<id<<" is known by clients"
2199 <<"; not deleting yet"<<std::endl;
2201 obj->m_pending_deactivation = true;
2205 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2206 <<"object id="<<id<<" is not known by clients"
2207 <<"; deleting"<<std::endl;
2209 // Tell the object about removal
2210 obj->removingFromEnvironment();
2211 // Deregister in scripting api
2212 m_script->removeObjectReference(obj);
2214 // Delete active object
2215 if(obj->environmentDeletes())
2217 // Id to be removed from m_active_objects
2218 objects_to_remove.push_back(id);
2221 // Remove references from m_active_objects
2222 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2223 i != objects_to_remove.end(); ++i) {
2224 m_active_objects.erase(*i);
2228 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2229 const std::string &savedir, const Settings &conf)
2232 if (name == "sqlite3")
2233 return new PlayerDatabaseSQLite3(savedir);
2234 else if (name == "dummy")
2235 return new Database_Dummy();
2237 else if (name == "postgresql") {
2238 std::string connect_string = "";
2239 conf.getNoEx("pgsql_player_connection", connect_string);
2240 return new PlayerDatabasePostgreSQL(connect_string);
2243 else if (name == "files")
2244 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2246 throw BaseException(std::string("Database backend ") + name + " not supported.");
2249 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2250 const Settings &cmd_args)
2252 std::string migrate_to = cmd_args.get("migrate-players");
2254 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2255 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2256 errorstream << "Cannot read world.mt!" << std::endl;
2260 if (!world_mt.exists("player_backend")) {
2261 errorstream << "Please specify your current backend in world.mt:"
2263 << " player_backend = {files|sqlite3|postgresql}"
2268 std::string backend = world_mt.get("player_backend");
2269 if (backend == migrate_to) {
2270 errorstream << "Cannot migrate: new backend is same"
2271 << " as the old one" << std::endl;
2275 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2278 if (backend == "files") {
2279 // Create backup directory
2280 fs::CreateDir(players_backup_path);
2284 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2285 game_params.world_path, world_mt);
2286 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2287 game_params.world_path, world_mt);
2289 std::vector<std::string> player_list;
2290 srcdb->listPlayers(player_list);
2291 for (std::vector<std::string>::const_iterator it = player_list.begin();
2292 it != player_list.end(); ++it) {
2293 actionstream << "Migrating player " << it->c_str() << std::endl;
2294 RemotePlayer player(it->c_str(), NULL);
2295 PlayerSAO playerSAO(NULL, &player, 15000, false);
2297 srcdb->loadPlayer(&player, &playerSAO);
2299 playerSAO.finalize(&player, std::set<std::string>());
2300 player.setPlayerSAO(&playerSAO);
2302 dstdb->savePlayer(&player);
2304 // For files source, move player files to backup dir
2305 if (backend == "files") {
2307 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2308 players_backup_path + DIR_DELIM + (*it));
2312 actionstream << "Successfully migrated " << player_list.size() << " players"
2314 world_mt.set("player_backend", migrate_to);
2315 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2316 errorstream << "Failed to update world.mt!" << std::endl;
2318 actionstream << "world.mt updated" << std::endl;
2320 // When migration is finished from file backend, remove players directory if empty
2321 if (backend == "files") {
2322 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2329 } catch (BaseException &e) {
2330 errorstream << "An error occured during migration: " << e.what() << std::endl;