3 Copyright (C) 2010-2013 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.
21 #include "environment.h"
24 #include "collision.h"
25 #include "content_mapnode.h"
27 #include "serverobject.h"
28 #include "content_sao.h"
32 #include "scripting_game.h"
34 #include "nodemetadata.h"
37 #include "clientmap.h"
38 #include "localplayer.h"
39 #include "mapblock_mesh.h"
43 #include "daynightratio.h"
47 #include "voxelalgorithms.h"
48 #include "util/serialize.h"
49 #include "util/basic_macros.h"
50 #include "util/pointedthing.h"
51 #include "threading/mutex_auto_lock.h"
53 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
55 // A number that is much smaller than the timeout for particle spawners should/could ever be
56 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
58 Environment::Environment():
59 m_time_of_day_speed(0),
61 m_time_of_day_f(9000./24000),
62 m_time_conversion_skew(0.0f),
63 m_enable_day_night_ratio_override(false),
64 m_day_night_ratio_override(0.0f)
66 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
67 m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval");
68 m_cache_abm_interval = g_settings->getFloat("abm_interval");
69 m_cache_nodetimer_interval = g_settings->getFloat("nodetimer_interval");
72 Environment::~Environment()
76 u32 Environment::getDayNightRatio()
78 MutexAutoLock lock(this->m_time_lock);
79 if (m_enable_day_night_ratio_override)
80 return m_day_night_ratio_override;
81 return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
84 void Environment::setTimeOfDaySpeed(float speed)
86 m_time_of_day_speed = speed;
89 void Environment::setDayNightRatioOverride(bool enable, u32 value)
91 MutexAutoLock lock(this->m_time_lock);
92 m_enable_day_night_ratio_override = enable;
93 m_day_night_ratio_override = value;
96 void Environment::setTimeOfDay(u32 time)
98 MutexAutoLock lock(this->m_time_lock);
99 if (m_time_of_day > time)
101 m_time_of_day = time;
102 m_time_of_day_f = (float)time / 24000.0;
105 u32 Environment::getTimeOfDay()
107 MutexAutoLock lock(this->m_time_lock);
108 return m_time_of_day;
111 float Environment::getTimeOfDayF()
113 MutexAutoLock lock(this->m_time_lock);
114 return m_time_of_day_f;
117 void Environment::stepTimeOfDay(float dtime)
119 MutexAutoLock lock(this->m_time_lock);
121 // Cached in order to prevent the two reads we do to give
122 // different results (can be written by code not under the lock)
123 f32 cached_time_of_day_speed = m_time_of_day_speed;
125 f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
126 m_time_conversion_skew += dtime;
127 u32 units = (u32)(m_time_conversion_skew * speed);
131 if (m_time_of_day + units >= 24000) {
135 m_time_of_day = (m_time_of_day + units) % 24000;
137 m_time_of_day_f = (float)m_time_of_day / 24000.0;
140 m_time_conversion_skew -= (f32)units / speed;
143 m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
144 if (m_time_of_day_f > 1.0)
145 m_time_of_day_f -= 1.0;
146 if (m_time_of_day_f < 0.0)
147 m_time_of_day_f += 1.0;
151 u32 Environment::getDayCount()
153 // Atomic<u32> counter
162 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
166 // Initialize timer to random value to spread processing
167 float itv = abm->getTriggerInterval();
168 itv = MYMAX(0.001, itv); // No less than 1ms
169 int minval = MYMAX(-0.51*itv, -60); // Clamp to
170 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
171 timer = myrand_range(minval, maxval);
178 void LBMContentMapping::deleteContents()
180 for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
181 it != lbm_list.end(); ++it) {
186 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
188 // Add the lbm_def to the LBMContentMapping.
189 // Unknown names get added to the global NameIdMapping.
190 INodeDefManager *nodedef = gamedef->ndef();
192 lbm_list.push_back(lbm_def);
194 for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
195 it != lbm_def->trigger_contents.end(); ++it) {
196 std::set<content_t> c_ids;
197 bool found = nodedef->getIds(*it, c_ids);
199 content_t c_id = gamedef->allocateUnknownNodeId(*it);
200 if (c_id == CONTENT_IGNORE) {
201 // Seems it can't be allocated.
202 warningstream << "Could not internalize node name \"" << *it
203 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
209 for (std::set<content_t>::const_iterator iit =
210 c_ids.begin(); iit != c_ids.end(); ++iit) {
211 content_t c_id = *iit;
212 map[c_id].push_back(lbm_def);
217 const std::vector<LoadingBlockModifierDef *> *
218 LBMContentMapping::lookup(content_t c) const
220 container_map::const_iterator it = map.find(c);
223 // This first dereferences the iterator, returning
224 // a std::vector<LoadingBlockModifierDef *>
225 // reference, then we convert it to a pointer.
226 return &(it->second);
229 LBMManager::~LBMManager()
231 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
232 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
235 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
236 it != m_lbm_lookup.end(); ++it) {
237 (it->second).deleteContents();
241 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
243 // Precondition, in query mode the map isn't used anymore
244 FATAL_ERROR_IF(m_query_mode == true,
245 "attempted to modify LBMManager in query mode");
247 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
248 throw ModError("Error adding LBM \"" + lbm_def->name +
249 "\": Does not follow naming conventions: "
250 "Only chararacters [a-z0-9_:] are allowed.");
253 m_lbm_defs[lbm_def->name] = lbm_def;
256 void LBMManager::loadIntroductionTimes(const std::string ×,
257 IGameDef *gamedef, u32 now)
262 // Storing it in a map first instead of
263 // handling the stuff directly in the loop
264 // removes all duplicate entries.
265 // TODO make this std::unordered_map
266 std::map<std::string, u32> introduction_times;
269 The introduction times string consists of name~time entries,
270 with each entry terminated by a semicolon. The time is decimal.
275 while ((idx_new = times.find(";", idx)) != std::string::npos) {
276 std::string entry = times.substr(idx, idx_new - idx);
277 std::vector<std::string> components = str_split(entry, '~');
278 if (components.size() != 2)
279 throw SerializationError("Introduction times entry \""
280 + entry + "\" requires exactly one '~'!");
281 const std::string &name = components[0];
282 u32 time = from_string<u32>(components[1]);
283 introduction_times[name] = time;
287 // Put stuff from introduction_times into m_lbm_lookup
288 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
289 it != introduction_times.end(); ++it) {
290 const std::string &name = it->first;
291 u32 time = it->second;
293 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
294 m_lbm_defs.find(name);
295 if (def_it == m_lbm_defs.end()) {
296 // This seems to be an LBM entry for
297 // an LBM we haven't loaded. Discard it.
300 LoadingBlockModifierDef *lbm_def = def_it->second;
301 if (lbm_def->run_at_every_load) {
302 // This seems to be an LBM entry for
303 // an LBM that runs at every load.
304 // Don't add it just yet.
308 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
310 // Erase the entry so that we know later
311 // what elements didn't get put into m_lbm_lookup
312 m_lbm_defs.erase(name);
315 // Now also add the elements from m_lbm_defs to m_lbm_lookup
316 // that weren't added in the previous step.
317 // They are introduced first time to this world,
318 // or are run at every load (introducement time hardcoded to U32_MAX).
320 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
321 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
323 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
324 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
325 if (it->second->run_at_every_load) {
326 lbms_running_always.addLBM(it->second, gamedef);
328 lbms_we_introduce_now.addLBM(it->second, gamedef);
332 // Clear the list, so that we don't delete remaining elements
333 // twice in the destructor
337 std::string LBMManager::createIntroductionTimesString()
339 // Precondition, we must be in query mode
340 FATAL_ERROR_IF(m_query_mode == false,
341 "attempted to query on non fully set up LBMManager");
343 std::ostringstream oss;
344 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
345 it != m_lbm_lookup.end(); ++it) {
346 u32 time = it->first;
347 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
348 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
349 iit != lbm_list.end(); ++iit) {
350 // Don't add if the LBM runs at every load,
351 // then introducement time is hardcoded
352 // and doesn't need to be stored
353 if ((*iit)->run_at_every_load)
355 oss << (*iit)->name << "~" << time << ";";
361 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
363 // Precondition, we need m_lbm_lookup to be initialized
364 FATAL_ERROR_IF(m_query_mode == false,
365 "attempted to query on non fully set up LBMManager");
366 v3s16 pos_of_block = block->getPosRelative();
370 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
371 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
372 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
373 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
375 n = block->getNodeNoEx(pos);
377 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
378 iit != m_lbm_lookup.end(); ++iit) {
379 const std::vector<LoadingBlockModifierDef *> *lbm_list =
380 iit->second.lookup(c);
383 for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
384 lbm_list->begin(); iit != lbm_list->end(); ++iit) {
385 (*iit)->trigger(env, pos + pos_of_block, n);
395 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
398 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
399 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
400 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
403 if (p.getDistanceFrom(p0) <= r) {
410 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
412 std::set<v3s16> &blocks_removed,
413 std::set<v3s16> &blocks_added)
418 std::set<v3s16> newlist = m_forceloaded_list;
419 for(std::vector<v3s16>::iterator i = active_positions.begin();
420 i != active_positions.end(); ++i)
422 fillRadiusBlock(*i, radius, newlist);
426 Find out which blocks on the old list are not on the new list
428 // Go through old list
429 for(std::set<v3s16>::iterator i = m_list.begin();
430 i != m_list.end(); ++i)
433 // If not on new list, it's been removed
434 if(newlist.find(p) == newlist.end())
435 blocks_removed.insert(p);
439 Find out which blocks on the new list are not on the old list
441 // Go through new list
442 for(std::set<v3s16>::iterator i = newlist.begin();
443 i != newlist.end(); ++i)
446 // If not on old list, it's been added
447 if(m_list.find(p) == m_list.end())
448 blocks_added.insert(p);
455 for(std::set<v3s16>::iterator i = newlist.begin();
456 i != newlist.end(); ++i)
467 ServerEnvironment::ServerEnvironment(ServerMap *map,
468 GameScripting *scriptIface, IGameDef *gamedef,
469 const std::string &path_world) :
471 m_script(scriptIface),
473 m_path_world(path_world),
474 m_send_recommended_timer(0),
475 m_active_block_interval_overload_skip(0),
477 m_game_time_fraction_counter(0),
478 m_last_clear_objects_time(0),
479 m_recommended_send_interval(0.1),
480 m_max_lag_estimate(0.1)
484 ServerEnvironment::~ServerEnvironment()
486 // Clear active block list.
487 // This makes the next one delete all active objects.
488 m_active_blocks.clear();
490 // Convert all objects to static and delete the active objects
491 deactivateFarObjects(true);
496 // Delete ActiveBlockModifiers
497 for (std::vector<ABMWithState>::iterator
498 i = m_abms.begin(); i != m_abms.end(); ++i){
502 // Deallocate players
503 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
504 i != m_players.end(); ++i) {
509 Map & ServerEnvironment::getMap()
514 ServerMap & ServerEnvironment::getServerMap()
519 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
521 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
522 i != m_players.end(); ++i) {
523 RemotePlayer *player = *i;
524 if (player->peer_id == peer_id)
530 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
532 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
533 i != m_players.end(); ++i) {
534 RemotePlayer *player = *i;
535 if (strcmp(player->getName(), name) == 0)
541 void ServerEnvironment::addPlayer(RemotePlayer *player)
543 DSTACK(FUNCTION_NAME);
545 Check that peer_ids are unique.
546 Also check that names are unique.
547 Exception: there can be multiple players with peer_id=0
549 // If peer id is non-zero, it has to be unique.
550 if (player->peer_id != 0)
551 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
552 // Name has to be unique.
553 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
555 m_players.push_back(player);
558 void ServerEnvironment::removePlayer(RemotePlayer *player)
560 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
561 it != m_players.end(); ++it) {
562 if ((*it) == player) {
570 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
572 float distance = pos1.getDistanceFrom(pos2);
574 //calculate normalized direction vector
575 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
576 (pos2.Y - pos1.Y)/distance,
577 (pos2.Z - pos1.Z)/distance);
579 //find out if there's a node on path between pos1 and pos2
580 for (float i = 1; i < distance; i += stepsize) {
581 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
582 normalized_vector.Y * i,
583 normalized_vector.Z * i) +pos1,BS);
585 MapNode n = getMap().getNodeNoEx(pos);
587 if(n.param0 != CONTENT_AIR) {
597 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
598 const std::string &str_reason, bool reconnect)
600 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
601 it != m_players.end(); ++it) {
602 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
603 ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id,
604 player->protocol_version, reason, str_reason, reconnect);
608 void ServerEnvironment::saveLoadedPlayers()
610 std::string players_path = m_path_world + DIR_DELIM "players";
611 fs::CreateDir(players_path);
613 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
614 it != m_players.end();
616 if ((*it)->checkModified()) {
617 (*it)->save(players_path, m_gamedef);
622 void ServerEnvironment::savePlayer(RemotePlayer *player)
624 std::string players_path = m_path_world + DIR_DELIM "players";
625 fs::CreateDir(players_path);
627 player->save(players_path, m_gamedef);
630 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao)
632 bool newplayer = false;
634 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
635 std::string path = players_path + playername;
637 RemotePlayer *player = getPlayer(playername.c_str());
639 player = new RemotePlayer("", m_gamedef->idef());
643 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
644 //// Open file and deserialize
645 std::ifstream is(path.c_str(), std::ios_base::binary);
649 player->deSerialize(is, path, sao);
652 if (player->getName() == playername) {
657 path = players_path + playername + itos(i);
661 infostream << "Player file for player " << playername
662 << " not found" << std::endl;
672 player->setModified(false);
676 void ServerEnvironment::saveMeta()
678 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
680 // Open file and serialize
681 std::ostringstream ss(std::ios_base::binary);
684 args.setU64("game_time", m_game_time);
685 args.setU64("time_of_day", getTimeOfDay());
686 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
687 args.setU64("lbm_introduction_times_version", 1);
688 args.set("lbm_introduction_times",
689 m_lbm_mgr.createIntroductionTimesString());
690 args.setU64("day_count", m_day_count);
694 if(!fs::safeWriteToFile(path, ss.str()))
696 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
698 throw SerializationError("Couldn't save env meta");
702 void ServerEnvironment::loadMeta()
704 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
706 // Open file and deserialize
707 std::ifstream is(path.c_str(), std::ios_base::binary);
709 infostream << "ServerEnvironment::loadMeta(): Failed to open "
710 << path << std::endl;
711 throw SerializationError("Couldn't load env meta");
716 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
717 throw SerializationError("ServerEnvironment::loadMeta(): "
718 "EnvArgsEnd not found!");
722 m_game_time = args.getU64("game_time");
723 } catch (SettingNotFoundException &e) {
724 // Getting this is crucial, otherwise timestamps are useless
725 throw SerializationError("Couldn't load env meta game_time");
728 setTimeOfDay(args.exists("time_of_day") ?
729 // set day to morning by default
730 args.getU64("time_of_day") : 9000);
732 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
733 // If missing, do as if clearObjects was never called
734 args.getU64("last_clear_objects_time") : 0;
736 std::string lbm_introduction_times = "";
738 u64 ver = args.getU64("lbm_introduction_times_version");
740 lbm_introduction_times = args.get("lbm_introduction_times");
742 infostream << "ServerEnvironment::loadMeta(): Non-supported"
743 << " introduction time version " << ver << std::endl;
745 } catch (SettingNotFoundException &e) {
746 // No problem, this is expected. Just continue with an empty string
748 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
750 m_day_count = args.exists("day_count") ?
751 args.getU64("day_count") : 0;
754 void ServerEnvironment::loadDefaultMeta()
756 m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
761 ActiveBlockModifier *abm;
763 std::set<content_t> required_neighbors;
769 ServerEnvironment *m_env;
770 std::vector<std::vector<ActiveABM> *> m_aabms;
772 ABMHandler(std::vector<ABMWithState> &abms,
773 float dtime_s, ServerEnvironment *env,
779 INodeDefManager *ndef = env->getGameDef()->ndef();
780 for(std::vector<ABMWithState>::iterator
781 i = abms.begin(); i != abms.end(); ++i) {
782 ActiveBlockModifier *abm = i->abm;
783 float trigger_interval = abm->getTriggerInterval();
784 if(trigger_interval < 0.001)
785 trigger_interval = 0.001;
786 float actual_interval = dtime_s;
789 if(i->timer < trigger_interval)
791 i->timer -= trigger_interval;
792 actual_interval = trigger_interval;
794 float chance = abm->getTriggerChance();
799 if(abm->getSimpleCatchUp()) {
800 float intervals = actual_interval / trigger_interval;
803 aabm.chance = chance / intervals;
807 aabm.chance = chance;
810 std::set<std::string> required_neighbors_s
811 = abm->getRequiredNeighbors();
812 for(std::set<std::string>::iterator
813 i = required_neighbors_s.begin();
814 i != required_neighbors_s.end(); ++i)
816 ndef->getIds(*i, aabm.required_neighbors);
819 std::set<std::string> contents_s = abm->getTriggerContents();
820 for(std::set<std::string>::iterator
821 i = contents_s.begin(); i != contents_s.end(); ++i)
823 std::set<content_t> ids;
824 ndef->getIds(*i, ids);
825 for(std::set<content_t>::const_iterator k = ids.begin();
829 if (c >= m_aabms.size())
830 m_aabms.resize(c + 256, NULL);
832 m_aabms[c] = new std::vector<ActiveABM>;
833 m_aabms[c]->push_back(aabm);
841 for (size_t i = 0; i < m_aabms.size(); i++)
845 // Find out how many objects the given block and its neighbours contain.
846 // Returns the number of objects in the block, and also in 'wider' the
847 // number of objects in the block and all its neighbours. The latter
848 // may an estimate if any neighbours are unloaded.
849 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
852 u32 wider_unknown_count = 0;
853 for(s16 x=-1; x<=1; x++)
854 for(s16 y=-1; y<=1; y++)
855 for(s16 z=-1; z<=1; z++)
857 MapBlock *block2 = map->getBlockNoCreateNoEx(
858 block->getPos() + v3s16(x,y,z));
860 wider_unknown_count++;
863 wider += block2->m_static_objects.m_active.size()
864 + block2->m_static_objects.m_stored.size();
867 u32 active_object_count = block->m_static_objects.m_active.size();
868 u32 wider_known_count = 3*3*3 - wider_unknown_count;
869 wider += wider_unknown_count * wider / wider_known_count;
870 return active_object_count;
873 void apply(MapBlock *block)
875 if(m_aabms.empty() || block->isDummy())
878 ServerMap *map = &m_env->getServerMap();
880 u32 active_object_count_wider;
881 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
882 m_env->m_added_objects = 0;
885 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
886 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
887 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
889 const MapNode &n = block->getNodeUnsafe(p0);
890 content_t c = n.getContent();
892 if (c >= m_aabms.size() || !m_aabms[c])
895 v3s16 p = p0 + block->getPosRelative();
896 for(std::vector<ActiveABM>::iterator
897 i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
898 if(myrand() % i->chance != 0)
902 if(!i->required_neighbors.empty())
905 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
906 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
907 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
911 MapNode n = map->getNodeNoEx(p1);
912 content_t c = n.getContent();
913 std::set<content_t>::const_iterator k;
914 k = i->required_neighbors.find(c);
915 if(k != i->required_neighbors.end()){
919 // No required neighbor found
924 // Call all the trigger variations
925 i->abm->trigger(m_env, p, n);
926 i->abm->trigger(m_env, p, n,
927 active_object_count, active_object_count_wider);
929 // Count surrounding objects again if the abms added any
930 if(m_env->m_added_objects > 0) {
931 active_object_count = countObjects(block, map, active_object_count_wider);
932 m_env->m_added_objects = 0;
939 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
941 // Reset usage timer immediately, otherwise a block that becomes active
942 // again at around the same time as it would normally be unloaded will
943 // get unloaded incorrectly. (I think this still leaves a small possibility
944 // of a race condition between this and server::AsyncRunStep, which only
945 // some kind of synchronisation will fix, but it at least reduces the window
946 // of opportunity for it to break from seconds to nanoseconds)
947 block->resetUsageTimer();
949 // Get time difference
951 u32 stamp = block->getTimestamp();
952 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
953 dtime_s = m_game_time - stamp;
954 dtime_s += additional_dtime;
956 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
957 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
959 // Remove stored static objects if clearObjects was called since block's timestamp
960 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
961 block->m_static_objects.m_stored.clear();
962 // do not set changed flag to avoid unnecessary mapblock writes
965 // Set current time as timestamp
966 block->setTimestampNoChangedFlag(m_game_time);
968 /*infostream<<"ServerEnvironment::activateBlock(): block is "
969 <<dtime_s<<" seconds old."<<std::endl;*/
971 // Activate stored objects
972 activateObjects(block, dtime_s);
974 /* Handle LoadingBlockModifiers */
975 m_lbm_mgr.applyLBMs(this, block, stamp);
978 std::vector<NodeTimer> elapsed_timers =
979 block->m_node_timers.step((float)dtime_s);
980 if (!elapsed_timers.empty()) {
982 for (std::vector<NodeTimer>::iterator
983 i = elapsed_timers.begin();
984 i != elapsed_timers.end(); ++i){
985 n = block->getNodeNoEx(i->position);
986 v3s16 p = i->position + block->getPosRelative();
987 if (m_script->node_on_timer(p, n, i->elapsed))
988 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
992 /* Handle ActiveBlockModifiers */
993 ABMHandler abmhandler(m_abms, dtime_s, this, false);
994 abmhandler.apply(block);
997 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
999 m_abms.push_back(ABMWithState(abm));
1002 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1004 m_lbm_mgr.addLBMDef(lbm);
1007 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1009 INodeDefManager *ndef = m_gamedef->ndef();
1010 MapNode n_old = m_map->getNodeNoEx(p);
1013 if (ndef->get(n_old).has_on_destruct)
1014 m_script->node_on_destruct(p, n_old);
1017 if (!m_map->addNodeWithEvent(p, n))
1020 // Update active VoxelManipulator if a mapgen thread
1021 m_map->updateVManip(p);
1023 // Call post-destructor
1024 if (ndef->get(n_old).has_after_destruct)
1025 m_script->node_after_destruct(p, n_old);
1028 if (ndef->get(n).has_on_construct)
1029 m_script->node_on_construct(p, n);
1034 bool ServerEnvironment::removeNode(v3s16 p)
1036 INodeDefManager *ndef = m_gamedef->ndef();
1037 MapNode n_old = m_map->getNodeNoEx(p);
1040 if (ndef->get(n_old).has_on_destruct)
1041 m_script->node_on_destruct(p, n_old);
1044 // This is slightly optimized compared to addNodeWithEvent(air)
1045 if (!m_map->removeNodeWithEvent(p))
1048 // Update active VoxelManipulator if a mapgen thread
1049 m_map->updateVManip(p);
1051 // Call post-destructor
1052 if (ndef->get(n_old).has_after_destruct)
1053 m_script->node_after_destruct(p, n_old);
1055 // Air doesn't require constructor
1059 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1061 if (!m_map->addNodeWithEvent(p, n, false))
1064 // Update active VoxelManipulator if a mapgen thread
1065 m_map->updateVManip(p);
1070 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1072 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1073 i != m_active_objects.end(); ++i) {
1074 ServerActiveObject* obj = i->second;
1076 v3f objectpos = obj->getBasePosition();
1077 if (objectpos.getDistanceFrom(pos) > radius)
1079 objects.push_back(id);
1083 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1085 infostream << "ServerEnvironment::clearObjects(): "
1086 << "Removing all active objects" << std::endl;
1087 std::vector<u16> objects_to_remove;
1088 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1089 i != m_active_objects.end(); ++i) {
1090 ServerActiveObject* obj = i->second;
1091 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1094 // Delete static object if block is loaded
1095 if (obj->m_static_exists) {
1096 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1098 block->m_static_objects.remove(id);
1099 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1100 MOD_REASON_CLEAR_ALL_OBJECTS);
1101 obj->m_static_exists = false;
1104 // If known by some client, don't delete immediately
1105 if (obj->m_known_by_count > 0) {
1106 obj->m_pending_deactivation = true;
1107 obj->m_removed = true;
1111 // Tell the object about removal
1112 obj->removingFromEnvironment();
1113 // Deregister in scripting api
1114 m_script->removeObjectReference(obj);
1116 // Delete active object
1117 if (obj->environmentDeletes())
1119 // Id to be removed from m_active_objects
1120 objects_to_remove.push_back(id);
1123 // Remove references from m_active_objects
1124 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1125 i != objects_to_remove.end(); ++i) {
1126 m_active_objects.erase(*i);
1129 // Get list of loaded blocks
1130 std::vector<v3s16> loaded_blocks;
1131 infostream << "ServerEnvironment::clearObjects(): "
1132 << "Listing all loaded blocks" << std::endl;
1133 m_map->listAllLoadedBlocks(loaded_blocks);
1134 infostream << "ServerEnvironment::clearObjects(): "
1135 << "Done listing all loaded blocks: "
1136 << loaded_blocks.size()<<std::endl;
1138 // Get list of loadable blocks
1139 std::vector<v3s16> loadable_blocks;
1140 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1141 infostream << "ServerEnvironment::clearObjects(): "
1142 << "Listing all loadable blocks" << std::endl;
1143 m_map->listAllLoadableBlocks(loadable_blocks);
1144 infostream << "ServerEnvironment::clearObjects(): "
1145 << "Done listing all loadable blocks: "
1146 << loadable_blocks.size() << std::endl;
1148 loadable_blocks = loaded_blocks;
1151 infostream << "ServerEnvironment::clearObjects(): "
1152 << "Now clearing objects in " << loadable_blocks.size()
1153 << " blocks" << std::endl;
1155 // Grab a reference on each loaded block to avoid unloading it
1156 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1157 i != loaded_blocks.end(); ++i) {
1159 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1160 assert(block != NULL);
1164 // Remove objects in all loadable blocks
1165 u32 unload_interval = U32_MAX;
1166 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1167 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1168 unload_interval = MYMAX(unload_interval, 1);
1170 u32 report_interval = loadable_blocks.size() / 10;
1171 u32 num_blocks_checked = 0;
1172 u32 num_blocks_cleared = 0;
1173 u32 num_objs_cleared = 0;
1174 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1175 i != loadable_blocks.end(); ++i) {
1177 MapBlock *block = m_map->emergeBlock(p, false);
1179 errorstream << "ServerEnvironment::clearObjects(): "
1180 << "Failed to emerge block " << PP(p) << std::endl;
1183 u32 num_stored = block->m_static_objects.m_stored.size();
1184 u32 num_active = block->m_static_objects.m_active.size();
1185 if (num_stored != 0 || num_active != 0) {
1186 block->m_static_objects.m_stored.clear();
1187 block->m_static_objects.m_active.clear();
1188 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1189 MOD_REASON_CLEAR_ALL_OBJECTS);
1190 num_objs_cleared += num_stored + num_active;
1191 num_blocks_cleared++;
1193 num_blocks_checked++;
1195 if (report_interval != 0 &&
1196 num_blocks_checked % report_interval == 0) {
1197 float percent = 100.0 * (float)num_blocks_checked /
1198 loadable_blocks.size();
1199 infostream << "ServerEnvironment::clearObjects(): "
1200 << "Cleared " << num_objs_cleared << " objects"
1201 << " in " << num_blocks_cleared << " blocks ("
1202 << percent << "%)" << std::endl;
1204 if (num_blocks_checked % unload_interval == 0) {
1205 m_map->unloadUnreferencedBlocks();
1208 m_map->unloadUnreferencedBlocks();
1210 // Drop references that were added above
1211 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1212 i != loaded_blocks.end(); ++i) {
1214 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1219 m_last_clear_objects_time = m_game_time;
1221 infostream << "ServerEnvironment::clearObjects(): "
1222 << "Finished: Cleared " << num_objs_cleared << " objects"
1223 << " in " << num_blocks_cleared << " blocks" << std::endl;
1226 void ServerEnvironment::step(float dtime)
1228 DSTACK(FUNCTION_NAME);
1230 //TimeTaker timer("ServerEnv step");
1232 /* Step time of day */
1233 stepTimeOfDay(dtime);
1236 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1237 // really matter that much.
1238 static const float server_step = g_settings->getFloat("dedicated_server_step");
1239 m_recommended_send_interval = server_step;
1245 m_game_time_fraction_counter += dtime;
1246 u32 inc_i = (u32)m_game_time_fraction_counter;
1247 m_game_time += inc_i;
1248 m_game_time_fraction_counter -= (float)inc_i;
1255 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1256 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1257 i != m_players.end(); ++i) {
1258 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1261 // Ignore disconnected players
1262 if(player->peer_id == 0)
1266 player->move(dtime, this, 100*BS);
1271 Manage active block list
1273 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1274 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1276 Get player block positions
1278 std::vector<v3s16> players_blockpos;
1279 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1280 i != m_players.end(); ++i) {
1281 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1284 // Ignore disconnected players
1285 if (player->peer_id == 0)
1288 PlayerSAO *playersao = player->getPlayerSAO();
1291 v3s16 blockpos = getNodeBlockPos(
1292 floatToInt(playersao->getBasePosition(), BS));
1293 players_blockpos.push_back(blockpos);
1297 Update list of active blocks, collecting changes
1299 static const s16 active_block_range = g_settings->getS16("active_block_range");
1300 std::set<v3s16> blocks_removed;
1301 std::set<v3s16> blocks_added;
1302 m_active_blocks.update(players_blockpos, active_block_range,
1303 blocks_removed, blocks_added);
1306 Handle removed blocks
1309 // Convert active objects that are no more in active blocks to static
1310 deactivateFarObjects(false);
1312 for(std::set<v3s16>::iterator
1313 i = blocks_removed.begin();
1314 i != blocks_removed.end(); ++i) {
1317 /* infostream<<"Server: Block " << PP(p)
1318 << " became inactive"<<std::endl; */
1320 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1324 // Set current time as timestamp (and let it set ChangedFlag)
1325 block->setTimestamp(m_game_time);
1332 for(std::set<v3s16>::iterator
1333 i = blocks_added.begin();
1334 i != blocks_added.end(); ++i)
1338 MapBlock *block = m_map->getBlockOrEmerge(p);
1340 m_active_blocks.m_list.erase(p);
1344 activateBlock(block);
1345 /* infostream<<"Server: Block " << PP(p)
1346 << " became active"<<std::endl; */
1351 Mess around in active blocks
1353 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1354 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1356 float dtime = m_cache_nodetimer_interval;
1358 for(std::set<v3s16>::iterator
1359 i = m_active_blocks.m_list.begin();
1360 i != m_active_blocks.m_list.end(); ++i)
1364 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1365 <<") being handled"<<std::endl;*/
1367 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1371 // Reset block usage timer
1372 block->resetUsageTimer();
1374 // Set current time as timestamp
1375 block->setTimestampNoChangedFlag(m_game_time);
1376 // If time has changed much from the one on disk,
1377 // set block to be saved when it is unloaded
1378 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1379 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1380 MOD_REASON_BLOCK_EXPIRED);
1383 std::vector<NodeTimer> elapsed_timers =
1384 block->m_node_timers.step((float)dtime);
1385 if (!elapsed_timers.empty()) {
1387 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1388 i != elapsed_timers.end(); ++i) {
1389 n = block->getNodeNoEx(i->position);
1390 p = i->position + block->getPosRelative();
1391 if (m_script->node_on_timer(p, n, i->elapsed)) {
1392 block->setNodeTimer(NodeTimer(
1393 i->timeout, 0, i->position));
1400 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1402 if(m_active_block_interval_overload_skip > 0){
1403 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1404 m_active_block_interval_overload_skip--;
1407 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1408 TimeTaker timer("modify in active blocks per interval");
1410 // Initialize handling of ActiveBlockModifiers
1411 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1413 for(std::set<v3s16>::iterator
1414 i = m_active_blocks.m_list.begin();
1415 i != m_active_blocks.m_list.end(); ++i)
1419 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1420 <<") being handled"<<std::endl;*/
1422 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1426 // Set current time as timestamp
1427 block->setTimestampNoChangedFlag(m_game_time);
1429 /* Handle ActiveBlockModifiers */
1430 abmhandler.apply(block);
1433 u32 time_ms = timer.stop(true);
1434 u32 max_time_ms = 200;
1435 if(time_ms > max_time_ms){
1436 warningstream<<"active block modifiers took "
1437 <<time_ms<<"ms (longer than "
1438 <<max_time_ms<<"ms)"<<std::endl;
1439 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1444 Step script environment (run global on_step())
1446 m_script->environment_Step(dtime);
1452 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1453 //TimeTaker timer("Step active objects");
1455 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1457 // This helps the objects to send data at the same time
1458 bool send_recommended = false;
1459 m_send_recommended_timer += dtime;
1460 if(m_send_recommended_timer > getSendRecommendedInterval())
1462 m_send_recommended_timer -= getSendRecommendedInterval();
1463 send_recommended = true;
1466 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1467 i != m_active_objects.end(); ++i) {
1468 ServerActiveObject* obj = i->second;
1469 // Don't step if is to be removed or stored statically
1470 if(obj->m_removed || obj->m_pending_deactivation)
1473 obj->step(dtime, send_recommended);
1474 // Read messages from object
1475 while(!obj->m_messages_out.empty())
1477 m_active_object_messages.push(
1478 obj->m_messages_out.front());
1479 obj->m_messages_out.pop();
1485 Manage active objects
1487 if(m_object_management_interval.step(dtime, 0.5))
1489 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1491 Remove objects that satisfy (m_removed && m_known_by_count==0)
1493 removeRemovedObjects();
1497 Manage particle spawner expiration
1499 if (m_particle_management_interval.step(dtime, 1.0)) {
1500 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1501 i != m_particle_spawners.end(); ) {
1502 //non expiring spawners
1503 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1509 if (i->second <= 0.f)
1510 m_particle_spawners.erase(i++);
1517 u32 ServerEnvironment::addParticleSpawner(float exptime)
1519 // Timers with lifetime 0 do not expire
1520 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1523 for (;;) { // look for unused particlespawner id
1525 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1526 if (f == m_particle_spawners.end()) {
1527 m_particle_spawners[id] = time;
1534 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1536 u32 id = addParticleSpawner(exptime);
1537 m_particle_spawner_attachments[id] = attached_id;
1538 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1539 obj->attachParticleSpawner(id);
1544 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1546 m_particle_spawners.erase(id);
1547 UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1548 if (it != m_particle_spawner_attachments.end()) {
1549 u16 obj_id = (*it).second;
1550 ServerActiveObject *sao = getActiveObject(obj_id);
1551 if (sao != NULL && remove_from_object) {
1552 sao->detachParticleSpawner(id);
1554 m_particle_spawner_attachments.erase(id);
1558 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1560 ActiveObjectMap::iterator n = m_active_objects.find(id);
1561 return (n != m_active_objects.end() ? n->second : NULL);
1564 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1569 return objects.find(id) == objects.end();
1572 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1574 //try to reuse id's as late as possible
1575 static u16 last_used_id = 0;
1576 u16 startid = last_used_id;
1580 if(isFreeServerActiveObjectId(last_used_id, objects))
1581 return last_used_id;
1583 if(last_used_id == startid)
1588 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1590 assert(object); // Pre-condition
1592 u16 id = addActiveObjectRaw(object, true, 0);
1597 Finds out what new objects have been added to
1598 inside a radius around a position
1600 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1602 std::set<u16> ¤t_objects,
1603 std::queue<u16> &added_objects)
1605 f32 radius_f = radius * BS;
1606 f32 player_radius_f = player_radius * BS;
1608 if (player_radius_f < 0)
1609 player_radius_f = 0;
1611 Go through the object list,
1612 - discard m_removed objects,
1613 - discard objects that are too far away,
1614 - discard objects that are found in current_objects.
1615 - add remaining objects to added_objects
1617 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1618 i != m_active_objects.end(); ++i) {
1622 ServerActiveObject *object = i->second;
1626 // Discard if removed or deactivating
1627 if(object->m_removed || object->m_pending_deactivation)
1630 f32 distance_f = object->getBasePosition().
1631 getDistanceFrom(playersao->getBasePosition());
1632 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1633 // Discard if too far
1634 if (distance_f > player_radius_f && player_radius_f != 0)
1636 } else if (distance_f > radius_f)
1639 // Discard if already on current_objects
1640 std::set<u16>::iterator n;
1641 n = current_objects.find(id);
1642 if(n != current_objects.end())
1644 // Add to added_objects
1645 added_objects.push(id);
1650 Finds out what objects have been removed from
1651 inside a radius around a position
1653 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1655 std::set<u16> ¤t_objects,
1656 std::queue<u16> &removed_objects)
1658 f32 radius_f = radius * BS;
1659 f32 player_radius_f = player_radius * BS;
1661 if (player_radius_f < 0)
1662 player_radius_f = 0;
1664 Go through current_objects; object is removed if:
1665 - object is not found in m_active_objects (this is actually an
1666 error condition; objects should be set m_removed=true and removed
1667 only after all clients have been informed about removal), or
1668 - object has m_removed=true, or
1669 - object is too far away
1671 for(std::set<u16>::iterator
1672 i = current_objects.begin();
1673 i != current_objects.end(); ++i)
1676 ServerActiveObject *object = getActiveObject(id);
1678 if (object == NULL) {
1679 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1680 << " object in current_objects is NULL" << std::endl;
1681 removed_objects.push(id);
1685 if (object->m_removed || object->m_pending_deactivation) {
1686 removed_objects.push(id);
1690 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1691 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1692 if (distance_f <= player_radius_f || player_radius_f == 0)
1694 } else if (distance_f <= radius_f)
1697 // Object is no longer visible
1698 removed_objects.push(id);
1702 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1703 v3s16 blockpos, bool static_exists, v3s16 static_block)
1705 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1709 for (std::map<u16, StaticObject>::iterator
1710 so_it = block->m_static_objects.m_active.begin();
1711 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1712 // Get the ServerActiveObject counterpart to this StaticObject
1713 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1714 if (ao_it == m_active_objects.end()) {
1715 // If this ever happens, there must be some kind of nasty bug.
1716 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1717 "Object from MapBlock::m_static_objects::m_active not found "
1718 "in m_active_objects";
1722 ServerActiveObject *sao = ao_it->second;
1723 sao->m_static_exists = static_exists;
1724 sao->m_static_block = static_block;
1728 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1730 if(m_active_object_messages.empty())
1731 return ActiveObjectMessage(0);
1733 ActiveObjectMessage message = m_active_object_messages.front();
1734 m_active_object_messages.pop();
1739 ************ Private methods *************
1742 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1743 bool set_changed, u32 dtime_s)
1745 assert(object); // Pre-condition
1746 if(object->getId() == 0){
1747 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1750 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1751 <<"no free ids available"<<std::endl;
1752 if(object->environmentDeletes())
1756 object->setId(new_id);
1759 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1760 <<"supplied with id "<<object->getId()<<std::endl;
1763 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1764 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1765 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1766 if(object->environmentDeletes())
1771 if (objectpos_over_limit(object->getBasePosition())) {
1772 v3f p = object->getBasePosition();
1773 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1774 << "object position (" << p.X << "," << p.Y << "," << p.Z
1775 << ") outside maximum range" << std::endl;
1776 if (object->environmentDeletes())
1781 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1782 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1784 m_active_objects[object->getId()] = object;
1786 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1787 <<"Added id="<<object->getId()<<"; there are now "
1788 <<m_active_objects.size()<<" active objects."
1791 // Register reference in scripting api (must be done before post-init)
1792 m_script->addObjectReference(object);
1793 // Post-initialize object
1794 object->addedToEnvironment(dtime_s);
1796 // Add static data to block
1797 if(object->isStaticAllowed())
1799 // Add static object to active static list of the block
1800 v3f objectpos = object->getBasePosition();
1801 std::string staticdata = object->getStaticData();
1802 StaticObject s_obj(object->getType(), objectpos, staticdata);
1803 // Add to the block where the object is located in
1804 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1805 MapBlock *block = m_map->emergeBlock(blockpos);
1807 block->m_static_objects.m_active[object->getId()] = s_obj;
1808 object->m_static_exists = true;
1809 object->m_static_block = blockpos;
1812 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1813 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1815 v3s16 p = floatToInt(objectpos, BS);
1816 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1817 <<"could not emerge block for storing id="<<object->getId()
1818 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1822 return object->getId();
1826 Remove objects that satisfy (m_removed && m_known_by_count==0)
1828 void ServerEnvironment::removeRemovedObjects()
1830 std::vector<u16> objects_to_remove;
1831 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1832 i != m_active_objects.end(); ++i) {
1834 ServerActiveObject* obj = i->second;
1835 // This shouldn't happen but check it
1838 infostream<<"NULL object found in ServerEnvironment"
1839 <<" while finding removed objects. id="<<id<<std::endl;
1840 // Id to be removed from m_active_objects
1841 objects_to_remove.push_back(id);
1846 We will delete objects that are marked as removed or thatare
1847 waiting for deletion after deactivation
1849 if (!obj->m_removed && !obj->m_pending_deactivation)
1853 Delete static data from block if is marked as removed
1855 if(obj->m_static_exists && obj->m_removed)
1857 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1859 block->m_static_objects.remove(id);
1860 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1861 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1862 obj->m_static_exists = false;
1864 infostream<<"Failed to emerge block from which an object to "
1865 <<"be removed was loaded from. id="<<id<<std::endl;
1869 // If m_known_by_count > 0, don't actually remove. On some future
1870 // invocation this will be 0, which is when removal will continue.
1871 if(obj->m_known_by_count > 0)
1875 Move static data from active to stored if not marked as removed
1877 if(obj->m_static_exists && !obj->m_removed){
1878 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1880 std::map<u16, StaticObject>::iterator i =
1881 block->m_static_objects.m_active.find(id);
1882 if(i != block->m_static_objects.m_active.end()){
1883 block->m_static_objects.m_stored.push_back(i->second);
1884 block->m_static_objects.m_active.erase(id);
1885 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1886 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1889 infostream<<"Failed to emerge block from which an object to "
1890 <<"be deactivated was loaded from. id="<<id<<std::endl;
1894 // Tell the object about removal
1895 obj->removingFromEnvironment();
1896 // Deregister in scripting api
1897 m_script->removeObjectReference(obj);
1900 if(obj->environmentDeletes())
1903 // Id to be removed from m_active_objects
1904 objects_to_remove.push_back(id);
1906 // Remove references from m_active_objects
1907 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1908 i != objects_to_remove.end(); ++i) {
1909 m_active_objects.erase(*i);
1913 static void print_hexdump(std::ostream &o, const std::string &data)
1915 const int linelength = 16;
1916 for(int l=0; ; l++){
1917 int i0 = linelength * l;
1918 bool at_end = false;
1919 int thislinelength = linelength;
1920 if(i0 + thislinelength > (int)data.size()){
1921 thislinelength = data.size() - i0;
1924 for(int di=0; di<linelength; di++){
1927 if(di<thislinelength)
1928 snprintf(buf, 4, "%.2x ", data[i]);
1930 snprintf(buf, 4, " ");
1934 for(int di=0; di<thislinelength; di++){
1948 Convert stored objects from blocks near the players to active.
1950 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1955 // Ignore if no stored objects (to not set changed flag)
1956 if(block->m_static_objects.m_stored.empty())
1959 verbosestream<<"ServerEnvironment::activateObjects(): "
1960 <<"activating objects of block "<<PP(block->getPos())
1961 <<" ("<<block->m_static_objects.m_stored.size()
1962 <<" objects)"<<std::endl;
1963 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1965 errorstream<<"suspiciously large amount of objects detected: "
1966 <<block->m_static_objects.m_stored.size()<<" in "
1967 <<PP(block->getPos())
1968 <<"; removing all of them."<<std::endl;
1969 // Clear stored list
1970 block->m_static_objects.m_stored.clear();
1971 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1972 MOD_REASON_TOO_MANY_OBJECTS);
1976 // Activate stored objects
1977 std::vector<StaticObject> new_stored;
1978 for (std::vector<StaticObject>::iterator
1979 i = block->m_static_objects.m_stored.begin();
1980 i != block->m_static_objects.m_stored.end(); ++i) {
1981 StaticObject &s_obj = *i;
1983 // Create an active object from the data
1984 ServerActiveObject *obj = ServerActiveObject::create
1985 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1986 // If couldn't create object, store static data back.
1988 errorstream<<"ServerEnvironment::activateObjects(): "
1989 <<"failed to create active object from static object "
1990 <<"in block "<<PP(s_obj.pos/BS)
1991 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1992 print_hexdump(verbosestream, s_obj.data);
1994 new_stored.push_back(s_obj);
1997 verbosestream<<"ServerEnvironment::activateObjects(): "
1998 <<"activated static object pos="<<PP(s_obj.pos/BS)
1999 <<" type="<<(int)s_obj.type<<std::endl;
2000 // This will also add the object to the active static list
2001 addActiveObjectRaw(obj, false, dtime_s);
2003 // Clear stored list
2004 block->m_static_objects.m_stored.clear();
2005 // Add leftover failed stuff to stored list
2006 for(std::vector<StaticObject>::iterator
2007 i = new_stored.begin();
2008 i != new_stored.end(); ++i) {
2009 StaticObject &s_obj = *i;
2010 block->m_static_objects.m_stored.push_back(s_obj);
2013 // Turn the active counterparts of activated objects not pending for
2015 for(std::map<u16, StaticObject>::iterator
2016 i = block->m_static_objects.m_active.begin();
2017 i != block->m_static_objects.m_active.end(); ++i)
2020 ServerActiveObject *object = getActiveObject(id);
2022 object->m_pending_deactivation = false;
2026 Note: Block hasn't really been modified here.
2027 The objects have just been activated and moved from the stored
2028 static list to the active static list.
2029 As such, the block is essentially the same.
2030 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2031 Otherwise there would be a huge amount of unnecessary I/O.
2036 Convert objects that are not standing inside active blocks to static.
2038 If m_known_by_count != 0, active object is not deleted, but static
2039 data is still updated.
2041 If force_delete is set, active object is deleted nevertheless. It
2042 shall only be set so in the destructor of the environment.
2044 If block wasn't generated (not in memory or on disk),
2046 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2048 std::vector<u16> objects_to_remove;
2049 for(ActiveObjectMap::iterator i = m_active_objects.begin();
2050 i != m_active_objects.end(); ++i) {
2051 ServerActiveObject* obj = i->second;
2054 // Do not deactivate if static data creation not allowed
2055 if(!force_delete && !obj->isStaticAllowed())
2058 // If pending deactivation, let removeRemovedObjects() do it
2059 if(!force_delete && obj->m_pending_deactivation)
2063 v3f objectpos = obj->getBasePosition();
2065 // The block in which the object resides in
2066 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2068 // If object's static data is stored in a deactivated block and object
2069 // is actually located in an active block, re-save to the block in
2070 // which the object is actually located in.
2072 obj->m_static_exists &&
2073 !m_active_blocks.contains(obj->m_static_block) &&
2074 m_active_blocks.contains(blockpos_o))
2076 v3s16 old_static_block = obj->m_static_block;
2078 // Save to block where object is located
2079 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2081 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2082 <<"Could not save object id="<<id
2083 <<" to it's current block "<<PP(blockpos_o)
2087 std::string staticdata_new = obj->getStaticData();
2088 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2089 block->m_static_objects.insert(id, s_obj);
2090 obj->m_static_block = blockpos_o;
2091 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2092 MOD_REASON_STATIC_DATA_ADDED);
2094 // Delete from block where object was located
2095 block = m_map->emergeBlock(old_static_block, false);
2097 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2098 <<"Could not delete object id="<<id
2099 <<" from it's previous block "<<PP(old_static_block)
2103 block->m_static_objects.remove(id);
2104 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2105 MOD_REASON_STATIC_DATA_REMOVED);
2109 // If block is active, don't remove
2110 if(!force_delete && m_active_blocks.contains(blockpos_o))
2113 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2114 <<"deactivating object id="<<id<<" on inactive block "
2115 <<PP(blockpos_o)<<std::endl;
2117 // If known by some client, don't immediately delete.
2118 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2121 Update the static data
2124 if(obj->isStaticAllowed())
2126 // Create new static object
2127 std::string staticdata_new = obj->getStaticData();
2128 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2130 bool stays_in_same_block = false;
2131 bool data_changed = true;
2133 if (obj->m_static_exists) {
2134 if (obj->m_static_block == blockpos_o)
2135 stays_in_same_block = true;
2137 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2140 std::map<u16, StaticObject>::iterator n =
2141 block->m_static_objects.m_active.find(id);
2142 if (n != block->m_static_objects.m_active.end()) {
2143 StaticObject static_old = n->second;
2145 float save_movem = obj->getMinimumSavedMovement();
2147 if (static_old.data == staticdata_new &&
2148 (static_old.pos - objectpos).getLength() < save_movem)
2149 data_changed = false;
2151 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2152 <<"id="<<id<<" m_static_exists=true but "
2153 <<"static data doesn't actually exist in "
2154 <<PP(obj->m_static_block)<<std::endl;
2159 bool shall_be_written = (!stays_in_same_block || data_changed);
2161 // Delete old static object
2162 if(obj->m_static_exists)
2164 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2167 block->m_static_objects.remove(id);
2168 obj->m_static_exists = false;
2169 // Only mark block as modified if data changed considerably
2170 if(shall_be_written)
2171 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2172 MOD_REASON_STATIC_DATA_CHANGED);
2176 // Add to the block where the object is located in
2177 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2178 // Get or generate the block
2179 MapBlock *block = NULL;
2181 block = m_map->emergeBlock(blockpos);
2182 } catch(InvalidPositionException &e){
2183 // Handled via NULL pointer
2184 // NOTE: emergeBlock's failure is usually determined by it
2185 // actually returning NULL
2190 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2191 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2192 << " statically but block " << PP(blockpos)
2193 << " already contains "
2194 << block->m_static_objects.m_stored.size()
2196 << " Forcing delete." << std::endl;
2197 force_delete = true;
2199 // If static counterpart already exists in target block,
2201 // This shouldn't happen because the object is removed from
2202 // the previous block before this according to
2203 // obj->m_static_block, but happens rarely for some unknown
2204 // reason. Unsuccessful attempts have been made to find
2206 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2207 warningstream<<"ServerEnv: Performing hack #83274"
2209 block->m_static_objects.remove(id);
2211 // Store static data
2212 u16 store_id = pending_delete ? id : 0;
2213 block->m_static_objects.insert(store_id, s_obj);
2215 // Only mark block as modified if data changed considerably
2216 if(shall_be_written)
2217 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2218 MOD_REASON_STATIC_DATA_CHANGED);
2220 obj->m_static_exists = true;
2221 obj->m_static_block = block->getPos();
2226 v3s16 p = floatToInt(objectpos, BS);
2227 errorstream<<"ServerEnv: Could not find or generate "
2228 <<"a block for storing id="<<obj->getId()
2229 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2236 If known by some client, set pending deactivation.
2237 Otherwise delete it immediately.
2240 if(pending_delete && !force_delete)
2242 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2243 <<"object id="<<id<<" is known by clients"
2244 <<"; not deleting yet"<<std::endl;
2246 obj->m_pending_deactivation = true;
2250 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2251 <<"object id="<<id<<" is not known by clients"
2252 <<"; deleting"<<std::endl;
2254 // Tell the object about removal
2255 obj->removingFromEnvironment();
2256 // Deregister in scripting api
2257 m_script->removeObjectReference(obj);
2259 // Delete active object
2260 if(obj->environmentDeletes())
2262 // Id to be removed from m_active_objects
2263 objects_to_remove.push_back(id);
2266 // Remove references from m_active_objects
2267 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2268 i != objects_to_remove.end(); ++i) {
2269 m_active_objects.erase(*i);
2275 #include "clientsimpleobject.h"
2281 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2282 ITextureSource *texturesource, IGameDef *gamedef,
2283 IrrlichtDevice *irr):
2285 m_local_player(NULL),
2287 m_texturesource(texturesource),
2292 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2295 ClientEnvironment::~ClientEnvironment()
2297 // delete active objects
2298 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2299 i != m_active_objects.end(); ++i) {
2303 for(std::vector<ClientSimpleObject*>::iterator
2304 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2312 Map & ClientEnvironment::getMap()
2317 ClientMap & ClientEnvironment::getClientMap()
2322 void ClientEnvironment::setLocalPlayer(LocalPlayer *player)
2324 DSTACK(FUNCTION_NAME);
2326 It is a failure if already is a local player
2328 FATAL_ERROR_IF(m_local_player != NULL,
2329 "Local player already allocated");
2331 m_local_player = player;
2334 void ClientEnvironment::step(float dtime)
2336 DSTACK(FUNCTION_NAME);
2338 /* Step time of day */
2339 stepTimeOfDay(dtime);
2341 // Get some settings
2342 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2343 bool free_move = fly_allowed && g_settings->getBool("free_move");
2346 LocalPlayer *lplayer = getLocalPlayer();
2348 // collision info queue
2349 std::vector<CollisionInfo> player_collisions;
2352 Get the speed the player is going
2354 bool is_climbing = lplayer->is_climbing;
2356 f32 player_speed = lplayer->getSpeed().getLength();
2359 Maximum position increment
2361 //f32 position_max_increment = 0.05*BS;
2362 f32 position_max_increment = 0.1*BS;
2364 // Maximum time increment (for collision detection etc)
2365 // time = distance / speed
2366 f32 dtime_max_increment = 1;
2367 if(player_speed > 0.001)
2368 dtime_max_increment = position_max_increment / player_speed;
2370 // Maximum time increment is 10ms or lower
2371 if(dtime_max_increment > 0.01)
2372 dtime_max_increment = 0.01;
2374 // Don't allow overly huge dtime
2378 f32 dtime_downcount = dtime;
2381 Stuff that has a maximum time increment
2390 if(dtime_downcount > dtime_max_increment)
2392 dtime_part = dtime_max_increment;
2393 dtime_downcount -= dtime_part;
2397 dtime_part = dtime_downcount;
2399 Setting this to 0 (no -=dtime_part) disables an infinite loop
2400 when dtime_part is so small that dtime_downcount -= dtime_part
2403 dtime_downcount = 0;
2412 if(!free_move && !is_climbing)
2415 v3f speed = lplayer->getSpeed();
2416 if(!lplayer->in_liquid)
2417 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2419 // Liquid floating / sinking
2420 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2421 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2423 // Liquid resistance
2424 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2426 // How much the node's viscosity blocks movement, ranges between 0 and 1
2427 // Should match the scale at which viscosity increase affects other liquid attributes
2428 const f32 viscosity_factor = 0.3;
2430 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2431 f32 dl = d_wanted.getLength();
2432 if(dl > lplayer->movement_liquid_fluidity_smooth)
2433 dl = lplayer->movement_liquid_fluidity_smooth;
2434 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2436 v3f d = d_wanted.normalize() * dl;
2440 lplayer->setSpeed(speed);
2445 This also does collision detection.
2447 lplayer->move(dtime_part, this, position_max_increment,
2448 &player_collisions);
2451 while(dtime_downcount > 0.001);
2453 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2455 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2456 i != player_collisions.end(); ++i) {
2457 CollisionInfo &info = *i;
2458 v3f speed_diff = info.new_speed - info.old_speed;;
2459 // Handle only fall damage
2460 // (because otherwise walking against something in fast_move kills you)
2461 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2463 // Get rid of other components
2466 f32 pre_factor = 1; // 1 hp per node/s
2467 f32 tolerance = BS*14; // 5 without damage
2468 f32 post_factor = 1; // 1 hp per node/s
2469 if(info.type == COLLISION_NODE)
2471 const ContentFeatures &f = m_gamedef->ndef()->
2472 get(m_map->getNodeNoEx(info.node_p));
2473 // Determine fall damage multiplier
2474 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2475 pre_factor = 1.0 + (float)addp/100.0;
2477 float speed = pre_factor * speed_diff.getLength();
2478 if(speed > tolerance)
2480 f32 damage_f = (speed - tolerance)/BS * post_factor;
2481 u16 damage = (u16)(damage_f+0.5);
2483 damageLocalPlayer(damage, true);
2484 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2485 m_gamedef->event()->put(e);
2491 A quick draft of lava damage
2493 if(m_lava_hurt_interval.step(dtime, 1.0))
2495 v3f pf = lplayer->getPosition();
2497 // Feet, middle and head
2498 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2499 MapNode n1 = m_map->getNodeNoEx(p1);
2500 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2501 MapNode n2 = m_map->getNodeNoEx(p2);
2502 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2503 MapNode n3 = m_map->getNodeNoEx(p3);
2505 u32 damage_per_second = 0;
2506 damage_per_second = MYMAX(damage_per_second,
2507 m_gamedef->ndef()->get(n1).damage_per_second);
2508 damage_per_second = MYMAX(damage_per_second,
2509 m_gamedef->ndef()->get(n2).damage_per_second);
2510 damage_per_second = MYMAX(damage_per_second,
2511 m_gamedef->ndef()->get(n3).damage_per_second);
2513 if(damage_per_second != 0)
2515 damageLocalPlayer(damage_per_second, true);
2519 // Protocol v29 make this behaviour obsolete
2520 if (((Client*) getGameDef())->getProtoVersion() < 29) {
2524 if (m_drowning_interval.step(dtime, 2.0)) {
2525 v3f pf = lplayer->getPosition();
2528 v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
2529 MapNode n = m_map->getNodeNoEx(p);
2530 ContentFeatures c = m_gamedef->ndef()->get(n);
2531 u8 drowning_damage = c.drowning;
2532 if (drowning_damage > 0 && lplayer->hp > 0) {
2533 u16 breath = lplayer->getBreath();
2540 lplayer->setBreath(breath);
2541 updateLocalPlayerBreath(breath);
2544 if (lplayer->getBreath() == 0 && drowning_damage > 0) {
2545 damageLocalPlayer(drowning_damage, true);
2548 if (m_breathing_interval.step(dtime, 0.5)) {
2549 v3f pf = lplayer->getPosition();
2552 v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
2553 MapNode n = m_map->getNodeNoEx(p);
2554 ContentFeatures c = m_gamedef->ndef()->get(n);
2556 lplayer->setBreath(11);
2557 } else if (c.drowning == 0) {
2558 u16 breath = lplayer->getBreath();
2561 lplayer->setBreath(breath);
2562 updateLocalPlayerBreath(breath);
2568 // Update lighting on local player (used for wield item)
2569 u32 day_night_ratio = getDayNightRatio();
2573 // On InvalidPositionException, use this as default
2574 // (day: LIGHT_SUN, night: 0)
2575 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2577 v3s16 p = lplayer->getLightPosition();
2578 node_at_lplayer = m_map->getNodeNoEx(p);
2580 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2581 u8 day = light & 0xff;
2582 u8 night = (light >> 8) & 0xff;
2583 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2587 Step active objects and update lighting of them
2590 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2591 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2592 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2593 i != m_active_objects.end(); ++i) {
2594 ClientActiveObject* obj = i->second;
2596 obj->step(dtime, this);
2605 v3s16 p = obj->getLightPosition();
2606 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2608 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2610 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2612 obj->updateLight(light);
2617 Step and handle simple objects
2619 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2620 for(std::vector<ClientSimpleObject*>::iterator
2621 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2622 std::vector<ClientSimpleObject*>::iterator cur = i;
2623 ClientSimpleObject *simple = *cur;
2625 simple->step(dtime);
2626 if(simple->m_to_be_removed) {
2628 i = m_simple_objects.erase(cur);
2636 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2638 m_simple_objects.push_back(simple);
2641 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2643 ClientActiveObject *obj = getActiveObject(id);
2644 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2645 return (GenericCAO*) obj;
2650 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2652 UNORDERED_MAP<u16, ClientActiveObject*>::iterator n = m_active_objects.find(id);
2653 if (n == m_active_objects.end())
2658 bool isFreeClientActiveObjectId(const u16 id,
2659 UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2664 return objects.find(id) == objects.end();
2667 u16 getFreeClientActiveObjectId(UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2669 //try to reuse id's as late as possible
2670 static u16 last_used_id = 0;
2671 u16 startid = last_used_id;
2674 if (isFreeClientActiveObjectId(last_used_id, objects))
2675 return last_used_id;
2677 if (last_used_id == startid)
2682 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2684 assert(object); // Pre-condition
2685 if(object->getId() == 0)
2687 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2690 infostream<<"ClientEnvironment::addActiveObject(): "
2691 <<"no free ids available"<<std::endl;
2695 object->setId(new_id);
2697 if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) {
2698 infostream<<"ClientEnvironment::addActiveObject(): "
2699 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2703 infostream<<"ClientEnvironment::addActiveObject(): "
2704 <<"added (id="<<object->getId()<<")"<<std::endl;
2705 m_active_objects[object->getId()] = object;
2706 object->addToScene(m_smgr, m_texturesource, m_irr);
2707 { // Update lighting immediately
2712 v3s16 p = object->getLightPosition();
2713 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2715 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2717 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2719 object->updateLight(light);
2721 return object->getId();
2724 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2725 const std::string &init_data)
2727 ClientActiveObject* obj =
2728 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2731 infostream<<"ClientEnvironment::addActiveObject(): "
2732 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2741 obj->initialize(init_data);
2743 catch(SerializationError &e)
2745 errorstream<<"ClientEnvironment::addActiveObject():"
2746 <<" id="<<id<<" type="<<type
2747 <<": SerializationError in initialize(): "
2749 <<": init_data="<<serializeJsonString(init_data)
2753 addActiveObject(obj);
2756 void ClientEnvironment::removeActiveObject(u16 id)
2758 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2759 <<"id="<<id<<std::endl;
2760 ClientActiveObject* obj = getActiveObject(id);
2762 infostream<<"ClientEnvironment::removeActiveObject(): "
2763 <<"id="<<id<<" not found"<<std::endl;
2766 obj->removeFromScene(true);
2768 m_active_objects.erase(id);
2771 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2773 ClientActiveObject *obj = getActiveObject(id);
2775 infostream << "ClientEnvironment::processActiveObjectMessage():"
2776 << " got message for id=" << id << ", which doesn't exist."
2782 obj->processMessage(data);
2783 } catch (SerializationError &e) {
2784 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2785 << " id=" << id << " type=" << obj->getType()
2786 << " SerializationError in processMessage(): " << e.what()
2792 Callbacks for activeobjects
2795 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2797 LocalPlayer *lplayer = getLocalPlayer();
2801 if (lplayer->hp > damage)
2802 lplayer->hp -= damage;
2807 ClientEnvEvent event;
2808 event.type = CEE_PLAYER_DAMAGE;
2809 event.player_damage.amount = damage;
2810 event.player_damage.send_to_server = handle_hp;
2811 m_client_event_queue.push(event);
2814 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2816 ClientEnvEvent event;
2817 event.type = CEE_PLAYER_BREATH;
2818 event.player_breath.amount = breath;
2819 m_client_event_queue.push(event);
2823 Client likes to call these
2826 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2827 std::vector<DistanceSortedActiveObject> &dest)
2829 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2830 i != m_active_objects.end(); ++i) {
2831 ClientActiveObject* obj = i->second;
2833 f32 d = (obj->getPosition() - origin).getLength();
2838 DistanceSortedActiveObject dso(obj, d);
2840 dest.push_back(dso);
2844 ClientEnvEvent ClientEnvironment::getClientEvent()
2846 ClientEnvEvent event;
2847 if(m_client_event_queue.empty())
2848 event.type = CEE_NONE;
2850 event = m_client_event_queue.front();
2851 m_client_event_queue.pop();
2856 ClientActiveObject * ClientEnvironment::getSelectedActiveObject(
2857 const core::line3d<f32> &shootline_on_map, v3f *intersection_point,
2858 v3s16 *intersection_normal)
2860 std::vector<DistanceSortedActiveObject> objects;
2861 getActiveObjects(shootline_on_map.start,
2862 shootline_on_map.getLength() + 3, objects);
2863 const v3f line_vector = shootline_on_map.getVector();
2866 // After this, the closest object is the first in the array.
2867 std::sort(objects.begin(), objects.end());
2869 /* Because objects can have different nodebox sizes,
2870 * the object whose center is the nearest isn't necessarily
2871 * the closest one. If an object is found, don't stop
2874 f32 d_min = shootline_on_map.getLength();
2875 ClientActiveObject *nearest_obj = NULL;
2876 for (u32 i = 0; i < objects.size(); i++) {
2877 ClientActiveObject *obj = objects[i].obj;
2879 aabb3f *selection_box = obj->getSelectionBox();
2880 if (selection_box == NULL)
2883 v3f pos = obj->getPosition();
2885 aabb3f offsetted_box(selection_box->MinEdge + pos,
2886 selection_box->MaxEdge + pos);
2888 if (offsetted_box.getCenter().getDistanceFrom(
2889 shootline_on_map.start) > d_min + 9.6f*BS) {
2890 // Probably there is no active object that has bigger nodebox than
2891 // (-5.5,-5.5,-5.5,5.5,5.5,5.5)
2892 // 9.6 > 5.5*sqrt(3)
2896 v3f current_intersection;
2897 v3s16 current_normal;
2898 if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector,
2899 ¤t_intersection, ¤t_normal)) {
2900 f32 d_current = current_intersection.getDistanceFrom(
2901 shootline_on_map.start);
2902 if (d_current <= d_min) {
2905 *intersection_point = current_intersection;
2906 *intersection_normal = current_normal;
2915 Check if a node is pointable
2917 static inline bool isPointableNode(const MapNode &n,
2918 INodeDefManager *ndef, bool liquids_pointable)
2920 const ContentFeatures &features = ndef->get(n);
2921 return features.pointable ||
2922 (liquids_pointable && features.isLiquid());
2925 PointedThing ClientEnvironment::getPointedThing(
2926 core::line3d<f32> shootline,
2927 bool liquids_pointable,
2928 bool look_for_object)
2930 PointedThing result;
2932 INodeDefManager *nodedef = m_map->getNodeDefManager();
2934 core::aabbox3d<s16> maximal_exceed = nodedef->getSelectionBoxIntUnion();
2935 // The code needs to search these nodes
2936 core::aabbox3d<s16> search_range(-maximal_exceed.MaxEdge,
2937 -maximal_exceed.MinEdge);
2938 // If a node is found, there might be a larger node behind.
2939 // To find it, we have to go further.
2940 s16 maximal_overcheck =
2941 std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X))
2942 + std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y))
2943 + std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z));
2945 const v3f original_vector = shootline.getVector();
2946 const f32 original_length = original_vector.getLength();
2948 f32 min_distance = original_length;
2950 // First try to find an active object
2951 if (look_for_object) {
2952 ClientActiveObject *selected_object = getSelectedActiveObject(
2953 shootline, &result.intersection_point,
2954 &result.intersection_normal);
2956 if (selected_object != NULL) {
2958 (result.intersection_point - shootline.start).getLength();
2960 result.type = POINTEDTHING_OBJECT;
2961 result.object_id = selected_object->getId();
2966 if (original_length > 0) {
2967 shootline.end = shootline.start
2968 + shootline.getVector() / original_length * min_distance;
2971 // Try to find a node that is closer than the selected active
2972 // object (if it exists).
2974 voxalgo::VoxelLineIterator iterator(shootline.start / BS,
2975 shootline.getVector() / BS);
2976 v3s16 oldnode = iterator.m_current_node_pos;
2977 // Indicates that a node was found.
2978 bool is_node_found = false;
2979 // If a node is found, it is possible that there's a node
2980 // behind it with a large nodebox, so continue the search.
2981 u16 node_foundcounter = 0;
2982 // If a node is found, this is the center of the
2983 // first nodebox the shootline meets.
2984 v3f found_boxcenter(0, 0, 0);
2985 // The untested nodes are in this range.
2986 core::aabbox3d<s16> new_nodes;
2988 // Test the nodes around the current node in search_range.
2989 new_nodes = search_range;
2990 new_nodes.MinEdge += iterator.m_current_node_pos;
2991 new_nodes.MaxEdge += iterator.m_current_node_pos;
2993 // Only check new nodes
2994 v3s16 delta = iterator.m_current_node_pos - oldnode;
2996 new_nodes.MinEdge.X = new_nodes.MaxEdge.X;
2997 else if (delta.X < 0)
2998 new_nodes.MaxEdge.X = new_nodes.MinEdge.X;
2999 else if (delta.Y > 0)
3000 new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y;
3001 else if (delta.Y < 0)
3002 new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y;
3003 else if (delta.Z > 0)
3004 new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z;
3005 else if (delta.Z < 0)
3006 new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z;
3008 // For each untested node
3009 for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) {
3010 for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) {
3011 for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) {
3014 bool is_valid_position;
3016 n = m_map->getNodeNoEx(np, &is_valid_position);
3017 if (!(is_valid_position &&
3018 isPointableNode(n, nodedef, liquids_pointable))) {
3021 std::vector<aabb3f> boxes;
3022 n.getSelectionBoxes(nodedef, &boxes,
3023 n.getNeighbors(np, m_map));
3025 v3f npf = intToFloat(np, BS);
3026 for (std::vector<aabb3f>::const_iterator i = boxes.begin();
3027 i != boxes.end(); ++i) {
3031 v3f intersection_point;
3032 v3s16 intersection_normal;
3033 if (!boxLineCollision(box, shootline.start, shootline.getVector(),
3034 &intersection_point, &intersection_normal)) {
3037 f32 distance = (intersection_point - shootline.start).getLength();
3038 if (distance >= min_distance) {
3041 result.type = POINTEDTHING_NODE;
3042 result.node_undersurface = np;
3043 result.intersection_point = intersection_point;
3044 result.intersection_normal = intersection_normal;
3045 found_boxcenter = box.getCenter();
3046 min_distance = distance;
3047 is_node_found = true;
3052 if (is_node_found) {
3053 node_foundcounter++;
3054 if (node_foundcounter > maximal_overcheck) {
3059 if (iterator.hasNext()) {
3060 oldnode = iterator.m_current_node_pos;
3067 if (is_node_found) {
3068 // Set undersurface and abovesurface nodes
3070 v3f fake_intersection = result.intersection_point;
3071 // Move intersection towards its source block.
3072 if (fake_intersection.X < found_boxcenter.X)
3073 fake_intersection.X += d;
3075 fake_intersection.X -= d;
3077 if (fake_intersection.Y < found_boxcenter.Y)
3078 fake_intersection.Y += d;
3080 fake_intersection.Y -= d;
3082 if (fake_intersection.Z < found_boxcenter.Z)
3083 fake_intersection.Z += d;
3085 fake_intersection.Z -= d;
3087 result.node_real_undersurface = floatToInt(fake_intersection, BS);
3088 result.node_abovesurface = result.node_real_undersurface
3089 + result.intersection_normal;
3094 #endif // #ifndef SERVER