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 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
612 if (player->checkModified()) {
613 player->save(players_path, m_gamedef);
618 void ServerEnvironment::savePlayer(RemotePlayer *player)
620 std::string players_path = m_path_world + DIR_DELIM "players";
621 fs::CreateDir(players_path);
623 player->save(players_path, m_gamedef);
626 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername)
628 bool newplayer = false;
630 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
631 std::string path = players_path + playername;
633 RemotePlayer *player = getPlayer(playername.c_str());
635 player = new RemotePlayer("", m_gamedef->idef());
639 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
640 //// Open file and deserialize
641 std::ifstream is(path.c_str(), std::ios_base::binary);
644 player->deSerialize(is, path);
647 if (player->getName() == playername) {
652 path = players_path + playername + itos(i);
656 infostream << "Player file for player " << playername
657 << " not found" << std::endl;
665 player->setModified(false);
669 void ServerEnvironment::saveMeta()
671 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
673 // Open file and serialize
674 std::ostringstream ss(std::ios_base::binary);
677 args.setU64("game_time", m_game_time);
678 args.setU64("time_of_day", getTimeOfDay());
679 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
680 args.setU64("lbm_introduction_times_version", 1);
681 args.set("lbm_introduction_times",
682 m_lbm_mgr.createIntroductionTimesString());
683 args.setU64("day_count", m_day_count);
687 if(!fs::safeWriteToFile(path, ss.str()))
689 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
691 throw SerializationError("Couldn't save env meta");
695 void ServerEnvironment::loadMeta()
697 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
699 // Open file and deserialize
700 std::ifstream is(path.c_str(), std::ios_base::binary);
702 infostream << "ServerEnvironment::loadMeta(): Failed to open "
703 << path << std::endl;
704 throw SerializationError("Couldn't load env meta");
709 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
710 throw SerializationError("ServerEnvironment::loadMeta(): "
711 "EnvArgsEnd not found!");
715 m_game_time = args.getU64("game_time");
716 } catch (SettingNotFoundException &e) {
717 // Getting this is crucial, otherwise timestamps are useless
718 throw SerializationError("Couldn't load env meta game_time");
721 setTimeOfDay(args.exists("time_of_day") ?
722 // set day to morning by default
723 args.getU64("time_of_day") : 9000);
725 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
726 // If missing, do as if clearObjects was never called
727 args.getU64("last_clear_objects_time") : 0;
729 std::string lbm_introduction_times = "";
731 u64 ver = args.getU64("lbm_introduction_times_version");
733 lbm_introduction_times = args.get("lbm_introduction_times");
735 infostream << "ServerEnvironment::loadMeta(): Non-supported"
736 << " introduction time version " << ver << std::endl;
738 } catch (SettingNotFoundException &e) {
739 // No problem, this is expected. Just continue with an empty string
741 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
743 m_day_count = args.exists("day_count") ?
744 args.getU64("day_count") : 0;
747 void ServerEnvironment::loadDefaultMeta()
749 m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
754 ActiveBlockModifier *abm;
756 std::set<content_t> required_neighbors;
762 ServerEnvironment *m_env;
763 std::map<content_t, std::vector<ActiveABM> > m_aabms;
765 ABMHandler(std::vector<ABMWithState> &abms,
766 float dtime_s, ServerEnvironment *env,
772 INodeDefManager *ndef = env->getGameDef()->ndef();
773 for(std::vector<ABMWithState>::iterator
774 i = abms.begin(); i != abms.end(); ++i) {
775 ActiveBlockModifier *abm = i->abm;
776 float trigger_interval = abm->getTriggerInterval();
777 if(trigger_interval < 0.001)
778 trigger_interval = 0.001;
779 float actual_interval = dtime_s;
782 if(i->timer < trigger_interval)
784 i->timer -= trigger_interval;
785 actual_interval = trigger_interval;
787 float chance = abm->getTriggerChance();
792 if(abm->getSimpleCatchUp()) {
793 float intervals = actual_interval / trigger_interval;
796 aabm.chance = chance / intervals;
800 aabm.chance = chance;
803 std::set<std::string> required_neighbors_s
804 = abm->getRequiredNeighbors();
805 for(std::set<std::string>::iterator
806 i = required_neighbors_s.begin();
807 i != required_neighbors_s.end(); ++i)
809 ndef->getIds(*i, aabm.required_neighbors);
812 std::set<std::string> contents_s = abm->getTriggerContents();
813 for(std::set<std::string>::iterator
814 i = contents_s.begin(); i != contents_s.end(); ++i)
816 std::set<content_t> ids;
817 ndef->getIds(*i, ids);
818 for(std::set<content_t>::const_iterator k = ids.begin();
822 std::map<content_t, std::vector<ActiveABM> >::iterator j;
824 if(j == m_aabms.end()){
825 std::vector<ActiveABM> aabmlist;
826 m_aabms[c] = aabmlist;
829 j->second.push_back(aabm);
834 // Find out how many objects the given block and its neighbours contain.
835 // Returns the number of objects in the block, and also in 'wider' the
836 // number of objects in the block and all its neighbours. The latter
837 // may an estimate if any neighbours are unloaded.
838 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
841 u32 wider_unknown_count = 0;
842 for(s16 x=-1; x<=1; x++)
843 for(s16 y=-1; y<=1; y++)
844 for(s16 z=-1; z<=1; z++)
846 MapBlock *block2 = map->getBlockNoCreateNoEx(
847 block->getPos() + v3s16(x,y,z));
849 wider_unknown_count++;
852 wider += block2->m_static_objects.m_active.size()
853 + block2->m_static_objects.m_stored.size();
856 u32 active_object_count = block->m_static_objects.m_active.size();
857 u32 wider_known_count = 3*3*3 - wider_unknown_count;
858 wider += wider_unknown_count * wider / wider_known_count;
859 return active_object_count;
862 void apply(MapBlock *block)
867 ServerMap *map = &m_env->getServerMap();
869 u32 active_object_count_wider;
870 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
871 m_env->m_added_objects = 0;
874 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
875 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
876 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
878 MapNode n = block->getNodeNoEx(p0);
879 content_t c = n.getContent();
880 v3s16 p = p0 + block->getPosRelative();
882 std::map<content_t, std::vector<ActiveABM> >::iterator j;
884 if(j == m_aabms.end())
887 for(std::vector<ActiveABM>::iterator
888 i = j->second.begin(); i != j->second.end(); ++i) {
889 if(myrand() % i->chance != 0)
893 if(!i->required_neighbors.empty())
896 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
897 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
898 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
902 MapNode n = map->getNodeNoEx(p1);
903 content_t c = n.getContent();
904 std::set<content_t>::const_iterator k;
905 k = i->required_neighbors.find(c);
906 if(k != i->required_neighbors.end()){
910 // No required neighbor found
915 // Call all the trigger variations
916 i->abm->trigger(m_env, p, n);
917 i->abm->trigger(m_env, p, n,
918 active_object_count, active_object_count_wider);
920 // Count surrounding objects again if the abms added any
921 if(m_env->m_added_objects > 0) {
922 active_object_count = countObjects(block, map, active_object_count_wider);
923 m_env->m_added_objects = 0;
930 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
932 // Reset usage timer immediately, otherwise a block that becomes active
933 // again at around the same time as it would normally be unloaded will
934 // get unloaded incorrectly. (I think this still leaves a small possibility
935 // of a race condition between this and server::AsyncRunStep, which only
936 // some kind of synchronisation will fix, but it at least reduces the window
937 // of opportunity for it to break from seconds to nanoseconds)
938 block->resetUsageTimer();
940 // Get time difference
942 u32 stamp = block->getTimestamp();
943 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
944 dtime_s = m_game_time - stamp;
945 dtime_s += additional_dtime;
947 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
948 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
950 // Remove stored static objects if clearObjects was called since block's timestamp
951 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
952 block->m_static_objects.m_stored.clear();
953 // do not set changed flag to avoid unnecessary mapblock writes
956 // Set current time as timestamp
957 block->setTimestampNoChangedFlag(m_game_time);
959 /*infostream<<"ServerEnvironment::activateBlock(): block is "
960 <<dtime_s<<" seconds old."<<std::endl;*/
962 // Activate stored objects
963 activateObjects(block, dtime_s);
965 /* Handle LoadingBlockModifiers */
966 m_lbm_mgr.applyLBMs(this, block, stamp);
969 std::vector<NodeTimer> elapsed_timers =
970 block->m_node_timers.step((float)dtime_s);
971 if (!elapsed_timers.empty()) {
973 for (std::vector<NodeTimer>::iterator
974 i = elapsed_timers.begin();
975 i != elapsed_timers.end(); ++i){
976 n = block->getNodeNoEx(i->position);
977 v3s16 p = i->position + block->getPosRelative();
978 if (m_script->node_on_timer(p, n, i->elapsed))
979 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
983 /* Handle ActiveBlockModifiers */
984 ABMHandler abmhandler(m_abms, dtime_s, this, false);
985 abmhandler.apply(block);
988 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
990 m_abms.push_back(ABMWithState(abm));
993 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
995 m_lbm_mgr.addLBMDef(lbm);
998 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1000 INodeDefManager *ndef = m_gamedef->ndef();
1001 MapNode n_old = m_map->getNodeNoEx(p);
1004 if (ndef->get(n_old).has_on_destruct)
1005 m_script->node_on_destruct(p, n_old);
1008 if (!m_map->addNodeWithEvent(p, n))
1011 // Update active VoxelManipulator if a mapgen thread
1012 m_map->updateVManip(p);
1014 // Call post-destructor
1015 if (ndef->get(n_old).has_after_destruct)
1016 m_script->node_after_destruct(p, n_old);
1019 if (ndef->get(n).has_on_construct)
1020 m_script->node_on_construct(p, n);
1025 bool ServerEnvironment::removeNode(v3s16 p)
1027 INodeDefManager *ndef = m_gamedef->ndef();
1028 MapNode n_old = m_map->getNodeNoEx(p);
1031 if (ndef->get(n_old).has_on_destruct)
1032 m_script->node_on_destruct(p, n_old);
1035 // This is slightly optimized compared to addNodeWithEvent(air)
1036 if (!m_map->removeNodeWithEvent(p))
1039 // Update active VoxelManipulator if a mapgen thread
1040 m_map->updateVManip(p);
1042 // Call post-destructor
1043 if (ndef->get(n_old).has_after_destruct)
1044 m_script->node_after_destruct(p, n_old);
1046 // Air doesn't require constructor
1050 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1052 if (!m_map->addNodeWithEvent(p, n, false))
1055 // Update active VoxelManipulator if a mapgen thread
1056 m_map->updateVManip(p);
1061 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1063 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1064 i != m_active_objects.end(); ++i) {
1065 ServerActiveObject* obj = i->second;
1067 v3f objectpos = obj->getBasePosition();
1068 if (objectpos.getDistanceFrom(pos) > radius)
1070 objects.push_back(id);
1074 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1076 infostream << "ServerEnvironment::clearObjects(): "
1077 << "Removing all active objects" << std::endl;
1078 std::vector<u16> objects_to_remove;
1079 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1080 i != m_active_objects.end(); ++i) {
1081 ServerActiveObject* obj = i->second;
1082 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1085 // Delete static object if block is loaded
1086 if (obj->m_static_exists) {
1087 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1089 block->m_static_objects.remove(id);
1090 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1091 MOD_REASON_CLEAR_ALL_OBJECTS);
1092 obj->m_static_exists = false;
1095 // If known by some client, don't delete immediately
1096 if (obj->m_known_by_count > 0) {
1097 obj->m_pending_deactivation = true;
1098 obj->m_removed = true;
1102 // Tell the object about removal
1103 obj->removingFromEnvironment();
1104 // Deregister in scripting api
1105 m_script->removeObjectReference(obj);
1107 // Delete active object
1108 if (obj->environmentDeletes())
1110 // Id to be removed from m_active_objects
1111 objects_to_remove.push_back(id);
1114 // Remove references from m_active_objects
1115 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1116 i != objects_to_remove.end(); ++i) {
1117 m_active_objects.erase(*i);
1120 // Get list of loaded blocks
1121 std::vector<v3s16> loaded_blocks;
1122 infostream << "ServerEnvironment::clearObjects(): "
1123 << "Listing all loaded blocks" << std::endl;
1124 m_map->listAllLoadedBlocks(loaded_blocks);
1125 infostream << "ServerEnvironment::clearObjects(): "
1126 << "Done listing all loaded blocks: "
1127 << loaded_blocks.size()<<std::endl;
1129 // Get list of loadable blocks
1130 std::vector<v3s16> loadable_blocks;
1131 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1132 infostream << "ServerEnvironment::clearObjects(): "
1133 << "Listing all loadable blocks" << std::endl;
1134 m_map->listAllLoadableBlocks(loadable_blocks);
1135 infostream << "ServerEnvironment::clearObjects(): "
1136 << "Done listing all loadable blocks: "
1137 << loadable_blocks.size() << std::endl;
1139 loadable_blocks = loaded_blocks;
1142 infostream << "ServerEnvironment::clearObjects(): "
1143 << "Now clearing objects in " << loadable_blocks.size()
1144 << " blocks" << std::endl;
1146 // Grab a reference on each loaded block to avoid unloading it
1147 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1148 i != loaded_blocks.end(); ++i) {
1150 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1151 assert(block != NULL);
1155 // Remove objects in all loadable blocks
1156 u32 unload_interval = U32_MAX;
1157 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1158 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1159 unload_interval = MYMAX(unload_interval, 1);
1161 u32 report_interval = loadable_blocks.size() / 10;
1162 u32 num_blocks_checked = 0;
1163 u32 num_blocks_cleared = 0;
1164 u32 num_objs_cleared = 0;
1165 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1166 i != loadable_blocks.end(); ++i) {
1168 MapBlock *block = m_map->emergeBlock(p, false);
1170 errorstream << "ServerEnvironment::clearObjects(): "
1171 << "Failed to emerge block " << PP(p) << std::endl;
1174 u32 num_stored = block->m_static_objects.m_stored.size();
1175 u32 num_active = block->m_static_objects.m_active.size();
1176 if (num_stored != 0 || num_active != 0) {
1177 block->m_static_objects.m_stored.clear();
1178 block->m_static_objects.m_active.clear();
1179 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1180 MOD_REASON_CLEAR_ALL_OBJECTS);
1181 num_objs_cleared += num_stored + num_active;
1182 num_blocks_cleared++;
1184 num_blocks_checked++;
1186 if (report_interval != 0 &&
1187 num_blocks_checked % report_interval == 0) {
1188 float percent = 100.0 * (float)num_blocks_checked /
1189 loadable_blocks.size();
1190 infostream << "ServerEnvironment::clearObjects(): "
1191 << "Cleared " << num_objs_cleared << " objects"
1192 << " in " << num_blocks_cleared << " blocks ("
1193 << percent << "%)" << std::endl;
1195 if (num_blocks_checked % unload_interval == 0) {
1196 m_map->unloadUnreferencedBlocks();
1199 m_map->unloadUnreferencedBlocks();
1201 // Drop references that were added above
1202 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1203 i != loaded_blocks.end(); ++i) {
1205 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1210 m_last_clear_objects_time = m_game_time;
1212 infostream << "ServerEnvironment::clearObjects(): "
1213 << "Finished: Cleared " << num_objs_cleared << " objects"
1214 << " in " << num_blocks_cleared << " blocks" << std::endl;
1217 void ServerEnvironment::step(float dtime)
1219 DSTACK(FUNCTION_NAME);
1221 //TimeTaker timer("ServerEnv step");
1223 /* Step time of day */
1224 stepTimeOfDay(dtime);
1227 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1228 // really matter that much.
1229 static const float server_step = g_settings->getFloat("dedicated_server_step");
1230 m_recommended_send_interval = server_step;
1236 m_game_time_fraction_counter += dtime;
1237 u32 inc_i = (u32)m_game_time_fraction_counter;
1238 m_game_time += inc_i;
1239 m_game_time_fraction_counter -= (float)inc_i;
1246 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1247 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1248 i != m_players.end(); ++i) {
1249 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1252 // Ignore disconnected players
1253 if(player->peer_id == 0)
1257 player->move(dtime, this, 100*BS);
1262 Manage active block list
1264 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1265 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1267 Get player block positions
1269 std::vector<v3s16> players_blockpos;
1270 for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
1271 i != m_players.end(); ++i) {
1272 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1274 // Ignore disconnected players
1275 if (player->peer_id == 0)
1278 v3s16 blockpos = getNodeBlockPos(
1279 floatToInt(player->getPosition(), BS));
1280 players_blockpos.push_back(blockpos);
1284 Update list of active blocks, collecting changes
1286 static const s16 active_block_range = g_settings->getS16("active_block_range");
1287 std::set<v3s16> blocks_removed;
1288 std::set<v3s16> blocks_added;
1289 m_active_blocks.update(players_blockpos, active_block_range,
1290 blocks_removed, blocks_added);
1293 Handle removed blocks
1296 // Convert active objects that are no more in active blocks to static
1297 deactivateFarObjects(false);
1299 for(std::set<v3s16>::iterator
1300 i = blocks_removed.begin();
1301 i != blocks_removed.end(); ++i) {
1304 /* infostream<<"Server: Block " << PP(p)
1305 << " became inactive"<<std::endl; */
1307 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1311 // Set current time as timestamp (and let it set ChangedFlag)
1312 block->setTimestamp(m_game_time);
1319 for(std::set<v3s16>::iterator
1320 i = blocks_added.begin();
1321 i != blocks_added.end(); ++i)
1325 MapBlock *block = m_map->getBlockOrEmerge(p);
1327 m_active_blocks.m_list.erase(p);
1331 activateBlock(block);
1332 /* infostream<<"Server: Block " << PP(p)
1333 << " became active"<<std::endl; */
1338 Mess around in active blocks
1340 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1341 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1343 float dtime = m_cache_nodetimer_interval;
1345 for(std::set<v3s16>::iterator
1346 i = m_active_blocks.m_list.begin();
1347 i != m_active_blocks.m_list.end(); ++i)
1351 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1352 <<") being handled"<<std::endl;*/
1354 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1358 // Reset block usage timer
1359 block->resetUsageTimer();
1361 // Set current time as timestamp
1362 block->setTimestampNoChangedFlag(m_game_time);
1363 // If time has changed much from the one on disk,
1364 // set block to be saved when it is unloaded
1365 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1366 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1367 MOD_REASON_BLOCK_EXPIRED);
1370 std::vector<NodeTimer> elapsed_timers =
1371 block->m_node_timers.step((float)dtime);
1372 if (!elapsed_timers.empty()) {
1374 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1375 i != elapsed_timers.end(); ++i) {
1376 n = block->getNodeNoEx(i->position);
1377 p = i->position + block->getPosRelative();
1378 if (m_script->node_on_timer(p, n, i->elapsed)) {
1379 block->setNodeTimer(NodeTimer(
1380 i->timeout, 0, i->position));
1387 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1389 if(m_active_block_interval_overload_skip > 0){
1390 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1391 m_active_block_interval_overload_skip--;
1394 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1395 TimeTaker timer("modify in active blocks per interval");
1397 // Initialize handling of ActiveBlockModifiers
1398 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1400 for(std::set<v3s16>::iterator
1401 i = m_active_blocks.m_list.begin();
1402 i != m_active_blocks.m_list.end(); ++i)
1406 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1407 <<") being handled"<<std::endl;*/
1409 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1413 // Set current time as timestamp
1414 block->setTimestampNoChangedFlag(m_game_time);
1416 /* Handle ActiveBlockModifiers */
1417 abmhandler.apply(block);
1420 u32 time_ms = timer.stop(true);
1421 u32 max_time_ms = 200;
1422 if(time_ms > max_time_ms){
1423 warningstream<<"active block modifiers took "
1424 <<time_ms<<"ms (longer than "
1425 <<max_time_ms<<"ms)"<<std::endl;
1426 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1431 Step script environment (run global on_step())
1433 m_script->environment_Step(dtime);
1439 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1440 //TimeTaker timer("Step active objects");
1442 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1444 // This helps the objects to send data at the same time
1445 bool send_recommended = false;
1446 m_send_recommended_timer += dtime;
1447 if(m_send_recommended_timer > getSendRecommendedInterval())
1449 m_send_recommended_timer -= getSendRecommendedInterval();
1450 send_recommended = true;
1453 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1454 i != m_active_objects.end(); ++i) {
1455 ServerActiveObject* obj = i->second;
1456 // Don't step if is to be removed or stored statically
1457 if(obj->m_removed || obj->m_pending_deactivation)
1460 obj->step(dtime, send_recommended);
1461 // Read messages from object
1462 while(!obj->m_messages_out.empty())
1464 m_active_object_messages.push(
1465 obj->m_messages_out.front());
1466 obj->m_messages_out.pop();
1472 Manage active objects
1474 if(m_object_management_interval.step(dtime, 0.5))
1476 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1478 Remove objects that satisfy (m_removed && m_known_by_count==0)
1480 removeRemovedObjects();
1484 Manage particle spawner expiration
1486 if (m_particle_management_interval.step(dtime, 1.0)) {
1487 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1488 i != m_particle_spawners.end(); ) {
1489 //non expiring spawners
1490 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1496 if (i->second <= 0.f)
1497 m_particle_spawners.erase(i++);
1504 u32 ServerEnvironment::addParticleSpawner(float exptime)
1506 // Timers with lifetime 0 do not expire
1507 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1510 for (;;) { // look for unused particlespawner id
1512 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1513 if (f == m_particle_spawners.end()) {
1514 m_particle_spawners[id] = time;
1521 u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
1523 u32 id = addParticleSpawner(exptime);
1524 m_particle_spawner_attachments[id] = attached_id;
1525 if (ServerActiveObject *obj = getActiveObject(attached_id)) {
1526 obj->attachParticleSpawner(id);
1531 void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
1533 m_particle_spawners.erase(id);
1534 UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
1535 if (it != m_particle_spawner_attachments.end()) {
1536 u16 obj_id = (*it).second;
1537 ServerActiveObject *sao = getActiveObject(obj_id);
1538 if (sao != NULL && remove_from_object) {
1539 sao->detachParticleSpawner(id);
1541 m_particle_spawner_attachments.erase(id);
1545 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1547 ActiveObjectMap::iterator n = m_active_objects.find(id);
1548 return (n != m_active_objects.end() ? n->second : NULL);
1551 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1556 return objects.find(id) == objects.end();
1559 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1561 //try to reuse id's as late as possible
1562 static u16 last_used_id = 0;
1563 u16 startid = last_used_id;
1567 if(isFreeServerActiveObjectId(last_used_id, objects))
1568 return last_used_id;
1570 if(last_used_id == startid)
1575 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1577 assert(object); // Pre-condition
1579 u16 id = addActiveObjectRaw(object, true, 0);
1584 Finds out what new objects have been added to
1585 inside a radius around a position
1587 void ServerEnvironment::getAddedActiveObjects(RemotePlayer *player, s16 radius,
1589 std::set<u16> ¤t_objects,
1590 std::queue<u16> &added_objects)
1592 f32 radius_f = radius * BS;
1593 f32 player_radius_f = player_radius * BS;
1595 if (player_radius_f < 0)
1596 player_radius_f = 0;
1599 Go through the object list,
1600 - discard m_removed objects,
1601 - discard objects that are too far away,
1602 - discard objects that are found in current_objects.
1603 - add remaining objects to added_objects
1605 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1606 i != m_active_objects.end(); ++i) {
1610 ServerActiveObject *object = i->second;
1614 // Discard if removed or deactivating
1615 if(object->m_removed || object->m_pending_deactivation)
1618 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1619 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1620 // Discard if too far
1621 if (distance_f > player_radius_f && player_radius_f != 0)
1623 } else if (distance_f > radius_f)
1626 // Discard if already on current_objects
1627 std::set<u16>::iterator n;
1628 n = current_objects.find(id);
1629 if(n != current_objects.end())
1631 // Add to added_objects
1632 added_objects.push(id);
1637 Finds out what objects have been removed from
1638 inside a radius around a position
1640 void ServerEnvironment::getRemovedActiveObjects(RemotePlayer *player, s16 radius,
1642 std::set<u16> ¤t_objects,
1643 std::queue<u16> &removed_objects)
1645 f32 radius_f = radius * BS;
1646 f32 player_radius_f = player_radius * BS;
1648 if (player_radius_f < 0)
1649 player_radius_f = 0;
1652 Go through current_objects; object is removed if:
1653 - object is not found in m_active_objects (this is actually an
1654 error condition; objects should be set m_removed=true and removed
1655 only after all clients have been informed about removal), or
1656 - object has m_removed=true, or
1657 - object is too far away
1659 for(std::set<u16>::iterator
1660 i = current_objects.begin();
1661 i != current_objects.end(); ++i)
1664 ServerActiveObject *object = getActiveObject(id);
1666 if (object == NULL) {
1667 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1668 << " object in current_objects is NULL" << std::endl;
1669 removed_objects.push(id);
1673 if (object->m_removed || object->m_pending_deactivation) {
1674 removed_objects.push(id);
1678 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1679 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1680 if (distance_f <= player_radius_f || player_radius_f == 0)
1682 } else if (distance_f <= radius_f)
1685 // Object is no longer visible
1686 removed_objects.push(id);
1690 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1691 v3s16 blockpos, bool static_exists, v3s16 static_block)
1693 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1697 for (std::map<u16, StaticObject>::iterator
1698 so_it = block->m_static_objects.m_active.begin();
1699 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1700 // Get the ServerActiveObject counterpart to this StaticObject
1701 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1702 if (ao_it == m_active_objects.end()) {
1703 // If this ever happens, there must be some kind of nasty bug.
1704 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1705 "Object from MapBlock::m_static_objects::m_active not found "
1706 "in m_active_objects";
1710 ServerActiveObject *sao = ao_it->second;
1711 sao->m_static_exists = static_exists;
1712 sao->m_static_block = static_block;
1716 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1718 if(m_active_object_messages.empty())
1719 return ActiveObjectMessage(0);
1721 ActiveObjectMessage message = m_active_object_messages.front();
1722 m_active_object_messages.pop();
1727 ************ Private methods *************
1730 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1731 bool set_changed, u32 dtime_s)
1733 assert(object); // Pre-condition
1734 if(object->getId() == 0){
1735 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1738 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1739 <<"no free ids available"<<std::endl;
1740 if(object->environmentDeletes())
1744 object->setId(new_id);
1747 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1748 <<"supplied with id "<<object->getId()<<std::endl;
1751 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1752 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1753 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1754 if(object->environmentDeletes())
1759 if (objectpos_over_limit(object->getBasePosition())) {
1760 v3f p = object->getBasePosition();
1761 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1762 << "object position (" << p.X << "," << p.Y << "," << p.Z
1763 << ") outside maximum range" << std::endl;
1764 if (object->environmentDeletes())
1769 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1770 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1772 m_active_objects[object->getId()] = object;
1774 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1775 <<"Added id="<<object->getId()<<"; there are now "
1776 <<m_active_objects.size()<<" active objects."
1779 // Register reference in scripting api (must be done before post-init)
1780 m_script->addObjectReference(object);
1781 // Post-initialize object
1782 object->addedToEnvironment(dtime_s);
1784 // Add static data to block
1785 if(object->isStaticAllowed())
1787 // Add static object to active static list of the block
1788 v3f objectpos = object->getBasePosition();
1789 std::string staticdata = object->getStaticData();
1790 StaticObject s_obj(object->getType(), objectpos, staticdata);
1791 // Add to the block where the object is located in
1792 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1793 MapBlock *block = m_map->emergeBlock(blockpos);
1795 block->m_static_objects.m_active[object->getId()] = s_obj;
1796 object->m_static_exists = true;
1797 object->m_static_block = blockpos;
1800 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1801 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1803 v3s16 p = floatToInt(objectpos, BS);
1804 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1805 <<"could not emerge block for storing id="<<object->getId()
1806 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1810 return object->getId();
1814 Remove objects that satisfy (m_removed && m_known_by_count==0)
1816 void ServerEnvironment::removeRemovedObjects()
1818 std::vector<u16> objects_to_remove;
1819 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1820 i != m_active_objects.end(); ++i) {
1822 ServerActiveObject* obj = i->second;
1823 // This shouldn't happen but check it
1826 infostream<<"NULL object found in ServerEnvironment"
1827 <<" while finding removed objects. id="<<id<<std::endl;
1828 // Id to be removed from m_active_objects
1829 objects_to_remove.push_back(id);
1834 We will delete objects that are marked as removed or thatare
1835 waiting for deletion after deactivation
1837 if (!obj->m_removed && !obj->m_pending_deactivation)
1841 Delete static data from block if is marked as removed
1843 if(obj->m_static_exists && obj->m_removed)
1845 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1847 block->m_static_objects.remove(id);
1848 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1849 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1850 obj->m_static_exists = false;
1852 infostream<<"Failed to emerge block from which an object to "
1853 <<"be removed was loaded from. id="<<id<<std::endl;
1857 // If m_known_by_count > 0, don't actually remove. On some future
1858 // invocation this will be 0, which is when removal will continue.
1859 if(obj->m_known_by_count > 0)
1863 Move static data from active to stored if not marked as removed
1865 if(obj->m_static_exists && !obj->m_removed){
1866 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1868 std::map<u16, StaticObject>::iterator i =
1869 block->m_static_objects.m_active.find(id);
1870 if(i != block->m_static_objects.m_active.end()){
1871 block->m_static_objects.m_stored.push_back(i->second);
1872 block->m_static_objects.m_active.erase(id);
1873 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1874 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1877 infostream<<"Failed to emerge block from which an object to "
1878 <<"be deactivated was loaded from. id="<<id<<std::endl;
1882 // Tell the object about removal
1883 obj->removingFromEnvironment();
1884 // Deregister in scripting api
1885 m_script->removeObjectReference(obj);
1888 if(obj->environmentDeletes())
1891 // Id to be removed from m_active_objects
1892 objects_to_remove.push_back(id);
1894 // Remove references from m_active_objects
1895 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1896 i != objects_to_remove.end(); ++i) {
1897 m_active_objects.erase(*i);
1901 static void print_hexdump(std::ostream &o, const std::string &data)
1903 const int linelength = 16;
1904 for(int l=0; ; l++){
1905 int i0 = linelength * l;
1906 bool at_end = false;
1907 int thislinelength = linelength;
1908 if(i0 + thislinelength > (int)data.size()){
1909 thislinelength = data.size() - i0;
1912 for(int di=0; di<linelength; di++){
1915 if(di<thislinelength)
1916 snprintf(buf, 4, "%.2x ", data[i]);
1918 snprintf(buf, 4, " ");
1922 for(int di=0; di<thislinelength; di++){
1936 Convert stored objects from blocks near the players to active.
1938 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1943 // Ignore if no stored objects (to not set changed flag)
1944 if(block->m_static_objects.m_stored.empty())
1947 verbosestream<<"ServerEnvironment::activateObjects(): "
1948 <<"activating objects of block "<<PP(block->getPos())
1949 <<" ("<<block->m_static_objects.m_stored.size()
1950 <<" objects)"<<std::endl;
1951 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1953 errorstream<<"suspiciously large amount of objects detected: "
1954 <<block->m_static_objects.m_stored.size()<<" in "
1955 <<PP(block->getPos())
1956 <<"; removing all of them."<<std::endl;
1957 // Clear stored list
1958 block->m_static_objects.m_stored.clear();
1959 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1960 MOD_REASON_TOO_MANY_OBJECTS);
1964 // Activate stored objects
1965 std::vector<StaticObject> new_stored;
1966 for (std::vector<StaticObject>::iterator
1967 i = block->m_static_objects.m_stored.begin();
1968 i != block->m_static_objects.m_stored.end(); ++i) {
1969 StaticObject &s_obj = *i;
1971 // Create an active object from the data
1972 ServerActiveObject *obj = ServerActiveObject::create
1973 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1974 // If couldn't create object, store static data back.
1976 errorstream<<"ServerEnvironment::activateObjects(): "
1977 <<"failed to create active object from static object "
1978 <<"in block "<<PP(s_obj.pos/BS)
1979 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1980 print_hexdump(verbosestream, s_obj.data);
1982 new_stored.push_back(s_obj);
1985 verbosestream<<"ServerEnvironment::activateObjects(): "
1986 <<"activated static object pos="<<PP(s_obj.pos/BS)
1987 <<" type="<<(int)s_obj.type<<std::endl;
1988 // This will also add the object to the active static list
1989 addActiveObjectRaw(obj, false, dtime_s);
1991 // Clear stored list
1992 block->m_static_objects.m_stored.clear();
1993 // Add leftover failed stuff to stored list
1994 for(std::vector<StaticObject>::iterator
1995 i = new_stored.begin();
1996 i != new_stored.end(); ++i) {
1997 StaticObject &s_obj = *i;
1998 block->m_static_objects.m_stored.push_back(s_obj);
2001 // Turn the active counterparts of activated objects not pending for
2003 for(std::map<u16, StaticObject>::iterator
2004 i = block->m_static_objects.m_active.begin();
2005 i != block->m_static_objects.m_active.end(); ++i)
2008 ServerActiveObject *object = getActiveObject(id);
2010 object->m_pending_deactivation = false;
2014 Note: Block hasn't really been modified here.
2015 The objects have just been activated and moved from the stored
2016 static list to the active static list.
2017 As such, the block is essentially the same.
2018 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2019 Otherwise there would be a huge amount of unnecessary I/O.
2024 Convert objects that are not standing inside active blocks to static.
2026 If m_known_by_count != 0, active object is not deleted, but static
2027 data is still updated.
2029 If force_delete is set, active object is deleted nevertheless. It
2030 shall only be set so in the destructor of the environment.
2032 If block wasn't generated (not in memory or on disk),
2034 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2036 std::vector<u16> objects_to_remove;
2037 for(ActiveObjectMap::iterator i = m_active_objects.begin();
2038 i != m_active_objects.end(); ++i) {
2039 ServerActiveObject* obj = i->second;
2042 // Do not deactivate if static data creation not allowed
2043 if(!force_delete && !obj->isStaticAllowed())
2046 // If pending deactivation, let removeRemovedObjects() do it
2047 if(!force_delete && obj->m_pending_deactivation)
2051 v3f objectpos = obj->getBasePosition();
2053 // The block in which the object resides in
2054 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2056 // If object's static data is stored in a deactivated block and object
2057 // is actually located in an active block, re-save to the block in
2058 // which the object is actually located in.
2060 obj->m_static_exists &&
2061 !m_active_blocks.contains(obj->m_static_block) &&
2062 m_active_blocks.contains(blockpos_o))
2064 v3s16 old_static_block = obj->m_static_block;
2066 // Save to block where object is located
2067 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2069 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2070 <<"Could not save object id="<<id
2071 <<" to it's current block "<<PP(blockpos_o)
2075 std::string staticdata_new = obj->getStaticData();
2076 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2077 block->m_static_objects.insert(id, s_obj);
2078 obj->m_static_block = blockpos_o;
2079 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2080 MOD_REASON_STATIC_DATA_ADDED);
2082 // Delete from block where object was located
2083 block = m_map->emergeBlock(old_static_block, false);
2085 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2086 <<"Could not delete object id="<<id
2087 <<" from it's previous block "<<PP(old_static_block)
2091 block->m_static_objects.remove(id);
2092 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2093 MOD_REASON_STATIC_DATA_REMOVED);
2097 // If block is active, don't remove
2098 if(!force_delete && m_active_blocks.contains(blockpos_o))
2101 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2102 <<"deactivating object id="<<id<<" on inactive block "
2103 <<PP(blockpos_o)<<std::endl;
2105 // If known by some client, don't immediately delete.
2106 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2109 Update the static data
2112 if(obj->isStaticAllowed())
2114 // Create new static object
2115 std::string staticdata_new = obj->getStaticData();
2116 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2118 bool stays_in_same_block = false;
2119 bool data_changed = true;
2121 if (obj->m_static_exists) {
2122 if (obj->m_static_block == blockpos_o)
2123 stays_in_same_block = true;
2125 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2128 std::map<u16, StaticObject>::iterator n =
2129 block->m_static_objects.m_active.find(id);
2130 if (n != block->m_static_objects.m_active.end()) {
2131 StaticObject static_old = n->second;
2133 float save_movem = obj->getMinimumSavedMovement();
2135 if (static_old.data == staticdata_new &&
2136 (static_old.pos - objectpos).getLength() < save_movem)
2137 data_changed = false;
2139 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2140 <<"id="<<id<<" m_static_exists=true but "
2141 <<"static data doesn't actually exist in "
2142 <<PP(obj->m_static_block)<<std::endl;
2147 bool shall_be_written = (!stays_in_same_block || data_changed);
2149 // Delete old static object
2150 if(obj->m_static_exists)
2152 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2155 block->m_static_objects.remove(id);
2156 obj->m_static_exists = false;
2157 // Only mark block as modified if data changed considerably
2158 if(shall_be_written)
2159 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2160 MOD_REASON_STATIC_DATA_CHANGED);
2164 // Add to the block where the object is located in
2165 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2166 // Get or generate the block
2167 MapBlock *block = NULL;
2169 block = m_map->emergeBlock(blockpos);
2170 } catch(InvalidPositionException &e){
2171 // Handled via NULL pointer
2172 // NOTE: emergeBlock's failure is usually determined by it
2173 // actually returning NULL
2178 if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) {
2179 warningstream << "ServerEnv: Trying to store id = " << obj->getId()
2180 << " statically but block " << PP(blockpos)
2181 << " already contains "
2182 << block->m_static_objects.m_stored.size()
2184 << " Forcing delete." << std::endl;
2185 force_delete = true;
2187 // If static counterpart already exists in target block,
2189 // This shouldn't happen because the object is removed from
2190 // the previous block before this according to
2191 // obj->m_static_block, but happens rarely for some unknown
2192 // reason. Unsuccessful attempts have been made to find
2194 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2195 warningstream<<"ServerEnv: Performing hack #83274"
2197 block->m_static_objects.remove(id);
2199 // Store static data
2200 u16 store_id = pending_delete ? id : 0;
2201 block->m_static_objects.insert(store_id, s_obj);
2203 // Only mark block as modified if data changed considerably
2204 if(shall_be_written)
2205 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2206 MOD_REASON_STATIC_DATA_CHANGED);
2208 obj->m_static_exists = true;
2209 obj->m_static_block = block->getPos();
2214 v3s16 p = floatToInt(objectpos, BS);
2215 errorstream<<"ServerEnv: Could not find or generate "
2216 <<"a block for storing id="<<obj->getId()
2217 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2224 If known by some client, set pending deactivation.
2225 Otherwise delete it immediately.
2228 if(pending_delete && !force_delete)
2230 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2231 <<"object id="<<id<<" is known by clients"
2232 <<"; not deleting yet"<<std::endl;
2234 obj->m_pending_deactivation = true;
2238 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2239 <<"object id="<<id<<" is not known by clients"
2240 <<"; deleting"<<std::endl;
2242 // Tell the object about removal
2243 obj->removingFromEnvironment();
2244 // Deregister in scripting api
2245 m_script->removeObjectReference(obj);
2247 // Delete active object
2248 if(obj->environmentDeletes())
2250 // Id to be removed from m_active_objects
2251 objects_to_remove.push_back(id);
2254 // Remove references from m_active_objects
2255 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2256 i != objects_to_remove.end(); ++i) {
2257 m_active_objects.erase(*i);
2263 #include "clientsimpleobject.h"
2269 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2270 ITextureSource *texturesource, IGameDef *gamedef,
2271 IrrlichtDevice *irr):
2273 m_local_player(NULL),
2275 m_texturesource(texturesource),
2280 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2283 ClientEnvironment::~ClientEnvironment()
2285 // delete active objects
2286 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2287 i != m_active_objects.end(); ++i) {
2291 for(std::vector<ClientSimpleObject*>::iterator
2292 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2300 Map & ClientEnvironment::getMap()
2305 ClientMap & ClientEnvironment::getClientMap()
2310 void ClientEnvironment::setLocalPlayer(LocalPlayer *player)
2312 DSTACK(FUNCTION_NAME);
2314 It is a failure if already is a local player
2316 FATAL_ERROR_IF(m_local_player != NULL,
2317 "Local player already allocated");
2319 m_local_player = player;
2322 void ClientEnvironment::step(float dtime)
2324 DSTACK(FUNCTION_NAME);
2326 /* Step time of day */
2327 stepTimeOfDay(dtime);
2329 // Get some settings
2330 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2331 bool free_move = fly_allowed && g_settings->getBool("free_move");
2334 LocalPlayer *lplayer = getLocalPlayer();
2336 // collision info queue
2337 std::vector<CollisionInfo> player_collisions;
2340 Get the speed the player is going
2342 bool is_climbing = lplayer->is_climbing;
2344 f32 player_speed = lplayer->getSpeed().getLength();
2347 Maximum position increment
2349 //f32 position_max_increment = 0.05*BS;
2350 f32 position_max_increment = 0.1*BS;
2352 // Maximum time increment (for collision detection etc)
2353 // time = distance / speed
2354 f32 dtime_max_increment = 1;
2355 if(player_speed > 0.001)
2356 dtime_max_increment = position_max_increment / player_speed;
2358 // Maximum time increment is 10ms or lower
2359 if(dtime_max_increment > 0.01)
2360 dtime_max_increment = 0.01;
2362 // Don't allow overly huge dtime
2366 f32 dtime_downcount = dtime;
2369 Stuff that has a maximum time increment
2378 if(dtime_downcount > dtime_max_increment)
2380 dtime_part = dtime_max_increment;
2381 dtime_downcount -= dtime_part;
2385 dtime_part = dtime_downcount;
2387 Setting this to 0 (no -=dtime_part) disables an infinite loop
2388 when dtime_part is so small that dtime_downcount -= dtime_part
2391 dtime_downcount = 0;
2400 if(!free_move && !is_climbing)
2403 v3f speed = lplayer->getSpeed();
2404 if(!lplayer->in_liquid)
2405 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2407 // Liquid floating / sinking
2408 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2409 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2411 // Liquid resistance
2412 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2414 // How much the node's viscosity blocks movement, ranges between 0 and 1
2415 // Should match the scale at which viscosity increase affects other liquid attributes
2416 const f32 viscosity_factor = 0.3;
2418 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2419 f32 dl = d_wanted.getLength();
2420 if(dl > lplayer->movement_liquid_fluidity_smooth)
2421 dl = lplayer->movement_liquid_fluidity_smooth;
2422 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2424 v3f d = d_wanted.normalize() * dl;
2428 lplayer->setSpeed(speed);
2433 This also does collision detection.
2435 lplayer->move(dtime_part, this, position_max_increment,
2436 &player_collisions);
2439 while(dtime_downcount > 0.001);
2441 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2443 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2444 i != player_collisions.end(); ++i) {
2445 CollisionInfo &info = *i;
2446 v3f speed_diff = info.new_speed - info.old_speed;;
2447 // Handle only fall damage
2448 // (because otherwise walking against something in fast_move kills you)
2449 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2451 // Get rid of other components
2454 f32 pre_factor = 1; // 1 hp per node/s
2455 f32 tolerance = BS*14; // 5 without damage
2456 f32 post_factor = 1; // 1 hp per node/s
2457 if(info.type == COLLISION_NODE)
2459 const ContentFeatures &f = m_gamedef->ndef()->
2460 get(m_map->getNodeNoEx(info.node_p));
2461 // Determine fall damage multiplier
2462 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2463 pre_factor = 1.0 + (float)addp/100.0;
2465 float speed = pre_factor * speed_diff.getLength();
2466 if(speed > tolerance)
2468 f32 damage_f = (speed - tolerance)/BS * post_factor;
2469 u16 damage = (u16)(damage_f+0.5);
2471 damageLocalPlayer(damage, true);
2472 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2473 m_gamedef->event()->put(e);
2479 A quick draft of lava damage
2481 if(m_lava_hurt_interval.step(dtime, 1.0))
2483 v3f pf = lplayer->getPosition();
2485 // Feet, middle and head
2486 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2487 MapNode n1 = m_map->getNodeNoEx(p1);
2488 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2489 MapNode n2 = m_map->getNodeNoEx(p2);
2490 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2491 MapNode n3 = m_map->getNodeNoEx(p3);
2493 u32 damage_per_second = 0;
2494 damage_per_second = MYMAX(damage_per_second,
2495 m_gamedef->ndef()->get(n1).damage_per_second);
2496 damage_per_second = MYMAX(damage_per_second,
2497 m_gamedef->ndef()->get(n2).damage_per_second);
2498 damage_per_second = MYMAX(damage_per_second,
2499 m_gamedef->ndef()->get(n3).damage_per_second);
2501 if(damage_per_second != 0)
2503 damageLocalPlayer(damage_per_second, true);
2510 if(m_drowning_interval.step(dtime, 2.0))
2512 v3f pf = lplayer->getPosition();
2515 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2516 MapNode n = m_map->getNodeNoEx(p);
2517 ContentFeatures c = m_gamedef->ndef()->get(n);
2518 u8 drowning_damage = c.drowning;
2519 if(drowning_damage > 0 && lplayer->hp > 0){
2520 u16 breath = lplayer->getBreath();
2527 lplayer->setBreath(breath);
2528 updateLocalPlayerBreath(breath);
2531 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2532 damageLocalPlayer(drowning_damage, true);
2535 if(m_breathing_interval.step(dtime, 0.5))
2537 v3f pf = lplayer->getPosition();
2540 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2541 MapNode n = m_map->getNodeNoEx(p);
2542 ContentFeatures c = m_gamedef->ndef()->get(n);
2544 lplayer->setBreath(11);
2546 else if(c.drowning == 0){
2547 u16 breath = lplayer->getBreath();
2550 lplayer->setBreath(breath);
2551 updateLocalPlayerBreath(breath);
2556 // Update lighting on local player (used for wield item)
2557 u32 day_night_ratio = getDayNightRatio();
2561 // On InvalidPositionException, use this as default
2562 // (day: LIGHT_SUN, night: 0)
2563 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2565 v3s16 p = lplayer->getLightPosition();
2566 node_at_lplayer = m_map->getNodeNoEx(p);
2568 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2569 u8 day = light & 0xff;
2570 u8 night = (light >> 8) & 0xff;
2571 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2575 Step active objects and update lighting of them
2578 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2579 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2580 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2581 i != m_active_objects.end(); ++i) {
2582 ClientActiveObject* obj = i->second;
2584 obj->step(dtime, this);
2593 v3s16 p = obj->getLightPosition();
2594 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2596 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2598 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2600 obj->updateLight(light);
2605 Step and handle simple objects
2607 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2608 for(std::vector<ClientSimpleObject*>::iterator
2609 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2610 std::vector<ClientSimpleObject*>::iterator cur = i;
2611 ClientSimpleObject *simple = *cur;
2613 simple->step(dtime);
2614 if(simple->m_to_be_removed) {
2616 i = m_simple_objects.erase(cur);
2624 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2626 m_simple_objects.push_back(simple);
2629 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2631 ClientActiveObject *obj = getActiveObject(id);
2632 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2633 return (GenericCAO*) obj;
2638 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2640 UNORDERED_MAP<u16, ClientActiveObject*>::iterator n = m_active_objects.find(id);
2641 if (n == m_active_objects.end())
2646 bool isFreeClientActiveObjectId(const u16 id,
2647 UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2652 return objects.find(id) == objects.end();
2655 u16 getFreeClientActiveObjectId(UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2657 //try to reuse id's as late as possible
2658 static u16 last_used_id = 0;
2659 u16 startid = last_used_id;
2662 if (isFreeClientActiveObjectId(last_used_id, objects))
2663 return last_used_id;
2665 if (last_used_id == startid)
2670 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2672 assert(object); // Pre-condition
2673 if(object->getId() == 0)
2675 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2678 infostream<<"ClientEnvironment::addActiveObject(): "
2679 <<"no free ids available"<<std::endl;
2683 object->setId(new_id);
2685 if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) {
2686 infostream<<"ClientEnvironment::addActiveObject(): "
2687 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2691 infostream<<"ClientEnvironment::addActiveObject(): "
2692 <<"added (id="<<object->getId()<<")"<<std::endl;
2693 m_active_objects[object->getId()] = object;
2694 object->addToScene(m_smgr, m_texturesource, m_irr);
2695 { // Update lighting immediately
2700 v3s16 p = object->getLightPosition();
2701 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2703 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2705 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2707 object->updateLight(light);
2709 return object->getId();
2712 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2713 const std::string &init_data)
2715 ClientActiveObject* obj =
2716 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2719 infostream<<"ClientEnvironment::addActiveObject(): "
2720 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2729 obj->initialize(init_data);
2731 catch(SerializationError &e)
2733 errorstream<<"ClientEnvironment::addActiveObject():"
2734 <<" id="<<id<<" type="<<type
2735 <<": SerializationError in initialize(): "
2737 <<": init_data="<<serializeJsonString(init_data)
2741 addActiveObject(obj);
2744 void ClientEnvironment::removeActiveObject(u16 id)
2746 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2747 <<"id="<<id<<std::endl;
2748 ClientActiveObject* obj = getActiveObject(id);
2750 infostream<<"ClientEnvironment::removeActiveObject(): "
2751 <<"id="<<id<<" not found"<<std::endl;
2754 obj->removeFromScene(true);
2756 m_active_objects.erase(id);
2759 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2761 ClientActiveObject *obj = getActiveObject(id);
2763 infostream << "ClientEnvironment::processActiveObjectMessage():"
2764 << " got message for id=" << id << ", which doesn't exist."
2770 obj->processMessage(data);
2771 } catch (SerializationError &e) {
2772 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2773 << " id=" << id << " type=" << obj->getType()
2774 << " SerializationError in processMessage(): " << e.what()
2780 Callbacks for activeobjects
2783 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2785 LocalPlayer *lplayer = getLocalPlayer();
2789 if (lplayer->hp > damage)
2790 lplayer->hp -= damage;
2795 ClientEnvEvent event;
2796 event.type = CEE_PLAYER_DAMAGE;
2797 event.player_damage.amount = damage;
2798 event.player_damage.send_to_server = handle_hp;
2799 m_client_event_queue.push(event);
2802 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2804 ClientEnvEvent event;
2805 event.type = CEE_PLAYER_BREATH;
2806 event.player_breath.amount = breath;
2807 m_client_event_queue.push(event);
2811 Client likes to call these
2814 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2815 std::vector<DistanceSortedActiveObject> &dest)
2817 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2818 i != m_active_objects.end(); ++i) {
2819 ClientActiveObject* obj = i->second;
2821 f32 d = (obj->getPosition() - origin).getLength();
2826 DistanceSortedActiveObject dso(obj, d);
2828 dest.push_back(dso);
2832 ClientEnvEvent ClientEnvironment::getClientEvent()
2834 ClientEnvEvent event;
2835 if(m_client_event_queue.empty())
2836 event.type = CEE_NONE;
2838 event = m_client_event_queue.front();
2839 m_client_event_queue.pop();
2844 #endif // #ifndef SERVER