3 Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "serverenvironment.h"
21 #include "content_sao.h"
25 #include "nodemetadata.h"
30 #include "remoteplayer.h"
31 #include "scripting_server.h"
33 #include "voxelalgorithms.h"
34 #include "util/serialize.h"
35 #include "util/basic_macros.h"
36 #include "util/pointedthing.h"
37 #include "threading/mutex_auto_lock.h"
39 #include "gameparams.h"
40 #include "database-dummy.h"
41 #include "database-files.h"
42 #include "database-sqlite3.h"
44 #include "database-postgresql.h"
47 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
49 // A number that is much smaller than the timeout for particle spawners should/could ever be
50 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
56 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
60 // Initialize timer to random value to spread processing
61 float itv = abm->getTriggerInterval();
62 itv = MYMAX(0.001, itv); // No less than 1ms
63 int minval = MYMAX(-0.51*itv, -60); // Clamp to
64 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
65 timer = myrand_range(minval, maxval);
72 void LBMContentMapping::deleteContents()
74 for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
75 it != lbm_list.end(); ++it) {
80 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
82 // Add the lbm_def to the LBMContentMapping.
83 // Unknown names get added to the global NameIdMapping.
84 INodeDefManager *nodedef = gamedef->ndef();
86 lbm_list.push_back(lbm_def);
88 for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
89 it != lbm_def->trigger_contents.end(); ++it) {
90 std::set<content_t> c_ids;
91 bool found = nodedef->getIds(*it, c_ids);
93 content_t c_id = gamedef->allocateUnknownNodeId(*it);
94 if (c_id == CONTENT_IGNORE) {
95 // Seems it can't be allocated.
96 warningstream << "Could not internalize node name \"" << *it
97 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
103 for (std::set<content_t>::const_iterator iit =
104 c_ids.begin(); iit != c_ids.end(); ++iit) {
105 content_t c_id = *iit;
106 map[c_id].push_back(lbm_def);
111 const std::vector<LoadingBlockModifierDef *> *
112 LBMContentMapping::lookup(content_t c) const
114 container_map::const_iterator it = map.find(c);
117 // This first dereferences the iterator, returning
118 // a std::vector<LoadingBlockModifierDef *>
119 // reference, then we convert it to a pointer.
120 return &(it->second);
123 LBMManager::~LBMManager()
125 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
126 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
129 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
130 it != m_lbm_lookup.end(); ++it) {
131 (it->second).deleteContents();
135 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
137 // Precondition, in query mode the map isn't used anymore
138 FATAL_ERROR_IF(m_query_mode == true,
139 "attempted to modify LBMManager in query mode");
141 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
142 throw ModError("Error adding LBM \"" + lbm_def->name +
143 "\": Does not follow naming conventions: "
144 "Only chararacters [a-z0-9_:] are allowed.");
147 m_lbm_defs[lbm_def->name] = lbm_def;
150 void LBMManager::loadIntroductionTimes(const std::string ×,
151 IGameDef *gamedef, u32 now)
156 // Storing it in a map first instead of
157 // handling the stuff directly in the loop
158 // removes all duplicate entries.
159 // TODO make this std::unordered_map
160 std::map<std::string, u32> introduction_times;
163 The introduction times string consists of name~time entries,
164 with each entry terminated by a semicolon. The time is decimal.
169 while ((idx_new = times.find(";", idx)) != std::string::npos) {
170 std::string entry = times.substr(idx, idx_new - idx);
171 std::vector<std::string> components = str_split(entry, '~');
172 if (components.size() != 2)
173 throw SerializationError("Introduction times entry \""
174 + entry + "\" requires exactly one '~'!");
175 const std::string &name = components[0];
176 u32 time = from_string<u32>(components[1]);
177 introduction_times[name] = time;
181 // Put stuff from introduction_times into m_lbm_lookup
182 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
183 it != introduction_times.end(); ++it) {
184 const std::string &name = it->first;
185 u32 time = it->second;
187 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
188 m_lbm_defs.find(name);
189 if (def_it == m_lbm_defs.end()) {
190 // This seems to be an LBM entry for
191 // an LBM we haven't loaded. Discard it.
194 LoadingBlockModifierDef *lbm_def = def_it->second;
195 if (lbm_def->run_at_every_load) {
196 // This seems to be an LBM entry for
197 // an LBM that runs at every load.
198 // Don't add it just yet.
202 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
204 // Erase the entry so that we know later
205 // what elements didn't get put into m_lbm_lookup
206 m_lbm_defs.erase(name);
209 // Now also add the elements from m_lbm_defs to m_lbm_lookup
210 // that weren't added in the previous step.
211 // They are introduced first time to this world,
212 // or are run at every load (introducement time hardcoded to U32_MAX).
214 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
215 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
217 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
218 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
219 if (it->second->run_at_every_load) {
220 lbms_running_always.addLBM(it->second, gamedef);
222 lbms_we_introduce_now.addLBM(it->second, gamedef);
226 // Clear the list, so that we don't delete remaining elements
227 // twice in the destructor
231 std::string LBMManager::createIntroductionTimesString()
233 // Precondition, we must be in query mode
234 FATAL_ERROR_IF(m_query_mode == false,
235 "attempted to query on non fully set up LBMManager");
237 std::ostringstream oss;
238 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
239 it != m_lbm_lookup.end(); ++it) {
240 u32 time = it->first;
241 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
242 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
243 iit != lbm_list.end(); ++iit) {
244 // Don't add if the LBM runs at every load,
245 // then introducement time is hardcoded
246 // and doesn't need to be stored
247 if ((*iit)->run_at_every_load)
249 oss << (*iit)->name << "~" << time << ";";
255 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
257 // Precondition, we need m_lbm_lookup to be initialized
258 FATAL_ERROR_IF(m_query_mode == false,
259 "attempted to query on non fully set up LBMManager");
260 v3s16 pos_of_block = block->getPosRelative();
264 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
265 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
266 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
267 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
269 n = block->getNodeNoEx(pos);
271 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
272 iit != m_lbm_lookup.end(); ++iit) {
273 const std::vector<LoadingBlockModifierDef *> *lbm_list =
274 iit->second.lookup(c);
277 for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
278 lbm_list->begin(); iit != lbm_list->end(); ++iit) {
279 (*iit)->trigger(env, pos + pos_of_block, n);
289 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
292 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
293 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
294 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
297 if (p.getDistanceFrom(p0) <= r) {
304 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
306 std::set<v3s16> &blocks_removed,
307 std::set<v3s16> &blocks_added)
312 std::set<v3s16> newlist = m_forceloaded_list;
313 for(std::vector<v3s16>::iterator i = active_positions.begin();
314 i != active_positions.end(); ++i)
316 fillRadiusBlock(*i, radius, newlist);
320 Find out which blocks on the old list are not on the new list
322 // Go through old list
323 for(std::set<v3s16>::iterator i = m_list.begin();
324 i != m_list.end(); ++i)
327 // If not on new list, it's been removed
328 if(newlist.find(p) == newlist.end())
329 blocks_removed.insert(p);
333 Find out which blocks on the new list are not on the old list
335 // Go through new list
336 for(std::set<v3s16>::iterator i = newlist.begin();
337 i != newlist.end(); ++i)
340 // If not on old list, it's been added
341 if(m_list.find(p) == m_list.end())
342 blocks_added.insert(p);
349 for(std::set<v3s16>::iterator i = newlist.begin();
350 i != newlist.end(); ++i)
361 ServerEnvironment::ServerEnvironment(ServerMap *map,
362 ServerScripting *scriptIface, Server *server,
363 const std::string &path_world):
366 m_script(scriptIface),
368 m_path_world(path_world),
369 m_send_recommended_timer(0),
370 m_active_block_interval_overload_skip(0),
372 m_game_time_fraction_counter(0),
373 m_last_clear_objects_time(0),
374 m_recommended_send_interval(0.1),
375 m_max_lag_estimate(0.1),
376 m_player_database(NULL)
378 // Determine which database backend to use
379 std::string conf_path = path_world + DIR_DELIM + "world.mt";
381 bool succeeded = conf.readConfigFile(conf_path.c_str());
382 if (!succeeded || !conf.exists("player_backend")) {
383 // fall back to files
384 conf.set("player_backend", "files");
385 warningstream << "/!\\ You are using old player file backend. "
386 << "This backend is deprecated and will be removed in next release /!\\"
387 << std::endl << "Switching to SQLite3 or PostgreSQL is advised, "
388 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
390 if (!conf.updateConfigFile(conf_path.c_str())) {
391 errorstream << "ServerEnvironment::ServerEnvironment(): "
392 << "Failed to update world.mt!" << std::endl;
396 std::string name = "";
397 conf.getNoEx("player_backend", name);
398 m_player_database = openPlayerDatabase(name, path_world, conf);
401 ServerEnvironment::~ServerEnvironment()
403 // Clear active block list.
404 // This makes the next one delete all active objects.
405 m_active_blocks.clear();
407 // Convert all objects to static and delete the active objects
408 deactivateFarObjects(true);
413 // Delete ActiveBlockModifiers
414 for (std::vector<ABMWithState>::iterator
415 i = m_abms.begin(); i != m_abms.end(); ++i){
419 // Deallocate players
420 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
421 i != m_players.end(); ++i) {
425 delete m_player_database;
428 Map & ServerEnvironment::getMap()
433 ServerMap & ServerEnvironment::getServerMap()
438 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
440 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
441 i != m_players.end(); ++i) {
442 RemotePlayer *player = *i;
443 if (player->peer_id == peer_id)
449 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
451 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
452 i != m_players.end(); ++i) {
453 RemotePlayer *player = *i;
454 if (strcmp(player->getName(), name) == 0)
460 void ServerEnvironment::addPlayer(RemotePlayer *player)
462 DSTACK(FUNCTION_NAME);
464 Check that peer_ids are unique.
465 Also check that names are unique.
466 Exception: there can be multiple players with peer_id=0
468 // If peer id is non-zero, it has to be unique.
469 if (player->peer_id != 0)
470 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
471 // Name has to be unique.
472 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
474 m_players.push_back(player);
477 void ServerEnvironment::removePlayer(RemotePlayer *player)
479 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
480 it != m_players.end(); ++it) {
481 if ((*it) == player) {
489 bool ServerEnvironment::removePlayerFromDatabase(const std::string &name)
491 return m_player_database->removePlayer(name);
494 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
496 float distance = pos1.getDistanceFrom(pos2);
498 //calculate normalized direction vector
499 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
500 (pos2.Y - pos1.Y)/distance,
501 (pos2.Z - pos1.Z)/distance);
503 //find out if there's a node on path between pos1 and pos2
504 for (float i = 1; i < distance; i += stepsize) {
505 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
506 normalized_vector.Y * i,
507 normalized_vector.Z * i) +pos1,BS);
509 MapNode n = getMap().getNodeNoEx(pos);
511 if(n.param0 != CONTENT_AIR) {
521 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
522 const std::string &str_reason, bool reconnect)
524 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
525 it != m_players.end(); ++it) {
526 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
527 m_server->DenyAccessVerCompliant(player->peer_id,
528 player->protocol_version, reason, str_reason, reconnect);
532 void ServerEnvironment::saveLoadedPlayers()
534 std::string players_path = m_path_world + DIR_DELIM + "players";
535 fs::CreateDir(players_path);
537 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
538 it != m_players.end();
540 if ((*it)->checkModified() ||
541 ((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) {
543 m_player_database->savePlayer(*it);
544 } catch (DatabaseException &e) {
545 errorstream << "Failed to save player " << (*it)->getName() << " exception: "
546 << e.what() << std::endl;
553 void ServerEnvironment::savePlayer(RemotePlayer *player)
556 m_player_database->savePlayer(player);
557 } catch (DatabaseException &e) {
558 errorstream << "Failed to save player " << player->getName() << " exception: "
559 << e.what() << std::endl;
564 PlayerSAO *ServerEnvironment::loadPlayer(RemotePlayer *player, bool *new_player,
565 u16 peer_id, bool is_singleplayer)
567 PlayerSAO *playersao = new PlayerSAO(this, player, peer_id, is_singleplayer);
568 // Create player if it doesn't exist
569 if (!m_player_database->loadPlayer(player, playersao)) {
571 // Set player position
572 infostream << "Server: Finding spawn place for player \""
573 << player->getName() << "\"" << std::endl;
574 playersao->setBasePosition(m_server->findSpawnPos());
576 // Make sure the player is saved
577 player->setModified(true);
579 // If the player exists, ensure that they respawn inside legal bounds
580 // This fixes an assert crash when the player can't be added
581 // to the environment
582 if (objectpos_over_limit(playersao->getBasePosition())) {
583 actionstream << "Respawn position for player \""
584 << player->getName() << "\" outside limits, resetting" << std::endl;
585 playersao->setBasePosition(m_server->findSpawnPos());
589 // Add player to environment
592 /* Clean up old HUD elements from previous sessions */
595 /* Add object to environment */
596 addActiveObject(playersao);
601 void ServerEnvironment::saveMeta()
603 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
605 // Open file and serialize
606 std::ostringstream ss(std::ios_base::binary);
609 args.setU64("game_time", m_game_time);
610 args.setU64("time_of_day", getTimeOfDay());
611 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
612 args.setU64("lbm_introduction_times_version", 1);
613 args.set("lbm_introduction_times",
614 m_lbm_mgr.createIntroductionTimesString());
615 args.setU64("day_count", m_day_count);
619 if(!fs::safeWriteToFile(path, ss.str()))
621 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
623 throw SerializationError("Couldn't save env meta");
627 void ServerEnvironment::loadMeta()
629 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
631 // Open file and deserialize
632 std::ifstream is(path.c_str(), std::ios_base::binary);
634 infostream << "ServerEnvironment::loadMeta(): Failed to open "
635 << path << std::endl;
636 throw SerializationError("Couldn't load env meta");
641 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
642 throw SerializationError("ServerEnvironment::loadMeta(): "
643 "EnvArgsEnd not found!");
647 m_game_time = args.getU64("game_time");
648 } catch (SettingNotFoundException &e) {
649 // Getting this is crucial, otherwise timestamps are useless
650 throw SerializationError("Couldn't load env meta game_time");
653 setTimeOfDay(args.exists("time_of_day") ?
654 // set day to morning by default
655 args.getU64("time_of_day") : 9000);
657 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
658 // If missing, do as if clearObjects was never called
659 args.getU64("last_clear_objects_time") : 0;
661 std::string lbm_introduction_times = "";
663 u64 ver = args.getU64("lbm_introduction_times_version");
665 lbm_introduction_times = args.get("lbm_introduction_times");
667 infostream << "ServerEnvironment::loadMeta(): Non-supported"
668 << " introduction time version " << ver << std::endl;
670 } catch (SettingNotFoundException &e) {
671 // No problem, this is expected. Just continue with an empty string
673 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
675 m_day_count = args.exists("day_count") ?
676 args.getU64("day_count") : 0;
679 void ServerEnvironment::loadDefaultMeta()
681 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
686 ActiveBlockModifier *abm;
688 std::set<content_t> required_neighbors;
694 ServerEnvironment *m_env;
695 std::vector<std::vector<ActiveABM> *> m_aabms;
697 ABMHandler(std::vector<ABMWithState> &abms,
698 float dtime_s, ServerEnvironment *env,
704 INodeDefManager *ndef = env->getGameDef()->ndef();
705 for(std::vector<ABMWithState>::iterator
706 i = abms.begin(); i != abms.end(); ++i) {
707 ActiveBlockModifier *abm = i->abm;
708 float trigger_interval = abm->getTriggerInterval();
709 if(trigger_interval < 0.001)
710 trigger_interval = 0.001;
711 float actual_interval = dtime_s;
714 if(i->timer < trigger_interval)
716 i->timer -= trigger_interval;
717 actual_interval = trigger_interval;
719 float chance = abm->getTriggerChance();
724 if(abm->getSimpleCatchUp()) {
725 float intervals = actual_interval / trigger_interval;
728 aabm.chance = chance / intervals;
732 aabm.chance = chance;
735 std::set<std::string> required_neighbors_s
736 = abm->getRequiredNeighbors();
737 for(std::set<std::string>::iterator
738 i = required_neighbors_s.begin();
739 i != required_neighbors_s.end(); ++i)
741 ndef->getIds(*i, aabm.required_neighbors);
744 std::set<std::string> contents_s = abm->getTriggerContents();
745 for(std::set<std::string>::iterator
746 i = contents_s.begin(); i != contents_s.end(); ++i)
748 std::set<content_t> ids;
749 ndef->getIds(*i, ids);
750 for(std::set<content_t>::const_iterator k = ids.begin();
754 if (c >= m_aabms.size())
755 m_aabms.resize(c + 256, NULL);
757 m_aabms[c] = new std::vector<ActiveABM>;
758 m_aabms[c]->push_back(aabm);
766 for (size_t i = 0; i < m_aabms.size(); i++)
770 // Find out how many objects the given block and its neighbours contain.
771 // Returns the number of objects in the block, and also in 'wider' the
772 // number of objects in the block and all its neighbours. The latter
773 // may an estimate if any neighbours are unloaded.
774 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
777 u32 wider_unknown_count = 0;
778 for(s16 x=-1; x<=1; x++)
779 for(s16 y=-1; y<=1; y++)
780 for(s16 z=-1; z<=1; z++)
782 MapBlock *block2 = map->getBlockNoCreateNoEx(
783 block->getPos() + v3s16(x,y,z));
785 wider_unknown_count++;
788 wider += block2->m_static_objects.m_active.size()
789 + block2->m_static_objects.m_stored.size();
792 u32 active_object_count = block->m_static_objects.m_active.size();
793 u32 wider_known_count = 3*3*3 - wider_unknown_count;
794 wider += wider_unknown_count * wider / wider_known_count;
795 return active_object_count;
798 void apply(MapBlock *block)
800 if(m_aabms.empty() || block->isDummy())
803 ServerMap *map = &m_env->getServerMap();
805 u32 active_object_count_wider;
806 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
807 m_env->m_added_objects = 0;
810 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
811 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
812 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
814 const MapNode &n = block->getNodeUnsafe(p0);
815 content_t c = n.getContent();
817 if (c >= m_aabms.size() || !m_aabms[c])
820 v3s16 p = p0 + block->getPosRelative();
821 for(std::vector<ActiveABM>::iterator
822 i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
823 if(myrand() % i->chance != 0)
827 if(!i->required_neighbors.empty())
830 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
831 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
832 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
837 if (block->isValidPosition(p1)) {
838 // if the neighbor is found on the same map block
839 // get it straight from there
840 const MapNode &n = block->getNodeUnsafe(p1);
843 // otherwise consult the map
844 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
847 std::set<content_t>::const_iterator k;
848 k = i->required_neighbors.find(c);
849 if(k != i->required_neighbors.end()){
853 // No required neighbor found
858 // Call all the trigger variations
859 i->abm->trigger(m_env, p, n);
860 i->abm->trigger(m_env, p, n,
861 active_object_count, active_object_count_wider);
863 // Count surrounding objects again if the abms added any
864 if(m_env->m_added_objects > 0) {
865 active_object_count = countObjects(block, map, active_object_count_wider);
866 m_env->m_added_objects = 0;
873 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
875 // Reset usage timer immediately, otherwise a block that becomes active
876 // again at around the same time as it would normally be unloaded will
877 // get unloaded incorrectly. (I think this still leaves a small possibility
878 // of a race condition between this and server::AsyncRunStep, which only
879 // some kind of synchronisation will fix, but it at least reduces the window
880 // of opportunity for it to break from seconds to nanoseconds)
881 block->resetUsageTimer();
883 // Get time difference
885 u32 stamp = block->getTimestamp();
886 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
887 dtime_s = m_game_time - stamp;
888 dtime_s += additional_dtime;
890 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
891 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
893 // Remove stored static objects if clearObjects was called since block's timestamp
894 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
895 block->m_static_objects.m_stored.clear();
896 // do not set changed flag to avoid unnecessary mapblock writes
899 // Set current time as timestamp
900 block->setTimestampNoChangedFlag(m_game_time);
902 /*infostream<<"ServerEnvironment::activateBlock(): block is "
903 <<dtime_s<<" seconds old."<<std::endl;*/
905 // Activate stored objects
906 activateObjects(block, dtime_s);
908 /* Handle LoadingBlockModifiers */
909 m_lbm_mgr.applyLBMs(this, block, stamp);
912 std::vector<NodeTimer> elapsed_timers =
913 block->m_node_timers.step((float)dtime_s);
914 if (!elapsed_timers.empty()) {
916 for (std::vector<NodeTimer>::iterator
917 i = elapsed_timers.begin();
918 i != elapsed_timers.end(); ++i){
919 n = block->getNodeNoEx(i->position);
920 v3s16 p = i->position + block->getPosRelative();
921 if (m_script->node_on_timer(p, n, i->elapsed))
922 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
926 /* Handle ActiveBlockModifiers */
927 ABMHandler abmhandler(m_abms, dtime_s, this, false);
928 abmhandler.apply(block);
931 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
933 m_abms.push_back(ABMWithState(abm));
936 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
938 m_lbm_mgr.addLBMDef(lbm);
941 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
943 INodeDefManager *ndef = m_server->ndef();
944 MapNode n_old = m_map->getNodeNoEx(p);
947 if (ndef->get(n_old).has_on_destruct)
948 m_script->node_on_destruct(p, n_old);
951 if (!m_map->addNodeWithEvent(p, n))
954 // Update active VoxelManipulator if a mapgen thread
955 m_map->updateVManip(p);
957 // Call post-destructor
958 if (ndef->get(n_old).has_after_destruct)
959 m_script->node_after_destruct(p, n_old);
962 if (ndef->get(n).has_on_construct)
963 m_script->node_on_construct(p, n);
968 bool ServerEnvironment::removeNode(v3s16 p)
970 INodeDefManager *ndef = m_server->ndef();
971 MapNode n_old = m_map->getNodeNoEx(p);
974 if (ndef->get(n_old).has_on_destruct)
975 m_script->node_on_destruct(p, n_old);
978 // This is slightly optimized compared to addNodeWithEvent(air)
979 if (!m_map->removeNodeWithEvent(p))
982 // Update active VoxelManipulator if a mapgen thread
983 m_map->updateVManip(p);
985 // Call post-destructor
986 if (ndef->get(n_old).has_after_destruct)
987 m_script->node_after_destruct(p, n_old);
989 // Air doesn't require constructor
993 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
995 if (!m_map->addNodeWithEvent(p, n, false))
998 // Update active VoxelManipulator if a mapgen thread
999 m_map->updateVManip(p);
1004 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1006 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1007 i != m_active_objects.end(); ++i) {
1008 ServerActiveObject* obj = i->second;
1010 v3f objectpos = obj->getBasePosition();
1011 if (objectpos.getDistanceFrom(pos) > radius)
1013 objects.push_back(id);
1017 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1019 infostream << "ServerEnvironment::clearObjects(): "
1020 << "Removing all active objects" << std::endl;
1021 std::vector<u16> objects_to_remove;
1022 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1023 i != m_active_objects.end(); ++i) {
1024 ServerActiveObject* obj = i->second;
1025 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1028 // Delete static object if block is loaded
1029 if (obj->m_static_exists) {
1030 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1032 block->m_static_objects.remove(id);
1033 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1034 MOD_REASON_CLEAR_ALL_OBJECTS);
1035 obj->m_static_exists = false;
1038 // If known by some client, don't delete immediately
1039 if (obj->m_known_by_count > 0) {
1040 obj->m_pending_deactivation = true;
1041 obj->m_removed = true;
1045 // Tell the object about removal
1046 obj->removingFromEnvironment();
1047 // Deregister in scripting api
1048 m_script->removeObjectReference(obj);
1050 // Delete active object
1051 if (obj->environmentDeletes())
1053 // Id to be removed from m_active_objects
1054 objects_to_remove.push_back(id);
1057 // Remove references from m_active_objects
1058 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1059 i != objects_to_remove.end(); ++i) {
1060 m_active_objects.erase(*i);
1063 // Get list of loaded blocks
1064 std::vector<v3s16> loaded_blocks;
1065 infostream << "ServerEnvironment::clearObjects(): "
1066 << "Listing all loaded blocks" << std::endl;
1067 m_map->listAllLoadedBlocks(loaded_blocks);
1068 infostream << "ServerEnvironment::clearObjects(): "
1069 << "Done listing all loaded blocks: "
1070 << loaded_blocks.size()<<std::endl;
1072 // Get list of loadable blocks
1073 std::vector<v3s16> loadable_blocks;
1074 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1075 infostream << "ServerEnvironment::clearObjects(): "
1076 << "Listing all loadable blocks" << std::endl;
1077 m_map->listAllLoadableBlocks(loadable_blocks);
1078 infostream << "ServerEnvironment::clearObjects(): "
1079 << "Done listing all loadable blocks: "
1080 << loadable_blocks.size() << std::endl;
1082 loadable_blocks = loaded_blocks;
1085 infostream << "ServerEnvironment::clearObjects(): "
1086 << "Now clearing objects in " << loadable_blocks.size()
1087 << " blocks" << std::endl;
1089 // Grab a reference on each loaded block to avoid unloading it
1090 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1091 i != loaded_blocks.end(); ++i) {
1093 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1094 assert(block != NULL);
1098 // Remove objects in all loadable blocks
1099 u32 unload_interval = U32_MAX;
1100 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1101 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1102 unload_interval = MYMAX(unload_interval, 1);
1104 u32 report_interval = loadable_blocks.size() / 10;
1105 u32 num_blocks_checked = 0;
1106 u32 num_blocks_cleared = 0;
1107 u32 num_objs_cleared = 0;
1108 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1109 i != loadable_blocks.end(); ++i) {
1111 MapBlock *block = m_map->emergeBlock(p, false);
1113 errorstream << "ServerEnvironment::clearObjects(): "
1114 << "Failed to emerge block " << PP(p) << std::endl;
1117 u32 num_stored = block->m_static_objects.m_stored.size();
1118 u32 num_active = block->m_static_objects.m_active.size();
1119 if (num_stored != 0 || num_active != 0) {
1120 block->m_static_objects.m_stored.clear();
1121 block->m_static_objects.m_active.clear();
1122 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1123 MOD_REASON_CLEAR_ALL_OBJECTS);
1124 num_objs_cleared += num_stored + num_active;
1125 num_blocks_cleared++;
1127 num_blocks_checked++;
1129 if (report_interval != 0 &&
1130 num_blocks_checked % report_interval == 0) {
1131 float percent = 100.0 * (float)num_blocks_checked /
1132 loadable_blocks.size();
1133 infostream << "ServerEnvironment::clearObjects(): "
1134 << "Cleared " << num_objs_cleared << " objects"
1135 << " in " << num_blocks_cleared << " blocks ("
1136 << percent << "%)" << std::endl;
1138 if (num_blocks_checked % unload_interval == 0) {
1139 m_map->unloadUnreferencedBlocks();
1142 m_map->unloadUnreferencedBlocks();
1144 // Drop references that were added above
1145 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1146 i != loaded_blocks.end(); ++i) {
1148 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1153 m_last_clear_objects_time = m_game_time;
1155 infostream << "ServerEnvironment::clearObjects(): "
1156 << "Finished: Cleared " << num_objs_cleared << " objects"
1157 << " in " << num_blocks_cleared << " blocks" << std::endl;
1160 void ServerEnvironment::step(float dtime)
1162 DSTACK(FUNCTION_NAME);
1164 //TimeTaker timer("ServerEnv step");
1166 /* Step time of day */
1167 stepTimeOfDay(dtime);
1170 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1171 // really matter that much.
1172 static const float server_step = g_settings->getFloat("dedicated_server_step");
1173 m_recommended_send_interval = server_step;
1179 m_game_time_fraction_counter += dtime;
1180 u32 inc_i = (u32)m_game_time_fraction_counter;
1181 m_game_time += inc_i;
1182 m_game_time_fraction_counter -= (float)inc_i;
1189 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1190 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1191 i != m_players.end(); ++i) {
1192 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1195 // Ignore disconnected players
1196 if(player->peer_id == 0)
1200 player->move(dtime, this, 100*BS);
1205 Manage active block list
1207 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1208 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1210 Get player block positions
1212 std::vector<v3s16> players_blockpos;
1213 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1214 i != m_players.end(); ++i) {
1215 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1218 // Ignore disconnected players
1219 if (player->peer_id == 0)
1222 PlayerSAO *playersao = player->getPlayerSAO();
1225 v3s16 blockpos = getNodeBlockPos(
1226 floatToInt(playersao->getBasePosition(), BS));
1227 players_blockpos.push_back(blockpos);
1231 Update list of active blocks, collecting changes
1233 static const s16 active_block_range = g_settings->getS16("active_block_range");
1234 std::set<v3s16> blocks_removed;
1235 std::set<v3s16> blocks_added;
1236 m_active_blocks.update(players_blockpos, active_block_range,
1237 blocks_removed, blocks_added);
1240 Handle removed blocks
1243 // Convert active objects that are no more in active blocks to static
1244 deactivateFarObjects(false);
1246 for(std::set<v3s16>::iterator
1247 i = blocks_removed.begin();
1248 i != blocks_removed.end(); ++i) {
1251 /* infostream<<"Server: Block " << PP(p)
1252 << " became inactive"<<std::endl; */
1254 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1258 // Set current time as timestamp (and let it set ChangedFlag)
1259 block->setTimestamp(m_game_time);
1266 for(std::set<v3s16>::iterator
1267 i = blocks_added.begin();
1268 i != blocks_added.end(); ++i)
1272 MapBlock *block = m_map->getBlockOrEmerge(p);
1274 m_active_blocks.m_list.erase(p);
1278 activateBlock(block);
1279 /* infostream<<"Server: Block " << PP(p)
1280 << " became active"<<std::endl; */
1285 Mess around in active blocks
1287 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1288 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1290 float dtime = m_cache_nodetimer_interval;
1292 for(std::set<v3s16>::iterator
1293 i = m_active_blocks.m_list.begin();
1294 i != m_active_blocks.m_list.end(); ++i)
1298 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1299 <<") being handled"<<std::endl;*/
1301 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1305 // Reset block usage timer
1306 block->resetUsageTimer();
1308 // Set current time as timestamp
1309 block->setTimestampNoChangedFlag(m_game_time);
1310 // If time has changed much from the one on disk,
1311 // set block to be saved when it is unloaded
1312 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1313 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1314 MOD_REASON_BLOCK_EXPIRED);
1317 std::vector<NodeTimer> elapsed_timers =
1318 block->m_node_timers.step((float)dtime);
1319 if (!elapsed_timers.empty()) {
1321 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1322 i != elapsed_timers.end(); ++i) {
1323 n = block->getNodeNoEx(i->position);
1324 p = i->position + block->getPosRelative();
1325 if (m_script->node_on_timer(p, n, i->elapsed)) {
1326 block->setNodeTimer(NodeTimer(
1327 i->timeout, 0, i->position));
1334 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1336 if(m_active_block_interval_overload_skip > 0){
1337 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1338 m_active_block_interval_overload_skip--;
1341 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1342 TimeTaker timer("modify in active blocks per interval");
1344 // Initialize handling of ActiveBlockModifiers
1345 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1347 for(std::set<v3s16>::iterator
1348 i = m_active_blocks.m_list.begin();
1349 i != m_active_blocks.m_list.end(); ++i)
1353 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1354 <<") being handled"<<std::endl;*/
1356 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1360 // Set current time as timestamp
1361 block->setTimestampNoChangedFlag(m_game_time);
1363 /* Handle ActiveBlockModifiers */
1364 abmhandler.apply(block);
1367 u32 time_ms = timer.stop(true);
1368 u32 max_time_ms = 200;
1369 if(time_ms > max_time_ms){
1370 warningstream<<"active block modifiers took "
1371 <<time_ms<<"ms (longer than "
1372 <<max_time_ms<<"ms)"<<std::endl;
1373 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1378 Step script environment (run global on_step())
1380 m_script->environment_Step(dtime);
1386 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1387 //TimeTaker timer("Step active objects");
1389 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1391 // This helps the objects to send data at the same time
1392 bool send_recommended = false;
1393 m_send_recommended_timer += dtime;
1394 if(m_send_recommended_timer > getSendRecommendedInterval())
1396 m_send_recommended_timer -= getSendRecommendedInterval();
1397 send_recommended = true;
1400 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1401 i != m_active_objects.end(); ++i) {
1402 ServerActiveObject* obj = i->second;
1403 // Don't step if is to be removed or stored statically
1404 if(obj->m_removed || obj->m_pending_deactivation)
1407 obj->step(dtime, send_recommended);
1408 // Read messages from object
1409 while(!obj->m_messages_out.empty())
1411 m_active_object_messages.push(
1412 obj->m_messages_out.front());
1413 obj->m_messages_out.pop();
1419 Manage active objects
1421 if(m_object_management_interval.step(dtime, 0.5))
1423 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1425 Remove objects that satisfy (m_removed && m_known_by_count==0)
1427 removeRemovedObjects();
1431 Manage particle spawner expiration
1433 if (m_particle_management_interval.step(dtime, 1.0)) {
1434 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1435 i != m_particle_spawners.end(); ) {
1436 //non expiring spawners
1437 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1443 if (i->second <= 0.f)
1444 m_particle_spawners.erase(i++);
1451 u32 ServerEnvironment::addParticleSpawner(float exptime)
1453 // Timers with lifetime 0 do not expire
1454 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1457 for (;;) { // look for unused particlespawner id
1459 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1460 if (f == m_particle_spawners.end()) {
1461 m_particle_spawners[id] = time;
1468 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1470 u32 id = addParticleSpawner(exptime);
1471 m_particle_spawner_attachments[id] = attached_id;
1472 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1473 obj->attachParticleSpawner(id);
1478 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1480 m_particle_spawners.erase(id);
1481 UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1482 if (it != m_particle_spawner_attachments.end()) {
1483 u16 obj_id = (*it).second;
1484 ServerActiveObject *sao = getActiveObject(obj_id);
1485 if (sao != NULL && remove_from_object) {
1486 sao->detachParticleSpawner(id);
1488 m_particle_spawner_attachments.erase(id);
1492 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1494 ActiveObjectMap::iterator n = m_active_objects.find(id);
1495 return (n != m_active_objects.end() ? n->second : NULL);
1498 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1503 return objects.find(id) == objects.end();
1506 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1508 //try to reuse id's as late as possible
1509 static u16 last_used_id = 0;
1510 u16 startid = last_used_id;
1514 if(isFreeServerActiveObjectId(last_used_id, objects))
1515 return last_used_id;
1517 if(last_used_id == startid)
1522 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1524 assert(object); // Pre-condition
1526 u16 id = addActiveObjectRaw(object, true, 0);
1531 Finds out what new objects have been added to
1532 inside a radius around a position
1534 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1536 std::set<u16> ¤t_objects,
1537 std::queue<u16> &added_objects)
1539 f32 radius_f = radius * BS;
1540 f32 player_radius_f = player_radius * BS;
1542 if (player_radius_f < 0)
1543 player_radius_f = 0;
1545 Go through the object list,
1546 - discard m_removed objects,
1547 - discard objects that are too far away,
1548 - discard objects that are found in current_objects.
1549 - add remaining objects to added_objects
1551 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1552 i != m_active_objects.end(); ++i) {
1556 ServerActiveObject *object = i->second;
1560 // Discard if removed or deactivating
1561 if(object->m_removed || object->m_pending_deactivation)
1564 f32 distance_f = object->getBasePosition().
1565 getDistanceFrom(playersao->getBasePosition());
1566 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1567 // Discard if too far
1568 if (distance_f > player_radius_f && player_radius_f != 0)
1570 } else if (distance_f > radius_f)
1573 // Discard if already on current_objects
1574 std::set<u16>::iterator n;
1575 n = current_objects.find(id);
1576 if(n != current_objects.end())
1578 // Add to added_objects
1579 added_objects.push(id);
1584 Finds out what objects have been removed from
1585 inside a radius around a position
1587 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1589 std::set<u16> ¤t_objects,
1590 std::queue<u16> &removed_objects)
1592 f32 radius_f = radius * BS;
1593 f32 player_radius_f = player_radius * BS;
1595 if (player_radius_f < 0)
1596 player_radius_f = 0;
1598 Go through current_objects; object is removed if:
1599 - object is not found in m_active_objects (this is actually an
1600 error condition; objects should be set m_removed=true and removed
1601 only after all clients have been informed about removal), or
1602 - object has m_removed=true, or
1603 - object is too far away
1605 for(std::set<u16>::iterator
1606 i = current_objects.begin();
1607 i != current_objects.end(); ++i)
1610 ServerActiveObject *object = getActiveObject(id);
1612 if (object == NULL) {
1613 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1614 << " object in current_objects is NULL" << std::endl;
1615 removed_objects.push(id);
1619 if (object->m_removed || object->m_pending_deactivation) {
1620 removed_objects.push(id);
1624 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1625 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1626 if (distance_f <= player_radius_f || player_radius_f == 0)
1628 } else if (distance_f <= radius_f)
1631 // Object is no longer visible
1632 removed_objects.push(id);
1636 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1637 v3s16 blockpos, bool static_exists, v3s16 static_block)
1639 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1643 for (std::map<u16, StaticObject>::iterator
1644 so_it = block->m_static_objects.m_active.begin();
1645 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1646 // Get the ServerActiveObject counterpart to this StaticObject
1647 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1648 if (ao_it == m_active_objects.end()) {
1649 // If this ever happens, there must be some kind of nasty bug.
1650 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1651 "Object from MapBlock::m_static_objects::m_active not found "
1652 "in m_active_objects";
1656 ServerActiveObject *sao = ao_it->second;
1657 sao->m_static_exists = static_exists;
1658 sao->m_static_block = static_block;
1662 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1664 if(m_active_object_messages.empty())
1665 return ActiveObjectMessage(0);
1667 ActiveObjectMessage message = m_active_object_messages.front();
1668 m_active_object_messages.pop();
1673 ************ Private methods *************
1676 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1677 bool set_changed, u32 dtime_s)
1679 assert(object); // Pre-condition
1680 if(object->getId() == 0){
1681 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1684 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1685 <<"no free ids available"<<std::endl;
1686 if(object->environmentDeletes())
1690 object->setId(new_id);
1693 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1694 <<"supplied with id "<<object->getId()<<std::endl;
1697 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1698 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1699 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1700 if(object->environmentDeletes())
1705 if (objectpos_over_limit(object->getBasePosition())) {
1706 v3f p = object->getBasePosition();
1707 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1708 << "object position (" << p.X << "," << p.Y << "," << p.Z
1709 << ") outside maximum range" << std::endl;
1710 if (object->environmentDeletes())
1715 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1716 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1718 m_active_objects[object->getId()] = object;
1720 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1721 <<"Added id="<<object->getId()<<"; there are now "
1722 <<m_active_objects.size()<<" active objects."
1725 // Register reference in scripting api (must be done before post-init)
1726 m_script->addObjectReference(object);
1727 // Post-initialize object
1728 object->addedToEnvironment(dtime_s);
1730 // Add static data to block
1731 if(object->isStaticAllowed())
1733 // Add static object to active static list of the block
1734 v3f objectpos = object->getBasePosition();
1735 std::string staticdata = "";
1736 object->getStaticData(&staticdata);
1737 StaticObject s_obj(object->getType(), objectpos, staticdata);
1738 // Add to the block where the object is located in
1739 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1740 MapBlock *block = m_map->emergeBlock(blockpos);
1742 block->m_static_objects.m_active[object->getId()] = s_obj;
1743 object->m_static_exists = true;
1744 object->m_static_block = blockpos;
1747 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1748 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1750 v3s16 p = floatToInt(objectpos, BS);
1751 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1752 <<"could not emerge block for storing id="<<object->getId()
1753 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1757 return object->getId();
1761 Remove objects that satisfy (m_removed && m_known_by_count==0)
1763 void ServerEnvironment::removeRemovedObjects()
1765 std::vector<u16> objects_to_remove;
1766 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1767 i != m_active_objects.end(); ++i) {
1769 ServerActiveObject* obj = i->second;
1770 // This shouldn't happen but check it
1773 infostream<<"NULL object found in ServerEnvironment"
1774 <<" while finding removed objects. id="<<id<<std::endl;
1775 // Id to be removed from m_active_objects
1776 objects_to_remove.push_back(id);
1781 We will delete objects that are marked as removed or thatare
1782 waiting for deletion after deactivation
1784 if (!obj->m_removed && !obj->m_pending_deactivation)
1788 Delete static data from block if is marked as removed
1790 if(obj->m_static_exists && obj->m_removed)
1792 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1794 block->m_static_objects.remove(id);
1795 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1796 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1797 obj->m_static_exists = false;
1799 infostream<<"Failed to emerge block from which an object to "
1800 <<"be removed was loaded from. id="<<id<<std::endl;
1804 // If m_known_by_count > 0, don't actually remove. On some future
1805 // invocation this will be 0, which is when removal will continue.
1806 if(obj->m_known_by_count > 0)
1810 Move static data from active to stored if not marked as removed
1812 if(obj->m_static_exists && !obj->m_removed){
1813 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1815 std::map<u16, StaticObject>::iterator i =
1816 block->m_static_objects.m_active.find(id);
1817 if(i != block->m_static_objects.m_active.end()){
1818 block->m_static_objects.m_stored.push_back(i->second);
1819 block->m_static_objects.m_active.erase(id);
1820 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1821 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1824 infostream<<"Failed to emerge block from which an object to "
1825 <<"be deactivated was loaded from. id="<<id<<std::endl;
1829 // Tell the object about removal
1830 obj->removingFromEnvironment();
1831 // Deregister in scripting api
1832 m_script->removeObjectReference(obj);
1835 if(obj->environmentDeletes())
1838 // Id to be removed from m_active_objects
1839 objects_to_remove.push_back(id);
1841 // Remove references from m_active_objects
1842 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1843 i != objects_to_remove.end(); ++i) {
1844 m_active_objects.erase(*i);
1848 static void print_hexdump(std::ostream &o, const std::string &data)
1850 const int linelength = 16;
1851 for(int l=0; ; l++){
1852 int i0 = linelength * l;
1853 bool at_end = false;
1854 int thislinelength = linelength;
1855 if(i0 + thislinelength > (int)data.size()){
1856 thislinelength = data.size() - i0;
1859 for(int di=0; di<linelength; di++){
1862 if(di<thislinelength)
1863 snprintf(buf, 4, "%.2x ", data[i]);
1865 snprintf(buf, 4, " ");
1869 for(int di=0; di<thislinelength; di++){
1883 Convert stored objects from blocks near the players to active.
1885 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1890 // Ignore if no stored objects (to not set changed flag)
1891 if(block->m_static_objects.m_stored.empty())
1894 verbosestream<<"ServerEnvironment::activateObjects(): "
1895 <<"activating objects of block "<<PP(block->getPos())
1896 <<" ("<<block->m_static_objects.m_stored.size()
1897 <<" objects)"<<std::endl;
1898 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1900 errorstream<<"suspiciously large amount of objects detected: "
1901 <<block->m_static_objects.m_stored.size()<<" in "
1902 <<PP(block->getPos())
1903 <<"; removing all of them."<<std::endl;
1904 // Clear stored list
1905 block->m_static_objects.m_stored.clear();
1906 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1907 MOD_REASON_TOO_MANY_OBJECTS);
1911 // Activate stored objects
1912 std::vector<StaticObject> new_stored;
1913 for (std::vector<StaticObject>::iterator
1914 i = block->m_static_objects.m_stored.begin();
1915 i != block->m_static_objects.m_stored.end(); ++i) {
1916 StaticObject &s_obj = *i;
1918 // Create an active object from the data
1919 ServerActiveObject *obj = ServerActiveObject::create
1920 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1921 // If couldn't create object, store static data back.
1923 errorstream<<"ServerEnvironment::activateObjects(): "
1924 <<"failed to create active object from static object "
1925 <<"in block "<<PP(s_obj.pos/BS)
1926 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1927 print_hexdump(verbosestream, s_obj.data);
1929 new_stored.push_back(s_obj);
1932 verbosestream<<"ServerEnvironment::activateObjects(): "
1933 <<"activated static object pos="<<PP(s_obj.pos/BS)
1934 <<" type="<<(int)s_obj.type<<std::endl;
1935 // This will also add the object to the active static list
1936 addActiveObjectRaw(obj, false, dtime_s);
1938 // Clear stored list
1939 block->m_static_objects.m_stored.clear();
1940 // Add leftover failed stuff to stored list
1941 for(std::vector<StaticObject>::iterator
1942 i = new_stored.begin();
1943 i != new_stored.end(); ++i) {
1944 StaticObject &s_obj = *i;
1945 block->m_static_objects.m_stored.push_back(s_obj);
1948 // Turn the active counterparts of activated objects not pending for
1950 for(std::map<u16, StaticObject>::iterator
1951 i = block->m_static_objects.m_active.begin();
1952 i != block->m_static_objects.m_active.end(); ++i)
1955 ServerActiveObject *object = getActiveObject(id);
1957 object->m_pending_deactivation = false;
1961 Note: Block hasn't really been modified here.
1962 The objects have just been activated and moved from the stored
1963 static list to the active static list.
1964 As such, the block is essentially the same.
1965 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1966 Otherwise there would be a huge amount of unnecessary I/O.
1971 Convert objects that are not standing inside active blocks to static.
1973 If m_known_by_count != 0, active object is not deleted, but static
1974 data is still updated.
1976 If force_delete is set, active object is deleted nevertheless. It
1977 shall only be set so in the destructor of the environment.
1979 If block wasn't generated (not in memory or on disk),
1981 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1983 std::vector<u16> objects_to_remove;
1984 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1985 i != m_active_objects.end(); ++i) {
1986 // force_delete might be overriden per object
1987 bool force_delete = _force_delete;
1989 ServerActiveObject* obj = i->second;
1992 // Do not deactivate if static data creation not allowed
1993 if(!force_delete && !obj->isStaticAllowed())
1996 // If pending deactivation, let removeRemovedObjects() do it
1997 if(!force_delete && obj->m_pending_deactivation)
2001 v3f objectpos = obj->getBasePosition();
2003 // The block in which the object resides in
2004 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2006 // If object's static data is stored in a deactivated block and object
2007 // is actually located in an active block, re-save to the block in
2008 // which the object is actually located in.
2010 obj->m_static_exists &&
2011 !m_active_blocks.contains(obj->m_static_block) &&
2012 m_active_blocks.contains(blockpos_o))
2014 v3s16 old_static_block = obj->m_static_block;
2016 // Save to block where object is located
2017 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2019 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2020 <<"Could not save object id="<<id
2021 <<" to it's current block "<<PP(blockpos_o)
2025 std::string staticdata_new = "";
2026 obj->getStaticData(&staticdata_new);
2027 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2028 block->m_static_objects.insert(id, s_obj);
2029 obj->m_static_block = blockpos_o;
2030 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2031 MOD_REASON_STATIC_DATA_ADDED);
2033 // Delete from block where object was located
2034 block = m_map->emergeBlock(old_static_block, false);
2036 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2037 <<"Could not delete object id="<<id
2038 <<" from it's previous block "<<PP(old_static_block)
2042 block->m_static_objects.remove(id);
2043 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2044 MOD_REASON_STATIC_DATA_REMOVED);
2048 // If block is active, don't remove
2049 if(!force_delete && m_active_blocks.contains(blockpos_o))
2052 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2053 <<"deactivating object id="<<id<<" on inactive block "
2054 <<PP(blockpos_o)<<std::endl;
2056 // If known by some client, don't immediately delete.
2057 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2060 Update the static data
2063 if(obj->isStaticAllowed())
2065 // Create new static object
2066 std::string staticdata_new = "";
2067 obj->getStaticData(&staticdata_new);
2068 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2070 bool stays_in_same_block = false;
2071 bool data_changed = true;
2073 if (obj->m_static_exists) {
2074 if (obj->m_static_block == blockpos_o)
2075 stays_in_same_block = true;
2077 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2080 std::map<u16, StaticObject>::iterator n =
2081 block->m_static_objects.m_active.find(id);
2082 if (n != block->m_static_objects.m_active.end()) {
2083 StaticObject static_old = n->second;
2085 float save_movem = obj->getMinimumSavedMovement();
2087 if (static_old.data == staticdata_new &&
2088 (static_old.pos - objectpos).getLength() < save_movem)
2089 data_changed = false;
2091 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2092 <<"id="<<id<<" m_static_exists=true but "
2093 <<"static data doesn't actually exist in "
2094 <<PP(obj->m_static_block)<<std::endl;
2099 bool shall_be_written = (!stays_in_same_block || data_changed);
2101 // Delete old static object
2102 if(obj->m_static_exists)
2104 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2107 block->m_static_objects.remove(id);
2108 obj->m_static_exists = false;
2109 // Only mark block as modified if data changed considerably
2110 if(shall_be_written)
2111 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2112 MOD_REASON_STATIC_DATA_CHANGED);
2116 // Add to the block where the object is located in
2117 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2118 // Get or generate the block
2119 MapBlock *block = NULL;
2121 block = m_map->emergeBlock(blockpos);
2122 } catch(InvalidPositionException &e){
2123 // Handled via NULL pointer
2124 // NOTE: emergeBlock's failure is usually determined by it
2125 // actually returning NULL
2130 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2131 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2132 << " statically but block " << PP(blockpos)
2133 << " already contains "
2134 << block->m_static_objects.m_stored.size()
2136 << " Forcing delete." << std::endl;
2137 force_delete = true;
2139 // If static counterpart already exists in target block,
2141 // This shouldn't happen because the object is removed from
2142 // the previous block before this according to
2143 // obj->m_static_block, but happens rarely for some unknown
2144 // reason. Unsuccessful attempts have been made to find
2146 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2147 warningstream<<"ServerEnv: Performing hack #83274"
2149 block->m_static_objects.remove(id);
2151 // Store static data
2152 u16 store_id = pending_delete ? id : 0;
2153 block->m_static_objects.insert(store_id, s_obj);
2155 // Only mark block as modified if data changed considerably
2156 if(shall_be_written)
2157 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2158 MOD_REASON_STATIC_DATA_CHANGED);
2160 obj->m_static_exists = true;
2161 obj->m_static_block = block->getPos();
2166 v3s16 p = floatToInt(objectpos, BS);
2167 errorstream<<"ServerEnv: Could not find or generate "
2168 <<"a block for storing id="<<obj->getId()
2169 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2176 If known by some client, set pending deactivation.
2177 Otherwise delete it immediately.
2180 if(pending_delete && !force_delete)
2182 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2183 <<"object id="<<id<<" is known by clients"
2184 <<"; not deleting yet"<<std::endl;
2186 obj->m_pending_deactivation = true;
2190 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2191 <<"object id="<<id<<" is not known by clients"
2192 <<"; deleting"<<std::endl;
2194 // Tell the object about removal
2195 obj->removingFromEnvironment();
2196 // Deregister in scripting api
2197 m_script->removeObjectReference(obj);
2199 // Delete active object
2200 if(obj->environmentDeletes())
2202 // Id to be removed from m_active_objects
2203 objects_to_remove.push_back(id);
2206 // Remove references from m_active_objects
2207 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2208 i != objects_to_remove.end(); ++i) {
2209 m_active_objects.erase(*i);
2213 PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
2214 const std::string &savedir, const Settings &conf)
2217 if (name == "sqlite3")
2218 return new PlayerDatabaseSQLite3(savedir);
2219 else if (name == "dummy")
2220 return new Database_Dummy();
2222 else if (name == "postgresql") {
2223 std::string connect_string = "";
2224 conf.getNoEx("pgsql_player_connection", connect_string);
2225 return new PlayerDatabasePostgreSQL(connect_string);
2228 else if (name == "files")
2229 return new PlayerDatabaseFiles(savedir + DIR_DELIM + "players");
2231 throw BaseException(std::string("Database backend ") + name + " not supported.");
2234 bool ServerEnvironment::migratePlayersDatabase(const GameParams &game_params,
2235 const Settings &cmd_args)
2237 std::string migrate_to = cmd_args.get("migrate-players");
2239 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
2240 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
2241 errorstream << "Cannot read world.mt!" << std::endl;
2245 if (!world_mt.exists("player_backend")) {
2246 errorstream << "Please specify your current backend in world.mt:"
2248 << " player_backend = {files|sqlite3|postgresql}"
2253 std::string backend = world_mt.get("player_backend");
2254 if (backend == migrate_to) {
2255 errorstream << "Cannot migrate: new backend is same"
2256 << " as the old one" << std::endl;
2260 const std::string players_backup_path = game_params.world_path + DIR_DELIM
2263 if (backend == "files") {
2264 // Create backup directory
2265 fs::CreateDir(players_backup_path);
2269 PlayerDatabase *srcdb = ServerEnvironment::openPlayerDatabase(backend,
2270 game_params.world_path, world_mt);
2271 PlayerDatabase *dstdb = ServerEnvironment::openPlayerDatabase(migrate_to,
2272 game_params.world_path, world_mt);
2274 std::vector<std::string> player_list;
2275 srcdb->listPlayers(player_list);
2276 for (std::vector<std::string>::const_iterator it = player_list.begin();
2277 it != player_list.end(); ++it) {
2278 actionstream << "Migrating player " << it->c_str() << std::endl;
2279 RemotePlayer player(it->c_str(), NULL);
2280 PlayerSAO playerSAO(NULL, &player, 15000, false);
2282 srcdb->loadPlayer(&player, &playerSAO);
2284 playerSAO.finalize(&player, std::set<std::string>());
2285 player.setPlayerSAO(&playerSAO);
2287 dstdb->savePlayer(&player);
2289 // For files source, move player files to backup dir
2290 if (backend == "files") {
2292 game_params.world_path + DIR_DELIM + "players" + DIR_DELIM + (*it),
2293 players_backup_path + DIR_DELIM + (*it));
2297 actionstream << "Successfully migrated " << player_list.size() << " players"
2299 world_mt.set("player_backend", migrate_to);
2300 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
2301 errorstream << "Failed to update world.mt!" << std::endl;
2303 actionstream << "world.mt updated" << std::endl;
2305 // When migration is finished from file backend, remove players directory if empty
2306 if (backend == "files") {
2307 fs::DeleteSingleFileOrEmptyDirectory(game_params.world_path + DIR_DELIM
2314 } catch (BaseException &e) {
2315 errorstream << "An error occured during migration: " << e.what() << std::endl;