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_game.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"
40 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
42 // A number that is much smaller than the timeout for particle spawners should/could ever be
43 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
49 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
53 // Initialize timer to random value to spread processing
54 float itv = abm->getTriggerInterval();
55 itv = MYMAX(0.001, itv); // No less than 1ms
56 int minval = MYMAX(-0.51*itv, -60); // Clamp to
57 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
58 timer = myrand_range(minval, maxval);
65 void LBMContentMapping::deleteContents()
67 for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
68 it != lbm_list.end(); ++it) {
73 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
75 // Add the lbm_def to the LBMContentMapping.
76 // Unknown names get added to the global NameIdMapping.
77 INodeDefManager *nodedef = gamedef->ndef();
79 lbm_list.push_back(lbm_def);
81 for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
82 it != lbm_def->trigger_contents.end(); ++it) {
83 std::set<content_t> c_ids;
84 bool found = nodedef->getIds(*it, c_ids);
86 content_t c_id = gamedef->allocateUnknownNodeId(*it);
87 if (c_id == CONTENT_IGNORE) {
88 // Seems it can't be allocated.
89 warningstream << "Could not internalize node name \"" << *it
90 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
96 for (std::set<content_t>::const_iterator iit =
97 c_ids.begin(); iit != c_ids.end(); ++iit) {
98 content_t c_id = *iit;
99 map[c_id].push_back(lbm_def);
104 const std::vector<LoadingBlockModifierDef *> *
105 LBMContentMapping::lookup(content_t c) const
107 container_map::const_iterator it = map.find(c);
110 // This first dereferences the iterator, returning
111 // a std::vector<LoadingBlockModifierDef *>
112 // reference, then we convert it to a pointer.
113 return &(it->second);
116 LBMManager::~LBMManager()
118 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
119 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
122 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
123 it != m_lbm_lookup.end(); ++it) {
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 chararacters [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 (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
211 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
212 if (it->second->run_at_every_load) {
213 lbms_running_always.addLBM(it->second, gamedef);
215 lbms_we_introduce_now.addLBM(it->second, gamedef);
219 // Clear the list, so that we don't delete remaining elements
220 // twice in the destructor
224 std::string LBMManager::createIntroductionTimesString()
226 // Precondition, we must be in query mode
227 FATAL_ERROR_IF(m_query_mode == false,
228 "attempted to query on non fully set up LBMManager");
230 std::ostringstream oss;
231 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
232 it != m_lbm_lookup.end(); ++it) {
233 u32 time = it->first;
234 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
235 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
236 iit != lbm_list.end(); ++iit) {
237 // Don't add if the LBM runs at every load,
238 // then introducement time is hardcoded
239 // and doesn't need to be stored
240 if ((*iit)->run_at_every_load)
242 oss << (*iit)->name << "~" << time << ";";
248 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
250 // Precondition, we need m_lbm_lookup to be initialized
251 FATAL_ERROR_IF(m_query_mode == false,
252 "attempted to query on non fully set up LBMManager");
253 v3s16 pos_of_block = block->getPosRelative();
257 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
258 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
259 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
260 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
262 n = block->getNodeNoEx(pos);
264 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
265 iit != m_lbm_lookup.end(); ++iit) {
266 const std::vector<LoadingBlockModifierDef *> *lbm_list =
267 iit->second.lookup(c);
270 for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
271 lbm_list->begin(); iit != lbm_list->end(); ++iit) {
272 (*iit)->trigger(env, pos + pos_of_block, n);
282 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
285 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
286 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
287 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
290 if (p.getDistanceFrom(p0) <= r) {
297 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
299 std::set<v3s16> &blocks_removed,
300 std::set<v3s16> &blocks_added)
305 std::set<v3s16> newlist = m_forceloaded_list;
306 for(std::vector<v3s16>::iterator i = active_positions.begin();
307 i != active_positions.end(); ++i)
309 fillRadiusBlock(*i, radius, newlist);
313 Find out which blocks on the old list are not on the new list
315 // Go through old list
316 for(std::set<v3s16>::iterator i = m_list.begin();
317 i != m_list.end(); ++i)
320 // If not on new list, it's been removed
321 if(newlist.find(p) == newlist.end())
322 blocks_removed.insert(p);
326 Find out which blocks on the new list are not on the old list
328 // Go through new list
329 for(std::set<v3s16>::iterator i = newlist.begin();
330 i != newlist.end(); ++i)
333 // If not on old list, it's been added
334 if(m_list.find(p) == m_list.end())
335 blocks_added.insert(p);
342 for(std::set<v3s16>::iterator i = newlist.begin();
343 i != newlist.end(); ++i)
354 ServerEnvironment::ServerEnvironment(ServerMap *map,
355 GameScripting *scriptIface, Server *server,
356 const std::string &path_world) :
358 m_script(scriptIface),
360 m_path_world(path_world),
361 m_send_recommended_timer(0),
362 m_active_block_interval_overload_skip(0),
364 m_game_time_fraction_counter(0),
365 m_last_clear_objects_time(0),
366 m_recommended_send_interval(0.1),
367 m_max_lag_estimate(0.1)
371 ServerEnvironment::~ServerEnvironment()
373 // Clear active block list.
374 // This makes the next one delete all active objects.
375 m_active_blocks.clear();
377 // Convert all objects to static and delete the active objects
378 deactivateFarObjects(true);
383 // Delete ActiveBlockModifiers
384 for (std::vector<ABMWithState>::iterator
385 i = m_abms.begin(); i != m_abms.end(); ++i){
389 // Deallocate players
390 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
391 i != m_players.end(); ++i) {
396 Map & ServerEnvironment::getMap()
401 ServerMap & ServerEnvironment::getServerMap()
406 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
408 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
409 i != m_players.end(); ++i) {
410 RemotePlayer *player = *i;
411 if (player->peer_id == peer_id)
417 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
419 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
420 i != m_players.end(); ++i) {
421 RemotePlayer *player = *i;
422 if (strcmp(player->getName(), name) == 0)
428 void ServerEnvironment::addPlayer(RemotePlayer *player)
430 DSTACK(FUNCTION_NAME);
432 Check that peer_ids are unique.
433 Also check that names are unique.
434 Exception: there can be multiple players with peer_id=0
436 // If peer id is non-zero, it has to be unique.
437 if (player->peer_id != 0)
438 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
439 // Name has to be unique.
440 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
442 m_players.push_back(player);
445 void ServerEnvironment::removePlayer(RemotePlayer *player)
447 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
448 it != m_players.end(); ++it) {
449 if ((*it) == player) {
457 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
459 float distance = pos1.getDistanceFrom(pos2);
461 //calculate normalized direction vector
462 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
463 (pos2.Y - pos1.Y)/distance,
464 (pos2.Z - pos1.Z)/distance);
466 //find out if there's a node on path between pos1 and pos2
467 for (float i = 1; i < distance; i += stepsize) {
468 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
469 normalized_vector.Y * i,
470 normalized_vector.Z * i) +pos1,BS);
472 MapNode n = getMap().getNodeNoEx(pos);
474 if(n.param0 != CONTENT_AIR) {
484 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
485 const std::string &str_reason, bool reconnect)
487 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
488 it != m_players.end(); ++it) {
489 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
490 m_server->DenyAccessVerCompliant(player->peer_id,
491 player->protocol_version, reason, str_reason, reconnect);
495 void ServerEnvironment::saveLoadedPlayers()
497 std::string players_path = m_path_world + DIR_DELIM "players";
498 fs::CreateDir(players_path);
500 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
501 it != m_players.end();
503 if ((*it)->checkModified()) {
504 (*it)->save(players_path, m_server);
509 void ServerEnvironment::savePlayer(RemotePlayer *player)
511 std::string players_path = m_path_world + DIR_DELIM "players";
512 fs::CreateDir(players_path);
514 player->save(players_path, m_server);
517 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao)
519 bool newplayer = false;
521 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
522 std::string path = players_path + playername;
524 RemotePlayer *player = getPlayer(playername.c_str());
526 player = new RemotePlayer("", m_server->idef());
530 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
531 //// Open file and deserialize
532 std::ifstream is(path.c_str(), std::ios_base::binary);
536 player->deSerialize(is, path, sao);
539 if (player->getName() == playername) {
544 path = players_path + playername + itos(i);
548 infostream << "Player file for player " << playername
549 << " not found" << std::endl;
559 player->setModified(false);
563 void ServerEnvironment::saveMeta()
565 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
567 // Open file and serialize
568 std::ostringstream ss(std::ios_base::binary);
571 args.setU64("game_time", m_game_time);
572 args.setU64("time_of_day", getTimeOfDay());
573 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
574 args.setU64("lbm_introduction_times_version", 1);
575 args.set("lbm_introduction_times",
576 m_lbm_mgr.createIntroductionTimesString());
577 args.setU64("day_count", m_day_count);
581 if(!fs::safeWriteToFile(path, ss.str()))
583 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
585 throw SerializationError("Couldn't save env meta");
589 void ServerEnvironment::loadMeta()
591 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
593 // Open file and deserialize
594 std::ifstream is(path.c_str(), std::ios_base::binary);
596 infostream << "ServerEnvironment::loadMeta(): Failed to open "
597 << path << std::endl;
598 throw SerializationError("Couldn't load env meta");
603 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
604 throw SerializationError("ServerEnvironment::loadMeta(): "
605 "EnvArgsEnd not found!");
609 m_game_time = args.getU64("game_time");
610 } catch (SettingNotFoundException &e) {
611 // Getting this is crucial, otherwise timestamps are useless
612 throw SerializationError("Couldn't load env meta game_time");
615 setTimeOfDay(args.exists("time_of_day") ?
616 // set day to morning by default
617 args.getU64("time_of_day") : 9000);
619 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
620 // If missing, do as if clearObjects was never called
621 args.getU64("last_clear_objects_time") : 0;
623 std::string lbm_introduction_times = "";
625 u64 ver = args.getU64("lbm_introduction_times_version");
627 lbm_introduction_times = args.get("lbm_introduction_times");
629 infostream << "ServerEnvironment::loadMeta(): Non-supported"
630 << " introduction time version " << ver << std::endl;
632 } catch (SettingNotFoundException &e) {
633 // No problem, this is expected. Just continue with an empty string
635 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
637 m_day_count = args.exists("day_count") ?
638 args.getU64("day_count") : 0;
641 void ServerEnvironment::loadDefaultMeta()
643 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
648 ActiveBlockModifier *abm;
650 std::set<content_t> required_neighbors;
656 ServerEnvironment *m_env;
657 std::vector<std::vector<ActiveABM> *> m_aabms;
659 ABMHandler(std::vector<ABMWithState> &abms,
660 float dtime_s, ServerEnvironment *env,
666 INodeDefManager *ndef = env->getGameDef()->ndef();
667 for(std::vector<ABMWithState>::iterator
668 i = abms.begin(); i != abms.end(); ++i) {
669 ActiveBlockModifier *abm = i->abm;
670 float trigger_interval = abm->getTriggerInterval();
671 if(trigger_interval < 0.001)
672 trigger_interval = 0.001;
673 float actual_interval = dtime_s;
676 if(i->timer < trigger_interval)
678 i->timer -= trigger_interval;
679 actual_interval = trigger_interval;
681 float chance = abm->getTriggerChance();
686 if(abm->getSimpleCatchUp()) {
687 float intervals = actual_interval / trigger_interval;
690 aabm.chance = chance / intervals;
694 aabm.chance = chance;
697 std::set<std::string> required_neighbors_s
698 = abm->getRequiredNeighbors();
699 for(std::set<std::string>::iterator
700 i = required_neighbors_s.begin();
701 i != required_neighbors_s.end(); ++i)
703 ndef->getIds(*i, aabm.required_neighbors);
706 std::set<std::string> contents_s = abm->getTriggerContents();
707 for(std::set<std::string>::iterator
708 i = contents_s.begin(); i != contents_s.end(); ++i)
710 std::set<content_t> ids;
711 ndef->getIds(*i, ids);
712 for(std::set<content_t>::const_iterator k = ids.begin();
716 if (c >= m_aabms.size())
717 m_aabms.resize(c + 256, NULL);
719 m_aabms[c] = new std::vector<ActiveABM>;
720 m_aabms[c]->push_back(aabm);
728 for (size_t i = 0; i < m_aabms.size(); i++)
732 // Find out how many objects the given block and its neighbours contain.
733 // Returns the number of objects in the block, and also in 'wider' the
734 // number of objects in the block and all its neighbours. The latter
735 // may an estimate if any neighbours are unloaded.
736 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
739 u32 wider_unknown_count = 0;
740 for(s16 x=-1; x<=1; x++)
741 for(s16 y=-1; y<=1; y++)
742 for(s16 z=-1; z<=1; z++)
744 MapBlock *block2 = map->getBlockNoCreateNoEx(
745 block->getPos() + v3s16(x,y,z));
747 wider_unknown_count++;
750 wider += block2->m_static_objects.m_active.size()
751 + block2->m_static_objects.m_stored.size();
754 u32 active_object_count = block->m_static_objects.m_active.size();
755 u32 wider_known_count = 3*3*3 - wider_unknown_count;
756 wider += wider_unknown_count * wider / wider_known_count;
757 return active_object_count;
760 void apply(MapBlock *block)
762 if(m_aabms.empty() || block->isDummy())
765 ServerMap *map = &m_env->getServerMap();
767 u32 active_object_count_wider;
768 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
769 m_env->m_added_objects = 0;
772 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
773 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
774 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
776 const MapNode &n = block->getNodeUnsafe(p0);
777 content_t c = n.getContent();
779 if (c >= m_aabms.size() || !m_aabms[c])
782 v3s16 p = p0 + block->getPosRelative();
783 for(std::vector<ActiveABM>::iterator
784 i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
785 if(myrand() % i->chance != 0)
789 if(!i->required_neighbors.empty())
792 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
793 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
794 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
799 if (block->isValidPosition(p1)) {
800 // if the neighbor is found on the same map block
801 // get it straight from there
802 const MapNode &n = block->getNodeUnsafe(p1);
805 // otherwise consult the map
806 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
809 std::set<content_t>::const_iterator k;
810 k = i->required_neighbors.find(c);
811 if(k != i->required_neighbors.end()){
815 // No required neighbor found
820 // Call all the trigger variations
821 i->abm->trigger(m_env, p, n);
822 i->abm->trigger(m_env, p, n,
823 active_object_count, active_object_count_wider);
825 // Count surrounding objects again if the abms added any
826 if(m_env->m_added_objects > 0) {
827 active_object_count = countObjects(block, map, active_object_count_wider);
828 m_env->m_added_objects = 0;
835 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
837 // Reset usage timer immediately, otherwise a block that becomes active
838 // again at around the same time as it would normally be unloaded will
839 // get unloaded incorrectly. (I think this still leaves a small possibility
840 // of a race condition between this and server::AsyncRunStep, which only
841 // some kind of synchronisation will fix, but it at least reduces the window
842 // of opportunity for it to break from seconds to nanoseconds)
843 block->resetUsageTimer();
845 // Get time difference
847 u32 stamp = block->getTimestamp();
848 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
849 dtime_s = m_game_time - stamp;
850 dtime_s += additional_dtime;
852 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
853 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
855 // Remove stored static objects if clearObjects was called since block's timestamp
856 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
857 block->m_static_objects.m_stored.clear();
858 // do not set changed flag to avoid unnecessary mapblock writes
861 // Set current time as timestamp
862 block->setTimestampNoChangedFlag(m_game_time);
864 /*infostream<<"ServerEnvironment::activateBlock(): block is "
865 <<dtime_s<<" seconds old."<<std::endl;*/
867 // Activate stored objects
868 activateObjects(block, dtime_s);
870 /* Handle LoadingBlockModifiers */
871 m_lbm_mgr.applyLBMs(this, block, stamp);
874 std::vector<NodeTimer> elapsed_timers =
875 block->m_node_timers.step((float)dtime_s);
876 if (!elapsed_timers.empty()) {
878 for (std::vector<NodeTimer>::iterator
879 i = elapsed_timers.begin();
880 i != elapsed_timers.end(); ++i){
881 n = block->getNodeNoEx(i->position);
882 v3s16 p = i->position + block->getPosRelative();
883 if (m_script->node_on_timer(p, n, i->elapsed))
884 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
888 /* Handle ActiveBlockModifiers */
889 ABMHandler abmhandler(m_abms, dtime_s, this, false);
890 abmhandler.apply(block);
893 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
895 m_abms.push_back(ABMWithState(abm));
898 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
900 m_lbm_mgr.addLBMDef(lbm);
903 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
905 INodeDefManager *ndef = m_server->ndef();
906 MapNode n_old = m_map->getNodeNoEx(p);
909 if (ndef->get(n_old).has_on_destruct)
910 m_script->node_on_destruct(p, n_old);
913 if (!m_map->addNodeWithEvent(p, n))
916 // Update active VoxelManipulator if a mapgen thread
917 m_map->updateVManip(p);
919 // Call post-destructor
920 if (ndef->get(n_old).has_after_destruct)
921 m_script->node_after_destruct(p, n_old);
924 if (ndef->get(n).has_on_construct)
925 m_script->node_on_construct(p, n);
930 bool ServerEnvironment::removeNode(v3s16 p)
932 INodeDefManager *ndef = m_server->ndef();
933 MapNode n_old = m_map->getNodeNoEx(p);
936 if (ndef->get(n_old).has_on_destruct)
937 m_script->node_on_destruct(p, n_old);
940 // This is slightly optimized compared to addNodeWithEvent(air)
941 if (!m_map->removeNodeWithEvent(p))
944 // Update active VoxelManipulator if a mapgen thread
945 m_map->updateVManip(p);
947 // Call post-destructor
948 if (ndef->get(n_old).has_after_destruct)
949 m_script->node_after_destruct(p, n_old);
951 // Air doesn't require constructor
955 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
957 if (!m_map->addNodeWithEvent(p, n, false))
960 // Update active VoxelManipulator if a mapgen thread
961 m_map->updateVManip(p);
966 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
968 for (ActiveObjectMap::iterator i = m_active_objects.begin();
969 i != m_active_objects.end(); ++i) {
970 ServerActiveObject* obj = i->second;
972 v3f objectpos = obj->getBasePosition();
973 if (objectpos.getDistanceFrom(pos) > radius)
975 objects.push_back(id);
979 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
981 infostream << "ServerEnvironment::clearObjects(): "
982 << "Removing all active objects" << std::endl;
983 std::vector<u16> objects_to_remove;
984 for (ActiveObjectMap::iterator i = m_active_objects.begin();
985 i != m_active_objects.end(); ++i) {
986 ServerActiveObject* obj = i->second;
987 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
990 // Delete static object if block is loaded
991 if (obj->m_static_exists) {
992 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
994 block->m_static_objects.remove(id);
995 block->raiseModified(MOD_STATE_WRITE_NEEDED,
996 MOD_REASON_CLEAR_ALL_OBJECTS);
997 obj->m_static_exists = false;
1000 // If known by some client, don't delete immediately
1001 if (obj->m_known_by_count > 0) {
1002 obj->m_pending_deactivation = true;
1003 obj->m_removed = true;
1007 // Tell the object about removal
1008 obj->removingFromEnvironment();
1009 // Deregister in scripting api
1010 m_script->removeObjectReference(obj);
1012 // Delete active object
1013 if (obj->environmentDeletes())
1015 // Id to be removed from m_active_objects
1016 objects_to_remove.push_back(id);
1019 // Remove references from m_active_objects
1020 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1021 i != objects_to_remove.end(); ++i) {
1022 m_active_objects.erase(*i);
1025 // Get list of loaded blocks
1026 std::vector<v3s16> loaded_blocks;
1027 infostream << "ServerEnvironment::clearObjects(): "
1028 << "Listing all loaded blocks" << std::endl;
1029 m_map->listAllLoadedBlocks(loaded_blocks);
1030 infostream << "ServerEnvironment::clearObjects(): "
1031 << "Done listing all loaded blocks: "
1032 << loaded_blocks.size()<<std::endl;
1034 // Get list of loadable blocks
1035 std::vector<v3s16> loadable_blocks;
1036 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1037 infostream << "ServerEnvironment::clearObjects(): "
1038 << "Listing all loadable blocks" << std::endl;
1039 m_map->listAllLoadableBlocks(loadable_blocks);
1040 infostream << "ServerEnvironment::clearObjects(): "
1041 << "Done listing all loadable blocks: "
1042 << loadable_blocks.size() << std::endl;
1044 loadable_blocks = loaded_blocks;
1047 infostream << "ServerEnvironment::clearObjects(): "
1048 << "Now clearing objects in " << loadable_blocks.size()
1049 << " blocks" << std::endl;
1051 // Grab a reference on each loaded block to avoid unloading it
1052 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1053 i != loaded_blocks.end(); ++i) {
1055 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1056 assert(block != NULL);
1060 // Remove objects in all loadable blocks
1061 u32 unload_interval = U32_MAX;
1062 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1063 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1064 unload_interval = MYMAX(unload_interval, 1);
1066 u32 report_interval = loadable_blocks.size() / 10;
1067 u32 num_blocks_checked = 0;
1068 u32 num_blocks_cleared = 0;
1069 u32 num_objs_cleared = 0;
1070 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1071 i != loadable_blocks.end(); ++i) {
1073 MapBlock *block = m_map->emergeBlock(p, false);
1075 errorstream << "ServerEnvironment::clearObjects(): "
1076 << "Failed to emerge block " << PP(p) << std::endl;
1079 u32 num_stored = block->m_static_objects.m_stored.size();
1080 u32 num_active = block->m_static_objects.m_active.size();
1081 if (num_stored != 0 || num_active != 0) {
1082 block->m_static_objects.m_stored.clear();
1083 block->m_static_objects.m_active.clear();
1084 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1085 MOD_REASON_CLEAR_ALL_OBJECTS);
1086 num_objs_cleared += num_stored + num_active;
1087 num_blocks_cleared++;
1089 num_blocks_checked++;
1091 if (report_interval != 0 &&
1092 num_blocks_checked % report_interval == 0) {
1093 float percent = 100.0 * (float)num_blocks_checked /
1094 loadable_blocks.size();
1095 infostream << "ServerEnvironment::clearObjects(): "
1096 << "Cleared " << num_objs_cleared << " objects"
1097 << " in " << num_blocks_cleared << " blocks ("
1098 << percent << "%)" << std::endl;
1100 if (num_blocks_checked % unload_interval == 0) {
1101 m_map->unloadUnreferencedBlocks();
1104 m_map->unloadUnreferencedBlocks();
1106 // Drop references that were added above
1107 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1108 i != loaded_blocks.end(); ++i) {
1110 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1115 m_last_clear_objects_time = m_game_time;
1117 infostream << "ServerEnvironment::clearObjects(): "
1118 << "Finished: Cleared " << num_objs_cleared << " objects"
1119 << " in " << num_blocks_cleared << " blocks" << std::endl;
1122 void ServerEnvironment::step(float dtime)
1124 DSTACK(FUNCTION_NAME);
1126 //TimeTaker timer("ServerEnv step");
1128 /* Step time of day */
1129 stepTimeOfDay(dtime);
1132 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1133 // really matter that much.
1134 static const float server_step = g_settings->getFloat("dedicated_server_step");
1135 m_recommended_send_interval = server_step;
1141 m_game_time_fraction_counter += dtime;
1142 u32 inc_i = (u32)m_game_time_fraction_counter;
1143 m_game_time += inc_i;
1144 m_game_time_fraction_counter -= (float)inc_i;
1151 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1152 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1153 i != m_players.end(); ++i) {
1154 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1157 // Ignore disconnected players
1158 if(player->peer_id == 0)
1162 player->move(dtime, this, 100*BS);
1167 Manage active block list
1169 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1170 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1172 Get player block positions
1174 std::vector<v3s16> players_blockpos;
1175 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1176 i != m_players.end(); ++i) {
1177 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1180 // Ignore disconnected players
1181 if (player->peer_id == 0)
1184 PlayerSAO *playersao = player->getPlayerSAO();
1187 v3s16 blockpos = getNodeBlockPos(
1188 floatToInt(playersao->getBasePosition(), BS));
1189 players_blockpos.push_back(blockpos);
1193 Update list of active blocks, collecting changes
1195 static const s16 active_block_range = g_settings->getS16("active_block_range");
1196 std::set<v3s16> blocks_removed;
1197 std::set<v3s16> blocks_added;
1198 m_active_blocks.update(players_blockpos, active_block_range,
1199 blocks_removed, blocks_added);
1202 Handle removed blocks
1205 // Convert active objects that are no more in active blocks to static
1206 deactivateFarObjects(false);
1208 for(std::set<v3s16>::iterator
1209 i = blocks_removed.begin();
1210 i != blocks_removed.end(); ++i) {
1213 /* infostream<<"Server: Block " << PP(p)
1214 << " became inactive"<<std::endl; */
1216 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1220 // Set current time as timestamp (and let it set ChangedFlag)
1221 block->setTimestamp(m_game_time);
1228 for(std::set<v3s16>::iterator
1229 i = blocks_added.begin();
1230 i != blocks_added.end(); ++i)
1234 MapBlock *block = m_map->getBlockOrEmerge(p);
1236 m_active_blocks.m_list.erase(p);
1240 activateBlock(block);
1241 /* infostream<<"Server: Block " << PP(p)
1242 << " became active"<<std::endl; */
1247 Mess around in active blocks
1249 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1250 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1252 float dtime = m_cache_nodetimer_interval;
1254 for(std::set<v3s16>::iterator
1255 i = m_active_blocks.m_list.begin();
1256 i != m_active_blocks.m_list.end(); ++i)
1260 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1261 <<") being handled"<<std::endl;*/
1263 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1267 // Reset block usage timer
1268 block->resetUsageTimer();
1270 // Set current time as timestamp
1271 block->setTimestampNoChangedFlag(m_game_time);
1272 // If time has changed much from the one on disk,
1273 // set block to be saved when it is unloaded
1274 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1275 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1276 MOD_REASON_BLOCK_EXPIRED);
1279 std::vector<NodeTimer> elapsed_timers =
1280 block->m_node_timers.step((float)dtime);
1281 if (!elapsed_timers.empty()) {
1283 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1284 i != elapsed_timers.end(); ++i) {
1285 n = block->getNodeNoEx(i->position);
1286 p = i->position + block->getPosRelative();
1287 if (m_script->node_on_timer(p, n, i->elapsed)) {
1288 block->setNodeTimer(NodeTimer(
1289 i->timeout, 0, i->position));
1296 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1298 if(m_active_block_interval_overload_skip > 0){
1299 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1300 m_active_block_interval_overload_skip--;
1303 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1304 TimeTaker timer("modify in active blocks per interval");
1306 // Initialize handling of ActiveBlockModifiers
1307 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1309 for(std::set<v3s16>::iterator
1310 i = m_active_blocks.m_list.begin();
1311 i != m_active_blocks.m_list.end(); ++i)
1315 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1316 <<") being handled"<<std::endl;*/
1318 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1322 // Set current time as timestamp
1323 block->setTimestampNoChangedFlag(m_game_time);
1325 /* Handle ActiveBlockModifiers */
1326 abmhandler.apply(block);
1329 u32 time_ms = timer.stop(true);
1330 u32 max_time_ms = 200;
1331 if(time_ms > max_time_ms){
1332 warningstream<<"active block modifiers took "
1333 <<time_ms<<"ms (longer than "
1334 <<max_time_ms<<"ms)"<<std::endl;
1335 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1340 Step script environment (run global on_step())
1342 m_script->environment_Step(dtime);
1348 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1349 //TimeTaker timer("Step active objects");
1351 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1353 // This helps the objects to send data at the same time
1354 bool send_recommended = false;
1355 m_send_recommended_timer += dtime;
1356 if(m_send_recommended_timer > getSendRecommendedInterval())
1358 m_send_recommended_timer -= getSendRecommendedInterval();
1359 send_recommended = true;
1362 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1363 i != m_active_objects.end(); ++i) {
1364 ServerActiveObject* obj = i->second;
1365 // Don't step if is to be removed or stored statically
1366 if(obj->m_removed || obj->m_pending_deactivation)
1369 obj->step(dtime, send_recommended);
1370 // Read messages from object
1371 while(!obj->m_messages_out.empty())
1373 m_active_object_messages.push(
1374 obj->m_messages_out.front());
1375 obj->m_messages_out.pop();
1381 Manage active objects
1383 if(m_object_management_interval.step(dtime, 0.5))
1385 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1387 Remove objects that satisfy (m_removed && m_known_by_count==0)
1389 removeRemovedObjects();
1393 Manage particle spawner expiration
1395 if (m_particle_management_interval.step(dtime, 1.0)) {
1396 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1397 i != m_particle_spawners.end(); ) {
1398 //non expiring spawners
1399 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1405 if (i->second <= 0.f)
1406 m_particle_spawners.erase(i++);
1413 u32 ServerEnvironment::addParticleSpawner(float exptime)
1415 // Timers with lifetime 0 do not expire
1416 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1419 for (;;) { // look for unused particlespawner id
1421 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1422 if (f == m_particle_spawners.end()) {
1423 m_particle_spawners[id] = time;
1430 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1432 u32 id = addParticleSpawner(exptime);
1433 m_particle_spawner_attachments[id] = attached_id;
1434 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1435 obj->attachParticleSpawner(id);
1440 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1442 m_particle_spawners.erase(id);
1443 UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1444 if (it != m_particle_spawner_attachments.end()) {
1445 u16 obj_id = (*it).second;
1446 ServerActiveObject *sao = getActiveObject(obj_id);
1447 if (sao != NULL && remove_from_object) {
1448 sao->detachParticleSpawner(id);
1450 m_particle_spawner_attachments.erase(id);
1454 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1456 ActiveObjectMap::iterator n = m_active_objects.find(id);
1457 return (n != m_active_objects.end() ? n->second : NULL);
1460 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1465 return objects.find(id) == objects.end();
1468 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1470 //try to reuse id's as late as possible
1471 static u16 last_used_id = 0;
1472 u16 startid = last_used_id;
1476 if(isFreeServerActiveObjectId(last_used_id, objects))
1477 return last_used_id;
1479 if(last_used_id == startid)
1484 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1486 assert(object); // Pre-condition
1488 u16 id = addActiveObjectRaw(object, true, 0);
1493 Finds out what new objects have been added to
1494 inside a radius around a position
1496 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1498 std::set<u16> ¤t_objects,
1499 std::queue<u16> &added_objects)
1501 f32 radius_f = radius * BS;
1502 f32 player_radius_f = player_radius * BS;
1504 if (player_radius_f < 0)
1505 player_radius_f = 0;
1507 Go through the object list,
1508 - discard m_removed objects,
1509 - discard objects that are too far away,
1510 - discard objects that are found in current_objects.
1511 - add remaining objects to added_objects
1513 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1514 i != m_active_objects.end(); ++i) {
1518 ServerActiveObject *object = i->second;
1522 // Discard if removed or deactivating
1523 if(object->m_removed || object->m_pending_deactivation)
1526 f32 distance_f = object->getBasePosition().
1527 getDistanceFrom(playersao->getBasePosition());
1528 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1529 // Discard if too far
1530 if (distance_f > player_radius_f && player_radius_f != 0)
1532 } else if (distance_f > radius_f)
1535 // Discard if already on current_objects
1536 std::set<u16>::iterator n;
1537 n = current_objects.find(id);
1538 if(n != current_objects.end())
1540 // Add to added_objects
1541 added_objects.push(id);
1546 Finds out what objects have been removed from
1547 inside a radius around a position
1549 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1551 std::set<u16> ¤t_objects,
1552 std::queue<u16> &removed_objects)
1554 f32 radius_f = radius * BS;
1555 f32 player_radius_f = player_radius * BS;
1557 if (player_radius_f < 0)
1558 player_radius_f = 0;
1560 Go through current_objects; object is removed if:
1561 - object is not found in m_active_objects (this is actually an
1562 error condition; objects should be set m_removed=true and removed
1563 only after all clients have been informed about removal), or
1564 - object has m_removed=true, or
1565 - object is too far away
1567 for(std::set<u16>::iterator
1568 i = current_objects.begin();
1569 i != current_objects.end(); ++i)
1572 ServerActiveObject *object = getActiveObject(id);
1574 if (object == NULL) {
1575 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1576 << " object in current_objects is NULL" << std::endl;
1577 removed_objects.push(id);
1581 if (object->m_removed || object->m_pending_deactivation) {
1582 removed_objects.push(id);
1586 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1587 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1588 if (distance_f <= player_radius_f || player_radius_f == 0)
1590 } else if (distance_f <= radius_f)
1593 // Object is no longer visible
1594 removed_objects.push(id);
1598 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1599 v3s16 blockpos, bool static_exists, v3s16 static_block)
1601 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1605 for (std::map<u16, StaticObject>::iterator
1606 so_it = block->m_static_objects.m_active.begin();
1607 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1608 // Get the ServerActiveObject counterpart to this StaticObject
1609 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1610 if (ao_it == m_active_objects.end()) {
1611 // If this ever happens, there must be some kind of nasty bug.
1612 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1613 "Object from MapBlock::m_static_objects::m_active not found "
1614 "in m_active_objects";
1618 ServerActiveObject *sao = ao_it->second;
1619 sao->m_static_exists = static_exists;
1620 sao->m_static_block = static_block;
1624 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1626 if(m_active_object_messages.empty())
1627 return ActiveObjectMessage(0);
1629 ActiveObjectMessage message = m_active_object_messages.front();
1630 m_active_object_messages.pop();
1635 ************ Private methods *************
1638 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1639 bool set_changed, u32 dtime_s)
1641 assert(object); // Pre-condition
1642 if(object->getId() == 0){
1643 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1646 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1647 <<"no free ids available"<<std::endl;
1648 if(object->environmentDeletes())
1652 object->setId(new_id);
1655 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1656 <<"supplied with id "<<object->getId()<<std::endl;
1659 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1660 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1661 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1662 if(object->environmentDeletes())
1667 if (objectpos_over_limit(object->getBasePosition())) {
1668 v3f p = object->getBasePosition();
1669 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1670 << "object position (" << p.X << "," << p.Y << "," << p.Z
1671 << ") outside maximum range" << std::endl;
1672 if (object->environmentDeletes())
1677 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1678 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1680 m_active_objects[object->getId()] = object;
1682 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1683 <<"Added id="<<object->getId()<<"; there are now "
1684 <<m_active_objects.size()<<" active objects."
1687 // Register reference in scripting api (must be done before post-init)
1688 m_script->addObjectReference(object);
1689 // Post-initialize object
1690 object->addedToEnvironment(dtime_s);
1692 // Add static data to block
1693 if(object->isStaticAllowed())
1695 // Add static object to active static list of the block
1696 v3f objectpos = object->getBasePosition();
1697 std::string staticdata = "";
1698 object->getStaticData(&staticdata);
1699 StaticObject s_obj(object->getType(), objectpos, staticdata);
1700 // Add to the block where the object is located in
1701 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1702 MapBlock *block = m_map->emergeBlock(blockpos);
1704 block->m_static_objects.m_active[object->getId()] = s_obj;
1705 object->m_static_exists = true;
1706 object->m_static_block = blockpos;
1709 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1710 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1712 v3s16 p = floatToInt(objectpos, BS);
1713 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1714 <<"could not emerge block for storing id="<<object->getId()
1715 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1719 return object->getId();
1723 Remove objects that satisfy (m_removed && m_known_by_count==0)
1725 void ServerEnvironment::removeRemovedObjects()
1727 std::vector<u16> objects_to_remove;
1728 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1729 i != m_active_objects.end(); ++i) {
1731 ServerActiveObject* obj = i->second;
1732 // This shouldn't happen but check it
1735 infostream<<"NULL object found in ServerEnvironment"
1736 <<" while finding removed objects. id="<<id<<std::endl;
1737 // Id to be removed from m_active_objects
1738 objects_to_remove.push_back(id);
1743 We will delete objects that are marked as removed or thatare
1744 waiting for deletion after deactivation
1746 if (!obj->m_removed && !obj->m_pending_deactivation)
1750 Delete static data from block if is marked as removed
1752 if(obj->m_static_exists && obj->m_removed)
1754 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1756 block->m_static_objects.remove(id);
1757 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1758 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1759 obj->m_static_exists = false;
1761 infostream<<"Failed to emerge block from which an object to "
1762 <<"be removed was loaded from. id="<<id<<std::endl;
1766 // If m_known_by_count > 0, don't actually remove. On some future
1767 // invocation this will be 0, which is when removal will continue.
1768 if(obj->m_known_by_count > 0)
1772 Move static data from active to stored if not marked as removed
1774 if(obj->m_static_exists && !obj->m_removed){
1775 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1777 std::map<u16, StaticObject>::iterator i =
1778 block->m_static_objects.m_active.find(id);
1779 if(i != block->m_static_objects.m_active.end()){
1780 block->m_static_objects.m_stored.push_back(i->second);
1781 block->m_static_objects.m_active.erase(id);
1782 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1783 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1786 infostream<<"Failed to emerge block from which an object to "
1787 <<"be deactivated was loaded from. id="<<id<<std::endl;
1791 // Tell the object about removal
1792 obj->removingFromEnvironment();
1793 // Deregister in scripting api
1794 m_script->removeObjectReference(obj);
1797 if(obj->environmentDeletes())
1800 // Id to be removed from m_active_objects
1801 objects_to_remove.push_back(id);
1803 // Remove references from m_active_objects
1804 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1805 i != objects_to_remove.end(); ++i) {
1806 m_active_objects.erase(*i);
1810 static void print_hexdump(std::ostream &o, const std::string &data)
1812 const int linelength = 16;
1813 for(int l=0; ; l++){
1814 int i0 = linelength * l;
1815 bool at_end = false;
1816 int thislinelength = linelength;
1817 if(i0 + thislinelength > (int)data.size()){
1818 thislinelength = data.size() - i0;
1821 for(int di=0; di<linelength; di++){
1824 if(di<thislinelength)
1825 snprintf(buf, 4, "%.2x ", data[i]);
1827 snprintf(buf, 4, " ");
1831 for(int di=0; di<thislinelength; di++){
1845 Convert stored objects from blocks near the players to active.
1847 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1852 // Ignore if no stored objects (to not set changed flag)
1853 if(block->m_static_objects.m_stored.empty())
1856 verbosestream<<"ServerEnvironment::activateObjects(): "
1857 <<"activating objects of block "<<PP(block->getPos())
1858 <<" ("<<block->m_static_objects.m_stored.size()
1859 <<" objects)"<<std::endl;
1860 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1862 errorstream<<"suspiciously large amount of objects detected: "
1863 <<block->m_static_objects.m_stored.size()<<" in "
1864 <<PP(block->getPos())
1865 <<"; removing all of them."<<std::endl;
1866 // Clear stored list
1867 block->m_static_objects.m_stored.clear();
1868 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1869 MOD_REASON_TOO_MANY_OBJECTS);
1873 // Activate stored objects
1874 std::vector<StaticObject> new_stored;
1875 for (std::vector<StaticObject>::iterator
1876 i = block->m_static_objects.m_stored.begin();
1877 i != block->m_static_objects.m_stored.end(); ++i) {
1878 StaticObject &s_obj = *i;
1880 // Create an active object from the data
1881 ServerActiveObject *obj = ServerActiveObject::create
1882 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1883 // If couldn't create object, store static data back.
1885 errorstream<<"ServerEnvironment::activateObjects(): "
1886 <<"failed to create active object from static object "
1887 <<"in block "<<PP(s_obj.pos/BS)
1888 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1889 print_hexdump(verbosestream, s_obj.data);
1891 new_stored.push_back(s_obj);
1894 verbosestream<<"ServerEnvironment::activateObjects(): "
1895 <<"activated static object pos="<<PP(s_obj.pos/BS)
1896 <<" type="<<(int)s_obj.type<<std::endl;
1897 // This will also add the object to the active static list
1898 addActiveObjectRaw(obj, false, dtime_s);
1900 // Clear stored list
1901 block->m_static_objects.m_stored.clear();
1902 // Add leftover failed stuff to stored list
1903 for(std::vector<StaticObject>::iterator
1904 i = new_stored.begin();
1905 i != new_stored.end(); ++i) {
1906 StaticObject &s_obj = *i;
1907 block->m_static_objects.m_stored.push_back(s_obj);
1910 // Turn the active counterparts of activated objects not pending for
1912 for(std::map<u16, StaticObject>::iterator
1913 i = block->m_static_objects.m_active.begin();
1914 i != block->m_static_objects.m_active.end(); ++i)
1917 ServerActiveObject *object = getActiveObject(id);
1919 object->m_pending_deactivation = false;
1923 Note: Block hasn't really been modified here.
1924 The objects have just been activated and moved from the stored
1925 static list to the active static list.
1926 As such, the block is essentially the same.
1927 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1928 Otherwise there would be a huge amount of unnecessary I/O.
1933 Convert objects that are not standing inside active blocks to static.
1935 If m_known_by_count != 0, active object is not deleted, but static
1936 data is still updated.
1938 If force_delete is set, active object is deleted nevertheless. It
1939 shall only be set so in the destructor of the environment.
1941 If block wasn't generated (not in memory or on disk),
1943 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1945 std::vector<u16> objects_to_remove;
1946 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1947 i != m_active_objects.end(); ++i) {
1948 ServerActiveObject* obj = i->second;
1951 // Do not deactivate if static data creation not allowed
1952 if(!force_delete && !obj->isStaticAllowed())
1955 // If pending deactivation, let removeRemovedObjects() do it
1956 if(!force_delete && obj->m_pending_deactivation)
1960 v3f objectpos = obj->getBasePosition();
1962 // The block in which the object resides in
1963 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1965 // If object's static data is stored in a deactivated block and object
1966 // is actually located in an active block, re-save to the block in
1967 // which the object is actually located in.
1969 obj->m_static_exists &&
1970 !m_active_blocks.contains(obj->m_static_block) &&
1971 m_active_blocks.contains(blockpos_o))
1973 v3s16 old_static_block = obj->m_static_block;
1975 // Save to block where object is located
1976 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1978 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1979 <<"Could not save object id="<<id
1980 <<" to it's current block "<<PP(blockpos_o)
1984 std::string staticdata_new = "";
1985 obj->getStaticData(&staticdata_new);
1986 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1987 block->m_static_objects.insert(id, s_obj);
1988 obj->m_static_block = blockpos_o;
1989 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1990 MOD_REASON_STATIC_DATA_ADDED);
1992 // Delete from block where object was located
1993 block = m_map->emergeBlock(old_static_block, false);
1995 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1996 <<"Could not delete object id="<<id
1997 <<" from it's previous block "<<PP(old_static_block)
2001 block->m_static_objects.remove(id);
2002 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2003 MOD_REASON_STATIC_DATA_REMOVED);
2007 // If block is active, don't remove
2008 if(!force_delete && m_active_blocks.contains(blockpos_o))
2011 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2012 <<"deactivating object id="<<id<<" on inactive block "
2013 <<PP(blockpos_o)<<std::endl;
2015 // If known by some client, don't immediately delete.
2016 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2019 Update the static data
2022 if(obj->isStaticAllowed())
2024 // Create new static object
2025 std::string staticdata_new = "";
2026 obj->getStaticData(&staticdata_new);
2027 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2029 bool stays_in_same_block = false;
2030 bool data_changed = true;
2032 if (obj->m_static_exists) {
2033 if (obj->m_static_block == blockpos_o)
2034 stays_in_same_block = true;
2036 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2039 std::map<u16, StaticObject>::iterator n =
2040 block->m_static_objects.m_active.find(id);
2041 if (n != block->m_static_objects.m_active.end()) {
2042 StaticObject static_old = n->second;
2044 float save_movem = obj->getMinimumSavedMovement();
2046 if (static_old.data == staticdata_new &&
2047 (static_old.pos - objectpos).getLength() < save_movem)
2048 data_changed = false;
2050 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2051 <<"id="<<id<<" m_static_exists=true but "
2052 <<"static data doesn't actually exist in "
2053 <<PP(obj->m_static_block)<<std::endl;
2058 bool shall_be_written = (!stays_in_same_block || data_changed);
2060 // Delete old static object
2061 if(obj->m_static_exists)
2063 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2066 block->m_static_objects.remove(id);
2067 obj->m_static_exists = false;
2068 // Only mark block as modified if data changed considerably
2069 if(shall_be_written)
2070 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2071 MOD_REASON_STATIC_DATA_CHANGED);
2075 // Add to the block where the object is located in
2076 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2077 // Get or generate the block
2078 MapBlock *block = NULL;
2080 block = m_map->emergeBlock(blockpos);
2081 } catch(InvalidPositionException &e){
2082 // Handled via NULL pointer
2083 // NOTE: emergeBlock's failure is usually determined by it
2084 // actually returning NULL
2089 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2090 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2091 << " statically but block " << PP(blockpos)
2092 << " already contains "
2093 << block->m_static_objects.m_stored.size()
2095 << " Forcing delete." << std::endl;
2096 force_delete = true;
2098 // If static counterpart already exists in target block,
2100 // This shouldn't happen because the object is removed from
2101 // the previous block before this according to
2102 // obj->m_static_block, but happens rarely for some unknown
2103 // reason. Unsuccessful attempts have been made to find
2105 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2106 warningstream<<"ServerEnv: Performing hack #83274"
2108 block->m_static_objects.remove(id);
2110 // Store static data
2111 u16 store_id = pending_delete ? id : 0;
2112 block->m_static_objects.insert(store_id, s_obj);
2114 // Only mark block as modified if data changed considerably
2115 if(shall_be_written)
2116 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2117 MOD_REASON_STATIC_DATA_CHANGED);
2119 obj->m_static_exists = true;
2120 obj->m_static_block = block->getPos();
2125 v3s16 p = floatToInt(objectpos, BS);
2126 errorstream<<"ServerEnv: Could not find or generate "
2127 <<"a block for storing id="<<obj->getId()
2128 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2135 If known by some client, set pending deactivation.
2136 Otherwise delete it immediately.
2139 if(pending_delete && !force_delete)
2141 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2142 <<"object id="<<id<<" is known by clients"
2143 <<"; not deleting yet"<<std::endl;
2145 obj->m_pending_deactivation = true;
2149 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2150 <<"object id="<<id<<" is not known by clients"
2151 <<"; deleting"<<std::endl;
2153 // Tell the object about removal
2154 obj->removingFromEnvironment();
2155 // Deregister in scripting api
2156 m_script->removeObjectReference(obj);
2158 // Delete active object
2159 if(obj->environmentDeletes())
2161 // Id to be removed from m_active_objects
2162 objects_to_remove.push_back(id);
2165 // Remove references from m_active_objects
2166 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2167 i != objects_to_remove.end(); ++i) {
2168 m_active_objects.erase(*i);