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):
359 m_script(scriptIface),
361 m_path_world(path_world),
362 m_send_recommended_timer(0),
363 m_active_block_interval_overload_skip(0),
365 m_game_time_fraction_counter(0),
366 m_last_clear_objects_time(0),
367 m_recommended_send_interval(0.1),
368 m_max_lag_estimate(0.1)
372 ServerEnvironment::~ServerEnvironment()
374 // Clear active block list.
375 // This makes the next one delete all active objects.
376 m_active_blocks.clear();
378 // Convert all objects to static and delete the active objects
379 deactivateFarObjects(true);
384 // Delete ActiveBlockModifiers
385 for (std::vector<ABMWithState>::iterator
386 i = m_abms.begin(); i != m_abms.end(); ++i){
390 // Deallocate players
391 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
392 i != m_players.end(); ++i) {
397 Map & ServerEnvironment::getMap()
402 ServerMap & ServerEnvironment::getServerMap()
407 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
409 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
410 i != m_players.end(); ++i) {
411 RemotePlayer *player = *i;
412 if (player->peer_id == peer_id)
418 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
420 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
421 i != m_players.end(); ++i) {
422 RemotePlayer *player = *i;
423 if (strcmp(player->getName(), name) == 0)
429 void ServerEnvironment::addPlayer(RemotePlayer *player)
431 DSTACK(FUNCTION_NAME);
433 Check that peer_ids are unique.
434 Also check that names are unique.
435 Exception: there can be multiple players with peer_id=0
437 // If peer id is non-zero, it has to be unique.
438 if (player->peer_id != 0)
439 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
440 // Name has to be unique.
441 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
443 m_players.push_back(player);
446 void ServerEnvironment::removePlayer(RemotePlayer *player)
448 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
449 it != m_players.end(); ++it) {
450 if ((*it) == player) {
458 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
460 float distance = pos1.getDistanceFrom(pos2);
462 //calculate normalized direction vector
463 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
464 (pos2.Y - pos1.Y)/distance,
465 (pos2.Z - pos1.Z)/distance);
467 //find out if there's a node on path between pos1 and pos2
468 for (float i = 1; i < distance; i += stepsize) {
469 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
470 normalized_vector.Y * i,
471 normalized_vector.Z * i) +pos1,BS);
473 MapNode n = getMap().getNodeNoEx(pos);
475 if(n.param0 != CONTENT_AIR) {
485 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
486 const std::string &str_reason, bool reconnect)
488 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
489 it != m_players.end(); ++it) {
490 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
491 m_server->DenyAccessVerCompliant(player->peer_id,
492 player->protocol_version, reason, str_reason, reconnect);
496 void ServerEnvironment::saveLoadedPlayers()
498 std::string players_path = m_path_world + DIR_DELIM "players";
499 fs::CreateDir(players_path);
501 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
502 it != m_players.end();
504 if ((*it)->checkModified() ||
505 ((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) {
506 (*it)->save(players_path, m_server);
511 void ServerEnvironment::savePlayer(RemotePlayer *player)
513 std::string players_path = m_path_world + DIR_DELIM "players";
514 fs::CreateDir(players_path);
516 player->save(players_path, m_server);
519 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao)
521 bool newplayer = false;
523 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
524 std::string path = players_path + playername;
526 RemotePlayer *player = getPlayer(playername.c_str());
528 player = new RemotePlayer("", m_server->idef());
532 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
533 //// Open file and deserialize
534 std::ifstream is(path.c_str(), std::ios_base::binary);
538 player->deSerialize(is, path, sao);
541 if (player->getName() == playername) {
546 path = players_path + playername + itos(i);
550 infostream << "Player file for player " << playername
551 << " not found" << std::endl;
561 player->setModified(false);
565 void ServerEnvironment::saveMeta()
567 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
569 // Open file and serialize
570 std::ostringstream ss(std::ios_base::binary);
573 args.setU64("game_time", m_game_time);
574 args.setU64("time_of_day", getTimeOfDay());
575 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
576 args.setU64("lbm_introduction_times_version", 1);
577 args.set("lbm_introduction_times",
578 m_lbm_mgr.createIntroductionTimesString());
579 args.setU64("day_count", m_day_count);
583 if(!fs::safeWriteToFile(path, ss.str()))
585 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
587 throw SerializationError("Couldn't save env meta");
591 void ServerEnvironment::loadMeta()
593 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
595 // Open file and deserialize
596 std::ifstream is(path.c_str(), std::ios_base::binary);
598 infostream << "ServerEnvironment::loadMeta(): Failed to open "
599 << path << std::endl;
600 throw SerializationError("Couldn't load env meta");
605 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
606 throw SerializationError("ServerEnvironment::loadMeta(): "
607 "EnvArgsEnd not found!");
611 m_game_time = args.getU64("game_time");
612 } catch (SettingNotFoundException &e) {
613 // Getting this is crucial, otherwise timestamps are useless
614 throw SerializationError("Couldn't load env meta game_time");
617 setTimeOfDay(args.exists("time_of_day") ?
618 // set day to morning by default
619 args.getU64("time_of_day") : 9000);
621 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
622 // If missing, do as if clearObjects was never called
623 args.getU64("last_clear_objects_time") : 0;
625 std::string lbm_introduction_times = "";
627 u64 ver = args.getU64("lbm_introduction_times_version");
629 lbm_introduction_times = args.get("lbm_introduction_times");
631 infostream << "ServerEnvironment::loadMeta(): Non-supported"
632 << " introduction time version " << ver << std::endl;
634 } catch (SettingNotFoundException &e) {
635 // No problem, this is expected. Just continue with an empty string
637 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time);
639 m_day_count = args.exists("day_count") ?
640 args.getU64("day_count") : 0;
643 void ServerEnvironment::loadDefaultMeta()
645 m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time);
650 ActiveBlockModifier *abm;
652 std::set<content_t> required_neighbors;
658 ServerEnvironment *m_env;
659 std::vector<std::vector<ActiveABM> *> m_aabms;
661 ABMHandler(std::vector<ABMWithState> &abms,
662 float dtime_s, ServerEnvironment *env,
668 INodeDefManager *ndef = env->getGameDef()->ndef();
669 for(std::vector<ABMWithState>::iterator
670 i = abms.begin(); i != abms.end(); ++i) {
671 ActiveBlockModifier *abm = i->abm;
672 float trigger_interval = abm->getTriggerInterval();
673 if(trigger_interval < 0.001)
674 trigger_interval = 0.001;
675 float actual_interval = dtime_s;
678 if(i->timer < trigger_interval)
680 i->timer -= trigger_interval;
681 actual_interval = trigger_interval;
683 float chance = abm->getTriggerChance();
688 if(abm->getSimpleCatchUp()) {
689 float intervals = actual_interval / trigger_interval;
692 aabm.chance = chance / intervals;
696 aabm.chance = chance;
699 std::set<std::string> required_neighbors_s
700 = abm->getRequiredNeighbors();
701 for(std::set<std::string>::iterator
702 i = required_neighbors_s.begin();
703 i != required_neighbors_s.end(); ++i)
705 ndef->getIds(*i, aabm.required_neighbors);
708 std::set<std::string> contents_s = abm->getTriggerContents();
709 for(std::set<std::string>::iterator
710 i = contents_s.begin(); i != contents_s.end(); ++i)
712 std::set<content_t> ids;
713 ndef->getIds(*i, ids);
714 for(std::set<content_t>::const_iterator k = ids.begin();
718 if (c >= m_aabms.size())
719 m_aabms.resize(c + 256, NULL);
721 m_aabms[c] = new std::vector<ActiveABM>;
722 m_aabms[c]->push_back(aabm);
730 for (size_t i = 0; i < m_aabms.size(); i++)
734 // Find out how many objects the given block and its neighbours contain.
735 // Returns the number of objects in the block, and also in 'wider' the
736 // number of objects in the block and all its neighbours. The latter
737 // may an estimate if any neighbours are unloaded.
738 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
741 u32 wider_unknown_count = 0;
742 for(s16 x=-1; x<=1; x++)
743 for(s16 y=-1; y<=1; y++)
744 for(s16 z=-1; z<=1; z++)
746 MapBlock *block2 = map->getBlockNoCreateNoEx(
747 block->getPos() + v3s16(x,y,z));
749 wider_unknown_count++;
752 wider += block2->m_static_objects.m_active.size()
753 + block2->m_static_objects.m_stored.size();
756 u32 active_object_count = block->m_static_objects.m_active.size();
757 u32 wider_known_count = 3*3*3 - wider_unknown_count;
758 wider += wider_unknown_count * wider / wider_known_count;
759 return active_object_count;
762 void apply(MapBlock *block)
764 if(m_aabms.empty() || block->isDummy())
767 ServerMap *map = &m_env->getServerMap();
769 u32 active_object_count_wider;
770 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
771 m_env->m_added_objects = 0;
774 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
775 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
776 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
778 const MapNode &n = block->getNodeUnsafe(p0);
779 content_t c = n.getContent();
781 if (c >= m_aabms.size() || !m_aabms[c])
784 v3s16 p = p0 + block->getPosRelative();
785 for(std::vector<ActiveABM>::iterator
786 i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
787 if(myrand() % i->chance != 0)
791 if(!i->required_neighbors.empty())
794 for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
795 for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
796 for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
801 if (block->isValidPosition(p1)) {
802 // if the neighbor is found on the same map block
803 // get it straight from there
804 const MapNode &n = block->getNodeUnsafe(p1);
807 // otherwise consult the map
808 MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
811 std::set<content_t>::const_iterator k;
812 k = i->required_neighbors.find(c);
813 if(k != i->required_neighbors.end()){
817 // No required neighbor found
822 // Call all the trigger variations
823 i->abm->trigger(m_env, p, n);
824 i->abm->trigger(m_env, p, n,
825 active_object_count, active_object_count_wider);
827 // Count surrounding objects again if the abms added any
828 if(m_env->m_added_objects > 0) {
829 active_object_count = countObjects(block, map, active_object_count_wider);
830 m_env->m_added_objects = 0;
837 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
839 // Reset usage timer immediately, otherwise a block that becomes active
840 // again at around the same time as it would normally be unloaded will
841 // get unloaded incorrectly. (I think this still leaves a small possibility
842 // of a race condition between this and server::AsyncRunStep, which only
843 // some kind of synchronisation will fix, but it at least reduces the window
844 // of opportunity for it to break from seconds to nanoseconds)
845 block->resetUsageTimer();
847 // Get time difference
849 u32 stamp = block->getTimestamp();
850 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
851 dtime_s = m_game_time - stamp;
852 dtime_s += additional_dtime;
854 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
855 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
857 // Remove stored static objects if clearObjects was called since block's timestamp
858 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
859 block->m_static_objects.m_stored.clear();
860 // do not set changed flag to avoid unnecessary mapblock writes
863 // Set current time as timestamp
864 block->setTimestampNoChangedFlag(m_game_time);
866 /*infostream<<"ServerEnvironment::activateBlock(): block is "
867 <<dtime_s<<" seconds old."<<std::endl;*/
869 // Activate stored objects
870 activateObjects(block, dtime_s);
872 /* Handle LoadingBlockModifiers */
873 m_lbm_mgr.applyLBMs(this, block, stamp);
876 std::vector<NodeTimer> elapsed_timers =
877 block->m_node_timers.step((float)dtime_s);
878 if (!elapsed_timers.empty()) {
880 for (std::vector<NodeTimer>::iterator
881 i = elapsed_timers.begin();
882 i != elapsed_timers.end(); ++i){
883 n = block->getNodeNoEx(i->position);
884 v3s16 p = i->position + block->getPosRelative();
885 if (m_script->node_on_timer(p, n, i->elapsed))
886 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
890 /* Handle ActiveBlockModifiers */
891 ABMHandler abmhandler(m_abms, dtime_s, this, false);
892 abmhandler.apply(block);
895 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
897 m_abms.push_back(ABMWithState(abm));
900 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
902 m_lbm_mgr.addLBMDef(lbm);
905 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
907 INodeDefManager *ndef = m_server->ndef();
908 MapNode n_old = m_map->getNodeNoEx(p);
911 if (ndef->get(n_old).has_on_destruct)
912 m_script->node_on_destruct(p, n_old);
915 if (!m_map->addNodeWithEvent(p, n))
918 // Update active VoxelManipulator if a mapgen thread
919 m_map->updateVManip(p);
921 // Call post-destructor
922 if (ndef->get(n_old).has_after_destruct)
923 m_script->node_after_destruct(p, n_old);
926 if (ndef->get(n).has_on_construct)
927 m_script->node_on_construct(p, n);
932 bool ServerEnvironment::removeNode(v3s16 p)
934 INodeDefManager *ndef = m_server->ndef();
935 MapNode n_old = m_map->getNodeNoEx(p);
938 if (ndef->get(n_old).has_on_destruct)
939 m_script->node_on_destruct(p, n_old);
942 // This is slightly optimized compared to addNodeWithEvent(air)
943 if (!m_map->removeNodeWithEvent(p))
946 // Update active VoxelManipulator if a mapgen thread
947 m_map->updateVManip(p);
949 // Call post-destructor
950 if (ndef->get(n_old).has_after_destruct)
951 m_script->node_after_destruct(p, n_old);
953 // Air doesn't require constructor
957 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
959 if (!m_map->addNodeWithEvent(p, n, false))
962 // Update active VoxelManipulator if a mapgen thread
963 m_map->updateVManip(p);
968 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
970 for (ActiveObjectMap::iterator i = m_active_objects.begin();
971 i != m_active_objects.end(); ++i) {
972 ServerActiveObject* obj = i->second;
974 v3f objectpos = obj->getBasePosition();
975 if (objectpos.getDistanceFrom(pos) > radius)
977 objects.push_back(id);
981 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
983 infostream << "ServerEnvironment::clearObjects(): "
984 << "Removing all active objects" << std::endl;
985 std::vector<u16> objects_to_remove;
986 for (ActiveObjectMap::iterator i = m_active_objects.begin();
987 i != m_active_objects.end(); ++i) {
988 ServerActiveObject* obj = i->second;
989 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
992 // Delete static object if block is loaded
993 if (obj->m_static_exists) {
994 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
996 block->m_static_objects.remove(id);
997 block->raiseModified(MOD_STATE_WRITE_NEEDED,
998 MOD_REASON_CLEAR_ALL_OBJECTS);
999 obj->m_static_exists = false;
1002 // If known by some client, don't delete immediately
1003 if (obj->m_known_by_count > 0) {
1004 obj->m_pending_deactivation = true;
1005 obj->m_removed = true;
1009 // Tell the object about removal
1010 obj->removingFromEnvironment();
1011 // Deregister in scripting api
1012 m_script->removeObjectReference(obj);
1014 // Delete active object
1015 if (obj->environmentDeletes())
1017 // Id to be removed from m_active_objects
1018 objects_to_remove.push_back(id);
1021 // Remove references from m_active_objects
1022 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1023 i != objects_to_remove.end(); ++i) {
1024 m_active_objects.erase(*i);
1027 // Get list of loaded blocks
1028 std::vector<v3s16> loaded_blocks;
1029 infostream << "ServerEnvironment::clearObjects(): "
1030 << "Listing all loaded blocks" << std::endl;
1031 m_map->listAllLoadedBlocks(loaded_blocks);
1032 infostream << "ServerEnvironment::clearObjects(): "
1033 << "Done listing all loaded blocks: "
1034 << loaded_blocks.size()<<std::endl;
1036 // Get list of loadable blocks
1037 std::vector<v3s16> loadable_blocks;
1038 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1039 infostream << "ServerEnvironment::clearObjects(): "
1040 << "Listing all loadable blocks" << std::endl;
1041 m_map->listAllLoadableBlocks(loadable_blocks);
1042 infostream << "ServerEnvironment::clearObjects(): "
1043 << "Done listing all loadable blocks: "
1044 << loadable_blocks.size() << std::endl;
1046 loadable_blocks = loaded_blocks;
1049 infostream << "ServerEnvironment::clearObjects(): "
1050 << "Now clearing objects in " << loadable_blocks.size()
1051 << " blocks" << std::endl;
1053 // Grab a reference on each loaded block to avoid unloading it
1054 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1055 i != loaded_blocks.end(); ++i) {
1057 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1058 assert(block != NULL);
1062 // Remove objects in all loadable blocks
1063 u32 unload_interval = U32_MAX;
1064 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1065 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1066 unload_interval = MYMAX(unload_interval, 1);
1068 u32 report_interval = loadable_blocks.size() / 10;
1069 u32 num_blocks_checked = 0;
1070 u32 num_blocks_cleared = 0;
1071 u32 num_objs_cleared = 0;
1072 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1073 i != loadable_blocks.end(); ++i) {
1075 MapBlock *block = m_map->emergeBlock(p, false);
1077 errorstream << "ServerEnvironment::clearObjects(): "
1078 << "Failed to emerge block " << PP(p) << std::endl;
1081 u32 num_stored = block->m_static_objects.m_stored.size();
1082 u32 num_active = block->m_static_objects.m_active.size();
1083 if (num_stored != 0 || num_active != 0) {
1084 block->m_static_objects.m_stored.clear();
1085 block->m_static_objects.m_active.clear();
1086 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1087 MOD_REASON_CLEAR_ALL_OBJECTS);
1088 num_objs_cleared += num_stored + num_active;
1089 num_blocks_cleared++;
1091 num_blocks_checked++;
1093 if (report_interval != 0 &&
1094 num_blocks_checked % report_interval == 0) {
1095 float percent = 100.0 * (float)num_blocks_checked /
1096 loadable_blocks.size();
1097 infostream << "ServerEnvironment::clearObjects(): "
1098 << "Cleared " << num_objs_cleared << " objects"
1099 << " in " << num_blocks_cleared << " blocks ("
1100 << percent << "%)" << std::endl;
1102 if (num_blocks_checked % unload_interval == 0) {
1103 m_map->unloadUnreferencedBlocks();
1106 m_map->unloadUnreferencedBlocks();
1108 // Drop references that were added above
1109 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1110 i != loaded_blocks.end(); ++i) {
1112 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1117 m_last_clear_objects_time = m_game_time;
1119 infostream << "ServerEnvironment::clearObjects(): "
1120 << "Finished: Cleared " << num_objs_cleared << " objects"
1121 << " in " << num_blocks_cleared << " blocks" << std::endl;
1124 void ServerEnvironment::step(float dtime)
1126 DSTACK(FUNCTION_NAME);
1128 //TimeTaker timer("ServerEnv step");
1130 /* Step time of day */
1131 stepTimeOfDay(dtime);
1134 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1135 // really matter that much.
1136 static const float server_step = g_settings->getFloat("dedicated_server_step");
1137 m_recommended_send_interval = server_step;
1143 m_game_time_fraction_counter += dtime;
1144 u32 inc_i = (u32)m_game_time_fraction_counter;
1145 m_game_time += inc_i;
1146 m_game_time_fraction_counter -= (float)inc_i;
1153 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1154 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1155 i != m_players.end(); ++i) {
1156 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1159 // Ignore disconnected players
1160 if(player->peer_id == 0)
1164 player->move(dtime, this, 100*BS);
1169 Manage active block list
1171 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1172 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1174 Get player block positions
1176 std::vector<v3s16> players_blockpos;
1177 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1178 i != m_players.end(); ++i) {
1179 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1182 // Ignore disconnected players
1183 if (player->peer_id == 0)
1186 PlayerSAO *playersao = player->getPlayerSAO();
1189 v3s16 blockpos = getNodeBlockPos(
1190 floatToInt(playersao->getBasePosition(), BS));
1191 players_blockpos.push_back(blockpos);
1195 Update list of active blocks, collecting changes
1197 static const s16 active_block_range = g_settings->getS16("active_block_range");
1198 std::set<v3s16> blocks_removed;
1199 std::set<v3s16> blocks_added;
1200 m_active_blocks.update(players_blockpos, active_block_range,
1201 blocks_removed, blocks_added);
1204 Handle removed blocks
1207 // Convert active objects that are no more in active blocks to static
1208 deactivateFarObjects(false);
1210 for(std::set<v3s16>::iterator
1211 i = blocks_removed.begin();
1212 i != blocks_removed.end(); ++i) {
1215 /* infostream<<"Server: Block " << PP(p)
1216 << " became inactive"<<std::endl; */
1218 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1222 // Set current time as timestamp (and let it set ChangedFlag)
1223 block->setTimestamp(m_game_time);
1230 for(std::set<v3s16>::iterator
1231 i = blocks_added.begin();
1232 i != blocks_added.end(); ++i)
1236 MapBlock *block = m_map->getBlockOrEmerge(p);
1238 m_active_blocks.m_list.erase(p);
1242 activateBlock(block);
1243 /* infostream<<"Server: Block " << PP(p)
1244 << " became active"<<std::endl; */
1249 Mess around in active blocks
1251 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1252 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1254 float dtime = m_cache_nodetimer_interval;
1256 for(std::set<v3s16>::iterator
1257 i = m_active_blocks.m_list.begin();
1258 i != m_active_blocks.m_list.end(); ++i)
1262 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1263 <<") being handled"<<std::endl;*/
1265 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1269 // Reset block usage timer
1270 block->resetUsageTimer();
1272 // Set current time as timestamp
1273 block->setTimestampNoChangedFlag(m_game_time);
1274 // If time has changed much from the one on disk,
1275 // set block to be saved when it is unloaded
1276 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1277 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1278 MOD_REASON_BLOCK_EXPIRED);
1281 std::vector<NodeTimer> elapsed_timers =
1282 block->m_node_timers.step((float)dtime);
1283 if (!elapsed_timers.empty()) {
1285 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1286 i != elapsed_timers.end(); ++i) {
1287 n = block->getNodeNoEx(i->position);
1288 p = i->position + block->getPosRelative();
1289 if (m_script->node_on_timer(p, n, i->elapsed)) {
1290 block->setNodeTimer(NodeTimer(
1291 i->timeout, 0, i->position));
1298 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1300 if(m_active_block_interval_overload_skip > 0){
1301 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1302 m_active_block_interval_overload_skip--;
1305 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1306 TimeTaker timer("modify in active blocks per interval");
1308 // Initialize handling of ActiveBlockModifiers
1309 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1311 for(std::set<v3s16>::iterator
1312 i = m_active_blocks.m_list.begin();
1313 i != m_active_blocks.m_list.end(); ++i)
1317 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1318 <<") being handled"<<std::endl;*/
1320 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1324 // Set current time as timestamp
1325 block->setTimestampNoChangedFlag(m_game_time);
1327 /* Handle ActiveBlockModifiers */
1328 abmhandler.apply(block);
1331 u32 time_ms = timer.stop(true);
1332 u32 max_time_ms = 200;
1333 if(time_ms > max_time_ms){
1334 warningstream<<"active block modifiers took "
1335 <<time_ms<<"ms (longer than "
1336 <<max_time_ms<<"ms)"<<std::endl;
1337 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1342 Step script environment (run global on_step())
1344 m_script->environment_Step(dtime);
1350 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1351 //TimeTaker timer("Step active objects");
1353 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1355 // This helps the objects to send data at the same time
1356 bool send_recommended = false;
1357 m_send_recommended_timer += dtime;
1358 if(m_send_recommended_timer > getSendRecommendedInterval())
1360 m_send_recommended_timer -= getSendRecommendedInterval();
1361 send_recommended = true;
1364 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1365 i != m_active_objects.end(); ++i) {
1366 ServerActiveObject* obj = i->second;
1367 // Don't step if is to be removed or stored statically
1368 if(obj->m_removed || obj->m_pending_deactivation)
1371 obj->step(dtime, send_recommended);
1372 // Read messages from object
1373 while(!obj->m_messages_out.empty())
1375 m_active_object_messages.push(
1376 obj->m_messages_out.front());
1377 obj->m_messages_out.pop();
1383 Manage active objects
1385 if(m_object_management_interval.step(dtime, 0.5))
1387 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1389 Remove objects that satisfy (m_removed && m_known_by_count==0)
1391 removeRemovedObjects();
1395 Manage particle spawner expiration
1397 if (m_particle_management_interval.step(dtime, 1.0)) {
1398 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1399 i != m_particle_spawners.end(); ) {
1400 //non expiring spawners
1401 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1407 if (i->second <= 0.f)
1408 m_particle_spawners.erase(i++);
1415 u32 ServerEnvironment::addParticleSpawner(float exptime)
1417 // Timers with lifetime 0 do not expire
1418 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1421 for (;;) { // look for unused particlespawner id
1423 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1424 if (f == m_particle_spawners.end()) {
1425 m_particle_spawners[id] = time;
1432 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1434 u32 id = addParticleSpawner(exptime);
1435 m_particle_spawner_attachments[id] = attached_id;
1436 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1437 obj->attachParticleSpawner(id);
1442 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1444 m_particle_spawners.erase(id);
1445 UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1446 if (it != m_particle_spawner_attachments.end()) {
1447 u16 obj_id = (*it).second;
1448 ServerActiveObject *sao = getActiveObject(obj_id);
1449 if (sao != NULL && remove_from_object) {
1450 sao->detachParticleSpawner(id);
1452 m_particle_spawner_attachments.erase(id);
1456 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1458 ActiveObjectMap::iterator n = m_active_objects.find(id);
1459 return (n != m_active_objects.end() ? n->second : NULL);
1462 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1467 return objects.find(id) == objects.end();
1470 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1472 //try to reuse id's as late as possible
1473 static u16 last_used_id = 0;
1474 u16 startid = last_used_id;
1478 if(isFreeServerActiveObjectId(last_used_id, objects))
1479 return last_used_id;
1481 if(last_used_id == startid)
1486 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1488 assert(object); // Pre-condition
1490 u16 id = addActiveObjectRaw(object, true, 0);
1495 Finds out what new objects have been added to
1496 inside a radius around a position
1498 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1500 std::set<u16> ¤t_objects,
1501 std::queue<u16> &added_objects)
1503 f32 radius_f = radius * BS;
1504 f32 player_radius_f = player_radius * BS;
1506 if (player_radius_f < 0)
1507 player_radius_f = 0;
1509 Go through the object list,
1510 - discard m_removed objects,
1511 - discard objects that are too far away,
1512 - discard objects that are found in current_objects.
1513 - add remaining objects to added_objects
1515 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1516 i != m_active_objects.end(); ++i) {
1520 ServerActiveObject *object = i->second;
1524 // Discard if removed or deactivating
1525 if(object->m_removed || object->m_pending_deactivation)
1528 f32 distance_f = object->getBasePosition().
1529 getDistanceFrom(playersao->getBasePosition());
1530 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1531 // Discard if too far
1532 if (distance_f > player_radius_f && player_radius_f != 0)
1534 } else if (distance_f > radius_f)
1537 // Discard if already on current_objects
1538 std::set<u16>::iterator n;
1539 n = current_objects.find(id);
1540 if(n != current_objects.end())
1542 // Add to added_objects
1543 added_objects.push(id);
1548 Finds out what objects have been removed from
1549 inside a radius around a position
1551 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1553 std::set<u16> ¤t_objects,
1554 std::queue<u16> &removed_objects)
1556 f32 radius_f = radius * BS;
1557 f32 player_radius_f = player_radius * BS;
1559 if (player_radius_f < 0)
1560 player_radius_f = 0;
1562 Go through current_objects; object is removed if:
1563 - object is not found in m_active_objects (this is actually an
1564 error condition; objects should be set m_removed=true and removed
1565 only after all clients have been informed about removal), or
1566 - object has m_removed=true, or
1567 - object is too far away
1569 for(std::set<u16>::iterator
1570 i = current_objects.begin();
1571 i != current_objects.end(); ++i)
1574 ServerActiveObject *object = getActiveObject(id);
1576 if (object == NULL) {
1577 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1578 << " object in current_objects is NULL" << std::endl;
1579 removed_objects.push(id);
1583 if (object->m_removed || object->m_pending_deactivation) {
1584 removed_objects.push(id);
1588 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1589 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1590 if (distance_f <= player_radius_f || player_radius_f == 0)
1592 } else if (distance_f <= radius_f)
1595 // Object is no longer visible
1596 removed_objects.push(id);
1600 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1601 v3s16 blockpos, bool static_exists, v3s16 static_block)
1603 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1607 for (std::map<u16, StaticObject>::iterator
1608 so_it = block->m_static_objects.m_active.begin();
1609 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1610 // Get the ServerActiveObject counterpart to this StaticObject
1611 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1612 if (ao_it == m_active_objects.end()) {
1613 // If this ever happens, there must be some kind of nasty bug.
1614 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1615 "Object from MapBlock::m_static_objects::m_active not found "
1616 "in m_active_objects";
1620 ServerActiveObject *sao = ao_it->second;
1621 sao->m_static_exists = static_exists;
1622 sao->m_static_block = static_block;
1626 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1628 if(m_active_object_messages.empty())
1629 return ActiveObjectMessage(0);
1631 ActiveObjectMessage message = m_active_object_messages.front();
1632 m_active_object_messages.pop();
1637 ************ Private methods *************
1640 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1641 bool set_changed, u32 dtime_s)
1643 assert(object); // Pre-condition
1644 if(object->getId() == 0){
1645 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1648 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1649 <<"no free ids available"<<std::endl;
1650 if(object->environmentDeletes())
1654 object->setId(new_id);
1657 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1658 <<"supplied with id "<<object->getId()<<std::endl;
1661 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1662 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1663 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1664 if(object->environmentDeletes())
1669 if (objectpos_over_limit(object->getBasePosition())) {
1670 v3f p = object->getBasePosition();
1671 warningstream << "ServerEnvironment::addActiveObjectRaw(): "
1672 << "object position (" << p.X << "," << p.Y << "," << p.Z
1673 << ") outside maximum range" << std::endl;
1674 if (object->environmentDeletes())
1679 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1680 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1682 m_active_objects[object->getId()] = object;
1684 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1685 <<"Added id="<<object->getId()<<"; there are now "
1686 <<m_active_objects.size()<<" active objects."
1689 // Register reference in scripting api (must be done before post-init)
1690 m_script->addObjectReference(object);
1691 // Post-initialize object
1692 object->addedToEnvironment(dtime_s);
1694 // Add static data to block
1695 if(object->isStaticAllowed())
1697 // Add static object to active static list of the block
1698 v3f objectpos = object->getBasePosition();
1699 std::string staticdata = "";
1700 object->getStaticData(&staticdata);
1701 StaticObject s_obj(object->getType(), objectpos, staticdata);
1702 // Add to the block where the object is located in
1703 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1704 MapBlock *block = m_map->emergeBlock(blockpos);
1706 block->m_static_objects.m_active[object->getId()] = s_obj;
1707 object->m_static_exists = true;
1708 object->m_static_block = blockpos;
1711 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1712 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1714 v3s16 p = floatToInt(objectpos, BS);
1715 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1716 <<"could not emerge block for storing id="<<object->getId()
1717 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1721 return object->getId();
1725 Remove objects that satisfy (m_removed && m_known_by_count==0)
1727 void ServerEnvironment::removeRemovedObjects()
1729 std::vector<u16> objects_to_remove;
1730 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1731 i != m_active_objects.end(); ++i) {
1733 ServerActiveObject* obj = i->second;
1734 // This shouldn't happen but check it
1737 infostream<<"NULL object found in ServerEnvironment"
1738 <<" while finding removed objects. id="<<id<<std::endl;
1739 // Id to be removed from m_active_objects
1740 objects_to_remove.push_back(id);
1745 We will delete objects that are marked as removed or thatare
1746 waiting for deletion after deactivation
1748 if (!obj->m_removed && !obj->m_pending_deactivation)
1752 Delete static data from block if is marked as removed
1754 if(obj->m_static_exists && obj->m_removed)
1756 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1758 block->m_static_objects.remove(id);
1759 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1760 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1761 obj->m_static_exists = false;
1763 infostream<<"Failed to emerge block from which an object to "
1764 <<"be removed was loaded from. id="<<id<<std::endl;
1768 // If m_known_by_count > 0, don't actually remove. On some future
1769 // invocation this will be 0, which is when removal will continue.
1770 if(obj->m_known_by_count > 0)
1774 Move static data from active to stored if not marked as removed
1776 if(obj->m_static_exists && !obj->m_removed){
1777 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1779 std::map<u16, StaticObject>::iterator i =
1780 block->m_static_objects.m_active.find(id);
1781 if(i != block->m_static_objects.m_active.end()){
1782 block->m_static_objects.m_stored.push_back(i->second);
1783 block->m_static_objects.m_active.erase(id);
1784 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1785 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1788 infostream<<"Failed to emerge block from which an object to "
1789 <<"be deactivated was loaded from. id="<<id<<std::endl;
1793 // Tell the object about removal
1794 obj->removingFromEnvironment();
1795 // Deregister in scripting api
1796 m_script->removeObjectReference(obj);
1799 if(obj->environmentDeletes())
1802 // Id to be removed from m_active_objects
1803 objects_to_remove.push_back(id);
1805 // Remove references from m_active_objects
1806 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1807 i != objects_to_remove.end(); ++i) {
1808 m_active_objects.erase(*i);
1812 static void print_hexdump(std::ostream &o, const std::string &data)
1814 const int linelength = 16;
1815 for(int l=0; ; l++){
1816 int i0 = linelength * l;
1817 bool at_end = false;
1818 int thislinelength = linelength;
1819 if(i0 + thislinelength > (int)data.size()){
1820 thislinelength = data.size() - i0;
1823 for(int di=0; di<linelength; di++){
1826 if(di<thislinelength)
1827 snprintf(buf, 4, "%.2x ", data[i]);
1829 snprintf(buf, 4, " ");
1833 for(int di=0; di<thislinelength; di++){
1847 Convert stored objects from blocks near the players to active.
1849 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1854 // Ignore if no stored objects (to not set changed flag)
1855 if(block->m_static_objects.m_stored.empty())
1858 verbosestream<<"ServerEnvironment::activateObjects(): "
1859 <<"activating objects of block "<<PP(block->getPos())
1860 <<" ("<<block->m_static_objects.m_stored.size()
1861 <<" objects)"<<std::endl;
1862 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1864 errorstream<<"suspiciously large amount of objects detected: "
1865 <<block->m_static_objects.m_stored.size()<<" in "
1866 <<PP(block->getPos())
1867 <<"; removing all of them."<<std::endl;
1868 // Clear stored list
1869 block->m_static_objects.m_stored.clear();
1870 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1871 MOD_REASON_TOO_MANY_OBJECTS);
1875 // Activate stored objects
1876 std::vector<StaticObject> new_stored;
1877 for (std::vector<StaticObject>::iterator
1878 i = block->m_static_objects.m_stored.begin();
1879 i != block->m_static_objects.m_stored.end(); ++i) {
1880 StaticObject &s_obj = *i;
1882 // Create an active object from the data
1883 ServerActiveObject *obj = ServerActiveObject::create
1884 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1885 // If couldn't create object, store static data back.
1887 errorstream<<"ServerEnvironment::activateObjects(): "
1888 <<"failed to create active object from static object "
1889 <<"in block "<<PP(s_obj.pos/BS)
1890 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1891 print_hexdump(verbosestream, s_obj.data);
1893 new_stored.push_back(s_obj);
1896 verbosestream<<"ServerEnvironment::activateObjects(): "
1897 <<"activated static object pos="<<PP(s_obj.pos/BS)
1898 <<" type="<<(int)s_obj.type<<std::endl;
1899 // This will also add the object to the active static list
1900 addActiveObjectRaw(obj, false, dtime_s);
1902 // Clear stored list
1903 block->m_static_objects.m_stored.clear();
1904 // Add leftover failed stuff to stored list
1905 for(std::vector<StaticObject>::iterator
1906 i = new_stored.begin();
1907 i != new_stored.end(); ++i) {
1908 StaticObject &s_obj = *i;
1909 block->m_static_objects.m_stored.push_back(s_obj);
1912 // Turn the active counterparts of activated objects not pending for
1914 for(std::map<u16, StaticObject>::iterator
1915 i = block->m_static_objects.m_active.begin();
1916 i != block->m_static_objects.m_active.end(); ++i)
1919 ServerActiveObject *object = getActiveObject(id);
1921 object->m_pending_deactivation = false;
1925 Note: Block hasn't really been modified here.
1926 The objects have just been activated and moved from the stored
1927 static list to the active static list.
1928 As such, the block is essentially the same.
1929 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1930 Otherwise there would be a huge amount of unnecessary I/O.
1935 Convert objects that are not standing inside active blocks to static.
1937 If m_known_by_count != 0, active object is not deleted, but static
1938 data is still updated.
1940 If force_delete is set, active object is deleted nevertheless. It
1941 shall only be set so in the destructor of the environment.
1943 If block wasn't generated (not in memory or on disk),
1945 void ServerEnvironment::deactivateFarObjects(bool _force_delete)
1947 std::vector<u16> objects_to_remove;
1948 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1949 i != m_active_objects.end(); ++i) {
1950 // force_delete might be overriden per object
1951 bool force_delete = _force_delete;
1953 ServerActiveObject* obj = i->second;
1956 // Do not deactivate if static data creation not allowed
1957 if(!force_delete && !obj->isStaticAllowed())
1960 // If pending deactivation, let removeRemovedObjects() do it
1961 if(!force_delete && obj->m_pending_deactivation)
1965 v3f objectpos = obj->getBasePosition();
1967 // The block in which the object resides in
1968 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1970 // If object's static data is stored in a deactivated block and object
1971 // is actually located in an active block, re-save to the block in
1972 // which the object is actually located in.
1974 obj->m_static_exists &&
1975 !m_active_blocks.contains(obj->m_static_block) &&
1976 m_active_blocks.contains(blockpos_o))
1978 v3s16 old_static_block = obj->m_static_block;
1980 // Save to block where object is located
1981 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1983 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1984 <<"Could not save object id="<<id
1985 <<" to it's current block "<<PP(blockpos_o)
1989 std::string staticdata_new = "";
1990 obj->getStaticData(&staticdata_new);
1991 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1992 block->m_static_objects.insert(id, s_obj);
1993 obj->m_static_block = blockpos_o;
1994 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1995 MOD_REASON_STATIC_DATA_ADDED);
1997 // Delete from block where object was located
1998 block = m_map->emergeBlock(old_static_block, false);
2000 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2001 <<"Could not delete object id="<<id
2002 <<" from it's previous block "<<PP(old_static_block)
2006 block->m_static_objects.remove(id);
2007 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2008 MOD_REASON_STATIC_DATA_REMOVED);
2012 // If block is active, don't remove
2013 if(!force_delete && m_active_blocks.contains(blockpos_o))
2016 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2017 <<"deactivating object id="<<id<<" on inactive block "
2018 <<PP(blockpos_o)<<std::endl;
2020 // If known by some client, don't immediately delete.
2021 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2024 Update the static data
2027 if(obj->isStaticAllowed())
2029 // Create new static object
2030 std::string staticdata_new = "";
2031 obj->getStaticData(&staticdata_new);
2032 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2034 bool stays_in_same_block = false;
2035 bool data_changed = true;
2037 if (obj->m_static_exists) {
2038 if (obj->m_static_block == blockpos_o)
2039 stays_in_same_block = true;
2041 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2044 std::map<u16, StaticObject>::iterator n =
2045 block->m_static_objects.m_active.find(id);
2046 if (n != block->m_static_objects.m_active.end()) {
2047 StaticObject static_old = n->second;
2049 float save_movem = obj->getMinimumSavedMovement();
2051 if (static_old.data == staticdata_new &&
2052 (static_old.pos - objectpos).getLength() < save_movem)
2053 data_changed = false;
2055 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2056 <<"id="<<id<<" m_static_exists=true but "
2057 <<"static data doesn't actually exist in "
2058 <<PP(obj->m_static_block)<<std::endl;
2063 bool shall_be_written = (!stays_in_same_block || data_changed);
2065 // Delete old static object
2066 if(obj->m_static_exists)
2068 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2071 block->m_static_objects.remove(id);
2072 obj->m_static_exists = false;
2073 // Only mark block as modified if data changed considerably
2074 if(shall_be_written)
2075 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2076 MOD_REASON_STATIC_DATA_CHANGED);
2080 // Add to the block where the object is located in
2081 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2082 // Get or generate the block
2083 MapBlock *block = NULL;
2085 block = m_map->emergeBlock(blockpos);
2086 } catch(InvalidPositionException &e){
2087 // Handled via NULL pointer
2088 // NOTE: emergeBlock's failure is usually determined by it
2089 // actually returning NULL
2094 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2095 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2096 << " statically but block " << PP(blockpos)
2097 << " already contains "
2098 << block->m_static_objects.m_stored.size()
2100 << " Forcing delete." << std::endl;
2101 force_delete = true;
2103 // If static counterpart already exists in target block,
2105 // This shouldn't happen because the object is removed from
2106 // the previous block before this according to
2107 // obj->m_static_block, but happens rarely for some unknown
2108 // reason. Unsuccessful attempts have been made to find
2110 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2111 warningstream<<"ServerEnv: Performing hack #83274"
2113 block->m_static_objects.remove(id);
2115 // Store static data
2116 u16 store_id = pending_delete ? id : 0;
2117 block->m_static_objects.insert(store_id, s_obj);
2119 // Only mark block as modified if data changed considerably
2120 if(shall_be_written)
2121 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2122 MOD_REASON_STATIC_DATA_CHANGED);
2124 obj->m_static_exists = true;
2125 obj->m_static_block = block->getPos();
2130 v3s16 p = floatToInt(objectpos, BS);
2131 errorstream<<"ServerEnv: Could not find or generate "
2132 <<"a block for storing id="<<obj->getId()
2133 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2140 If known by some client, set pending deactivation.
2141 Otherwise delete it immediately.
2144 if(pending_delete && !force_delete)
2146 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2147 <<"object id="<<id<<" is known by clients"
2148 <<"; not deleting yet"<<std::endl;
2150 obj->m_pending_deactivation = true;
2154 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2155 <<"object id="<<id<<" is not known by clients"
2156 <<"; deleting"<<std::endl;
2158 // Tell the object about removal
2159 obj->removingFromEnvironment();
2160 // Deregister in scripting api
2161 m_script->removeObjectReference(obj);
2163 // Delete active object
2164 if(obj->environmentDeletes())
2166 // Id to be removed from m_active_objects
2167 objects_to_remove.push_back(id);
2170 // Remove references from m_active_objects
2171 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2172 i != objects_to_remove.end(); ++i) {
2173 m_active_objects.erase(*i);