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 "threading/mutex_auto_lock.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
53 // A number that is much smaller than the timeout for particle spawners should/could ever be
54 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
56 Environment::Environment():
57 m_time_of_day_speed(0),
59 m_time_of_day_f(9000./24000),
60 m_time_conversion_skew(0.0f),
61 m_enable_day_night_ratio_override(false),
62 m_day_night_ratio_override(0.0f)
64 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
65 m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval");
66 m_cache_abm_interval = g_settings->getFloat("abm_interval");
67 m_cache_nodetimer_interval = g_settings->getFloat("nodetimer_interval");
70 Environment::~Environment()
74 u32 Environment::getDayNightRatio()
76 MutexAutoLock lock(this->m_time_lock);
77 if (m_enable_day_night_ratio_override)
78 return m_day_night_ratio_override;
79 return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
82 void Environment::setTimeOfDaySpeed(float speed)
84 m_time_of_day_speed = speed;
87 void Environment::setDayNightRatioOverride(bool enable, u32 value)
89 MutexAutoLock lock(this->m_time_lock);
90 m_enable_day_night_ratio_override = enable;
91 m_day_night_ratio_override = value;
94 void Environment::setTimeOfDay(u32 time)
96 MutexAutoLock lock(this->m_time_lock);
97 if (m_time_of_day > time)
100 m_time_of_day_f = (float)time / 24000.0;
103 u32 Environment::getTimeOfDay()
105 MutexAutoLock lock(this->m_time_lock);
106 return m_time_of_day;
109 float Environment::getTimeOfDayF()
111 MutexAutoLock lock(this->m_time_lock);
112 return m_time_of_day_f;
115 void Environment::stepTimeOfDay(float dtime)
117 MutexAutoLock lock(this->m_time_lock);
119 // Cached in order to prevent the two reads we do to give
120 // different results (can be written by code not under the lock)
121 f32 cached_time_of_day_speed = m_time_of_day_speed;
123 f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
124 m_time_conversion_skew += dtime;
125 u32 units = (u32)(m_time_conversion_skew * speed);
129 if (m_time_of_day + units >= 24000) {
133 m_time_of_day = (m_time_of_day + units) % 24000;
135 m_time_of_day_f = (float)m_time_of_day / 24000.0;
138 m_time_conversion_skew -= (f32)units / speed;
141 m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
142 if (m_time_of_day_f > 1.0)
143 m_time_of_day_f -= 1.0;
144 if (m_time_of_day_f < 0.0)
145 m_time_of_day_f += 1.0;
149 u32 Environment::getDayCount()
151 // Atomic<u32> counter
160 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
164 // Initialize timer to random value to spread processing
165 float itv = abm->getTriggerInterval();
166 itv = MYMAX(0.001, itv); // No less than 1ms
167 int minval = MYMAX(-0.51*itv, -60); // Clamp to
168 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
169 timer = myrand_range(minval, maxval);
176 void LBMContentMapping::deleteContents()
178 for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
179 it != lbm_list.end(); ++it) {
184 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
186 // Add the lbm_def to the LBMContentMapping.
187 // Unknown names get added to the global NameIdMapping.
188 INodeDefManager *nodedef = gamedef->ndef();
190 lbm_list.push_back(lbm_def);
192 for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
193 it != lbm_def->trigger_contents.end(); ++it) {
194 std::set<content_t> c_ids;
195 bool found = nodedef->getIds(*it, c_ids);
197 content_t c_id = gamedef->allocateUnknownNodeId(*it);
198 if (c_id == CONTENT_IGNORE) {
199 // Seems it can't be allocated.
200 warningstream << "Could not internalize node name \"" << *it
201 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
207 for (std::set<content_t>::const_iterator iit =
208 c_ids.begin(); iit != c_ids.end(); ++iit) {
209 content_t c_id = *iit;
210 map[c_id].push_back(lbm_def);
215 const std::vector<LoadingBlockModifierDef *> *
216 LBMContentMapping::lookup(content_t c) const
218 container_map::const_iterator it = map.find(c);
221 // This first dereferences the iterator, returning
222 // a std::vector<LoadingBlockModifierDef *>
223 // reference, then we convert it to a pointer.
224 return &(it->second);
227 LBMManager::~LBMManager()
229 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
230 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
233 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
234 it != m_lbm_lookup.end(); ++it) {
235 (it->second).deleteContents();
239 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
241 // Precondition, in query mode the map isn't used anymore
242 FATAL_ERROR_IF(m_query_mode == true,
243 "attempted to modify LBMManager in query mode");
245 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
246 throw ModError("Error adding LBM \"" + lbm_def->name +
247 "\": Does not follow naming conventions: "
248 "Only chararacters [a-z0-9_:] are allowed.");
251 m_lbm_defs[lbm_def->name] = lbm_def;
254 void LBMManager::loadIntroductionTimes(const std::string ×,
255 IGameDef *gamedef, u32 now)
260 // Storing it in a map first instead of
261 // handling the stuff directly in the loop
262 // removes all duplicate entries.
263 // TODO make this std::unordered_map
264 std::map<std::string, u32> introduction_times;
267 The introduction times string consists of name~time entries,
268 with each entry terminated by a semicolon. The time is decimal.
273 while ((idx_new = times.find(";", idx)) != std::string::npos) {
274 std::string entry = times.substr(idx, idx_new - idx);
275 std::vector<std::string> components = str_split(entry, '~');
276 if (components.size() != 2)
277 throw SerializationError("Introduction times entry \""
278 + entry + "\" requires exactly one '~'!");
279 const std::string &name = components[0];
280 u32 time = from_string<u32>(components[1]);
281 introduction_times[name] = time;
285 // Put stuff from introduction_times into m_lbm_lookup
286 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
287 it != introduction_times.end(); ++it) {
288 const std::string &name = it->first;
289 u32 time = it->second;
291 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
292 m_lbm_defs.find(name);
293 if (def_it == m_lbm_defs.end()) {
294 // This seems to be an LBM entry for
295 // an LBM we haven't loaded. Discard it.
298 LoadingBlockModifierDef *lbm_def = def_it->second;
299 if (lbm_def->run_at_every_load) {
300 // This seems to be an LBM entry for
301 // an LBM that runs at every load.
302 // Don't add it just yet.
306 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
308 // Erase the entry so that we know later
309 // what elements didn't get put into m_lbm_lookup
310 m_lbm_defs.erase(name);
313 // Now also add the elements from m_lbm_defs to m_lbm_lookup
314 // that weren't added in the previous step.
315 // They are introduced first time to this world,
316 // or are run at every load (introducement time hardcoded to U32_MAX).
318 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
319 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
321 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
322 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
323 if (it->second->run_at_every_load) {
324 lbms_running_always.addLBM(it->second, gamedef);
326 lbms_we_introduce_now.addLBM(it->second, gamedef);
330 // Clear the list, so that we don't delete remaining elements
331 // twice in the destructor
335 std::string LBMManager::createIntroductionTimesString()
337 // Precondition, we must be in query mode
338 FATAL_ERROR_IF(m_query_mode == false,
339 "attempted to query on non fully set up LBMManager");
341 std::ostringstream oss;
342 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
343 it != m_lbm_lookup.end(); ++it) {
344 u32 time = it->first;
345 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
346 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
347 iit != lbm_list.end(); ++iit) {
348 // Don't add if the LBM runs at every load,
349 // then introducement time is hardcoded
350 // and doesn't need to be stored
351 if ((*iit)->run_at_every_load)
353 oss << (*iit)->name << "~" << time << ";";
359 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
361 // Precondition, we need m_lbm_lookup to be initialized
362 FATAL_ERROR_IF(m_query_mode == false,
363 "attempted to query on non fully set up LBMManager");
364 v3s16 pos_of_block = block->getPosRelative();
368 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
369 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
370 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
371 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
373 n = block->getNodeNoEx(pos);
375 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
376 iit != m_lbm_lookup.end(); ++iit) {
377 const std::vector<LoadingBlockModifierDef *> *lbm_list =
378 iit->second.lookup(c);
381 for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
382 lbm_list->begin(); iit != lbm_list->end(); ++iit) {
383 (*iit)->trigger(env, pos + pos_of_block, n);
393 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
396 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
397 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
398 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
405 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
407 std::set<v3s16> &blocks_removed,
408 std::set<v3s16> &blocks_added)
413 std::set<v3s16> newlist = m_forceloaded_list;
414 for(std::vector<v3s16>::iterator i = active_positions.begin();
415 i != active_positions.end(); ++i)
417 fillRadiusBlock(*i, radius, newlist);
421 Find out which blocks on the old list are not on the new list
423 // Go through old list
424 for(std::set<v3s16>::iterator i = m_list.begin();
425 i != m_list.end(); ++i)
428 // If not on new list, it's been removed
429 if(newlist.find(p) == newlist.end())
430 blocks_removed.insert(p);
434 Find out which blocks on the new list are not on the old list
436 // Go through new list
437 for(std::set<v3s16>::iterator i = newlist.begin();
438 i != newlist.end(); ++i)
441 // If not on old list, it's been added
442 if(m_list.find(p) == m_list.end())
443 blocks_added.insert(p);
450 for(std::set<v3s16>::iterator i = newlist.begin();
451 i != newlist.end(); ++i)
462 ServerEnvironment::ServerEnvironment(ServerMap *map,
463 GameScripting *scriptIface, IGameDef *gamedef,
464 const std::string &path_world) :
466 m_script(scriptIface),
468 m_path_world(path_world),
469 m_send_recommended_timer(0),
470 m_active_block_interval_overload_skip(0),
472 m_game_time_fraction_counter(0),
473 m_last_clear_objects_time(0),
474 m_recommended_send_interval(0.1),
475 m_max_lag_estimate(0.1)
479 ServerEnvironment::~ServerEnvironment()
481 // Clear active block list.
482 // This makes the next one delete all active objects.
483 m_active_blocks.clear();
485 // Convert all objects to static and delete the active objects
486 deactivateFarObjects(true);
491 // Delete ActiveBlockModifiers
492 for (std::vector<ABMWithState>::iterator
493 i = m_abms.begin(); i != m_abms.end(); ++i){
497 // Deallocate players
498 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
499 i != m_players.end(); ++i) {
504 Map & ServerEnvironment::getMap()
509 ServerMap & ServerEnvironment::getServerMap()
514 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
516 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
517 i != m_players.end(); ++i) {
518 RemotePlayer *player = *i;
519 if (player->peer_id == peer_id)
525 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
527 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
528 i != m_players.end(); ++i) {
529 RemotePlayer *player = *i;
530 if (strcmp(player->getName(), name) == 0)
536 void ServerEnvironment::addPlayer(RemotePlayer *player)
538 DSTACK(FUNCTION_NAME);
540 Check that peer_ids are unique.
541 Also check that names are unique.
542 Exception: there can be multiple players with peer_id=0
544 // If peer id is non-zero, it has to be unique.
545 if (player->peer_id != 0)
546 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
547 // Name has to be unique.
548 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
550 m_players.push_back(player);
553 void ServerEnvironment::removePlayer(RemotePlayer *player)
555 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
556 it != m_players.end(); ++it) {
557 if ((*it) == player) {
565 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
567 float distance = pos1.getDistanceFrom(pos2);
569 //calculate normalized direction vector
570 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
571 (pos2.Y - pos1.Y)/distance,
572 (pos2.Z - pos1.Z)/distance);
574 //find out if there's a node on path between pos1 and pos2
575 for (float i = 1; i < distance; i += stepsize) {
576 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
577 normalized_vector.Y * i,
578 normalized_vector.Z * i) +pos1,BS);
580 MapNode n = getMap().getNodeNoEx(pos);
582 if(n.param0 != CONTENT_AIR) {
592 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
593 const std::string &str_reason, bool reconnect)
595 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
596 it != m_players.end(); ++it) {
597 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
598 ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id,
599 player->protocol_version, reason, str_reason, reconnect);
603 void ServerEnvironment::saveLoadedPlayers()
605 std::string players_path = m_path_world + DIR_DELIM "players";
606 fs::CreateDir(players_path);
608 for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
609 it != m_players.end();
611 if ((*it)->checkModified()) {
612 (*it)->save(players_path, m_gamedef);
617 void ServerEnvironment::savePlayer(RemotePlayer *player)
619 std::string players_path = m_path_world + DIR_DELIM "players";
620 fs::CreateDir(players_path);
622 player->save(players_path, m_gamedef);
625 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao)
627 bool newplayer = false;
629 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
630 std::string path = players_path + playername;
632 RemotePlayer *player = getPlayer(playername.c_str());
634 player = new RemotePlayer("", m_gamedef->idef());
638 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
639 //// Open file and deserialize
640 std::ifstream is(path.c_str(), std::ios_base::binary);
644 player->deSerialize(is, path, sao);
647 if (player->getName() == playername) {
652 path = players_path + playername + itos(i);
656 infostream << "Player file for player " << playername
657 << " not found" << std::endl;
667 player->setModified(false);
671 void ServerEnvironment::saveMeta()
673 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
675 // Open file and serialize
676 std::ostringstream ss(std::ios_base::binary);
679 args.setU64("game_time", m_game_time);
680 args.setU64("time_of_day", getTimeOfDay());
681 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
682 args.setU64("lbm_introduction_times_version", 1);
683 args.set("lbm_introduction_times",
684 m_lbm_mgr.createIntroductionTimesString());
685 args.setU64("day_count", m_day_count);
689 if(!fs::safeWriteToFile(path, ss.str()))
691 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
693 throw SerializationError("Couldn't save env meta");
697 void ServerEnvironment::loadMeta()
699 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
701 // Open file and deserialize
702 std::ifstream is(path.c_str(), std::ios_base::binary);
704 infostream << "ServerEnvironment::loadMeta(): Failed to open "
705 << path << std::endl;
706 throw SerializationError("Couldn't load env meta");
711 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
712 throw SerializationError("ServerEnvironment::loadMeta(): "
713 "EnvArgsEnd not found!");
717 m_game_time = args.getU64("game_time");
718 } catch (SettingNotFoundException &e) {
719 // Getting this is crucial, otherwise timestamps are useless
720 throw SerializationError("Couldn't load env meta game_time");
723 setTimeOfDay(args.exists("time_of_day") ?
724 // set day to morning by default
725 args.getU64("time_of_day") : 9000);
727 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
728 // If missing, do as if clearObjects was never called
729 args.getU64("last_clear_objects_time") : 0;
731 std::string lbm_introduction_times = "";
733 u64 ver = args.getU64("lbm_introduction_times_version");
735 lbm_introduction_times = args.get("lbm_introduction_times");
737 infostream << "ServerEnvironment::loadMeta(): Non-supported"
738 << " introduction time version " << ver << std::endl;
740 } catch (SettingNotFoundException &e) {
741 // No problem, this is expected. Just continue with an empty string
743 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
745 m_day_count = args.exists("day_count") ?
746 args.getU64("day_count") : 0;
749 void ServerEnvironment::loadDefaultMeta()
751 m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
756 ActiveBlockModifier *abm;
758 std::set<content_t> required_neighbors;
764 ServerEnvironment *m_env;
765 std::map<content_t, std::vector<ActiveABM> > m_aabms;
767 ABMHandler(std::vector<ABMWithState> &abms,
768 float dtime_s, ServerEnvironment *env,
774 INodeDefManager *ndef = env->getGameDef()->ndef();
775 for(std::vector<ABMWithState>::iterator
776 i = abms.begin(); i != abms.end(); ++i) {
777 ActiveBlockModifier *abm = i->abm;
778 float trigger_interval = abm->getTriggerInterval();
779 if(trigger_interval < 0.001)
780 trigger_interval = 0.001;
781 float actual_interval = dtime_s;
784 if(i->timer < trigger_interval)
786 i->timer -= trigger_interval;
787 actual_interval = trigger_interval;
789 float chance = abm->getTriggerChance();
794 if(abm->getSimpleCatchUp()) {
795 float intervals = actual_interval / trigger_interval;
798 aabm.chance = chance / intervals;
802 aabm.chance = chance;
805 std::set<std::string> required_neighbors_s
806 = abm->getRequiredNeighbors();
807 for(std::set<std::string>::iterator
808 i = required_neighbors_s.begin();
809 i != required_neighbors_s.end(); ++i)
811 ndef->getIds(*i, aabm.required_neighbors);
814 std::set<std::string> contents_s = abm->getTriggerContents();
815 for(std::set<std::string>::iterator
816 i = contents_s.begin(); i != contents_s.end(); ++i)
818 std::set<content_t> ids;
819 ndef->getIds(*i, ids);
820 for(std::set<content_t>::const_iterator k = ids.begin();
824 std::map<content_t, std::vector<ActiveABM> >::iterator j;
826 if(j == m_aabms.end()){
827 std::vector<ActiveABM> aabmlist;
828 m_aabms[c] = aabmlist;
831 j->second.push_back(aabm);
836 // Find out how many objects the given block and its neighbours contain.
837 // Returns the number of objects in the block, and also in 'wider' the
838 // number of objects in the block and all its neighbours. The latter
839 // may an estimate if any neighbours are unloaded.
840 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
843 u32 wider_unknown_count = 0;
844 for(s16 x=-1; x<=1; x++)
845 for(s16 y=-1; y<=1; y++)
846 for(s16 z=-1; z<=1; z++)
848 MapBlock *block2 = map->getBlockNoCreateNoEx(
849 block->getPos() + v3s16(x,y,z));
851 wider_unknown_count++;
854 wider += block2->m_static_objects.m_active.size()
855 + block2->m_static_objects.m_stored.size();
858 u32 active_object_count = block->m_static_objects.m_active.size();
859 u32 wider_known_count = 3*3*3 - wider_unknown_count;
860 wider += wider_unknown_count * wider / wider_known_count;
861 return active_object_count;
864 void apply(MapBlock *block)
869 ServerMap *map = &m_env->getServerMap();
871 u32 active_object_count_wider;
872 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
873 m_env->m_added_objects = 0;
876 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
877 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
878 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
880 MapNode n = block->getNodeNoEx(p0);
881 content_t c = n.getContent();
882 v3s16 p = p0 + block->getPosRelative();
884 std::map<content_t, std::vector<ActiveABM> >::iterator j;
886 if(j == m_aabms.end())
889 for(std::vector<ActiveABM>::iterator
890 i = j->second.begin(); i != j->second.end(); ++i) {
891 if(myrand() % i->chance != 0)
895 if(!i->required_neighbors.empty())
898 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
899 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
900 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
904 MapNode n = map->getNodeNoEx(p1);
905 content_t c = n.getContent();
906 std::set<content_t>::const_iterator k;
907 k = i->required_neighbors.find(c);
908 if(k != i->required_neighbors.end()){
912 // No required neighbor found
917 // Call all the trigger variations
918 i->abm->trigger(m_env, p, n);
919 i->abm->trigger(m_env, p, n,
920 active_object_count, active_object_count_wider);
922 // Count surrounding objects again if the abms added any
923 if(m_env->m_added_objects > 0) {
924 active_object_count = countObjects(block, map, active_object_count_wider);
925 m_env->m_added_objects = 0;
932 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
934 // Reset usage timer immediately, otherwise a block that becomes active
935 // again at around the same time as it would normally be unloaded will
936 // get unloaded incorrectly. (I think this still leaves a small possibility
937 // of a race condition between this and server::AsyncRunStep, which only
938 // some kind of synchronisation will fix, but it at least reduces the window
939 // of opportunity for it to break from seconds to nanoseconds)
940 block->resetUsageTimer();
942 // Get time difference
944 u32 stamp = block->getTimestamp();
945 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
946 dtime_s = m_game_time - stamp;
947 dtime_s += additional_dtime;
949 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
950 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
952 // Remove stored static objects if clearObjects was called since block's timestamp
953 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
954 block->m_static_objects.m_stored.clear();
955 // do not set changed flag to avoid unnecessary mapblock writes
958 // Set current time as timestamp
959 block->setTimestampNoChangedFlag(m_game_time);
961 /*infostream<<"ServerEnvironment::activateBlock(): block is "
962 <<dtime_s<<" seconds old."<<std::endl;*/
964 // Activate stored objects
965 activateObjects(block, dtime_s);
967 /* Handle LoadingBlockModifiers */
968 m_lbm_mgr.applyLBMs(this, block, stamp);
971 std::vector<NodeTimer> elapsed_timers =
972 block->m_node_timers.step((float)dtime_s);
973 if (!elapsed_timers.empty()) {
975 for (std::vector<NodeTimer>::iterator
976 i = elapsed_timers.begin();
977 i != elapsed_timers.end(); ++i){
978 n = block->getNodeNoEx(i->position);
979 v3s16 p = i->position + block->getPosRelative();
980 if (m_script->node_on_timer(p, n, i->elapsed))
981 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
985 /* Handle ActiveBlockModifiers */
986 ABMHandler abmhandler(m_abms, dtime_s, this, false);
987 abmhandler.apply(block);
990 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
992 m_abms.push_back(ABMWithState(abm));
995 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
997 m_lbm_mgr.addLBMDef(lbm);
1000 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1002 INodeDefManager *ndef = m_gamedef->ndef();
1003 MapNode n_old = m_map->getNodeNoEx(p);
1006 if (ndef->get(n_old).has_on_destruct)
1007 m_script->node_on_destruct(p, n_old);
1010 if (!m_map->addNodeWithEvent(p, n))
1013 // Update active VoxelManipulator if a mapgen thread
1014 m_map->updateVManip(p);
1016 // Call post-destructor
1017 if (ndef->get(n_old).has_after_destruct)
1018 m_script->node_after_destruct(p, n_old);
1021 if (ndef->get(n).has_on_construct)
1022 m_script->node_on_construct(p, n);
1027 bool ServerEnvironment::removeNode(v3s16 p)
1029 INodeDefManager *ndef = m_gamedef->ndef();
1030 MapNode n_old = m_map->getNodeNoEx(p);
1033 if (ndef->get(n_old).has_on_destruct)
1034 m_script->node_on_destruct(p, n_old);
1037 // This is slightly optimized compared to addNodeWithEvent(air)
1038 if (!m_map->removeNodeWithEvent(p))
1041 // Update active VoxelManipulator if a mapgen thread
1042 m_map->updateVManip(p);
1044 // Call post-destructor
1045 if (ndef->get(n_old).has_after_destruct)
1046 m_script->node_after_destruct(p, n_old);
1048 // Air doesn't require constructor
1052 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1054 if (!m_map->addNodeWithEvent(p, n, false))
1057 // Update active VoxelManipulator if a mapgen thread
1058 m_map->updateVManip(p);
1063 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1065 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1066 i != m_active_objects.end(); ++i) {
1067 ServerActiveObject* obj = i->second;
1069 v3f objectpos = obj->getBasePosition();
1070 if (objectpos.getDistanceFrom(pos) > radius)
1072 objects.push_back(id);
1076 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1078 infostream << "ServerEnvironment::clearObjects(): "
1079 << "Removing all active objects" << std::endl;
1080 std::vector<u16> objects_to_remove;
1081 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1082 i != m_active_objects.end(); ++i) {
1083 ServerActiveObject* obj = i->second;
1084 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1087 // Delete static object if block is loaded
1088 if (obj->m_static_exists) {
1089 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1091 block->m_static_objects.remove(id);
1092 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1093 MOD_REASON_CLEAR_ALL_OBJECTS);
1094 obj->m_static_exists = false;
1097 // If known by some client, don't delete immediately
1098 if (obj->m_known_by_count > 0) {
1099 obj->m_pending_deactivation = true;
1100 obj->m_removed = true;
1104 // Tell the object about removal
1105 obj->removingFromEnvironment();
1106 // Deregister in scripting api
1107 m_script->removeObjectReference(obj);
1109 // Delete active object
1110 if (obj->environmentDeletes())
1112 // Id to be removed from m_active_objects
1113 objects_to_remove.push_back(id);
1116 // Remove references from m_active_objects
1117 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1118 i != objects_to_remove.end(); ++i) {
1119 m_active_objects.erase(*i);
1122 // Get list of loaded blocks
1123 std::vector<v3s16> loaded_blocks;
1124 infostream << "ServerEnvironment::clearObjects(): "
1125 << "Listing all loaded blocks" << std::endl;
1126 m_map->listAllLoadedBlocks(loaded_blocks);
1127 infostream << "ServerEnvironment::clearObjects(): "
1128 << "Done listing all loaded blocks: "
1129 << loaded_blocks.size()<<std::endl;
1131 // Get list of loadable blocks
1132 std::vector<v3s16> loadable_blocks;
1133 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1134 infostream << "ServerEnvironment::clearObjects(): "
1135 << "Listing all loadable blocks" << std::endl;
1136 m_map->listAllLoadableBlocks(loadable_blocks);
1137 infostream << "ServerEnvironment::clearObjects(): "
1138 << "Done listing all loadable blocks: "
1139 << loadable_blocks.size() << std::endl;
1141 loadable_blocks = loaded_blocks;
1144 infostream << "ServerEnvironment::clearObjects(): "
1145 << "Now clearing objects in " << loadable_blocks.size()
1146 << " blocks" << std::endl;
1148 // Grab a reference on each loaded block to avoid unloading it
1149 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1150 i != loaded_blocks.end(); ++i) {
1152 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1153 assert(block != NULL);
1157 // Remove objects in all loadable blocks
1158 u32 unload_interval = U32_MAX;
1159 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1160 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1161 unload_interval = MYMAX(unload_interval, 1);
1163 u32 report_interval = loadable_blocks.size() / 10;
1164 u32 num_blocks_checked = 0;
1165 u32 num_blocks_cleared = 0;
1166 u32 num_objs_cleared = 0;
1167 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1168 i != loadable_blocks.end(); ++i) {
1170 MapBlock *block = m_map->emergeBlock(p, false);
1172 errorstream << "ServerEnvironment::clearObjects(): "
1173 << "Failed to emerge block " << PP(p) << std::endl;
1176 u32 num_stored = block->m_static_objects.m_stored.size();
1177 u32 num_active = block->m_static_objects.m_active.size();
1178 if (num_stored != 0 || num_active != 0) {
1179 block->m_static_objects.m_stored.clear();
1180 block->m_static_objects.m_active.clear();
1181 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1182 MOD_REASON_CLEAR_ALL_OBJECTS);
1183 num_objs_cleared += num_stored + num_active;
1184 num_blocks_cleared++;
1186 num_blocks_checked++;
1188 if (report_interval != 0 &&
1189 num_blocks_checked % report_interval == 0) {
1190 float percent = 100.0 * (float)num_blocks_checked /
1191 loadable_blocks.size();
1192 infostream << "ServerEnvironment::clearObjects(): "
1193 << "Cleared " << num_objs_cleared << " objects"
1194 << " in " << num_blocks_cleared << " blocks ("
1195 << percent << "%)" << std::endl;
1197 if (num_blocks_checked % unload_interval == 0) {
1198 m_map->unloadUnreferencedBlocks();
1201 m_map->unloadUnreferencedBlocks();
1203 // Drop references that were added above
1204 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1205 i != loaded_blocks.end(); ++i) {
1207 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1212 m_last_clear_objects_time = m_game_time;
1214 infostream << "ServerEnvironment::clearObjects(): "
1215 << "Finished: Cleared " << num_objs_cleared << " objects"
1216 << " in " << num_blocks_cleared << " blocks" << std::endl;
1219 void ServerEnvironment::step(float dtime)
1221 DSTACK(FUNCTION_NAME);
1223 //TimeTaker timer("ServerEnv step");
1225 /* Step time of day */
1226 stepTimeOfDay(dtime);
1229 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1230 // really matter that much.
1231 static const float server_step = g_settings->getFloat("dedicated_server_step");
1232 m_recommended_send_interval = server_step;
1238 m_game_time_fraction_counter += dtime;
1239 u32 inc_i = (u32)m_game_time_fraction_counter;
1240 m_game_time += inc_i;
1241 m_game_time_fraction_counter -= (float)inc_i;
1248 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1249 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1250 i != m_players.end(); ++i) {
1251 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1254 // Ignore disconnected players
1255 if(player->peer_id == 0)
1259 player->move(dtime, this, 100*BS);
1264 Manage active block list
1266 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1267 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1269 Get player block positions
1271 std::vector<v3s16> players_blockpos;
1272 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1273 i != m_players.end(); ++i) {
1274 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1277 // Ignore disconnected players
1278 if (player->peer_id == 0)
1281 PlayerSAO *playersao = player->getPlayerSAO();
1284 v3s16 blockpos = getNodeBlockPos(
1285 floatToInt(playersao->getBasePosition(), BS));
1286 players_blockpos.push_back(blockpos);
1290 Update list of active blocks, collecting changes
1292 static const s16 active_block_range = g_settings->getS16("active_block_range");
1293 std::set<v3s16> blocks_removed;
1294 std::set<v3s16> blocks_added;
1295 m_active_blocks.update(players_blockpos, active_block_range,
1296 blocks_removed, blocks_added);
1299 Handle removed blocks
1302 // Convert active objects that are no more in active blocks to static
1303 deactivateFarObjects(false);
1305 for(std::set<v3s16>::iterator
1306 i = blocks_removed.begin();
1307 i != blocks_removed.end(); ++i) {
1310 /* infostream<<"Server: Block " << PP(p)
1311 << " became inactive"<<std::endl; */
1313 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1317 // Set current time as timestamp (and let it set ChangedFlag)
1318 block->setTimestamp(m_game_time);
1325 for(std::set<v3s16>::iterator
1326 i = blocks_added.begin();
1327 i != blocks_added.end(); ++i)
1331 MapBlock *block = m_map->getBlockOrEmerge(p);
1333 m_active_blocks.m_list.erase(p);
1337 activateBlock(block);
1338 /* infostream<<"Server: Block " << PP(p)
1339 << " became active"<<std::endl; */
1344 Mess around in active blocks
1346 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1347 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1349 float dtime = m_cache_nodetimer_interval;
1351 for(std::set<v3s16>::iterator
1352 i = m_active_blocks.m_list.begin();
1353 i != m_active_blocks.m_list.end(); ++i)
1357 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1358 <<") being handled"<<std::endl;*/
1360 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1364 // Reset block usage timer
1365 block->resetUsageTimer();
1367 // Set current time as timestamp
1368 block->setTimestampNoChangedFlag(m_game_time);
1369 // If time has changed much from the one on disk,
1370 // set block to be saved when it is unloaded
1371 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1372 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1373 MOD_REASON_BLOCK_EXPIRED);
1376 std::vector<NodeTimer> elapsed_timers =
1377 block->m_node_timers.step((float)dtime);
1378 if (!elapsed_timers.empty()) {
1380 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1381 i != elapsed_timers.end(); ++i) {
1382 n = block->getNodeNoEx(i->position);
1383 p = i->position + block->getPosRelative();
1384 if (m_script->node_on_timer(p, n, i->elapsed)) {
1385 block->setNodeTimer(NodeTimer(
1386 i->timeout, 0, i->position));
1393 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1395 if(m_active_block_interval_overload_skip > 0){
1396 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1397 m_active_block_interval_overload_skip--;
1400 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1401 TimeTaker timer("modify in active blocks per interval");
1403 // Initialize handling of ActiveBlockModifiers
1404 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1406 for(std::set<v3s16>::iterator
1407 i = m_active_blocks.m_list.begin();
1408 i != m_active_blocks.m_list.end(); ++i)
1412 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1413 <<") being handled"<<std::endl;*/
1415 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1419 // Set current time as timestamp
1420 block->setTimestampNoChangedFlag(m_game_time);
1422 /* Handle ActiveBlockModifiers */
1423 abmhandler.apply(block);
1426 u32 time_ms = timer.stop(true);
1427 u32 max_time_ms = 200;
1428 if(time_ms > max_time_ms){
1429 warningstream<<"active block modifiers took "
1430 <<time_ms<<"ms (longer than "
1431 <<max_time_ms<<"ms)"<<std::endl;
1432 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1437 Step script environment (run global on_step())
1439 m_script->environment_Step(dtime);
1445 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1446 //TimeTaker timer("Step active objects");
1448 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1450 // This helps the objects to send data at the same time
1451 bool send_recommended = false;
1452 m_send_recommended_timer += dtime;
1453 if(m_send_recommended_timer > getSendRecommendedInterval())
1455 m_send_recommended_timer -= getSendRecommendedInterval();
1456 send_recommended = true;
1459 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1460 i != m_active_objects.end(); ++i) {
1461 ServerActiveObject* obj = i->second;
1462 // Don't step if is to be removed or stored statically
1463 if(obj->m_removed || obj->m_pending_deactivation)
1466 obj->step(dtime, send_recommended);
1467 // Read messages from object
1468 while(!obj->m_messages_out.empty())
1470 m_active_object_messages.push(
1471 obj->m_messages_out.front());
1472 obj->m_messages_out.pop();
1478 Manage active objects
1480 if(m_object_management_interval.step(dtime, 0.5))
1482 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1484 Remove objects that satisfy (m_removed && m_known_by_count==0)
1486 removeRemovedObjects();
1490 Manage particle spawner expiration
1492 if (m_particle_management_interval.step(dtime, 1.0)) {
1493 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1494 i != m_particle_spawners.end(); ) {
1495 //non expiring spawners
1496 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1502 if (i->second <= 0.f)
1503 m_particle_spawners.erase(i++);
1510 u32 ServerEnvironment::addParticleSpawner(float exptime)
1512 // Timers with lifetime 0 do not expire
1513 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1516 for (;;) { // look for unused particlespawner id
1518 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1519 if (f == m_particle_spawners.end()) {
1520 m_particle_spawners[id] = time;
1527 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1529 u32 id = addParticleSpawner(exptime);
1530 m_particle_spawner_attachments[id] = attached_id;
1531 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1532 obj->attachParticleSpawner(id);
1537 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1539 m_particle_spawners.erase(id);
1540 UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1541 if (it != m_particle_spawner_attachments.end()) {
1542 u16 obj_id = (*it).second;
1543 ServerActiveObject *sao = getActiveObject(obj_id);
1544 if (sao != NULL && remove_from_object) {
1545 sao->detachParticleSpawner(id);
1547 m_particle_spawner_attachments.erase(id);
1551 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1553 ActiveObjectMap::iterator n = m_active_objects.find(id);
1554 return (n != m_active_objects.end() ? n->second : NULL);
1557 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1562 return objects.find(id) == objects.end();
1565 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1567 //try to reuse id's as late as possible
1568 static u16 last_used_id = 0;
1569 u16 startid = last_used_id;
1573 if(isFreeServerActiveObjectId(last_used_id, objects))
1574 return last_used_id;
1576 if(last_used_id == startid)
1581 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1583 assert(object); // Pre-condition
1585 u16 id = addActiveObjectRaw(object, true, 0);
1590 Finds out what new objects have been added to
1591 inside a radius around a position
1593 void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
1595 std::set<u16> ¤t_objects,
1596 std::queue<u16> &added_objects)
1598 f32 radius_f = radius * BS;
1599 f32 player_radius_f = player_radius * BS;
1601 if (player_radius_f < 0)
1602 player_radius_f = 0;
1604 Go through the object list,
1605 - discard m_removed objects,
1606 - discard objects that are too far away,
1607 - discard objects that are found in current_objects.
1608 - add remaining objects to added_objects
1610 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1611 i != m_active_objects.end(); ++i) {
1615 ServerActiveObject *object = i->second;
1619 // Discard if removed or deactivating
1620 if(object->m_removed || object->m_pending_deactivation)
1623 f32 distance_f = object->getBasePosition().
1624 getDistanceFrom(playersao->getBasePosition());
1625 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1626 // Discard if too far
1627 if (distance_f > player_radius_f && player_radius_f != 0)
1629 } else if (distance_f > radius_f)
1632 // Discard if already on current_objects
1633 std::set<u16>::iterator n;
1634 n = current_objects.find(id);
1635 if(n != current_objects.end())
1637 // Add to added_objects
1638 added_objects.push(id);
1643 Finds out what objects have been removed from
1644 inside a radius around a position
1646 void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
1648 std::set<u16> ¤t_objects,
1649 std::queue<u16> &removed_objects)
1651 f32 radius_f = radius * BS;
1652 f32 player_radius_f = player_radius * BS;
1654 if (player_radius_f < 0)
1655 player_radius_f = 0;
1657 Go through current_objects; object is removed if:
1658 - object is not found in m_active_objects (this is actually an
1659 error condition; objects should be set m_removed=true and removed
1660 only after all clients have been informed about removal), or
1661 - object has m_removed=true, or
1662 - object is too far away
1664 for(std::set<u16>::iterator
1665 i = current_objects.begin();
1666 i != current_objects.end(); ++i)
1669 ServerActiveObject *object = getActiveObject(id);
1671 if (object == NULL) {
1672 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1673 << " object in current_objects is NULL" << std::endl;
1674 removed_objects.push(id);
1678 if (object->m_removed || object->m_pending_deactivation) {
1679 removed_objects.push(id);
1683 f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
1684 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1685 if (distance_f <= player_radius_f || player_radius_f == 0)
1687 } else if (distance_f <= radius_f)
1690 // Object is no longer visible
1691 removed_objects.push(id);
1695 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1696 v3s16 blockpos, bool static_exists, v3s16 static_block)
1698 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1702 for (std::map<u16, StaticObject>::iterator
1703 so_it = block->m_static_objects.m_active.begin();
1704 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1705 // Get the ServerActiveObject counterpart to this StaticObject
1706 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1707 if (ao_it == m_active_objects.end()) {
1708 // If this ever happens, there must be some kind of nasty bug.
1709 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1710 "Object from MapBlock::m_static_objects::m_active not found "
1711 "in m_active_objects";
1715 ServerActiveObject *sao = ao_it->second;
1716 sao->m_static_exists = static_exists;
1717 sao->m_static_block = static_block;
1721 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1723 if(m_active_object_messages.empty())
1724 return ActiveObjectMessage(0);
1726 ActiveObjectMessage message = m_active_object_messages.front();
1727 m_active_object_messages.pop();
1732 ************ Private methods *************
1735 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1736 bool set_changed, u32 dtime_s)
1738 assert(object); // Pre-condition
1739 if(object->getId() == 0){
1740 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1743 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1744 <<"no free ids available"<<std::endl;
1745 if(object->environmentDeletes())
1749 object->setId(new_id);
1752 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1753 <<"supplied with id "<<object->getId()<<std::endl;
1756 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1757 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1758 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1759 if(object->environmentDeletes())
1764 if (objectpos_over_limit(object->getBasePosition())) {
1765 v3f p = object->getBasePosition();
1766 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1767 << "object position (" << p.X << "," << p.Y << "," << p.Z
1768 << ") outside maximum range" << std::endl;
1769 if (object->environmentDeletes())
1774 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1775 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1777 m_active_objects[object->getId()] = object;
1779 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1780 <<"Added id="<<object->getId()<<"; there are now "
1781 <<m_active_objects.size()<<" active objects."
1784 // Register reference in scripting api (must be done before post-init)
1785 m_script->addObjectReference(object);
1786 // Post-initialize object
1787 object->addedToEnvironment(dtime_s);
1789 // Add static data to block
1790 if(object->isStaticAllowed())
1792 // Add static object to active static list of the block
1793 v3f objectpos = object->getBasePosition();
1794 std::string staticdata = object->getStaticData();
1795 StaticObject s_obj(object->getType(), objectpos, staticdata);
1796 // Add to the block where the object is located in
1797 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1798 MapBlock *block = m_map->emergeBlock(blockpos);
1800 block->m_static_objects.m_active[object->getId()] = s_obj;
1801 object->m_static_exists = true;
1802 object->m_static_block = blockpos;
1805 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1806 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1808 v3s16 p = floatToInt(objectpos, BS);
1809 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1810 <<"could not emerge block for storing id="<<object->getId()
1811 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1815 return object->getId();
1819 Remove objects that satisfy (m_removed && m_known_by_count==0)
1821 void ServerEnvironment::removeRemovedObjects()
1823 std::vector<u16> objects_to_remove;
1824 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1825 i != m_active_objects.end(); ++i) {
1827 ServerActiveObject* obj = i->second;
1828 // This shouldn't happen but check it
1831 infostream<<"NULL object found in ServerEnvironment"
1832 <<" while finding removed objects. id="<<id<<std::endl;
1833 // Id to be removed from m_active_objects
1834 objects_to_remove.push_back(id);
1839 We will delete objects that are marked as removed or thatare
1840 waiting for deletion after deactivation
1842 if (!obj->m_removed && !obj->m_pending_deactivation)
1846 Delete static data from block if is marked as removed
1848 if(obj->m_static_exists && obj->m_removed)
1850 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1852 block->m_static_objects.remove(id);
1853 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1854 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1855 obj->m_static_exists = false;
1857 infostream<<"Failed to emerge block from which an object to "
1858 <<"be removed was loaded from. id="<<id<<std::endl;
1862 // If m_known_by_count > 0, don't actually remove. On some future
1863 // invocation this will be 0, which is when removal will continue.
1864 if(obj->m_known_by_count > 0)
1868 Move static data from active to stored if not marked as removed
1870 if(obj->m_static_exists && !obj->m_removed){
1871 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1873 std::map<u16, StaticObject>::iterator i =
1874 block->m_static_objects.m_active.find(id);
1875 if(i != block->m_static_objects.m_active.end()){
1876 block->m_static_objects.m_stored.push_back(i->second);
1877 block->m_static_objects.m_active.erase(id);
1878 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1879 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1882 infostream<<"Failed to emerge block from which an object to "
1883 <<"be deactivated was loaded from. id="<<id<<std::endl;
1887 // Tell the object about removal
1888 obj->removingFromEnvironment();
1889 // Deregister in scripting api
1890 m_script->removeObjectReference(obj);
1893 if(obj->environmentDeletes())
1896 // Id to be removed from m_active_objects
1897 objects_to_remove.push_back(id);
1899 // Remove references from m_active_objects
1900 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1901 i != objects_to_remove.end(); ++i) {
1902 m_active_objects.erase(*i);
1906 static void print_hexdump(std::ostream &o, const std::string &data)
1908 const int linelength = 16;
1909 for(int l=0; ; l++){
1910 int i0 = linelength * l;
1911 bool at_end = false;
1912 int thislinelength = linelength;
1913 if(i0 + thislinelength > (int)data.size()){
1914 thislinelength = data.size() - i0;
1917 for(int di=0; di<linelength; di++){
1920 if(di<thislinelength)
1921 snprintf(buf, 4, "%.2x ", data[i]);
1923 snprintf(buf, 4, " ");
1927 for(int di=0; di<thislinelength; di++){
1941 Convert stored objects from blocks near the players to active.
1943 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1948 // Ignore if no stored objects (to not set changed flag)
1949 if(block->m_static_objects.m_stored.empty())
1952 verbosestream<<"ServerEnvironment::activateObjects(): "
1953 <<"activating objects of block "<<PP(block->getPos())
1954 <<" ("<<block->m_static_objects.m_stored.size()
1955 <<" objects)"<<std::endl;
1956 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1958 errorstream<<"suspiciously large amount of objects detected: "
1959 <<block->m_static_objects.m_stored.size()<<" in "
1960 <<PP(block->getPos())
1961 <<"; removing all of them."<<std::endl;
1962 // Clear stored list
1963 block->m_static_objects.m_stored.clear();
1964 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1965 MOD_REASON_TOO_MANY_OBJECTS);
1969 // Activate stored objects
1970 std::vector<StaticObject> new_stored;
1971 for (std::vector<StaticObject>::iterator
1972 i = block->m_static_objects.m_stored.begin();
1973 i != block->m_static_objects.m_stored.end(); ++i) {
1974 StaticObject &s_obj = *i;
1976 // Create an active object from the data
1977 ServerActiveObject *obj = ServerActiveObject::create
1978 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1979 // If couldn't create object, store static data back.
1981 errorstream<<"ServerEnvironment::activateObjects(): "
1982 <<"failed to create active object from static object "
1983 <<"in block "<<PP(s_obj.pos/BS)
1984 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1985 print_hexdump(verbosestream, s_obj.data);
1987 new_stored.push_back(s_obj);
1990 verbosestream<<"ServerEnvironment::activateObjects(): "
1991 <<"activated static object pos="<<PP(s_obj.pos/BS)
1992 <<" type="<<(int)s_obj.type<<std::endl;
1993 // This will also add the object to the active static list
1994 addActiveObjectRaw(obj, false, dtime_s);
1996 // Clear stored list
1997 block->m_static_objects.m_stored.clear();
1998 // Add leftover failed stuff to stored list
1999 for(std::vector<StaticObject>::iterator
2000 i = new_stored.begin();
2001 i != new_stored.end(); ++i) {
2002 StaticObject &s_obj = *i;
2003 block->m_static_objects.m_stored.push_back(s_obj);
2006 // Turn the active counterparts of activated objects not pending for
2008 for(std::map<u16, StaticObject>::iterator
2009 i = block->m_static_objects.m_active.begin();
2010 i != block->m_static_objects.m_active.end(); ++i)
2013 ServerActiveObject *object = getActiveObject(id);
2015 object->m_pending_deactivation = false;
2019 Note: Block hasn't really been modified here.
2020 The objects have just been activated and moved from the stored
2021 static list to the active static list.
2022 As such, the block is essentially the same.
2023 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2024 Otherwise there would be a huge amount of unnecessary I/O.
2029 Convert objects that are not standing inside active blocks to static.
2031 If m_known_by_count != 0, active object is not deleted, but static
2032 data is still updated.
2034 If force_delete is set, active object is deleted nevertheless. It
2035 shall only be set so in the destructor of the environment.
2037 If block wasn't generated (not in memory or on disk),
2039 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2041 std::vector<u16> objects_to_remove;
2042 for(ActiveObjectMap::iterator i = m_active_objects.begin();
2043 i != m_active_objects.end(); ++i) {
2044 ServerActiveObject* obj = i->second;
2047 // Do not deactivate if static data creation not allowed
2048 if(!force_delete && !obj->isStaticAllowed())
2051 // If pending deactivation, let removeRemovedObjects() do it
2052 if(!force_delete && obj->m_pending_deactivation)
2056 v3f objectpos = obj->getBasePosition();
2058 // The block in which the object resides in
2059 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2061 // If object's static data is stored in a deactivated block and object
2062 // is actually located in an active block, re-save to the block in
2063 // which the object is actually located in.
2065 obj->m_static_exists &&
2066 !m_active_blocks.contains(obj->m_static_block) &&
2067 m_active_blocks.contains(blockpos_o))
2069 v3s16 old_static_block = obj->m_static_block;
2071 // Save to block where object is located
2072 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2074 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2075 <<"Could not save object id="<<id
2076 <<" to it's current block "<<PP(blockpos_o)
2080 std::string staticdata_new = obj->getStaticData();
2081 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2082 block->m_static_objects.insert(id, s_obj);
2083 obj->m_static_block = blockpos_o;
2084 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2085 MOD_REASON_STATIC_DATA_ADDED);
2087 // Delete from block where object was located
2088 block = m_map->emergeBlock(old_static_block, false);
2090 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2091 <<"Could not delete object id="<<id
2092 <<" from it's previous block "<<PP(old_static_block)
2096 block->m_static_objects.remove(id);
2097 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2098 MOD_REASON_STATIC_DATA_REMOVED);
2102 // If block is active, don't remove
2103 if(!force_delete && m_active_blocks.contains(blockpos_o))
2106 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2107 <<"deactivating object id="<<id<<" on inactive block "
2108 <<PP(blockpos_o)<<std::endl;
2110 // If known by some client, don't immediately delete.
2111 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2114 Update the static data
2117 if(obj->isStaticAllowed())
2119 // Create new static object
2120 std::string staticdata_new = obj->getStaticData();
2121 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2123 bool stays_in_same_block = false;
2124 bool data_changed = true;
2126 if (obj->m_static_exists) {
2127 if (obj->m_static_block == blockpos_o)
2128 stays_in_same_block = true;
2130 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2133 std::map<u16, StaticObject>::iterator n =
2134 block->m_static_objects.m_active.find(id);
2135 if (n != block->m_static_objects.m_active.end()) {
2136 StaticObject static_old = n->second;
2138 float save_movem = obj->getMinimumSavedMovement();
2140 if (static_old.data == staticdata_new &&
2141 (static_old.pos - objectpos).getLength() < save_movem)
2142 data_changed = false;
2144 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2145 <<"id="<<id<<" m_static_exists=true but "
2146 <<"static data doesn't actually exist in "
2147 <<PP(obj->m_static_block)<<std::endl;
2152 bool shall_be_written = (!stays_in_same_block || data_changed);
2154 // Delete old static object
2155 if(obj->m_static_exists)
2157 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2160 block->m_static_objects.remove(id);
2161 obj->m_static_exists = false;
2162 // Only mark block as modified if data changed considerably
2163 if(shall_be_written)
2164 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2165 MOD_REASON_STATIC_DATA_CHANGED);
2169 // Add to the block where the object is located in
2170 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2171 // Get or generate the block
2172 MapBlock *block = NULL;
2174 block = m_map->emergeBlock(blockpos);
2175 } catch(InvalidPositionException &e){
2176 // Handled via NULL pointer
2177 // NOTE: emergeBlock's failure is usually determined by it
2178 // actually returning NULL
2183 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2184 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2185 << " statically but block " << PP(blockpos)
2186 << " already contains "
2187 << block->m_static_objects.m_stored.size()
2189 << " Forcing delete." << std::endl;
2190 force_delete = true;
2192 // If static counterpart already exists in target block,
2194 // This shouldn't happen because the object is removed from
2195 // the previous block before this according to
2196 // obj->m_static_block, but happens rarely for some unknown
2197 // reason. Unsuccessful attempts have been made to find
2199 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2200 warningstream<<"ServerEnv: Performing hack #83274"
2202 block->m_static_objects.remove(id);
2204 // Store static data
2205 u16 store_id = pending_delete ? id : 0;
2206 block->m_static_objects.insert(store_id, s_obj);
2208 // Only mark block as modified if data changed considerably
2209 if(shall_be_written)
2210 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2211 MOD_REASON_STATIC_DATA_CHANGED);
2213 obj->m_static_exists = true;
2214 obj->m_static_block = block->getPos();
2219 v3s16 p = floatToInt(objectpos, BS);
2220 errorstream<<"ServerEnv: Could not find or generate "
2221 <<"a block for storing id="<<obj->getId()
2222 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2229 If known by some client, set pending deactivation.
2230 Otherwise delete it immediately.
2233 if(pending_delete && !force_delete)
2235 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2236 <<"object id="<<id<<" is known by clients"
2237 <<"; not deleting yet"<<std::endl;
2239 obj->m_pending_deactivation = true;
2243 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2244 <<"object id="<<id<<" is not known by clients"
2245 <<"; deleting"<<std::endl;
2247 // Tell the object about removal
2248 obj->removingFromEnvironment();
2249 // Deregister in scripting api
2250 m_script->removeObjectReference(obj);
2252 // Delete active object
2253 if(obj->environmentDeletes())
2255 // Id to be removed from m_active_objects
2256 objects_to_remove.push_back(id);
2259 // Remove references from m_active_objects
2260 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2261 i != objects_to_remove.end(); ++i) {
2262 m_active_objects.erase(*i);
2268 #include "clientsimpleobject.h"
2274 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2275 ITextureSource *texturesource, IGameDef *gamedef,
2276 IrrlichtDevice *irr):
2278 m_local_player(NULL),
2280 m_texturesource(texturesource),
2285 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2288 ClientEnvironment::~ClientEnvironment()
2290 // delete active objects
2291 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2292 i != m_active_objects.end(); ++i) {
2296 for(std::vector<ClientSimpleObject*>::iterator
2297 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2305 Map & ClientEnvironment::getMap()
2310 ClientMap & ClientEnvironment::getClientMap()
2315 void ClientEnvironment::setLocalPlayer(LocalPlayer *player)
2317 DSTACK(FUNCTION_NAME);
2319 It is a failure if already is a local player
2321 FATAL_ERROR_IF(m_local_player != NULL,
2322 "Local player already allocated");
2324 m_local_player = player;
2327 void ClientEnvironment::step(float dtime)
2329 DSTACK(FUNCTION_NAME);
2331 /* Step time of day */
2332 stepTimeOfDay(dtime);
2334 // Get some settings
2335 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2336 bool free_move = fly_allowed && g_settings->getBool("free_move");
2339 LocalPlayer *lplayer = getLocalPlayer();
2341 // collision info queue
2342 std::vector<CollisionInfo> player_collisions;
2345 Get the speed the player is going
2347 bool is_climbing = lplayer->is_climbing;
2349 f32 player_speed = lplayer->getSpeed().getLength();
2352 Maximum position increment
2354 //f32 position_max_increment = 0.05*BS;
2355 f32 position_max_increment = 0.1*BS;
2357 // Maximum time increment (for collision detection etc)
2358 // time = distance / speed
2359 f32 dtime_max_increment = 1;
2360 if(player_speed > 0.001)
2361 dtime_max_increment = position_max_increment / player_speed;
2363 // Maximum time increment is 10ms or lower
2364 if(dtime_max_increment > 0.01)
2365 dtime_max_increment = 0.01;
2367 // Don't allow overly huge dtime
2371 f32 dtime_downcount = dtime;
2374 Stuff that has a maximum time increment
2383 if(dtime_downcount > dtime_max_increment)
2385 dtime_part = dtime_max_increment;
2386 dtime_downcount -= dtime_part;
2390 dtime_part = dtime_downcount;
2392 Setting this to 0 (no -=dtime_part) disables an infinite loop
2393 when dtime_part is so small that dtime_downcount -= dtime_part
2396 dtime_downcount = 0;
2405 if(!free_move && !is_climbing)
2408 v3f speed = lplayer->getSpeed();
2409 if(!lplayer->in_liquid)
2410 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2412 // Liquid floating / sinking
2413 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2414 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2416 // Liquid resistance
2417 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2419 // How much the node's viscosity blocks movement, ranges between 0 and 1
2420 // Should match the scale at which viscosity increase affects other liquid attributes
2421 const f32 viscosity_factor = 0.3;
2423 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2424 f32 dl = d_wanted.getLength();
2425 if(dl > lplayer->movement_liquid_fluidity_smooth)
2426 dl = lplayer->movement_liquid_fluidity_smooth;
2427 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2429 v3f d = d_wanted.normalize() * dl;
2433 lplayer->setSpeed(speed);
2438 This also does collision detection.
2440 lplayer->move(dtime_part, this, position_max_increment,
2441 &player_collisions);
2444 while(dtime_downcount > 0.001);
2446 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2448 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2449 i != player_collisions.end(); ++i) {
2450 CollisionInfo &info = *i;
2451 v3f speed_diff = info.new_speed - info.old_speed;;
2452 // Handle only fall damage
2453 // (because otherwise walking against something in fast_move kills you)
2454 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2456 // Get rid of other components
2459 f32 pre_factor = 1; // 1 hp per node/s
2460 f32 tolerance = BS*14; // 5 without damage
2461 f32 post_factor = 1; // 1 hp per node/s
2462 if(info.type == COLLISION_NODE)
2464 const ContentFeatures &f = m_gamedef->ndef()->
2465 get(m_map->getNodeNoEx(info.node_p));
2466 // Determine fall damage multiplier
2467 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2468 pre_factor = 1.0 + (float)addp/100.0;
2470 float speed = pre_factor * speed_diff.getLength();
2471 if(speed > tolerance)
2473 f32 damage_f = (speed - tolerance)/BS * post_factor;
2474 u16 damage = (u16)(damage_f+0.5);
2476 damageLocalPlayer(damage, true);
2477 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2478 m_gamedef->event()->put(e);
2484 A quick draft of lava damage
2486 if(m_lava_hurt_interval.step(dtime, 1.0))
2488 v3f pf = lplayer->getPosition();
2490 // Feet, middle and head
2491 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2492 MapNode n1 = m_map->getNodeNoEx(p1);
2493 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2494 MapNode n2 = m_map->getNodeNoEx(p2);
2495 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2496 MapNode n3 = m_map->getNodeNoEx(p3);
2498 u32 damage_per_second = 0;
2499 damage_per_second = MYMAX(damage_per_second,
2500 m_gamedef->ndef()->get(n1).damage_per_second);
2501 damage_per_second = MYMAX(damage_per_second,
2502 m_gamedef->ndef()->get(n2).damage_per_second);
2503 damage_per_second = MYMAX(damage_per_second,
2504 m_gamedef->ndef()->get(n3).damage_per_second);
2506 if(damage_per_second != 0)
2508 damageLocalPlayer(damage_per_second, true);
2515 if(m_drowning_interval.step(dtime, 2.0))
2517 v3f pf = lplayer->getPosition();
2520 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2521 MapNode n = m_map->getNodeNoEx(p);
2522 ContentFeatures c = m_gamedef->ndef()->get(n);
2523 u8 drowning_damage = c.drowning;
2524 if(drowning_damage > 0 && lplayer->hp > 0){
2525 u16 breath = lplayer->getBreath();
2532 lplayer->setBreath(breath);
2533 updateLocalPlayerBreath(breath);
2536 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2537 damageLocalPlayer(drowning_damage, true);
2540 if(m_breathing_interval.step(dtime, 0.5))
2542 v3f pf = lplayer->getPosition();
2545 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2546 MapNode n = m_map->getNodeNoEx(p);
2547 ContentFeatures c = m_gamedef->ndef()->get(n);
2549 lplayer->setBreath(11);
2551 else if(c.drowning == 0){
2552 u16 breath = lplayer->getBreath();
2555 lplayer->setBreath(breath);
2556 updateLocalPlayerBreath(breath);
2561 // Update lighting on local player (used for wield item)
2562 u32 day_night_ratio = getDayNightRatio();
2566 // On InvalidPositionException, use this as default
2567 // (day: LIGHT_SUN, night: 0)
2568 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2570 v3s16 p = lplayer->getLightPosition();
2571 node_at_lplayer = m_map->getNodeNoEx(p);
2573 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2574 u8 day = light & 0xff;
2575 u8 night = (light >> 8) & 0xff;
2576 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2580 Step active objects and update lighting of them
2583 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2584 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2585 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2586 i != m_active_objects.end(); ++i) {
2587 ClientActiveObject* obj = i->second;
2589 obj->step(dtime, this);
2598 v3s16 p = obj->getLightPosition();
2599 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2601 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2603 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2605 obj->updateLight(light);
2610 Step and handle simple objects
2612 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2613 for(std::vector<ClientSimpleObject*>::iterator
2614 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2615 std::vector<ClientSimpleObject*>::iterator cur = i;
2616 ClientSimpleObject *simple = *cur;
2618 simple->step(dtime);
2619 if(simple->m_to_be_removed) {
2621 i = m_simple_objects.erase(cur);
2629 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2631 m_simple_objects.push_back(simple);
2634 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2636 ClientActiveObject *obj = getActiveObject(id);
2637 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2638 return (GenericCAO*) obj;
2643 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2645 UNORDERED_MAP<u16, ClientActiveObject*>::iterator n = m_active_objects.find(id);
2646 if (n == m_active_objects.end())
2651 bool isFreeClientActiveObjectId(const u16 id,
2652 UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2657 return objects.find(id) == objects.end();
2660 u16 getFreeClientActiveObjectId(UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2662 //try to reuse id's as late as possible
2663 static u16 last_used_id = 0;
2664 u16 startid = last_used_id;
2667 if (isFreeClientActiveObjectId(last_used_id, objects))
2668 return last_used_id;
2670 if (last_used_id == startid)
2675 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2677 assert(object); // Pre-condition
2678 if(object->getId() == 0)
2680 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2683 infostream<<"ClientEnvironment::addActiveObject(): "
2684 <<"no free ids available"<<std::endl;
2688 object->setId(new_id);
2690 if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) {
2691 infostream<<"ClientEnvironment::addActiveObject(): "
2692 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2696 infostream<<"ClientEnvironment::addActiveObject(): "
2697 <<"added (id="<<object->getId()<<")"<<std::endl;
2698 m_active_objects[object->getId()] = object;
2699 object->addToScene(m_smgr, m_texturesource, m_irr);
2700 { // Update lighting immediately
2705 v3s16 p = object->getLightPosition();
2706 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2708 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2710 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2712 object->updateLight(light);
2714 return object->getId();
2717 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2718 const std::string &init_data)
2720 ClientActiveObject* obj =
2721 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2724 infostream<<"ClientEnvironment::addActiveObject(): "
2725 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2734 obj->initialize(init_data);
2736 catch(SerializationError &e)
2738 errorstream<<"ClientEnvironment::addActiveObject():"
2739 <<" id="<<id<<" type="<<type
2740 <<": SerializationError in initialize(): "
2742 <<": init_data="<<serializeJsonString(init_data)
2746 addActiveObject(obj);
2749 void ClientEnvironment::removeActiveObject(u16 id)
2751 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2752 <<"id="<<id<<std::endl;
2753 ClientActiveObject* obj = getActiveObject(id);
2755 infostream<<"ClientEnvironment::removeActiveObject(): "
2756 <<"id="<<id<<" not found"<<std::endl;
2759 obj->removeFromScene(true);
2761 m_active_objects.erase(id);
2764 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2766 ClientActiveObject *obj = getActiveObject(id);
2768 infostream << "ClientEnvironment::processActiveObjectMessage():"
2769 << " got message for id=" << id << ", which doesn't exist."
2775 obj->processMessage(data);
2776 } catch (SerializationError &e) {
2777 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2778 << " id=" << id << " type=" << obj->getType()
2779 << " SerializationError in processMessage(): " << e.what()
2785 Callbacks for activeobjects
2788 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2790 LocalPlayer *lplayer = getLocalPlayer();
2794 if (lplayer->hp > damage)
2795 lplayer->hp -= damage;
2800 ClientEnvEvent event;
2801 event.type = CEE_PLAYER_DAMAGE;
2802 event.player_damage.amount = damage;
2803 event.player_damage.send_to_server = handle_hp;
2804 m_client_event_queue.push(event);
2807 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2809 ClientEnvEvent event;
2810 event.type = CEE_PLAYER_BREATH;
2811 event.player_breath.amount = breath;
2812 m_client_event_queue.push(event);
2816 Client likes to call these
2819 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2820 std::vector<DistanceSortedActiveObject> &dest)
2822 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2823 i != m_active_objects.end(); ++i) {
2824 ClientActiveObject* obj = i->second;
2826 f32 d = (obj->getPosition() - origin).getLength();
2831 DistanceSortedActiveObject dso(obj, d);
2833 dest.push_back(dso);
2837 ClientEnvEvent ClientEnvironment::getClientEvent()
2839 ClientEnvEvent event;
2840 if(m_client_event_queue.empty())
2841 event.type = CEE_NONE;
2843 event = m_client_event_queue.front();
2844 m_client_event_queue.pop();
2849 #endif // #ifndef SERVER