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 "serverscripting.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 ServerScripting *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)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) {
505 (*it)->save(players_path, m_server);
510 void ServerEnvironment::savePlayer(RemotePlayer *player)
512 std::string players_path = m_path_world + DIR_DELIM "players";
513 fs::CreateDir(players_path);
515 player->save(players_path, m_server);
518 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao)
520 bool newplayer = false;
522 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
523 std::string path = players_path + playername;
525 RemotePlayer *player = getPlayer(playername.c_str());
527 player = new RemotePlayer("", m_server->idef());
531 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
532 //// Open file and deserialize
533 std::ifstream is(path.c_str(), std::ios_base::binary);
537 player->deSerialize(is, path, sao);
540 if (player->getName() == playername) {
545 path = players_path + playername + itos(i);
549 infostream << "Player file for player " << playername
550 << " not found" << std::endl;
560 player->setModified(false);
564 void ServerEnvironment::saveMeta()
566 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
568 // Open file and serialize
569 std::ostringstream ss(std::ios_base::binary);
572 args.setU64("game_time", m_game_time);
573 args.setU64("time_of_day", getTimeOfDay());
574 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
575 args.setU64("lbm_introduction_times_version", 1);
576 args.set("lbm_introduction_times",
577 m_lbm_mgr.createIntroductionTimesString());
578 args.setU64("day_count", m_day_count);
582 if(!fs::safeWriteToFile(path, ss.str()))
584 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
586 throw SerializationError("Couldn't save env meta");
590 void ServerEnvironment::loadMeta()
592 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
594 // Open file and deserialize
595 std::ifstream is(path.c_str(), std::ios_base::binary);
597 infostream << "ServerEnvironment::loadMeta(): Failed to open "
598 << path << std::endl;
599 throw SerializationError("Couldn't load env meta");
604 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
605 throw SerializationError("ServerEnvironment::loadMeta(): "
606 "EnvArgsEnd not found!");
610 m_game_time = args.getU64("game_time");
611 } catch (SettingNotFoundException &e) {
612 // Getting this is crucial, otherwise timestamps are useless
613 throw SerializationError("Couldn't load env meta game_time");
616 setTimeOfDay(args.exists("time_of_day") ?
617 // set day to morning by default
618 args.getU64("time_of_day") : 9000);
620 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
621 // If missing, do as if clearObjects was never called
622 args.getU64("last_clear_objects_time") : 0;
624 std::string lbm_introduction_times = "";
626 u64 ver = args.getU64("lbm_introduction_times_version");
628 lbm_introduction_times = args.get("lbm_introduction_times");
630 infostream << "ServerEnvironment::loadMeta(): Non-supported"
631 << " introduction time version " << ver << std::endl;
633 } catch (SettingNotFoundException &e) {
634 // No problem, this is expected. Just continue with an empty string
636 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
638 m_day_count = args.exists("day_count") ?
639 args.getU64("day_count") : 0;
642 void ServerEnvironment::loadDefaultMeta()
644 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
649 ActiveBlockModifier *abm;
651 std::set<content_t> required_neighbors;
657 ServerEnvironment *m_env;
658 std::vector<std::vector<ActiveABM> *> m_aabms;
660 ABMHandler(std::vector<ABMWithState> &abms,
661 float dtime_s, ServerEnvironment *env,
667 INodeDefManager *ndef = env->getGameDef()->ndef();
668 for(std::vector<ABMWithState>::iterator
669 i = abms.begin(); i != abms.end(); ++i) {
670 ActiveBlockModifier *abm = i->abm;
671 float trigger_interval = abm->getTriggerInterval();
672 if(trigger_interval < 0.001)
673 trigger_interval = 0.001;
674 float actual_interval = dtime_s;
677 if(i->timer < trigger_interval)
679 i->timer -= trigger_interval;
680 actual_interval = trigger_interval;
682 float chance = abm->getTriggerChance();
687 if(abm->getSimpleCatchUp()) {
688 float intervals = actual_interval / trigger_interval;
691 aabm.chance = chance / intervals;
695 aabm.chance = chance;
698 std::set<std::string> required_neighbors_s
699 = abm->getRequiredNeighbors();
700 for(std::set<std::string>::iterator
701 i = required_neighbors_s.begin();
702 i != required_neighbors_s.end(); ++i)
704 ndef->getIds(*i, aabm.required_neighbors);
707 std::set<std::string> contents_s = abm->getTriggerContents();
708 for(std::set<std::string>::iterator
709 i = contents_s.begin(); i != contents_s.end(); ++i)
711 std::set<content_t> ids;
712 ndef->getIds(*i, ids);
713 for(std::set<content_t>::const_iterator k = ids.begin();
717 if (c >= m_aabms.size())
718 m_aabms.resize(c + 256, NULL);
720 m_aabms[c] = new std::vector<ActiveABM>;
721 m_aabms[c]->push_back(aabm);
729 for (size_t i = 0; i < m_aabms.size(); i++)
733 // Find out how many objects the given block and its neighbours contain.
734 // Returns the number of objects in the block, and also in 'wider' the
735 // number of objects in the block and all its neighbours. The latter
736 // may an estimate if any neighbours are unloaded.
737 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
740 u32 wider_unknown_count = 0;
741 for(s16 x=-1; x<=1; x++)
742 for(s16 y=-1; y<=1; y++)
743 for(s16 z=-1; z<=1; z++)
745 MapBlock *block2 = map->getBlockNoCreateNoEx(
746 block->getPos() + v3s16(x,y,z));
748 wider_unknown_count++;
751 wider += block2->m_static_objects.m_active.size()
752 + block2->m_static_objects.m_stored.size();
755 u32 active_object_count = block->m_static_objects.m_active.size();
756 u32 wider_known_count = 3*3*3 - wider_unknown_count;
757 wider += wider_unknown_count * wider / wider_known_count;
758 return active_object_count;
761 void apply(MapBlock *block)
763 if(m_aabms.empty() || block->isDummy())
766 ServerMap *map = &m_env->getServerMap();
768 u32 active_object_count_wider;
769 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
770 m_env->m_added_objects = 0;
773 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
774 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
775 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
777 const MapNode &n = block->getNodeUnsafe(p0);
778 content_t c = n.getContent();
780 if (c >= m_aabms.size() || !m_aabms[c])
783 v3s16 p = p0 + block->getPosRelative();
784 for(std::vector<ActiveABM>::iterator
785 i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
786 if(myrand() % i->chance != 0)
790 if(!i->required_neighbors.empty())
793 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
794 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
795 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
800 if (block->isValidPosition(p1)) {
801 // if the neighbor is found on the same map block
802 // get it straight from there
803 const MapNode &n = block->getNodeUnsafe(p1);
806 // otherwise consult the map
807 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
810 std::set<content_t>::const_iterator k;
811 k = i->required_neighbors.find(c);
812 if(k != i->required_neighbors.end()){
816 // No required neighbor found
821 // Call all the trigger variations
822 i->abm->trigger(m_env, p, n);
823 i->abm->trigger(m_env, p, n,
824 active_object_count, active_object_count_wider);
826 // Count surrounding objects again if the abms added any
827 if(m_env->m_added_objects > 0) {
828 active_object_count = countObjects(block, map, active_object_count_wider);
829 m_env->m_added_objects = 0;
836 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
838 // Reset usage timer immediately, otherwise a block that becomes active
839 // again at around the same time as it would normally be unloaded will
840 // get unloaded incorrectly. (I think this still leaves a small possibility
841 // of a race condition between this and server::AsyncRunStep, which only
842 // some kind of synchronisation will fix, but it at least reduces the window
843 // of opportunity for it to break from seconds to nanoseconds)
844 block->resetUsageTimer();
846 // Get time difference
848 u32 stamp = block->getTimestamp();
849 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
850 dtime_s = m_game_time - stamp;
851 dtime_s += additional_dtime;
853 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
854 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
856 // Remove stored static objects if clearObjects was called since block's timestamp
857 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
858 block->m_static_objects.m_stored.clear();
859 // do not set changed flag to avoid unnecessary mapblock writes
862 // Set current time as timestamp
863 block->setTimestampNoChangedFlag(m_game_time);
865 /*infostream<<"ServerEnvironment::activateBlock(): block is "
866 <<dtime_s<<" seconds old."<<std::endl;*/
868 // Activate stored objects
869 activateObjects(block, dtime_s);
871 /* Handle LoadingBlockModifiers */
872 m_lbm_mgr.applyLBMs(this, block, stamp);
875 std::vector<NodeTimer> elapsed_timers =
876 block->m_node_timers.step((float)dtime_s);
877 if (!elapsed_timers.empty()) {
879 for (std::vector<NodeTimer>::iterator
880 i = elapsed_timers.begin();
881 i != elapsed_timers.end(); ++i){
882 n = block->getNodeNoEx(i->position);
883 v3s16 p = i->position + block->getPosRelative();
884 if (m_script->node_on_timer(p, n, i->elapsed))
885 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
889 /* Handle ActiveBlockModifiers */
890 ABMHandler abmhandler(m_abms, dtime_s, this, false);
891 abmhandler.apply(block);
894 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
896 m_abms.push_back(ABMWithState(abm));
899 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
901 m_lbm_mgr.addLBMDef(lbm);
904 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
906 INodeDefManager *ndef = m_server->ndef();
907 MapNode n_old = m_map->getNodeNoEx(p);
910 if (ndef->get(n_old).has_on_destruct)
911 m_script->node_on_destruct(p, n_old);
914 if (!m_map->addNodeWithEvent(p, n))
917 // Update active VoxelManipulator if a mapgen thread
918 m_map->updateVManip(p);
920 // Call post-destructor
921 if (ndef->get(n_old).has_after_destruct)
922 m_script->node_after_destruct(p, n_old);
925 if (ndef->get(n).has_on_construct)
926 m_script->node_on_construct(p, n);
931 bool ServerEnvironment::removeNode(v3s16 p)
933 INodeDefManager *ndef = m_server->ndef();
934 MapNode n_old = m_map->getNodeNoEx(p);
937 if (ndef->get(n_old).has_on_destruct)
938 m_script->node_on_destruct(p, n_old);
941 // This is slightly optimized compared to addNodeWithEvent(air)
942 if (!m_map->removeNodeWithEvent(p))
945 // Update active VoxelManipulator if a mapgen thread
946 m_map->updateVManip(p);
948 // Call post-destructor
949 if (ndef->get(n_old).has_after_destruct)
950 m_script->node_after_destruct(p, n_old);
952 // Air doesn't require constructor
956 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
958 if (!m_map->addNodeWithEvent(p, n, false))
961 // Update active VoxelManipulator if a mapgen thread
962 m_map->updateVManip(p);
967 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
969 for (ActiveObjectMap::iterator i = m_active_objects.begin();
970 i != m_active_objects.end(); ++i) {
971 ServerActiveObject* obj = i->second;
973 v3f objectpos = obj->getBasePosition();
974 if (objectpos.getDistanceFrom(pos) > radius)
976 objects.push_back(id);
980 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
982 infostream << "ServerEnvironment::clearObjects(): "
983 << "Removing all active objects" << std::endl;
984 std::vector<u16> objects_to_remove;
985 for (ActiveObjectMap::iterator i = m_active_objects.begin();
986 i != m_active_objects.end(); ++i) {
987 ServerActiveObject* obj = i->second;
988 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
991 // Delete static object if block is loaded
992 if (obj->m_static_exists) {
993 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
995 block->m_static_objects.remove(id);
996 block->raiseModified(MOD_STATE_WRITE_NEEDED,
997 MOD_REASON_CLEAR_ALL_OBJECTS);
998 obj->m_static_exists = false;
1001 // If known by some client, don't delete immediately
1002 if (obj->m_known_by_count > 0) {
1003 obj->m_pending_deactivation = true;
1004 obj->m_removed = true;
1008 // Tell the object about removal
1009 obj->removingFromEnvironment();
1010 // Deregister in scripting api
1011 m_script->removeObjectReference(obj);
1013 // Delete active object
1014 if (obj->environmentDeletes())
1016 // Id to be removed from m_active_objects
1017 objects_to_remove.push_back(id);
1020 // Remove references from m_active_objects
1021 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1022 i != objects_to_remove.end(); ++i) {
1023 m_active_objects.erase(*i);
1026 // Get list of loaded blocks
1027 std::vector<v3s16> loaded_blocks;
1028 infostream << "ServerEnvironment::clearObjects(): "
1029 << "Listing all loaded blocks" << std::endl;
1030 m_map->listAllLoadedBlocks(loaded_blocks);
1031 infostream << "ServerEnvironment::clearObjects(): "
1032 << "Done listing all loaded blocks: "
1033 << loaded_blocks.size()<<std::endl;
1035 // Get list of loadable blocks
1036 std::vector<v3s16> loadable_blocks;
1037 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1038 infostream << "ServerEnvironment::clearObjects(): "
1039 << "Listing all loadable blocks" << std::endl;
1040 m_map->listAllLoadableBlocks(loadable_blocks);
1041 infostream << "ServerEnvironment::clearObjects(): "
1042 << "Done listing all loadable blocks: "
1043 << loadable_blocks.size() << std::endl;
1045 loadable_blocks = loaded_blocks;
1048 infostream << "ServerEnvironment::clearObjects(): "
1049 << "Now clearing objects in " << loadable_blocks.size()
1050 << " blocks" << std::endl;
1052 // Grab a reference on each loaded block to avoid unloading it
1053 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1054 i != loaded_blocks.end(); ++i) {
1056 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1057 assert(block != NULL);
1061 // Remove objects in all loadable blocks
1062 u32 unload_interval = U32_MAX;
1063 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1064 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1065 unload_interval = MYMAX(unload_interval, 1);
1067 u32 report_interval = loadable_blocks.size() / 10;
1068 u32 num_blocks_checked = 0;
1069 u32 num_blocks_cleared = 0;
1070 u32 num_objs_cleared = 0;
1071 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1072 i != loadable_blocks.end(); ++i) {
1074 MapBlock *block = m_map->emergeBlock(p, false);
1076 errorstream << "ServerEnvironment::clearObjects(): "
1077 << "Failed to emerge block " << PP(p) << std::endl;
1080 u32 num_stored = block->m_static_objects.m_stored.size();
1081 u32 num_active = block->m_static_objects.m_active.size();
1082 if (num_stored != 0 || num_active != 0) {
1083 block->m_static_objects.m_stored.clear();
1084 block->m_static_objects.m_active.clear();
1085 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1086 MOD_REASON_CLEAR_ALL_OBJECTS);
1087 num_objs_cleared += num_stored + num_active;
1088 num_blocks_cleared++;
1090 num_blocks_checked++;
1092 if (report_interval != 0 &&
1093 num_blocks_checked % report_interval == 0) {
1094 float percent = 100.0 * (float)num_blocks_checked /
1095 loadable_blocks.size();
1096 infostream << "ServerEnvironment::clearObjects(): "
1097 << "Cleared " << num_objs_cleared << " objects"
1098 << " in " << num_blocks_cleared << " blocks ("
1099 << percent << "%)" << std::endl;
1101 if (num_blocks_checked % unload_interval == 0) {
1102 m_map->unloadUnreferencedBlocks();
1105 m_map->unloadUnreferencedBlocks();
1107 // Drop references that were added above
1108 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1109 i != loaded_blocks.end(); ++i) {
1111 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1116 m_last_clear_objects_time = m_game_time;
1118 infostream << "ServerEnvironment::clearObjects(): "
1119 << "Finished: Cleared " << num_objs_cleared << " objects"
1120 << " in " << num_blocks_cleared << " blocks" << std::endl;
1123 void ServerEnvironment::step(float dtime)
1125 DSTACK(FUNCTION_NAME);
1127 //TimeTaker timer("ServerEnv step");
1129 /* Step time of day */
1130 stepTimeOfDay(dtime);
1133 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1134 // really matter that much.
1135 static const float server_step = g_settings->getFloat("dedicated_server_step");
1136 m_recommended_send_interval = server_step;
1142 m_game_time_fraction_counter += dtime;
1143 u32 inc_i = (u32)m_game_time_fraction_counter;
1144 m_game_time += inc_i;
1145 m_game_time_fraction_counter -= (float)inc_i;
1152 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1153 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1154 i != m_players.end(); ++i) {
1155 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1158 // Ignore disconnected players
1159 if(player->peer_id == 0)
1163 player->move(dtime, this, 100*BS);
1168 Manage active block list
1170 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1171 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1173 Get player block positions
1175 std::vector<v3s16> players_blockpos;
1176 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1177 i != m_players.end(); ++i) {
1178 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1181 // Ignore disconnected players
1182 if (player->peer_id == 0)
1185 PlayerSAO *playersao = player->getPlayerSAO();
1188 v3s16 blockpos = getNodeBlockPos(
1189 floatToInt(playersao->getBasePosition(), BS));
1190 players_blockpos.push_back(blockpos);
1194 Update list of active blocks, collecting changes
1196 static const s16 active_block_range = g_settings->getS16("active_block_range");
1197 std::set<v3s16> blocks_removed;
1198 std::set<v3s16> blocks_added;
1199 m_active_blocks.update(players_blockpos, active_block_range,
1200 blocks_removed, blocks_added);
1203 Handle removed blocks
1206 // Convert active objects that are no more in active blocks to static
1207 deactivateFarObjects(false);
1209 for(std::set<v3s16>::iterator
1210 i = blocks_removed.begin();
1211 i != blocks_removed.end(); ++i) {
1214 /* infostream<<"Server: Block " << PP(p)
1215 << " became inactive"<<std::endl; */
1217 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1221 // Set current time as timestamp (and let it set ChangedFlag)
1222 block->setTimestamp(m_game_time);
1229 for(std::set<v3s16>::iterator
1230 i = blocks_added.begin();
1231 i != blocks_added.end(); ++i)
1235 MapBlock *block = m_map->getBlockOrEmerge(p);
1237 m_active_blocks.m_list.erase(p);
1241 activateBlock(block);
1242 /* infostream<<"Server: Block " << PP(p)
1243 << " became active"<<std::endl; */
1248 Mess around in active blocks
1250 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1251 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1253 float dtime = m_cache_nodetimer_interval;
1255 for(std::set<v3s16>::iterator
1256 i = m_active_blocks.m_list.begin();
1257 i != m_active_blocks.m_list.end(); ++i)
1261 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1262 <<") being handled"<<std::endl;*/
1264 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1268 // Reset block usage timer
1269 block->resetUsageTimer();
1271 // Set current time as timestamp
1272 block->setTimestampNoChangedFlag(m_game_time);
1273 // If time has changed much from the one on disk,
1274 // set block to be saved when it is unloaded
1275 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1276 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1277 MOD_REASON_BLOCK_EXPIRED);
1280 std::vector<NodeTimer> elapsed_timers =
1281 block->m_node_timers.step((float)dtime);
1282 if (!elapsed_timers.empty()) {
1284 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1285 i != elapsed_timers.end(); ++i) {
1286 n = block->getNodeNoEx(i->position);
1287 p = i->position + block->getPosRelative();
1288 if (m_script->node_on_timer(p, n, i->elapsed)) {
1289 block->setNodeTimer(NodeTimer(
1290 i->timeout, 0, i->position));
1297 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1299 if(m_active_block_interval_overload_skip > 0){
1300 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1301 m_active_block_interval_overload_skip--;
1304 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1305 TimeTaker timer("modify in active blocks per interval");
1307 // Initialize handling of ActiveBlockModifiers
1308 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1310 for(std::set<v3s16>::iterator
1311 i = m_active_blocks.m_list.begin();
1312 i != m_active_blocks.m_list.end(); ++i)
1316 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1317 <<") being handled"<<std::endl;*/
1319 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1323 // Set current time as timestamp
1324 block->setTimestampNoChangedFlag(m_game_time);
1326 /* Handle ActiveBlockModifiers */
1327 abmhandler.apply(block);
1330 u32 time_ms = timer.stop(true);
1331 u32 max_time_ms = 200;
1332 if(time_ms > max_time_ms){
1333 warningstream<<"active block modifiers took "
1334 <<time_ms<<"ms (longer than "
1335 <<max_time_ms<<"ms)"<<std::endl;
1336 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1341 Step script environment (run global on_step())
1343 m_script->environment_Step(dtime);
1349 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1350 //TimeTaker timer("Step active objects");
1352 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1354 // This helps the objects to send data at the same time
1355 bool send_recommended = false;
1356 m_send_recommended_timer += dtime;
1357 if(m_send_recommended_timer > getSendRecommendedInterval())
1359 m_send_recommended_timer -= getSendRecommendedInterval();
1360 send_recommended = true;
1363 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1364 i != m_active_objects.end(); ++i) {
1365 ServerActiveObject* obj = i->second;
1366 // Don't step if is to be removed or stored statically
1367 if(obj->m_removed || obj->m_pending_deactivation)
1370 obj->step(dtime, send_recommended);
1371 // Read messages from object
1372 while(!obj->m_messages_out.empty())
1374 m_active_object_messages.push(
1375 obj->m_messages_out.front());
1376 obj->m_messages_out.pop();
1382 Manage active objects
1384 if(m_object_management_interval.step(dtime, 0.5))
1386 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1388 Remove objects that satisfy (m_removed && m_known_by_count==0)
1390 removeRemovedObjects();
1394 Manage particle spawner expiration
1396 if (m_particle_management_interval.step(dtime, 1.0)) {
1397 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1398 i != m_particle_spawners.end(); ) {
1399 //non expiring spawners
1400 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1406 if (i->second <= 0.f)
1407 m_particle_spawners.erase(i++);
1414 u32 ServerEnvironment::addParticleSpawner(float exptime)
1416 // Timers with lifetime 0 do not expire
1417 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1420 for (;;) { // look for unused particlespawner id
1422 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1423 if (f == m_particle_spawners.end()) {
1424 m_particle_spawners[id] = time;
1431 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1433 u32 id = addParticleSpawner(exptime);
1434 m_particle_spawner_attachments[id] = attached_id;
1435 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1436 obj->attachParticleSpawner(id);
1441 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1443 m_particle_spawners.erase(id);
1444 UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1445 if (it != m_particle_spawner_attachments.end()) {
1446 u16 obj_id = (*it).second;
1447 ServerActiveObject *sao = getActiveObject(obj_id);
1448 if (sao != NULL && remove_from_object) {
1449 sao->detachParticleSpawner(id);
1451 m_particle_spawner_attachments.erase(id);
1455 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1457 ActiveObjectMap::iterator n = m_active_objects.find(id);
1458 return (n != m_active_objects.end() ? n->second : NULL);
1461 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1466 return objects.find(id) == objects.end();
1469 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1471 //try to reuse id's as late as possible
1472 static u16 last_used_id = 0;
1473 u16 startid = last_used_id;
1477 if(isFreeServerActiveObjectId(last_used_id, objects))
1478 return last_used_id;
1480 if(last_used_id == startid)
1485 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1487 assert(object); // Pre-condition
1489 u16 id = addActiveObjectRaw(object, true, 0);
1494 Finds out what new objects have been added to
1495 inside a radius around a position
1497 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1499 std::set<u16> ¤t_objects,
1500 std::queue<u16> &added_objects)
1502 f32 radius_f = radius * BS;
1503 f32 player_radius_f = player_radius * BS;
1505 if (player_radius_f < 0)
1506 player_radius_f = 0;
1508 Go through the object list,
1509 - discard m_removed objects,
1510 - discard objects that are too far away,
1511 - discard objects that are found in current_objects.
1512 - add remaining objects to added_objects
1514 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1515 i != m_active_objects.end(); ++i) {
1519 ServerActiveObject *object = i->second;
1523 // Discard if removed or deactivating
1524 if(object->m_removed || object->m_pending_deactivation)
1527 f32 distance_f = object->getBasePosition().
1528 getDistanceFrom(playersao->getBasePosition());
1529 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1530 // Discard if too far
1531 if (distance_f > player_radius_f && player_radius_f != 0)
1533 } else if (distance_f > radius_f)
1536 // Discard if already on current_objects
1537 std::set<u16>::iterator n;
1538 n = current_objects.find(id);
1539 if(n != current_objects.end())
1541 // Add to added_objects
1542 added_objects.push(id);
1547 Finds out what objects have been removed from
1548 inside a radius around a position
1550 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1552 std::set<u16> ¤t_objects,
1553 std::queue<u16> &removed_objects)
1555 f32 radius_f = radius * BS;
1556 f32 player_radius_f = player_radius * BS;
1558 if (player_radius_f < 0)
1559 player_radius_f = 0;
1561 Go through current_objects; object is removed if:
1562 - object is not found in m_active_objects (this is actually an
1563 error condition; objects should be set m_removed=true and removed
1564 only after all clients have been informed about removal), or
1565 - object has m_removed=true, or
1566 - object is too far away
1568 for(std::set<u16>::iterator
1569 i = current_objects.begin();
1570 i != current_objects.end(); ++i)
1573 ServerActiveObject *object = getActiveObject(id);
1575 if (object == NULL) {
1576 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1577 << " object in current_objects is NULL" << std::endl;
1578 removed_objects.push(id);
1582 if (object->m_removed || object->m_pending_deactivation) {
1583 removed_objects.push(id);
1587 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1588 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1589 if (distance_f <= player_radius_f || player_radius_f == 0)
1591 } else if (distance_f <= radius_f)
1594 // Object is no longer visible
1595 removed_objects.push(id);
1599 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1600 v3s16 blockpos, bool static_exists, v3s16 static_block)
1602 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1606 for (std::map<u16, StaticObject>::iterator
1607 so_it = block->m_static_objects.m_active.begin();
1608 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1609 // Get the ServerActiveObject counterpart to this StaticObject
1610 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1611 if (ao_it == m_active_objects.end()) {
1612 // If this ever happens, there must be some kind of nasty bug.
1613 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1614 "Object from MapBlock::m_static_objects::m_active not found "
1615 "in m_active_objects";
1619 ServerActiveObject *sao = ao_it->second;
1620 sao->m_static_exists = static_exists;
1621 sao->m_static_block = static_block;
1625 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1627 if(m_active_object_messages.empty())
1628 return ActiveObjectMessage(0);
1630 ActiveObjectMessage message = m_active_object_messages.front();
1631 m_active_object_messages.pop();
1636 ************ Private methods *************
1639 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1640 bool set_changed, u32 dtime_s)
1642 assert(object); // Pre-condition
1643 if(object->getId() == 0){
1644 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1647 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1648 <<"no free ids available"<<std::endl;
1649 if(object->environmentDeletes())
1653 object->setId(new_id);
1656 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1657 <<"supplied with id "<<object->getId()<<std::endl;
1660 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1661 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1662 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1663 if(object->environmentDeletes())
1668 if (objectpos_over_limit(object->getBasePosition())) {
1669 v3f p = object->getBasePosition();
1670 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1671 << "object position (" << p.X << "," << p.Y << "," << p.Z
1672 << ") outside maximum range" << std::endl;
1673 if (object->environmentDeletes())
1678 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1679 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1681 m_active_objects[object->getId()] = object;
1683 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1684 <<"Added id="<<object->getId()<<"; there are now "
1685 <<m_active_objects.size()<<" active objects."
1688 // Register reference in scripting api (must be done before post-init)
1689 m_script->addObjectReference(object);
1690 // Post-initialize object
1691 object->addedToEnvironment(dtime_s);
1693 // Add static data to block
1694 if(object->isStaticAllowed())
1696 // Add static object to active static list of the block
1697 v3f objectpos = object->getBasePosition();
1698 std::string staticdata = "";
1699 object->getStaticData(&staticdata);
1700 StaticObject s_obj(object->getType(), objectpos, staticdata);
1701 // Add to the block where the object is located in
1702 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1703 MapBlock *block = m_map->emergeBlock(blockpos);
1705 block->m_static_objects.m_active[object->getId()] = s_obj;
1706 object->m_static_exists = true;
1707 object->m_static_block = blockpos;
1710 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1711 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1713 v3s16 p = floatToInt(objectpos, BS);
1714 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1715 <<"could not emerge block for storing id="<<object->getId()
1716 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1720 return object->getId();
1724 Remove objects that satisfy (m_removed && m_known_by_count==0)
1726 void ServerEnvironment::removeRemovedObjects()
1728 std::vector<u16> objects_to_remove;
1729 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1730 i != m_active_objects.end(); ++i) {
1732 ServerActiveObject* obj = i->second;
1733 // This shouldn't happen but check it
1736 infostream<<"NULL object found in ServerEnvironment"
1737 <<" while finding removed objects. id="<<id<<std::endl;
1738 // Id to be removed from m_active_objects
1739 objects_to_remove.push_back(id);
1744 We will delete objects that are marked as removed or thatare
1745 waiting for deletion after deactivation
1747 if (!obj->m_removed && !obj->m_pending_deactivation)
1751 Delete static data from block if is marked as removed
1753 if(obj->m_static_exists && obj->m_removed)
1755 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1757 block->m_static_objects.remove(id);
1758 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1759 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1760 obj->m_static_exists = false;
1762 infostream<<"Failed to emerge block from which an object to "
1763 <<"be removed was loaded from. id="<<id<<std::endl;
1767 // If m_known_by_count > 0, don't actually remove. On some future
1768 // invocation this will be 0, which is when removal will continue.
1769 if(obj->m_known_by_count > 0)
1773 Move static data from active to stored if not marked as removed
1775 if(obj->m_static_exists && !obj->m_removed){
1776 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1778 std::map<u16, StaticObject>::iterator i =
1779 block->m_static_objects.m_active.find(id);
1780 if(i != block->m_static_objects.m_active.end()){
1781 block->m_static_objects.m_stored.push_back(i->second);
1782 block->m_static_objects.m_active.erase(id);
1783 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1784 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1787 infostream<<"Failed to emerge block from which an object to "
1788 <<"be deactivated was loaded from. id="<<id<<std::endl;
1792 // Tell the object about removal
1793 obj->removingFromEnvironment();
1794 // Deregister in scripting api
1795 m_script->removeObjectReference(obj);
1798 if(obj->environmentDeletes())
1801 // Id to be removed from m_active_objects
1802 objects_to_remove.push_back(id);
1804 // Remove references from m_active_objects
1805 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1806 i != objects_to_remove.end(); ++i) {
1807 m_active_objects.erase(*i);
1811 static void print_hexdump(std::ostream &o, const std::string &data)
1813 const int linelength = 16;
1814 for(int l=0; ; l++){
1815 int i0 = linelength * l;
1816 bool at_end = false;
1817 int thislinelength = linelength;
1818 if(i0 + thislinelength > (int)data.size()){
1819 thislinelength = data.size() - i0;
1822 for(int di=0; di<linelength; di++){
1825 if(di<thislinelength)
1826 snprintf(buf, 4, "%.2x ", data[i]);
1828 snprintf(buf, 4, " ");
1832 for(int di=0; di<thislinelength; di++){
1846 Convert stored objects from blocks near the players to active.
1848 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1853 // Ignore if no stored objects (to not set changed flag)
1854 if(block->m_static_objects.m_stored.empty())
1857 verbosestream<<"ServerEnvironment::activateObjects(): "
1858 <<"activating objects of block "<<PP(block->getPos())
1859 <<" ("<<block->m_static_objects.m_stored.size()
1860 <<" objects)"<<std::endl;
1861 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1863 errorstream<<"suspiciously large amount of objects detected: "
1864 <<block->m_static_objects.m_stored.size()<<" in "
1865 <<PP(block->getPos())
1866 <<"; removing all of them."<<std::endl;
1867 // Clear stored list
1868 block->m_static_objects.m_stored.clear();
1869 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1870 MOD_REASON_TOO_MANY_OBJECTS);
1874 // Activate stored objects
1875 std::vector<StaticObject> new_stored;
1876 for (std::vector<StaticObject>::iterator
1877 i = block->m_static_objects.m_stored.begin();
1878 i != block->m_static_objects.m_stored.end(); ++i) {
1879 StaticObject &s_obj = *i;
1881 // Create an active object from the data
1882 ServerActiveObject *obj = ServerActiveObject::create
1883 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1884 // If couldn't create object, store static data back.
1886 errorstream<<"ServerEnvironment::activateObjects(): "
1887 <<"failed to create active object from static object "
1888 <<"in block "<<PP(s_obj.pos/BS)
1889 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1890 print_hexdump(verbosestream, s_obj.data);
1892 new_stored.push_back(s_obj);
1895 verbosestream<<"ServerEnvironment::activateObjects(): "
1896 <<"activated static object pos="<<PP(s_obj.pos/BS)
1897 <<" type="<<(int)s_obj.type<<std::endl;
1898 // This will also add the object to the active static list
1899 addActiveObjectRaw(obj, false, dtime_s);
1901 // Clear stored list
1902 block->m_static_objects.m_stored.clear();
1903 // Add leftover failed stuff to stored list
1904 for(std::vector<StaticObject>::iterator
1905 i = new_stored.begin();
1906 i != new_stored.end(); ++i) {
1907 StaticObject &s_obj = *i;
1908 block->m_static_objects.m_stored.push_back(s_obj);
1911 // Turn the active counterparts of activated objects not pending for
1913 for(std::map<u16, StaticObject>::iterator
1914 i = block->m_static_objects.m_active.begin();
1915 i != block->m_static_objects.m_active.end(); ++i)
1918 ServerActiveObject *object = getActiveObject(id);
1920 object->m_pending_deactivation = false;
1924 Note: Block hasn't really been modified here.
1925 The objects have just been activated and moved from the stored
1926 static list to the active static list.
1927 As such, the block is essentially the same.
1928 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1929 Otherwise there would be a huge amount of unnecessary I/O.
1934 Convert objects that are not standing inside active blocks to static.
1936 If m_known_by_count != 0, active object is not deleted, but static
1937 data is still updated.
1939 If force_delete is set, active object is deleted nevertheless. It
1940 shall only be set so in the destructor of the environment.
1942 If block wasn't generated (not in memory or on disk),
1944 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1946 std::vector<u16> objects_to_remove;
1947 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1948 i != m_active_objects.end(); ++i) {
1949 // force_delete might be overriden per object
1950 bool force_delete = _force_delete;
1952 ServerActiveObject* obj = i->second;
1955 // Do not deactivate if static data creation not allowed
1956 if(!force_delete && !obj->isStaticAllowed())
1959 // If pending deactivation, let removeRemovedObjects() do it
1960 if(!force_delete && obj->m_pending_deactivation)
1964 v3f objectpos = obj->getBasePosition();
1966 // The block in which the object resides in
1967 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1969 // If object's static data is stored in a deactivated block and object
1970 // is actually located in an active block, re-save to the block in
1971 // which the object is actually located in.
1973 obj->m_static_exists &&
1974 !m_active_blocks.contains(obj->m_static_block) &&
1975 m_active_blocks.contains(blockpos_o))
1977 v3s16 old_static_block = obj->m_static_block;
1979 // Save to block where object is located
1980 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1982 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1983 <<"Could not save object id="<<id
1984 <<" to it's current block "<<PP(blockpos_o)
1988 std::string staticdata_new = "";
1989 obj->getStaticData(&staticdata_new);
1990 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1991 block->m_static_objects.insert(id, s_obj);
1992 obj->m_static_block = blockpos_o;
1993 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1994 MOD_REASON_STATIC_DATA_ADDED);
1996 // Delete from block where object was located
1997 block = m_map->emergeBlock(old_static_block, false);
1999 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2000 <<"Could not delete object id="<<id
2001 <<" from it's previous block "<<PP(old_static_block)
2005 block->m_static_objects.remove(id);
2006 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2007 MOD_REASON_STATIC_DATA_REMOVED);
2011 // If block is active, don't remove
2012 if(!force_delete && m_active_blocks.contains(blockpos_o))
2015 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2016 <<"deactivating object id="<<id<<" on inactive block "
2017 <<PP(blockpos_o)<<std::endl;
2019 // If known by some client, don't immediately delete.
2020 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2023 Update the static data
2026 if(obj->isStaticAllowed())
2028 // Create new static object
2029 std::string staticdata_new = "";
2030 obj->getStaticData(&staticdata_new);
2031 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2033 bool stays_in_same_block = false;
2034 bool data_changed = true;
2036 if (obj->m_static_exists) {
2037 if (obj->m_static_block == blockpos_o)
2038 stays_in_same_block = true;
2040 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2043 std::map<u16, StaticObject>::iterator n =
2044 block->m_static_objects.m_active.find(id);
2045 if (n != block->m_static_objects.m_active.end()) {
2046 StaticObject static_old = n->second;
2048 float save_movem = obj->getMinimumSavedMovement();
2050 if (static_old.data == staticdata_new &&
2051 (static_old.pos - objectpos).getLength() < save_movem)
2052 data_changed = false;
2054 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2055 <<"id="<<id<<" m_static_exists=true but "
2056 <<"static data doesn't actually exist in "
2057 <<PP(obj->m_static_block)<<std::endl;
2062 bool shall_be_written = (!stays_in_same_block || data_changed);
2064 // Delete old static object
2065 if(obj->m_static_exists)
2067 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2070 block->m_static_objects.remove(id);
2071 obj->m_static_exists = false;
2072 // Only mark block as modified if data changed considerably
2073 if(shall_be_written)
2074 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2075 MOD_REASON_STATIC_DATA_CHANGED);
2079 // Add to the block where the object is located in
2080 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2081 // Get or generate the block
2082 MapBlock *block = NULL;
2084 block = m_map->emergeBlock(blockpos);
2085 } catch(InvalidPositionException &e){
2086 // Handled via NULL pointer
2087 // NOTE: emergeBlock's failure is usually determined by it
2088 // actually returning NULL
2093 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2094 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2095 << " statically but block " << PP(blockpos)
2096 << " already contains "
2097 << block->m_static_objects.m_stored.size()
2099 << " Forcing delete." << std::endl;
2100 force_delete = true;
2102 // If static counterpart already exists in target block,
2104 // This shouldn't happen because the object is removed from
2105 // the previous block before this according to
2106 // obj->m_static_block, but happens rarely for some unknown
2107 // reason. Unsuccessful attempts have been made to find
2109 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2110 warningstream<<"ServerEnv: Performing hack #83274"
2112 block->m_static_objects.remove(id);
2114 // Store static data
2115 u16 store_id = pending_delete ? id : 0;
2116 block->m_static_objects.insert(store_id, s_obj);
2118 // Only mark block as modified if data changed considerably
2119 if(shall_be_written)
2120 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2121 MOD_REASON_STATIC_DATA_CHANGED);
2123 obj->m_static_exists = true;
2124 obj->m_static_block = block->getPos();
2129 v3s16 p = floatToInt(objectpos, BS);
2130 errorstream<<"ServerEnv: Could not find or generate "
2131 <<"a block for storing id="<<obj->getId()
2132 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2139 If known by some client, set pending deactivation.
2140 Otherwise delete it immediately.
2143 if(pending_delete && !force_delete)
2145 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2146 <<"object id="<<id<<" is known by clients"
2147 <<"; not deleting yet"<<std::endl;
2149 obj->m_pending_deactivation = true;
2153 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2154 <<"object id="<<id<<" is not known by clients"
2155 <<"; deleting"<<std::endl;
2157 // Tell the object about removal
2158 obj->removingFromEnvironment();
2159 // Deregister in scripting api
2160 m_script->removeObjectReference(obj);
2162 // Delete active object
2163 if(obj->environmentDeletes())
2165 // Id to be removed from m_active_objects
2166 objects_to_remove.push_back(id);
2169 // Remove references from m_active_objects
2170 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2171 i != objects_to_remove.end(); ++i) {
2172 m_active_objects.erase(*i);