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"
46 #include "util/serialize.h"
47 #include "util/basic_macros.h"
48 #include "threading/mutex_auto_lock.h"
50 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
52 // A number that is much smaller than the timeout for particle spawners should/could ever be
53 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
55 Environment::Environment():
56 m_time_of_day_speed(0),
58 m_time_of_day_f(9000./24000),
59 m_time_conversion_skew(0.0f),
60 m_enable_day_night_ratio_override(false),
61 m_day_night_ratio_override(0.0f)
63 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
64 m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval");
65 m_cache_abm_interval = g_settings->getFloat("abm_interval");
66 m_cache_nodetimer_interval = g_settings->getFloat("nodetimer_interval");
69 Environment::~Environment()
73 u32 Environment::getDayNightRatio()
75 MutexAutoLock lock(this->m_time_lock);
76 if (m_enable_day_night_ratio_override)
77 return m_day_night_ratio_override;
78 return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
81 void Environment::setTimeOfDaySpeed(float speed)
83 m_time_of_day_speed = speed;
86 void Environment::setDayNightRatioOverride(bool enable, u32 value)
88 MutexAutoLock lock(this->m_time_lock);
89 m_enable_day_night_ratio_override = enable;
90 m_day_night_ratio_override = value;
93 void Environment::setTimeOfDay(u32 time)
95 MutexAutoLock lock(this->m_time_lock);
96 if (m_time_of_day > time)
99 m_time_of_day_f = (float)time / 24000.0;
102 u32 Environment::getTimeOfDay()
104 MutexAutoLock lock(this->m_time_lock);
105 return m_time_of_day;
108 float Environment::getTimeOfDayF()
110 MutexAutoLock lock(this->m_time_lock);
111 return m_time_of_day_f;
114 void Environment::stepTimeOfDay(float dtime)
116 MutexAutoLock lock(this->m_time_lock);
118 // Cached in order to prevent the two reads we do to give
119 // different results (can be written by code not under the lock)
120 f32 cached_time_of_day_speed = m_time_of_day_speed;
122 f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
123 m_time_conversion_skew += dtime;
124 u32 units = (u32)(m_time_conversion_skew * speed);
128 if (m_time_of_day + units >= 24000) {
132 m_time_of_day = (m_time_of_day + units) % 24000;
134 m_time_of_day_f = (float)m_time_of_day / 24000.0;
137 m_time_conversion_skew -= (f32)units / speed;
140 m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
141 if (m_time_of_day_f > 1.0)
142 m_time_of_day_f -= 1.0;
143 if (m_time_of_day_f < 0.0)
144 m_time_of_day_f += 1.0;
148 u32 Environment::getDayCount()
150 // Atomic<u32> counter
159 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
163 // Initialize timer to random value to spread processing
164 float itv = abm->getTriggerInterval();
165 itv = MYMAX(0.001, itv); // No less than 1ms
166 int minval = MYMAX(-0.51*itv, -60); // Clamp to
167 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
168 timer = myrand_range(minval, maxval);
175 void LBMContentMapping::deleteContents()
177 for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
178 it != lbm_list.end(); ++it) {
183 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
185 // Add the lbm_def to the LBMContentMapping.
186 // Unknown names get added to the global NameIdMapping.
187 INodeDefManager *nodedef = gamedef->ndef();
189 lbm_list.push_back(lbm_def);
191 for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
192 it != lbm_def->trigger_contents.end(); ++it) {
193 std::set<content_t> c_ids;
194 bool found = nodedef->getIds(*it, c_ids);
196 content_t c_id = gamedef->allocateUnknownNodeId(*it);
197 if (c_id == CONTENT_IGNORE) {
198 // Seems it can't be allocated.
199 warningstream << "Could not internalize node name \"" << *it
200 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
206 for (std::set<content_t>::const_iterator iit =
207 c_ids.begin(); iit != c_ids.end(); ++iit) {
208 content_t c_id = *iit;
209 map[c_id].push_back(lbm_def);
214 const std::vector<LoadingBlockModifierDef *> *
215 LBMContentMapping::lookup(content_t c) const
217 container_map::const_iterator it = map.find(c);
220 // This first dereferences the iterator, returning
221 // a std::vector<LoadingBlockModifierDef *>
222 // reference, then we convert it to a pointer.
223 return &(it->second);
226 LBMManager::~LBMManager()
228 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
229 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
232 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
233 it != m_lbm_lookup.end(); ++it) {
234 (it->second).deleteContents();
238 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
240 // Precondition, in query mode the map isn't used anymore
241 FATAL_ERROR_IF(m_query_mode == true,
242 "attempted to modify LBMManager in query mode");
244 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
245 throw ModError("Error adding LBM \"" + lbm_def->name +
246 "\": Does not follow naming conventions: "
247 "Only chararacters [a-z0-9_:] are allowed.");
250 m_lbm_defs[lbm_def->name] = lbm_def;
253 void LBMManager::loadIntroductionTimes(const std::string ×,
254 IGameDef *gamedef, u32 now)
259 // Storing it in a map first instead of
260 // handling the stuff directly in the loop
261 // removes all duplicate entries.
262 // TODO make this std::unordered_map
263 std::map<std::string, u32> introduction_times;
266 The introduction times string consists of name~time entries,
267 with each entry terminated by a semicolon. The time is decimal.
272 while ((idx_new = times.find(";", idx)) != std::string::npos) {
273 std::string entry = times.substr(idx, idx_new - idx);
274 std::vector<std::string> components = str_split(entry, '~');
275 if (components.size() != 2)
276 throw SerializationError("Introduction times entry \""
277 + entry + "\" requires exactly one '~'!");
278 const std::string &name = components[0];
279 u32 time = from_string<u32>(components[1]);
280 introduction_times[name] = time;
284 // Put stuff from introduction_times into m_lbm_lookup
285 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
286 it != introduction_times.end(); ++it) {
287 const std::string &name = it->first;
288 u32 time = it->second;
290 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
291 m_lbm_defs.find(name);
292 if (def_it == m_lbm_defs.end()) {
293 // This seems to be an LBM entry for
294 // an LBM we haven't loaded. Discard it.
297 LoadingBlockModifierDef *lbm_def = def_it->second;
298 if (lbm_def->run_at_every_load) {
299 // This seems to be an LBM entry for
300 // an LBM that runs at every load.
301 // Don't add it just yet.
305 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
307 // Erase the entry so that we know later
308 // what elements didn't get put into m_lbm_lookup
309 m_lbm_defs.erase(name);
312 // Now also add the elements from m_lbm_defs to m_lbm_lookup
313 // that weren't added in the previous step.
314 // They are introduced first time to this world,
315 // or are run at every load (introducement time hardcoded to U32_MAX).
317 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
318 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
320 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
321 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
322 if (it->second->run_at_every_load) {
323 lbms_running_always.addLBM(it->second, gamedef);
325 lbms_we_introduce_now.addLBM(it->second, gamedef);
329 // Clear the list, so that we don't delete remaining elements
330 // twice in the destructor
334 std::string LBMManager::createIntroductionTimesString()
336 // Precondition, we must be in query mode
337 FATAL_ERROR_IF(m_query_mode == false,
338 "attempted to query on non fully set up LBMManager");
340 std::ostringstream oss;
341 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
342 it != m_lbm_lookup.end(); ++it) {
343 u32 time = it->first;
344 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
345 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
346 iit != lbm_list.end(); ++iit) {
347 // Don't add if the LBM runs at every load,
348 // then introducement time is hardcoded
349 // and doesn't need to be stored
350 if ((*iit)->run_at_every_load)
352 oss << (*iit)->name << "~" << time << ";";
358 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
360 // Precondition, we need m_lbm_lookup to be initialized
361 FATAL_ERROR_IF(m_query_mode == false,
362 "attempted to query on non fully set up LBMManager");
363 v3s16 pos_of_block = block->getPosRelative();
367 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
368 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
369 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
370 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
372 n = block->getNodeNoEx(pos);
374 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
375 iit != m_lbm_lookup.end(); ++iit) {
376 const std::vector<LoadingBlockModifierDef *> *lbm_list =
377 iit->second.lookup(c);
380 for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
381 lbm_list->begin(); iit != lbm_list->end(); ++iit) {
382 (*iit)->trigger(env, pos + pos_of_block, n);
392 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
395 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
396 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
397 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
400 if (p.getDistanceFrom(p0) <= r) {
407 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
409 std::set<v3s16> &blocks_removed,
410 std::set<v3s16> &blocks_added)
415 std::set<v3s16> newlist = m_forceloaded_list;
416 for(std::vector<v3s16>::iterator i = active_positions.begin();
417 i != active_positions.end(); ++i)
419 fillRadiusBlock(*i, radius, newlist);
423 Find out which blocks on the old list are not on the new list
425 // Go through old list
426 for(std::set<v3s16>::iterator i = m_list.begin();
427 i != m_list.end(); ++i)
430 // If not on new list, it's been removed
431 if(newlist.find(p) == newlist.end())
432 blocks_removed.insert(p);
436 Find out which blocks on the new list are not on the old list
438 // Go through new list
439 for(std::set<v3s16>::iterator i = newlist.begin();
440 i != newlist.end(); ++i)
443 // If not on old list, it's been added
444 if(m_list.find(p) == m_list.end())
445 blocks_added.insert(p);
452 for(std::set<v3s16>::iterator i = newlist.begin();
453 i != newlist.end(); ++i)
464 ServerEnvironment::ServerEnvironment(ServerMap *map,
465 GameScripting *scriptIface, IGameDef *gamedef,
466 const std::string &path_world) :
468 m_script(scriptIface),
470 m_path_world(path_world),
471 m_send_recommended_timer(0),
472 m_active_block_interval_overload_skip(0),
474 m_game_time_fraction_counter(0),
475 m_last_clear_objects_time(0),
476 m_recommended_send_interval(0.1),
477 m_max_lag_estimate(0.1)
481 ServerEnvironment::~ServerEnvironment()
483 // Clear active block list.
484 // This makes the next one delete all active objects.
485 m_active_blocks.clear();
487 // Convert all objects to static and delete the active objects
488 deactivateFarObjects(true);
493 // Delete ActiveBlockModifiers
494 for (std::vector<ABMWithState>::iterator
495 i = m_abms.begin(); i != m_abms.end(); ++i){
499 // Deallocate players
500 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
501 i != m_players.end(); ++i) {
506 Map & ServerEnvironment::getMap()
511 ServerMap & ServerEnvironment::getServerMap()
516 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
518 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
519 i != m_players.end(); ++i) {
520 RemotePlayer *player = *i;
521 if (player->peer_id == peer_id)
527 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
529 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
530 i != m_players.end(); ++i) {
531 RemotePlayer *player = *i;
532 if (strcmp(player->getName(), name) == 0)
538 void ServerEnvironment::addPlayer(RemotePlayer *player)
540 DSTACK(FUNCTION_NAME);
542 Check that peer_ids are unique.
543 Also check that names are unique.
544 Exception: there can be multiple players with peer_id=0
546 // If peer id is non-zero, it has to be unique.
547 if (player->peer_id != 0)
548 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
549 // Name has to be unique.
550 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
552 m_players.push_back(player);
555 void ServerEnvironment::removePlayer(RemotePlayer *player)
557 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
558 it != m_players.end(); ++it) {
559 if ((*it) == player) {
567 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
569 float distance = pos1.getDistanceFrom(pos2);
571 //calculate normalized direction vector
572 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
573 (pos2.Y - pos1.Y)/distance,
574 (pos2.Z - pos1.Z)/distance);
576 //find out if there's a node on path between pos1 and pos2
577 for (float i = 1; i < distance; i += stepsize) {
578 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
579 normalized_vector.Y * i,
580 normalized_vector.Z * i) +pos1,BS);
582 MapNode n = getMap().getNodeNoEx(pos);
584 if(n.param0 != CONTENT_AIR) {
594 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
595 const std::string &str_reason, bool reconnect)
597 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
598 it != m_players.end(); ++it) {
599 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
600 ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id,
601 player->protocol_version, reason, str_reason, reconnect);
605 void ServerEnvironment::saveLoadedPlayers()
607 std::string players_path = m_path_world + DIR_DELIM "players";
608 fs::CreateDir(players_path);
610 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
611 it != m_players.end();
613 if ((*it)->checkModified()) {
614 (*it)->save(players_path, m_gamedef);
619 void ServerEnvironment::savePlayer(RemotePlayer *player)
621 std::string players_path = m_path_world + DIR_DELIM "players";
622 fs::CreateDir(players_path);
624 player->save(players_path, m_gamedef);
627 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao)
629 bool newplayer = false;
631 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
632 std::string path = players_path + playername;
634 RemotePlayer *player = getPlayer(playername.c_str());
636 player = new RemotePlayer("", m_gamedef->idef());
640 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
641 //// Open file and deserialize
642 std::ifstream is(path.c_str(), std::ios_base::binary);
646 player->deSerialize(is, path, sao);
649 if (player->getName() == playername) {
654 path = players_path + playername + itos(i);
658 infostream << "Player file for player " << playername
659 << " not found" << std::endl;
669 player->setModified(false);
673 void ServerEnvironment::saveMeta()
675 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
677 // Open file and serialize
678 std::ostringstream ss(std::ios_base::binary);
681 args.setU64("game_time", m_game_time);
682 args.setU64("time_of_day", getTimeOfDay());
683 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
684 args.setU64("lbm_introduction_times_version", 1);
685 args.set("lbm_introduction_times",
686 m_lbm_mgr.createIntroductionTimesString());
687 args.setU64("day_count", m_day_count);
691 if(!fs::safeWriteToFile(path, ss.str()))
693 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
695 throw SerializationError("Couldn't save env meta");
699 void ServerEnvironment::loadMeta()
701 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
703 // Open file and deserialize
704 std::ifstream is(path.c_str(), std::ios_base::binary);
706 infostream << "ServerEnvironment::loadMeta(): Failed to open "
707 << path << std::endl;
708 throw SerializationError("Couldn't load env meta");
713 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
714 throw SerializationError("ServerEnvironment::loadMeta(): "
715 "EnvArgsEnd not found!");
719 m_game_time = args.getU64("game_time");
720 } catch (SettingNotFoundException &e) {
721 // Getting this is crucial, otherwise timestamps are useless
722 throw SerializationError("Couldn't load env meta game_time");
725 setTimeOfDay(args.exists("time_of_day") ?
726 // set day to morning by default
727 args.getU64("time_of_day") : 9000);
729 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
730 // If missing, do as if clearObjects was never called
731 args.getU64("last_clear_objects_time") : 0;
733 std::string lbm_introduction_times = "";
735 u64 ver = args.getU64("lbm_introduction_times_version");
737 lbm_introduction_times = args.get("lbm_introduction_times");
739 infostream << "ServerEnvironment::loadMeta(): Non-supported"
740 << " introduction time version " << ver << std::endl;
742 } catch (SettingNotFoundException &e) {
743 // No problem, this is expected. Just continue with an empty string
745 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
747 m_day_count = args.exists("day_count") ?
748 args.getU64("day_count") : 0;
751 void ServerEnvironment::loadDefaultMeta()
753 m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
758 ActiveBlockModifier *abm;
760 std::set<content_t> required_neighbors;
766 ServerEnvironment *m_env;
767 std::map<content_t, std::vector<ActiveABM> > m_aabms;
769 ABMHandler(std::vector<ABMWithState> &abms,
770 float dtime_s, ServerEnvironment *env,
776 INodeDefManager *ndef = env->getGameDef()->ndef();
777 for(std::vector<ABMWithState>::iterator
778 i = abms.begin(); i != abms.end(); ++i) {
779 ActiveBlockModifier *abm = i->abm;
780 float trigger_interval = abm->getTriggerInterval();
781 if(trigger_interval < 0.001)
782 trigger_interval = 0.001;
783 float actual_interval = dtime_s;
786 if(i->timer < trigger_interval)
788 i->timer -= trigger_interval;
789 actual_interval = trigger_interval;
791 float chance = abm->getTriggerChance();
796 if(abm->getSimpleCatchUp()) {
797 float intervals = actual_interval / trigger_interval;
800 aabm.chance = chance / intervals;
804 aabm.chance = chance;
807 std::set<std::string> required_neighbors_s
808 = abm->getRequiredNeighbors();
809 for(std::set<std::string>::iterator
810 i = required_neighbors_s.begin();
811 i != required_neighbors_s.end(); ++i)
813 ndef->getIds(*i, aabm.required_neighbors);
816 std::set<std::string> contents_s = abm->getTriggerContents();
817 for(std::set<std::string>::iterator
818 i = contents_s.begin(); i != contents_s.end(); ++i)
820 std::set<content_t> ids;
821 ndef->getIds(*i, ids);
822 for(std::set<content_t>::const_iterator k = ids.begin();
826 std::map<content_t, std::vector<ActiveABM> >::iterator j;
828 if(j == m_aabms.end()){
829 std::vector<ActiveABM> aabmlist;
830 m_aabms[c] = aabmlist;
833 j->second.push_back(aabm);
838 // Find out how many objects the given block and its neighbours contain.
839 // Returns the number of objects in the block, and also in 'wider' the
840 // number of objects in the block and all its neighbours. The latter
841 // may an estimate if any neighbours are unloaded.
842 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
845 u32 wider_unknown_count = 0;
846 for(s16 x=-1; x<=1; x++)
847 for(s16 y=-1; y<=1; y++)
848 for(s16 z=-1; z<=1; z++)
850 MapBlock *block2 = map->getBlockNoCreateNoEx(
851 block->getPos() + v3s16(x,y,z));
853 wider_unknown_count++;
856 wider += block2->m_static_objects.m_active.size()
857 + block2->m_static_objects.m_stored.size();
860 u32 active_object_count = block->m_static_objects.m_active.size();
861 u32 wider_known_count = 3*3*3 - wider_unknown_count;
862 wider += wider_unknown_count * wider / wider_known_count;
863 return active_object_count;
866 void apply(MapBlock *block)
871 ServerMap *map = &m_env->getServerMap();
873 u32 active_object_count_wider;
874 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
875 m_env->m_added_objects = 0;
878 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
879 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
880 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
882 MapNode n = block->getNodeNoEx(p0);
883 content_t c = n.getContent();
884 v3s16 p = p0 + block->getPosRelative();
886 std::map<content_t, std::vector<ActiveABM> >::iterator j;
888 if(j == m_aabms.end())
891 for(std::vector<ActiveABM>::iterator
892 i = j->second.begin(); i != j->second.end(); ++i) {
893 if(myrand() % i->chance != 0)
897 if(!i->required_neighbors.empty())
900 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
901 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
902 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
906 MapNode n = map->getNodeNoEx(p1);
907 content_t c = n.getContent();
908 std::set<content_t>::const_iterator k;
909 k = i->required_neighbors.find(c);
910 if(k != i->required_neighbors.end()){
914 // No required neighbor found
919 // Call all the trigger variations
920 i->abm->trigger(m_env, p, n);
921 i->abm->trigger(m_env, p, n,
922 active_object_count, active_object_count_wider);
924 // Count surrounding objects again if the abms added any
925 if(m_env->m_added_objects > 0) {
926 active_object_count = countObjects(block, map, active_object_count_wider);
927 m_env->m_added_objects = 0;
934 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
936 // Reset usage timer immediately, otherwise a block that becomes active
937 // again at around the same time as it would normally be unloaded will
938 // get unloaded incorrectly. (I think this still leaves a small possibility
939 // of a race condition between this and server::AsyncRunStep, which only
940 // some kind of synchronisation will fix, but it at least reduces the window
941 // of opportunity for it to break from seconds to nanoseconds)
942 block->resetUsageTimer();
944 // Get time difference
946 u32 stamp = block->getTimestamp();
947 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
948 dtime_s = m_game_time - stamp;
949 dtime_s += additional_dtime;
951 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
952 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
954 // Remove stored static objects if clearObjects was called since block's timestamp
955 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
956 block->m_static_objects.m_stored.clear();
957 // do not set changed flag to avoid unnecessary mapblock writes
960 // Set current time as timestamp
961 block->setTimestampNoChangedFlag(m_game_time);
963 /*infostream<<"ServerEnvironment::activateBlock(): block is "
964 <<dtime_s<<" seconds old."<<std::endl;*/
966 // Activate stored objects
967 activateObjects(block, dtime_s);
969 /* Handle LoadingBlockModifiers */
970 m_lbm_mgr.applyLBMs(this, block, stamp);
973 std::vector<NodeTimer> elapsed_timers =
974 block->m_node_timers.step((float)dtime_s);
975 if (!elapsed_timers.empty()) {
977 for (std::vector<NodeTimer>::iterator
978 i = elapsed_timers.begin();
979 i != elapsed_timers.end(); ++i){
980 n = block->getNodeNoEx(i->position);
981 v3s16 p = i->position + block->getPosRelative();
982 if (m_script->node_on_timer(p, n, i->elapsed))
983 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
987 /* Handle ActiveBlockModifiers */
988 ABMHandler abmhandler(m_abms, dtime_s, this, false);
989 abmhandler.apply(block);
992 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
994 m_abms.push_back(ABMWithState(abm));
997 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
999 m_lbm_mgr.addLBMDef(lbm);
1002 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1004 INodeDefManager *ndef = m_gamedef->ndef();
1005 MapNode n_old = m_map->getNodeNoEx(p);
1008 if (ndef->get(n_old).has_on_destruct)
1009 m_script->node_on_destruct(p, n_old);
1012 if (!m_map->addNodeWithEvent(p, n))
1015 // Update active VoxelManipulator if a mapgen thread
1016 m_map->updateVManip(p);
1018 // Call post-destructor
1019 if (ndef->get(n_old).has_after_destruct)
1020 m_script->node_after_destruct(p, n_old);
1023 if (ndef->get(n).has_on_construct)
1024 m_script->node_on_construct(p, n);
1029 bool ServerEnvironment::removeNode(v3s16 p)
1031 INodeDefManager *ndef = m_gamedef->ndef();
1032 MapNode n_old = m_map->getNodeNoEx(p);
1035 if (ndef->get(n_old).has_on_destruct)
1036 m_script->node_on_destruct(p, n_old);
1039 // This is slightly optimized compared to addNodeWithEvent(air)
1040 if (!m_map->removeNodeWithEvent(p))
1043 // Update active VoxelManipulator if a mapgen thread
1044 m_map->updateVManip(p);
1046 // Call post-destructor
1047 if (ndef->get(n_old).has_after_destruct)
1048 m_script->node_after_destruct(p, n_old);
1050 // Air doesn't require constructor
1054 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1056 if (!m_map->addNodeWithEvent(p, n, false))
1059 // Update active VoxelManipulator if a mapgen thread
1060 m_map->updateVManip(p);
1065 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1067 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1068 i != m_active_objects.end(); ++i) {
1069 ServerActiveObject* obj = i->second;
1071 v3f objectpos = obj->getBasePosition();
1072 if (objectpos.getDistanceFrom(pos) > radius)
1074 objects.push_back(id);
1078 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1080 infostream << "ServerEnvironment::clearObjects(): "
1081 << "Removing all active objects" << std::endl;
1082 std::vector<u16> objects_to_remove;
1083 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1084 i != m_active_objects.end(); ++i) {
1085 ServerActiveObject* obj = i->second;
1086 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1089 // Delete static object if block is loaded
1090 if (obj->m_static_exists) {
1091 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1093 block->m_static_objects.remove(id);
1094 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1095 MOD_REASON_CLEAR_ALL_OBJECTS);
1096 obj->m_static_exists = false;
1099 // If known by some client, don't delete immediately
1100 if (obj->m_known_by_count > 0) {
1101 obj->m_pending_deactivation = true;
1102 obj->m_removed = true;
1106 // Tell the object about removal
1107 obj->removingFromEnvironment();
1108 // Deregister in scripting api
1109 m_script->removeObjectReference(obj);
1111 // Delete active object
1112 if (obj->environmentDeletes())
1114 // Id to be removed from m_active_objects
1115 objects_to_remove.push_back(id);
1118 // Remove references from m_active_objects
1119 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1120 i != objects_to_remove.end(); ++i) {
1121 m_active_objects.erase(*i);
1124 // Get list of loaded blocks
1125 std::vector<v3s16> loaded_blocks;
1126 infostream << "ServerEnvironment::clearObjects(): "
1127 << "Listing all loaded blocks" << std::endl;
1128 m_map->listAllLoadedBlocks(loaded_blocks);
1129 infostream << "ServerEnvironment::clearObjects(): "
1130 << "Done listing all loaded blocks: "
1131 << loaded_blocks.size()<<std::endl;
1133 // Get list of loadable blocks
1134 std::vector<v3s16> loadable_blocks;
1135 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1136 infostream << "ServerEnvironment::clearObjects(): "
1137 << "Listing all loadable blocks" << std::endl;
1138 m_map->listAllLoadableBlocks(loadable_blocks);
1139 infostream << "ServerEnvironment::clearObjects(): "
1140 << "Done listing all loadable blocks: "
1141 << loadable_blocks.size() << std::endl;
1143 loadable_blocks = loaded_blocks;
1146 infostream << "ServerEnvironment::clearObjects(): "
1147 << "Now clearing objects in " << loadable_blocks.size()
1148 << " blocks" << std::endl;
1150 // Grab a reference on each loaded block to avoid unloading it
1151 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1152 i != loaded_blocks.end(); ++i) {
1154 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1155 assert(block != NULL);
1159 // Remove objects in all loadable blocks
1160 u32 unload_interval = U32_MAX;
1161 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1162 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1163 unload_interval = MYMAX(unload_interval, 1);
1165 u32 report_interval = loadable_blocks.size() / 10;
1166 u32 num_blocks_checked = 0;
1167 u32 num_blocks_cleared = 0;
1168 u32 num_objs_cleared = 0;
1169 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1170 i != loadable_blocks.end(); ++i) {
1172 MapBlock *block = m_map->emergeBlock(p, false);
1174 errorstream << "ServerEnvironment::clearObjects(): "
1175 << "Failed to emerge block " << PP(p) << std::endl;
1178 u32 num_stored = block->m_static_objects.m_stored.size();
1179 u32 num_active = block->m_static_objects.m_active.size();
1180 if (num_stored != 0 || num_active != 0) {
1181 block->m_static_objects.m_stored.clear();
1182 block->m_static_objects.m_active.clear();
1183 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1184 MOD_REASON_CLEAR_ALL_OBJECTS);
1185 num_objs_cleared += num_stored + num_active;
1186 num_blocks_cleared++;
1188 num_blocks_checked++;
1190 if (report_interval != 0 &&
1191 num_blocks_checked % report_interval == 0) {
1192 float percent = 100.0 * (float)num_blocks_checked /
1193 loadable_blocks.size();
1194 infostream << "ServerEnvironment::clearObjects(): "
1195 << "Cleared " << num_objs_cleared << " objects"
1196 << " in " << num_blocks_cleared << " blocks ("
1197 << percent << "%)" << std::endl;
1199 if (num_blocks_checked % unload_interval == 0) {
1200 m_map->unloadUnreferencedBlocks();
1203 m_map->unloadUnreferencedBlocks();
1205 // Drop references that were added above
1206 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1207 i != loaded_blocks.end(); ++i) {
1209 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1214 m_last_clear_objects_time = m_game_time;
1216 infostream << "ServerEnvironment::clearObjects(): "
1217 << "Finished: Cleared " << num_objs_cleared << " objects"
1218 << " in " << num_blocks_cleared << " blocks" << std::endl;
1221 void ServerEnvironment::step(float dtime)
1223 DSTACK(FUNCTION_NAME);
1225 //TimeTaker timer("ServerEnv step");
1227 /* Step time of day */
1228 stepTimeOfDay(dtime);
1231 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1232 // really matter that much.
1233 static const float server_step = g_settings->getFloat("dedicated_server_step");
1234 m_recommended_send_interval = server_step;
1240 m_game_time_fraction_counter += dtime;
1241 u32 inc_i = (u32)m_game_time_fraction_counter;
1242 m_game_time += inc_i;
1243 m_game_time_fraction_counter -= (float)inc_i;
1250 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1251 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1252 i != m_players.end(); ++i) {
1253 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1256 // Ignore disconnected players
1257 if(player->peer_id == 0)
1261 player->move(dtime, this, 100*BS);
1266 Manage active block list
1268 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1269 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1271 Get player block positions
1273 std::vector<v3s16> players_blockpos;
1274 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1275 i != m_players.end(); ++i) {
1276 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1279 // Ignore disconnected players
1280 if (player->peer_id == 0)
1283 PlayerSAO *playersao = player->getPlayerSAO();
1286 v3s16 blockpos = getNodeBlockPos(
1287 floatToInt(playersao->getBasePosition(), BS));
1288 players_blockpos.push_back(blockpos);
1292 Update list of active blocks, collecting changes
1294 static const s16 active_block_range = g_settings->getS16("active_block_range");
1295 std::set<v3s16> blocks_removed;
1296 std::set<v3s16> blocks_added;
1297 m_active_blocks.update(players_blockpos, active_block_range,
1298 blocks_removed, blocks_added);
1301 Handle removed blocks
1304 // Convert active objects that are no more in active blocks to static
1305 deactivateFarObjects(false);
1307 for(std::set<v3s16>::iterator
1308 i = blocks_removed.begin();
1309 i != blocks_removed.end(); ++i) {
1312 /* infostream<<"Server: Block " << PP(p)
1313 << " became inactive"<<std::endl; */
1315 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1319 // Set current time as timestamp (and let it set ChangedFlag)
1320 block->setTimestamp(m_game_time);
1327 for(std::set<v3s16>::iterator
1328 i = blocks_added.begin();
1329 i != blocks_added.end(); ++i)
1333 MapBlock *block = m_map->getBlockOrEmerge(p);
1335 m_active_blocks.m_list.erase(p);
1339 activateBlock(block);
1340 /* infostream<<"Server: Block " << PP(p)
1341 << " became active"<<std::endl; */
1346 Mess around in active blocks
1348 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1349 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1351 float dtime = m_cache_nodetimer_interval;
1353 for(std::set<v3s16>::iterator
1354 i = m_active_blocks.m_list.begin();
1355 i != m_active_blocks.m_list.end(); ++i)
1359 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1360 <<") being handled"<<std::endl;*/
1362 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1366 // Reset block usage timer
1367 block->resetUsageTimer();
1369 // Set current time as timestamp
1370 block->setTimestampNoChangedFlag(m_game_time);
1371 // If time has changed much from the one on disk,
1372 // set block to be saved when it is unloaded
1373 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1374 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1375 MOD_REASON_BLOCK_EXPIRED);
1378 std::vector<NodeTimer> elapsed_timers =
1379 block->m_node_timers.step((float)dtime);
1380 if (!elapsed_timers.empty()) {
1382 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1383 i != elapsed_timers.end(); ++i) {
1384 n = block->getNodeNoEx(i->position);
1385 p = i->position + block->getPosRelative();
1386 if (m_script->node_on_timer(p, n, i->elapsed)) {
1387 block->setNodeTimer(NodeTimer(
1388 i->timeout, 0, i->position));
1395 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1397 if(m_active_block_interval_overload_skip > 0){
1398 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1399 m_active_block_interval_overload_skip--;
1402 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1403 TimeTaker timer("modify in active blocks per interval");
1405 // Initialize handling of ActiveBlockModifiers
1406 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1408 for(std::set<v3s16>::iterator
1409 i = m_active_blocks.m_list.begin();
1410 i != m_active_blocks.m_list.end(); ++i)
1414 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1415 <<") being handled"<<std::endl;*/
1417 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1421 // Set current time as timestamp
1422 block->setTimestampNoChangedFlag(m_game_time);
1424 /* Handle ActiveBlockModifiers */
1425 abmhandler.apply(block);
1428 u32 time_ms = timer.stop(true);
1429 u32 max_time_ms = 200;
1430 if(time_ms > max_time_ms){
1431 warningstream<<"active block modifiers took "
1432 <<time_ms<<"ms (longer than "
1433 <<max_time_ms<<"ms)"<<std::endl;
1434 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1439 Step script environment (run global on_step())
1441 m_script->environment_Step(dtime);
1447 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1448 //TimeTaker timer("Step active objects");
1450 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1452 // This helps the objects to send data at the same time
1453 bool send_recommended = false;
1454 m_send_recommended_timer += dtime;
1455 if(m_send_recommended_timer > getSendRecommendedInterval())
1457 m_send_recommended_timer -= getSendRecommendedInterval();
1458 send_recommended = true;
1461 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1462 i != m_active_objects.end(); ++i) {
1463 ServerActiveObject* obj = i->second;
1464 // Don't step if is to be removed or stored statically
1465 if(obj->m_removed || obj->m_pending_deactivation)
1468 obj->step(dtime, send_recommended);
1469 // Read messages from object
1470 while(!obj->m_messages_out.empty())
1472 m_active_object_messages.push(
1473 obj->m_messages_out.front());
1474 obj->m_messages_out.pop();
1480 Manage active objects
1482 if(m_object_management_interval.step(dtime, 0.5))
1484 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1486 Remove objects that satisfy (m_removed && m_known_by_count==0)
1488 removeRemovedObjects();
1492 Manage particle spawner expiration
1494 if (m_particle_management_interval.step(dtime, 1.0)) {
1495 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1496 i != m_particle_spawners.end(); ) {
1497 //non expiring spawners
1498 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1504 if (i->second <= 0.f)
1505 m_particle_spawners.erase(i++);
1512 u32 ServerEnvironment::addParticleSpawner(float exptime)
1514 // Timers with lifetime 0 do not expire
1515 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1518 for (;;) { // look for unused particlespawner id
1520 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1521 if (f == m_particle_spawners.end()) {
1522 m_particle_spawners[id] = time;
1529 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1531 u32 id = addParticleSpawner(exptime);
1532 m_particle_spawner_attachments[id] = attached_id;
1533 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1534 obj->attachParticleSpawner(id);
1539 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1541 m_particle_spawners.erase(id);
1542 UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1543 if (it != m_particle_spawner_attachments.end()) {
1544 u16 obj_id = (*it).second;
1545 ServerActiveObject *sao = getActiveObject(obj_id);
1546 if (sao != NULL && remove_from_object) {
1547 sao->detachParticleSpawner(id);
1549 m_particle_spawner_attachments.erase(id);
1553 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1555 ActiveObjectMap::iterator n = m_active_objects.find(id);
1556 return (n != m_active_objects.end() ? n->second : NULL);
1559 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1564 return objects.find(id) == objects.end();
1567 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1569 //try to reuse id's as late as possible
1570 static u16 last_used_id = 0;
1571 u16 startid = last_used_id;
1575 if(isFreeServerActiveObjectId(last_used_id, objects))
1576 return last_used_id;
1578 if(last_used_id == startid)
1583 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1585 assert(object); // Pre-condition
1587 u16 id = addActiveObjectRaw(object, true, 0);
1592 Finds out what new objects have been added to
1593 inside a radius around a position
1595 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1597 std::set<u16> ¤t_objects,
1598 std::queue<u16> &added_objects)
1600 f32 radius_f = radius * BS;
1601 f32 player_radius_f = player_radius * BS;
1603 if (player_radius_f < 0)
1604 player_radius_f = 0;
1606 Go through the object list,
1607 - discard m_removed objects,
1608 - discard objects that are too far away,
1609 - discard objects that are found in current_objects.
1610 - add remaining objects to added_objects
1612 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1613 i != m_active_objects.end(); ++i) {
1617 ServerActiveObject *object = i->second;
1621 // Discard if removed or deactivating
1622 if(object->m_removed || object->m_pending_deactivation)
1625 f32 distance_f = object->getBasePosition().
1626 getDistanceFrom(playersao->getBasePosition());
1627 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1628 // Discard if too far
1629 if (distance_f > player_radius_f && player_radius_f != 0)
1631 } else if (distance_f > radius_f)
1634 // Discard if already on current_objects
1635 std::set<u16>::iterator n;
1636 n = current_objects.find(id);
1637 if(n != current_objects.end())
1639 // Add to added_objects
1640 added_objects.push(id);
1645 Finds out what objects have been removed from
1646 inside a radius around a position
1648 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1650 std::set<u16> ¤t_objects,
1651 std::queue<u16> &removed_objects)
1653 f32 radius_f = radius * BS;
1654 f32 player_radius_f = player_radius * BS;
1656 if (player_radius_f < 0)
1657 player_radius_f = 0;
1659 Go through current_objects; object is removed if:
1660 - object is not found in m_active_objects (this is actually an
1661 error condition; objects should be set m_removed=true and removed
1662 only after all clients have been informed about removal), or
1663 - object has m_removed=true, or
1664 - object is too far away
1666 for(std::set<u16>::iterator
1667 i = current_objects.begin();
1668 i != current_objects.end(); ++i)
1671 ServerActiveObject *object = getActiveObject(id);
1673 if (object == NULL) {
1674 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1675 << " object in current_objects is NULL" << std::endl;
1676 removed_objects.push(id);
1680 if (object->m_removed || object->m_pending_deactivation) {
1681 removed_objects.push(id);
1685 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1686 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1687 if (distance_f <= player_radius_f || player_radius_f == 0)
1689 } else if (distance_f <= radius_f)
1692 // Object is no longer visible
1693 removed_objects.push(id);
1697 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1698 v3s16 blockpos, bool static_exists, v3s16 static_block)
1700 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1704 for (std::map<u16, StaticObject>::iterator
1705 so_it = block->m_static_objects.m_active.begin();
1706 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1707 // Get the ServerActiveObject counterpart to this StaticObject
1708 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1709 if (ao_it == m_active_objects.end()) {
1710 // If this ever happens, there must be some kind of nasty bug.
1711 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1712 "Object from MapBlock::m_static_objects::m_active not found "
1713 "in m_active_objects";
1717 ServerActiveObject *sao = ao_it->second;
1718 sao->m_static_exists = static_exists;
1719 sao->m_static_block = static_block;
1723 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1725 if(m_active_object_messages.empty())
1726 return ActiveObjectMessage(0);
1728 ActiveObjectMessage message = m_active_object_messages.front();
1729 m_active_object_messages.pop();
1734 ************ Private methods *************
1737 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1738 bool set_changed, u32 dtime_s)
1740 assert(object); // Pre-condition
1741 if(object->getId() == 0){
1742 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1745 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1746 <<"no free ids available"<<std::endl;
1747 if(object->environmentDeletes())
1751 object->setId(new_id);
1754 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1755 <<"supplied with id "<<object->getId()<<std::endl;
1758 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1759 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1760 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1761 if(object->environmentDeletes())
1766 if (objectpos_over_limit(object->getBasePosition())) {
1767 v3f p = object->getBasePosition();
1768 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1769 << "object position (" << p.X << "," << p.Y << "," << p.Z
1770 << ") outside maximum range" << std::endl;
1771 if (object->environmentDeletes())
1776 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1777 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1779 m_active_objects[object->getId()] = object;
1781 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1782 <<"Added id="<<object->getId()<<"; there are now "
1783 <<m_active_objects.size()<<" active objects."
1786 // Register reference in scripting api (must be done before post-init)
1787 m_script->addObjectReference(object);
1788 // Post-initialize object
1789 object->addedToEnvironment(dtime_s);
1791 // Add static data to block
1792 if(object->isStaticAllowed())
1794 // Add static object to active static list of the block
1795 v3f objectpos = object->getBasePosition();
1796 std::string staticdata = object->getStaticData();
1797 StaticObject s_obj(object->getType(), objectpos, staticdata);
1798 // Add to the block where the object is located in
1799 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1800 MapBlock *block = m_map->emergeBlock(blockpos);
1802 block->m_static_objects.m_active[object->getId()] = s_obj;
1803 object->m_static_exists = true;
1804 object->m_static_block = blockpos;
1807 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1808 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1810 v3s16 p = floatToInt(objectpos, BS);
1811 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1812 <<"could not emerge block for storing id="<<object->getId()
1813 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1817 return object->getId();
1821 Remove objects that satisfy (m_removed && m_known_by_count==0)
1823 void ServerEnvironment::removeRemovedObjects()
1825 std::vector<u16> objects_to_remove;
1826 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1827 i != m_active_objects.end(); ++i) {
1829 ServerActiveObject* obj = i->second;
1830 // This shouldn't happen but check it
1833 infostream<<"NULL object found in ServerEnvironment"
1834 <<" while finding removed objects. id="<<id<<std::endl;
1835 // Id to be removed from m_active_objects
1836 objects_to_remove.push_back(id);
1841 We will delete objects that are marked as removed or thatare
1842 waiting for deletion after deactivation
1844 if (!obj->m_removed && !obj->m_pending_deactivation)
1848 Delete static data from block if is marked as removed
1850 if(obj->m_static_exists && obj->m_removed)
1852 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1854 block->m_static_objects.remove(id);
1855 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1856 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1857 obj->m_static_exists = false;
1859 infostream<<"Failed to emerge block from which an object to "
1860 <<"be removed was loaded from. id="<<id<<std::endl;
1864 // If m_known_by_count > 0, don't actually remove. On some future
1865 // invocation this will be 0, which is when removal will continue.
1866 if(obj->m_known_by_count > 0)
1870 Move static data from active to stored if not marked as removed
1872 if(obj->m_static_exists && !obj->m_removed){
1873 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1875 std::map<u16, StaticObject>::iterator i =
1876 block->m_static_objects.m_active.find(id);
1877 if(i != block->m_static_objects.m_active.end()){
1878 block->m_static_objects.m_stored.push_back(i->second);
1879 block->m_static_objects.m_active.erase(id);
1880 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1881 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1884 infostream<<"Failed to emerge block from which an object to "
1885 <<"be deactivated was loaded from. id="<<id<<std::endl;
1889 // Tell the object about removal
1890 obj->removingFromEnvironment();
1891 // Deregister in scripting api
1892 m_script->removeObjectReference(obj);
1895 if(obj->environmentDeletes())
1898 // Id to be removed from m_active_objects
1899 objects_to_remove.push_back(id);
1901 // Remove references from m_active_objects
1902 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1903 i != objects_to_remove.end(); ++i) {
1904 m_active_objects.erase(*i);
1908 static void print_hexdump(std::ostream &o, const std::string &data)
1910 const int linelength = 16;
1911 for(int l=0; ; l++){
1912 int i0 = linelength * l;
1913 bool at_end = false;
1914 int thislinelength = linelength;
1915 if(i0 + thislinelength > (int)data.size()){
1916 thislinelength = data.size() - i0;
1919 for(int di=0; di<linelength; di++){
1922 if(di<thislinelength)
1923 snprintf(buf, 4, "%.2x ", data[i]);
1925 snprintf(buf, 4, " ");
1929 for(int di=0; di<thislinelength; di++){
1943 Convert stored objects from blocks near the players to active.
1945 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1950 // Ignore if no stored objects (to not set changed flag)
1951 if(block->m_static_objects.m_stored.empty())
1954 verbosestream<<"ServerEnvironment::activateObjects(): "
1955 <<"activating objects of block "<<PP(block->getPos())
1956 <<" ("<<block->m_static_objects.m_stored.size()
1957 <<" objects)"<<std::endl;
1958 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1960 errorstream<<"suspiciously large amount of objects detected: "
1961 <<block->m_static_objects.m_stored.size()<<" in "
1962 <<PP(block->getPos())
1963 <<"; removing all of them."<<std::endl;
1964 // Clear stored list
1965 block->m_static_objects.m_stored.clear();
1966 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1967 MOD_REASON_TOO_MANY_OBJECTS);
1971 // Activate stored objects
1972 std::vector<StaticObject> new_stored;
1973 for (std::vector<StaticObject>::iterator
1974 i = block->m_static_objects.m_stored.begin();
1975 i != block->m_static_objects.m_stored.end(); ++i) {
1976 StaticObject &s_obj = *i;
1978 // Create an active object from the data
1979 ServerActiveObject *obj = ServerActiveObject::create
1980 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1981 // If couldn't create object, store static data back.
1983 errorstream<<"ServerEnvironment::activateObjects(): "
1984 <<"failed to create active object from static object "
1985 <<"in block "<<PP(s_obj.pos/BS)
1986 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1987 print_hexdump(verbosestream, s_obj.data);
1989 new_stored.push_back(s_obj);
1992 verbosestream<<"ServerEnvironment::activateObjects(): "
1993 <<"activated static object pos="<<PP(s_obj.pos/BS)
1994 <<" type="<<(int)s_obj.type<<std::endl;
1995 // This will also add the object to the active static list
1996 addActiveObjectRaw(obj, false, dtime_s);
1998 // Clear stored list
1999 block->m_static_objects.m_stored.clear();
2000 // Add leftover failed stuff to stored list
2001 for(std::vector<StaticObject>::iterator
2002 i = new_stored.begin();
2003 i != new_stored.end(); ++i) {
2004 StaticObject &s_obj = *i;
2005 block->m_static_objects.m_stored.push_back(s_obj);
2008 // Turn the active counterparts of activated objects not pending for
2010 for(std::map<u16, StaticObject>::iterator
2011 i = block->m_static_objects.m_active.begin();
2012 i != block->m_static_objects.m_active.end(); ++i)
2015 ServerActiveObject *object = getActiveObject(id);
2017 object->m_pending_deactivation = false;
2021 Note: Block hasn't really been modified here.
2022 The objects have just been activated and moved from the stored
2023 static list to the active static list.
2024 As such, the block is essentially the same.
2025 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2026 Otherwise there would be a huge amount of unnecessary I/O.
2031 Convert objects that are not standing inside active blocks to static.
2033 If m_known_by_count != 0, active object is not deleted, but static
2034 data is still updated.
2036 If force_delete is set, active object is deleted nevertheless. It
2037 shall only be set so in the destructor of the environment.
2039 If block wasn't generated (not in memory or on disk),
2041 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2043 std::vector<u16> objects_to_remove;
2044 for(ActiveObjectMap::iterator i = m_active_objects.begin();
2045 i != m_active_objects.end(); ++i) {
2046 ServerActiveObject* obj = i->second;
2049 // Do not deactivate if static data creation not allowed
2050 if(!force_delete && !obj->isStaticAllowed())
2053 // If pending deactivation, let removeRemovedObjects() do it
2054 if(!force_delete && obj->m_pending_deactivation)
2058 v3f objectpos = obj->getBasePosition();
2060 // The block in which the object resides in
2061 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2063 // If object's static data is stored in a deactivated block and object
2064 // is actually located in an active block, re-save to the block in
2065 // which the object is actually located in.
2067 obj->m_static_exists &&
2068 !m_active_blocks.contains(obj->m_static_block) &&
2069 m_active_blocks.contains(blockpos_o))
2071 v3s16 old_static_block = obj->m_static_block;
2073 // Save to block where object is located
2074 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2076 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2077 <<"Could not save object id="<<id
2078 <<" to it's current block "<<PP(blockpos_o)
2082 std::string staticdata_new = obj->getStaticData();
2083 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2084 block->m_static_objects.insert(id, s_obj);
2085 obj->m_static_block = blockpos_o;
2086 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2087 MOD_REASON_STATIC_DATA_ADDED);
2089 // Delete from block where object was located
2090 block = m_map->emergeBlock(old_static_block, false);
2092 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2093 <<"Could not delete object id="<<id
2094 <<" from it's previous block "<<PP(old_static_block)
2098 block->m_static_objects.remove(id);
2099 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2100 MOD_REASON_STATIC_DATA_REMOVED);
2104 // If block is active, don't remove
2105 if(!force_delete && m_active_blocks.contains(blockpos_o))
2108 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2109 <<"deactivating object id="<<id<<" on inactive block "
2110 <<PP(blockpos_o)<<std::endl;
2112 // If known by some client, don't immediately delete.
2113 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2116 Update the static data
2119 if(obj->isStaticAllowed())
2121 // Create new static object
2122 std::string staticdata_new = obj->getStaticData();
2123 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2125 bool stays_in_same_block = false;
2126 bool data_changed = true;
2128 if (obj->m_static_exists) {
2129 if (obj->m_static_block == blockpos_o)
2130 stays_in_same_block = true;
2132 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2135 std::map<u16, StaticObject>::iterator n =
2136 block->m_static_objects.m_active.find(id);
2137 if (n != block->m_static_objects.m_active.end()) {
2138 StaticObject static_old = n->second;
2140 float save_movem = obj->getMinimumSavedMovement();
2142 if (static_old.data == staticdata_new &&
2143 (static_old.pos - objectpos).getLength() < save_movem)
2144 data_changed = false;
2146 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2147 <<"id="<<id<<" m_static_exists=true but "
2148 <<"static data doesn't actually exist in "
2149 <<PP(obj->m_static_block)<<std::endl;
2154 bool shall_be_written = (!stays_in_same_block || data_changed);
2156 // Delete old static object
2157 if(obj->m_static_exists)
2159 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2162 block->m_static_objects.remove(id);
2163 obj->m_static_exists = false;
2164 // Only mark block as modified if data changed considerably
2165 if(shall_be_written)
2166 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2167 MOD_REASON_STATIC_DATA_CHANGED);
2171 // Add to the block where the object is located in
2172 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2173 // Get or generate the block
2174 MapBlock *block = NULL;
2176 block = m_map->emergeBlock(blockpos);
2177 } catch(InvalidPositionException &e){
2178 // Handled via NULL pointer
2179 // NOTE: emergeBlock's failure is usually determined by it
2180 // actually returning NULL
2185 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2186 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2187 << " statically but block " << PP(blockpos)
2188 << " already contains "
2189 << block->m_static_objects.m_stored.size()
2191 << " Forcing delete." << std::endl;
2192 force_delete = true;
2194 // If static counterpart already exists in target block,
2196 // This shouldn't happen because the object is removed from
2197 // the previous block before this according to
2198 // obj->m_static_block, but happens rarely for some unknown
2199 // reason. Unsuccessful attempts have been made to find
2201 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2202 warningstream<<"ServerEnv: Performing hack #83274"
2204 block->m_static_objects.remove(id);
2206 // Store static data
2207 u16 store_id = pending_delete ? id : 0;
2208 block->m_static_objects.insert(store_id, s_obj);
2210 // Only mark block as modified if data changed considerably
2211 if(shall_be_written)
2212 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2213 MOD_REASON_STATIC_DATA_CHANGED);
2215 obj->m_static_exists = true;
2216 obj->m_static_block = block->getPos();
2221 v3s16 p = floatToInt(objectpos, BS);
2222 errorstream<<"ServerEnv: Could not find or generate "
2223 <<"a block for storing id="<<obj->getId()
2224 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2231 If known by some client, set pending deactivation.
2232 Otherwise delete it immediately.
2235 if(pending_delete && !force_delete)
2237 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2238 <<"object id="<<id<<" is known by clients"
2239 <<"; not deleting yet"<<std::endl;
2241 obj->m_pending_deactivation = true;
2245 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2246 <<"object id="<<id<<" is not known by clients"
2247 <<"; deleting"<<std::endl;
2249 // Tell the object about removal
2250 obj->removingFromEnvironment();
2251 // Deregister in scripting api
2252 m_script->removeObjectReference(obj);
2254 // Delete active object
2255 if(obj->environmentDeletes())
2257 // Id to be removed from m_active_objects
2258 objects_to_remove.push_back(id);
2261 // Remove references from m_active_objects
2262 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2263 i != objects_to_remove.end(); ++i) {
2264 m_active_objects.erase(*i);
2270 #include "clientsimpleobject.h"
2276 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2277 ITextureSource *texturesource, IGameDef *gamedef,
2278 IrrlichtDevice *irr):
2280 m_local_player(NULL),
2282 m_texturesource(texturesource),
2287 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2290 ClientEnvironment::~ClientEnvironment()
2292 // delete active objects
2293 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2294 i != m_active_objects.end(); ++i) {
2298 for(std::vector<ClientSimpleObject*>::iterator
2299 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2307 Map & ClientEnvironment::getMap()
2312 ClientMap & ClientEnvironment::getClientMap()
2317 void ClientEnvironment::setLocalPlayer(LocalPlayer *player)
2319 DSTACK(FUNCTION_NAME);
2321 It is a failure if already is a local player
2323 FATAL_ERROR_IF(m_local_player != NULL,
2324 "Local player already allocated");
2326 m_local_player = player;
2329 void ClientEnvironment::step(float dtime)
2331 DSTACK(FUNCTION_NAME);
2333 /* Step time of day */
2334 stepTimeOfDay(dtime);
2336 // Get some settings
2337 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2338 bool free_move = fly_allowed && g_settings->getBool("free_move");
2341 LocalPlayer *lplayer = getLocalPlayer();
2343 // collision info queue
2344 std::vector<CollisionInfo> player_collisions;
2347 Get the speed the player is going
2349 bool is_climbing = lplayer->is_climbing;
2351 f32 player_speed = lplayer->getSpeed().getLength();
2354 Maximum position increment
2356 //f32 position_max_increment = 0.05*BS;
2357 f32 position_max_increment = 0.1*BS;
2359 // Maximum time increment (for collision detection etc)
2360 // time = distance / speed
2361 f32 dtime_max_increment = 1;
2362 if(player_speed > 0.001)
2363 dtime_max_increment = position_max_increment / player_speed;
2365 // Maximum time increment is 10ms or lower
2366 if(dtime_max_increment > 0.01)
2367 dtime_max_increment = 0.01;
2369 // Don't allow overly huge dtime
2373 f32 dtime_downcount = dtime;
2376 Stuff that has a maximum time increment
2385 if(dtime_downcount > dtime_max_increment)
2387 dtime_part = dtime_max_increment;
2388 dtime_downcount -= dtime_part;
2392 dtime_part = dtime_downcount;
2394 Setting this to 0 (no -=dtime_part) disables an infinite loop
2395 when dtime_part is so small that dtime_downcount -= dtime_part
2398 dtime_downcount = 0;
2407 if(!free_move && !is_climbing)
2410 v3f speed = lplayer->getSpeed();
2411 if(!lplayer->in_liquid)
2412 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2414 // Liquid floating / sinking
2415 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2416 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2418 // Liquid resistance
2419 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2421 // How much the node's viscosity blocks movement, ranges between 0 and 1
2422 // Should match the scale at which viscosity increase affects other liquid attributes
2423 const f32 viscosity_factor = 0.3;
2425 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2426 f32 dl = d_wanted.getLength();
2427 if(dl > lplayer->movement_liquid_fluidity_smooth)
2428 dl = lplayer->movement_liquid_fluidity_smooth;
2429 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2431 v3f d = d_wanted.normalize() * dl;
2435 lplayer->setSpeed(speed);
2440 This also does collision detection.
2442 lplayer->move(dtime_part, this, position_max_increment,
2443 &player_collisions);
2446 while(dtime_downcount > 0.001);
2448 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2450 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2451 i != player_collisions.end(); ++i) {
2452 CollisionInfo &info = *i;
2453 v3f speed_diff = info.new_speed - info.old_speed;;
2454 // Handle only fall damage
2455 // (because otherwise walking against something in fast_move kills you)
2456 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2458 // Get rid of other components
2461 f32 pre_factor = 1; // 1 hp per node/s
2462 f32 tolerance = BS*14; // 5 without damage
2463 f32 post_factor = 1; // 1 hp per node/s
2464 if(info.type == COLLISION_NODE)
2466 const ContentFeatures &f = m_gamedef->ndef()->
2467 get(m_map->getNodeNoEx(info.node_p));
2468 // Determine fall damage multiplier
2469 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2470 pre_factor = 1.0 + (float)addp/100.0;
2472 float speed = pre_factor * speed_diff.getLength();
2473 if(speed > tolerance)
2475 f32 damage_f = (speed - tolerance)/BS * post_factor;
2476 u16 damage = (u16)(damage_f+0.5);
2478 damageLocalPlayer(damage, true);
2479 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2480 m_gamedef->event()->put(e);
2486 A quick draft of lava damage
2488 if(m_lava_hurt_interval.step(dtime, 1.0))
2490 v3f pf = lplayer->getPosition();
2492 // Feet, middle and head
2493 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2494 MapNode n1 = m_map->getNodeNoEx(p1);
2495 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2496 MapNode n2 = m_map->getNodeNoEx(p2);
2497 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2498 MapNode n3 = m_map->getNodeNoEx(p3);
2500 u32 damage_per_second = 0;
2501 damage_per_second = MYMAX(damage_per_second,
2502 m_gamedef->ndef()->get(n1).damage_per_second);
2503 damage_per_second = MYMAX(damage_per_second,
2504 m_gamedef->ndef()->get(n2).damage_per_second);
2505 damage_per_second = MYMAX(damage_per_second,
2506 m_gamedef->ndef()->get(n3).damage_per_second);
2508 if(damage_per_second != 0)
2510 damageLocalPlayer(damage_per_second, true);
2514 // Protocol v29 make this behaviour obsolete
2515 if (((Client*) getGameDef())->getProtoVersion() < 29) {
2519 if (m_drowning_interval.step(dtime, 2.0)) {
2520 v3f pf = lplayer->getPosition();
2523 v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
2524 MapNode n = m_map->getNodeNoEx(p);
2525 ContentFeatures c = m_gamedef->ndef()->get(n);
2526 u8 drowning_damage = c.drowning;
2527 if (drowning_damage > 0 && lplayer->hp > 0) {
2528 u16 breath = lplayer->getBreath();
2535 lplayer->setBreath(breath);
2536 updateLocalPlayerBreath(breath);
2539 if (lplayer->getBreath() == 0 && drowning_damage > 0) {
2540 damageLocalPlayer(drowning_damage, true);
2543 if (m_breathing_interval.step(dtime, 0.5)) {
2544 v3f pf = lplayer->getPosition();
2547 v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS);
2548 MapNode n = m_map->getNodeNoEx(p);
2549 ContentFeatures c = m_gamedef->ndef()->get(n);
2551 lplayer->setBreath(11);
2552 } else if (c.drowning == 0) {
2553 u16 breath = lplayer->getBreath();
2556 lplayer->setBreath(breath);
2557 updateLocalPlayerBreath(breath);
2563 // Update lighting on local player (used for wield item)
2564 u32 day_night_ratio = getDayNightRatio();
2568 // On InvalidPositionException, use this as default
2569 // (day: LIGHT_SUN, night: 0)
2570 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2572 v3s16 p = lplayer->getLightPosition();
2573 node_at_lplayer = m_map->getNodeNoEx(p);
2575 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2576 u8 day = light & 0xff;
2577 u8 night = (light >> 8) & 0xff;
2578 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2582 Step active objects and update lighting of them
2585 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2586 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2587 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2588 i != m_active_objects.end(); ++i) {
2589 ClientActiveObject* obj = i->second;
2591 obj->step(dtime, this);
2600 v3s16 p = obj->getLightPosition();
2601 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2603 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2605 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2607 obj->updateLight(light);
2612 Step and handle simple objects
2614 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2615 for(std::vector<ClientSimpleObject*>::iterator
2616 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2617 std::vector<ClientSimpleObject*>::iterator cur = i;
2618 ClientSimpleObject *simple = *cur;
2620 simple->step(dtime);
2621 if(simple->m_to_be_removed) {
2623 i = m_simple_objects.erase(cur);
2631 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2633 m_simple_objects.push_back(simple);
2636 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2638 ClientActiveObject *obj = getActiveObject(id);
2639 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2640 return (GenericCAO*) obj;
2645 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2647 UNORDERED_MAP<u16, ClientActiveObject*>::iterator n = m_active_objects.find(id);
2648 if (n == m_active_objects.end())
2653 bool isFreeClientActiveObjectId(const u16 id,
2654 UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2659 return objects.find(id) == objects.end();
2662 u16 getFreeClientActiveObjectId(UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2664 //try to reuse id's as late as possible
2665 static u16 last_used_id = 0;
2666 u16 startid = last_used_id;
2669 if (isFreeClientActiveObjectId(last_used_id, objects))
2670 return last_used_id;
2672 if (last_used_id == startid)
2677 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2679 assert(object); // Pre-condition
2680 if(object->getId() == 0)
2682 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2685 infostream<<"ClientEnvironment::addActiveObject(): "
2686 <<"no free ids available"<<std::endl;
2690 object->setId(new_id);
2692 if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) {
2693 infostream<<"ClientEnvironment::addActiveObject(): "
2694 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2698 infostream<<"ClientEnvironment::addActiveObject(): "
2699 <<"added (id="<<object->getId()<<")"<<std::endl;
2700 m_active_objects[object->getId()] = object;
2701 object->addToScene(m_smgr, m_texturesource, m_irr);
2702 { // Update lighting immediately
2707 v3s16 p = object->getLightPosition();
2708 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2710 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2712 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2714 object->updateLight(light);
2716 return object->getId();
2719 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2720 const std::string &init_data)
2722 ClientActiveObject* obj =
2723 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2726 infostream<<"ClientEnvironment::addActiveObject(): "
2727 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2736 obj->initialize(init_data);
2738 catch(SerializationError &e)
2740 errorstream<<"ClientEnvironment::addActiveObject():"
2741 <<" id="<<id<<" type="<<type
2742 <<": SerializationError in initialize(): "
2744 <<": init_data="<<serializeJsonString(init_data)
2748 addActiveObject(obj);
2751 void ClientEnvironment::removeActiveObject(u16 id)
2753 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2754 <<"id="<<id<<std::endl;
2755 ClientActiveObject* obj = getActiveObject(id);
2757 infostream<<"ClientEnvironment::removeActiveObject(): "
2758 <<"id="<<id<<" not found"<<std::endl;
2761 obj->removeFromScene(true);
2763 m_active_objects.erase(id);
2766 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2768 ClientActiveObject *obj = getActiveObject(id);
2770 infostream << "ClientEnvironment::processActiveObjectMessage():"
2771 << " got message for id=" << id << ", which doesn't exist."
2777 obj->processMessage(data);
2778 } catch (SerializationError &e) {
2779 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2780 << " id=" << id << " type=" << obj->getType()
2781 << " SerializationError in processMessage(): " << e.what()
2787 Callbacks for activeobjects
2790 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2792 LocalPlayer *lplayer = getLocalPlayer();
2796 if (lplayer->hp > damage)
2797 lplayer->hp -= damage;
2802 ClientEnvEvent event;
2803 event.type = CEE_PLAYER_DAMAGE;
2804 event.player_damage.amount = damage;
2805 event.player_damage.send_to_server = handle_hp;
2806 m_client_event_queue.push(event);
2809 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2811 ClientEnvEvent event;
2812 event.type = CEE_PLAYER_BREATH;
2813 event.player_breath.amount = breath;
2814 m_client_event_queue.push(event);
2818 Client likes to call these
2821 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2822 std::vector<DistanceSortedActiveObject> &dest)
2824 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2825 i != m_active_objects.end(); ++i) {
2826 ClientActiveObject* obj = i->second;
2828 f32 d = (obj->getPosition() - origin).getLength();
2833 DistanceSortedActiveObject dso(obj, d);
2835 dest.push_back(dso);
2839 ClientEnvEvent ClientEnvironment::getClientEvent()
2841 ClientEnvEvent event;
2842 if(m_client_event_queue.empty())
2843 event.type = CEE_NONE;
2845 event = m_client_event_queue.front();
2846 m_client_event_queue.pop();
2851 #endif // #ifndef SERVER