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()
73 for(std::vector<Player*>::iterator i = m_players.begin();
74 i != m_players.end(); ++i) {
79 void Environment::addPlayer(Player *player)
81 DSTACK(FUNCTION_NAME);
83 Check that peer_ids are unique.
84 Also check that names are unique.
85 Exception: there can be multiple players with peer_id=0
87 // If peer id is non-zero, it has to be unique.
88 if(player->peer_id != 0)
89 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
90 // Name has to be unique.
91 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
93 m_players.push_back(player);
96 void Environment::removePlayer(Player* player)
98 for (std::vector<Player*>::iterator it = m_players.begin();
99 it != m_players.end(); ++it) {
100 if ((*it) == player) {
108 Player *Environment::getPlayer(u16 peer_id)
110 for (std::vector<Player*>::iterator i = m_players.begin();
111 i != m_players.end(); ++i) {
113 if (player->peer_id == peer_id)
119 Player *Environment::getPlayer(const char *name)
121 for (std::vector<Player*>::iterator i = m_players.begin();
122 i != m_players.end(); ++i) {
124 if(strcmp(player->getName(), name) == 0)
130 u32 Environment::getDayNightRatio()
132 MutexAutoLock lock(this->m_time_lock);
133 if (m_enable_day_night_ratio_override)
134 return m_day_night_ratio_override;
135 return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
138 void Environment::setTimeOfDaySpeed(float speed)
140 m_time_of_day_speed = speed;
143 void Environment::setDayNightRatioOverride(bool enable, u32 value)
145 MutexAutoLock lock(this->m_time_lock);
146 m_enable_day_night_ratio_override = enable;
147 m_day_night_ratio_override = value;
150 void Environment::setTimeOfDay(u32 time)
152 MutexAutoLock lock(this->m_time_lock);
153 if (m_time_of_day > time)
155 m_time_of_day = time;
156 m_time_of_day_f = (float)time / 24000.0;
159 u32 Environment::getTimeOfDay()
161 MutexAutoLock lock(this->m_time_lock);
162 return m_time_of_day;
165 float Environment::getTimeOfDayF()
167 MutexAutoLock lock(this->m_time_lock);
168 return m_time_of_day_f;
171 void Environment::stepTimeOfDay(float dtime)
173 MutexAutoLock lock(this->m_time_lock);
175 // Cached in order to prevent the two reads we do to give
176 // different results (can be written by code not under the lock)
177 f32 cached_time_of_day_speed = m_time_of_day_speed;
179 f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
180 m_time_conversion_skew += dtime;
181 u32 units = (u32)(m_time_conversion_skew * speed);
185 if (m_time_of_day + units >= 24000) {
189 m_time_of_day = (m_time_of_day + units) % 24000;
191 m_time_of_day_f = (float)m_time_of_day / 24000.0;
194 m_time_conversion_skew -= (f32)units / speed;
197 m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
198 if (m_time_of_day_f > 1.0)
199 m_time_of_day_f -= 1.0;
200 if (m_time_of_day_f < 0.0)
201 m_time_of_day_f += 1.0;
205 u32 Environment::getDayCount()
207 // Atomic<u32> counter
216 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
220 // Initialize timer to random value to spread processing
221 float itv = abm->getTriggerInterval();
222 itv = MYMAX(0.001, itv); // No less than 1ms
223 int minval = MYMAX(-0.51*itv, -60); // Clamp to
224 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
225 timer = myrand_range(minval, maxval);
232 void LBMContentMapping::deleteContents()
234 for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
235 it != lbm_list.end(); ++it) {
240 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
242 // Add the lbm_def to the LBMContentMapping.
243 // Unknown names get added to the global NameIdMapping.
244 INodeDefManager *nodedef = gamedef->ndef();
246 lbm_list.push_back(lbm_def);
248 for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
249 it != lbm_def->trigger_contents.end(); ++it) {
250 std::set<content_t> c_ids;
251 bool found = nodedef->getIds(*it, c_ids);
253 content_t c_id = gamedef->allocateUnknownNodeId(*it);
254 if (c_id == CONTENT_IGNORE) {
255 // Seems it can't be allocated.
256 warningstream << "Could not internalize node name \"" << *it
257 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
263 for (std::set<content_t>::const_iterator iit =
264 c_ids.begin(); iit != c_ids.end(); ++iit) {
265 content_t c_id = *iit;
266 map[c_id].push_back(lbm_def);
271 const std::vector<LoadingBlockModifierDef *> *
272 LBMContentMapping::lookup(content_t c) const
274 container_map::const_iterator it = map.find(c);
277 // This first dereferences the iterator, returning
278 // a std::vector<LoadingBlockModifierDef *>
279 // reference, then we convert it to a pointer.
280 return &(it->second);
283 LBMManager::~LBMManager()
285 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
286 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
289 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
290 it != m_lbm_lookup.end(); ++it) {
291 (it->second).deleteContents();
295 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
297 // Precondition, in query mode the map isn't used anymore
298 FATAL_ERROR_IF(m_query_mode == true,
299 "attempted to modify LBMManager in query mode");
301 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
302 throw ModError("Error adding LBM \"" + lbm_def->name +
303 "\": Does not follow naming conventions: "
304 "Only chararacters [a-z0-9_:] are allowed.");
307 m_lbm_defs[lbm_def->name] = lbm_def;
310 void LBMManager::loadIntroductionTimes(const std::string ×,
311 IGameDef *gamedef, u32 now)
316 // Storing it in a map first instead of
317 // handling the stuff directly in the loop
318 // removes all duplicate entries.
319 // TODO make this std::unordered_map
320 std::map<std::string, u32> introduction_times;
323 The introduction times string consists of name~time entries,
324 with each entry terminated by a semicolon. The time is decimal.
329 while ((idx_new = times.find(";", idx)) != std::string::npos) {
330 std::string entry = times.substr(idx, idx_new - idx);
331 std::vector<std::string> components = str_split(entry, '~');
332 if (components.size() != 2)
333 throw SerializationError("Introduction times entry \""
334 + entry + "\" requires exactly one '~'!");
335 const std::string &name = components[0];
336 u32 time = from_string<u32>(components[1]);
337 introduction_times[name] = time;
341 // Put stuff from introduction_times into m_lbm_lookup
342 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
343 it != introduction_times.end(); ++it) {
344 const std::string &name = it->first;
345 u32 time = it->second;
347 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
348 m_lbm_defs.find(name);
349 if (def_it == m_lbm_defs.end()) {
350 // This seems to be an LBM entry for
351 // an LBM we haven't loaded. Discard it.
354 LoadingBlockModifierDef *lbm_def = def_it->second;
355 if (lbm_def->run_at_every_load) {
356 // This seems to be an LBM entry for
357 // an LBM that runs at every load.
358 // Don't add it just yet.
362 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
364 // Erase the entry so that we know later
365 // what elements didn't get put into m_lbm_lookup
366 m_lbm_defs.erase(name);
369 // Now also add the elements from m_lbm_defs to m_lbm_lookup
370 // that weren't added in the previous step.
371 // They are introduced first time to this world,
372 // or are run at every load (introducement time hardcoded to U32_MAX).
374 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
375 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
377 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
378 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
379 if (it->second->run_at_every_load) {
380 lbms_running_always.addLBM(it->second, gamedef);
382 lbms_we_introduce_now.addLBM(it->second, gamedef);
386 // Clear the list, so that we don't delete remaining elements
387 // twice in the destructor
391 std::string LBMManager::createIntroductionTimesString()
393 // Precondition, we must be in query mode
394 FATAL_ERROR_IF(m_query_mode == false,
395 "attempted to query on non fully set up LBMManager");
397 std::ostringstream oss;
398 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
399 it != m_lbm_lookup.end(); ++it) {
400 u32 time = it->first;
401 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
402 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
403 iit != lbm_list.end(); ++iit) {
404 // Don't add if the LBM runs at every load,
405 // then introducement time is hardcoded
406 // and doesn't need to be stored
407 if ((*iit)->run_at_every_load)
409 oss << (*iit)->name << "~" << time << ";";
415 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
417 // Precondition, we need m_lbm_lookup to be initialized
418 FATAL_ERROR_IF(m_query_mode == false,
419 "attempted to query on non fully set up LBMManager");
420 v3s16 pos_of_block = block->getPosRelative();
424 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
425 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
426 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
427 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
429 n = block->getNodeNoEx(pos);
431 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
432 iit != m_lbm_lookup.end(); ++iit) {
433 const std::vector<LoadingBlockModifierDef *> *lbm_list =
434 iit->second.lookup(c);
437 for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
438 lbm_list->begin(); iit != lbm_list->end(); ++iit) {
439 (*iit)->trigger(env, pos + pos_of_block, n);
449 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
452 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
453 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
454 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
461 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
463 std::set<v3s16> &blocks_removed,
464 std::set<v3s16> &blocks_added)
469 std::set<v3s16> newlist = m_forceloaded_list;
470 for(std::vector<v3s16>::iterator i = active_positions.begin();
471 i != active_positions.end(); ++i)
473 fillRadiusBlock(*i, radius, newlist);
477 Find out which blocks on the old list are not on the new list
479 // Go through old list
480 for(std::set<v3s16>::iterator i = m_list.begin();
481 i != m_list.end(); ++i)
484 // If not on new list, it's been removed
485 if(newlist.find(p) == newlist.end())
486 blocks_removed.insert(p);
490 Find out which blocks on the new list are not on the old list
492 // Go through new list
493 for(std::set<v3s16>::iterator i = newlist.begin();
494 i != newlist.end(); ++i)
497 // If not on old list, it's been added
498 if(m_list.find(p) == m_list.end())
499 blocks_added.insert(p);
506 for(std::set<v3s16>::iterator i = newlist.begin();
507 i != newlist.end(); ++i)
518 ServerEnvironment::ServerEnvironment(ServerMap *map,
519 GameScripting *scriptIface, IGameDef *gamedef,
520 const std::string &path_world) :
522 m_script(scriptIface),
524 m_path_world(path_world),
525 m_send_recommended_timer(0),
526 m_active_block_interval_overload_skip(0),
528 m_game_time_fraction_counter(0),
529 m_last_clear_objects_time(0),
530 m_recommended_send_interval(0.1),
531 m_max_lag_estimate(0.1)
535 ServerEnvironment::~ServerEnvironment()
537 // Clear active block list.
538 // This makes the next one delete all active objects.
539 m_active_blocks.clear();
541 // Convert all objects to static and delete the active objects
542 deactivateFarObjects(true);
547 // Delete ActiveBlockModifiers
548 for(std::vector<ABMWithState>::iterator
549 i = m_abms.begin(); i != m_abms.end(); ++i){
554 Map & ServerEnvironment::getMap()
559 ServerMap & ServerEnvironment::getServerMap()
564 RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
566 return dynamic_cast<RemotePlayer *>(Environment::getPlayer(peer_id));
569 RemotePlayer *ServerEnvironment::getPlayer(const char* name)
571 return dynamic_cast<RemotePlayer *>(Environment::getPlayer(name));
574 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
576 float distance = pos1.getDistanceFrom(pos2);
578 //calculate normalized direction vector
579 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
580 (pos2.Y - pos1.Y)/distance,
581 (pos2.Z - pos1.Z)/distance);
583 //find out if there's a node on path between pos1 and pos2
584 for (float i = 1; i < distance; i += stepsize) {
585 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
586 normalized_vector.Y * i,
587 normalized_vector.Z * i) +pos1,BS);
589 MapNode n = getMap().getNodeNoEx(pos);
591 if(n.param0 != CONTENT_AIR) {
601 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
602 const std::string &str_reason, bool reconnect)
604 for (std::vector<Player*>::iterator it = m_players.begin();
605 it != m_players.end(); ++it) {
606 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
607 (*it)->protocol_version, reason, str_reason, reconnect);
611 void ServerEnvironment::saveLoadedPlayers()
613 std::string players_path = m_path_world + DIR_DELIM "players";
614 fs::CreateDir(players_path);
616 for (std::vector<Player*>::iterator it = m_players.begin();
617 it != m_players.end();
619 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
620 if (player->checkModified()) {
621 player->save(players_path);
626 void ServerEnvironment::savePlayer(RemotePlayer *player)
628 std::string players_path = m_path_world + DIR_DELIM "players";
629 fs::CreateDir(players_path);
631 player->save(players_path);
634 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername)
636 bool newplayer = false;
638 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
639 std::string path = players_path + playername;
641 RemotePlayer *player = getPlayer(playername.c_str());
643 player = new RemotePlayer(m_gamedef, "");
647 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
648 //// Open file and deserialize
649 std::ifstream is(path.c_str(), std::ios_base::binary);
652 player->deSerialize(is, path);
655 if (player->getName() == playername) {
660 path = players_path + playername + itos(i);
664 infostream << "Player file for player " << playername
665 << " not found" << std::endl;
673 player->setModified(false);
677 void ServerEnvironment::saveMeta()
679 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
681 // Open file and serialize
682 std::ostringstream ss(std::ios_base::binary);
685 args.setU64("game_time", m_game_time);
686 args.setU64("time_of_day", getTimeOfDay());
687 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
688 args.setU64("lbm_introduction_times_version", 1);
689 args.set("lbm_introduction_times",
690 m_lbm_mgr.createIntroductionTimesString());
691 args.setU64("day_count", m_day_count);
695 if(!fs::safeWriteToFile(path, ss.str()))
697 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
699 throw SerializationError("Couldn't save env meta");
703 void ServerEnvironment::loadMeta()
705 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
707 // Open file and deserialize
708 std::ifstream is(path.c_str(), std::ios_base::binary);
710 infostream << "ServerEnvironment::loadMeta(): Failed to open "
711 << path << std::endl;
712 throw SerializationError("Couldn't load env meta");
717 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
718 throw SerializationError("ServerEnvironment::loadMeta(): "
719 "EnvArgsEnd not found!");
723 m_game_time = args.getU64("game_time");
724 } catch (SettingNotFoundException &e) {
725 // Getting this is crucial, otherwise timestamps are useless
726 throw SerializationError("Couldn't load env meta game_time");
729 setTimeOfDay(args.exists("time_of_day") ?
730 // set day to morning by default
731 args.getU64("time_of_day") : 9000);
733 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
734 // If missing, do as if clearObjects was never called
735 args.getU64("last_clear_objects_time") : 0;
737 std::string lbm_introduction_times = "";
739 u64 ver = args.getU64("lbm_introduction_times_version");
741 lbm_introduction_times = args.get("lbm_introduction_times");
743 infostream << "ServerEnvironment::loadMeta(): Non-supported"
744 << " introduction time version " << ver << std::endl;
746 } catch (SettingNotFoundException &e) {
747 // No problem, this is expected. Just continue with an empty string
749 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
751 m_day_count = args.exists("day_count") ?
752 args.getU64("day_count") : 0;
755 void ServerEnvironment::loadDefaultMeta()
757 m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
762 ActiveBlockModifier *abm;
764 std::set<content_t> required_neighbors;
770 ServerEnvironment *m_env;
771 std::map<content_t, std::vector<ActiveABM> > m_aabms;
773 ABMHandler(std::vector<ABMWithState> &abms,
774 float dtime_s, ServerEnvironment *env,
780 INodeDefManager *ndef = env->getGameDef()->ndef();
781 for(std::vector<ABMWithState>::iterator
782 i = abms.begin(); i != abms.end(); ++i) {
783 ActiveBlockModifier *abm = i->abm;
784 float trigger_interval = abm->getTriggerInterval();
785 if(trigger_interval < 0.001)
786 trigger_interval = 0.001;
787 float actual_interval = dtime_s;
790 if(i->timer < trigger_interval)
792 i->timer -= trigger_interval;
793 actual_interval = trigger_interval;
795 float chance = abm->getTriggerChance();
800 if(abm->getSimpleCatchUp()) {
801 float intervals = actual_interval / trigger_interval;
804 aabm.chance = chance / intervals;
808 aabm.chance = chance;
811 std::set<std::string> required_neighbors_s
812 = abm->getRequiredNeighbors();
813 for(std::set<std::string>::iterator
814 i = required_neighbors_s.begin();
815 i != required_neighbors_s.end(); ++i)
817 ndef->getIds(*i, aabm.required_neighbors);
820 std::set<std::string> contents_s = abm->getTriggerContents();
821 for(std::set<std::string>::iterator
822 i = contents_s.begin(); i != contents_s.end(); ++i)
824 std::set<content_t> ids;
825 ndef->getIds(*i, ids);
826 for(std::set<content_t>::const_iterator k = ids.begin();
830 std::map<content_t, std::vector<ActiveABM> >::iterator j;
832 if(j == m_aabms.end()){
833 std::vector<ActiveABM> aabmlist;
834 m_aabms[c] = aabmlist;
837 j->second.push_back(aabm);
842 // Find out how many objects the given block and its neighbours contain.
843 // Returns the number of objects in the block, and also in 'wider' the
844 // number of objects in the block and all its neighbours. The latter
845 // may an estimate if any neighbours are unloaded.
846 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
849 u32 wider_unknown_count = 0;
850 for(s16 x=-1; x<=1; x++)
851 for(s16 y=-1; y<=1; y++)
852 for(s16 z=-1; z<=1; z++)
854 MapBlock *block2 = map->getBlockNoCreateNoEx(
855 block->getPos() + v3s16(x,y,z));
857 wider_unknown_count++;
860 wider += block2->m_static_objects.m_active.size()
861 + block2->m_static_objects.m_stored.size();
864 u32 active_object_count = block->m_static_objects.m_active.size();
865 u32 wider_known_count = 3*3*3 - wider_unknown_count;
866 wider += wider_unknown_count * wider / wider_known_count;
867 return active_object_count;
870 void apply(MapBlock *block)
875 ServerMap *map = &m_env->getServerMap();
877 u32 active_object_count_wider;
878 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
879 m_env->m_added_objects = 0;
882 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
883 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
884 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
886 MapNode n = block->getNodeNoEx(p0);
887 content_t c = n.getContent();
888 v3s16 p = p0 + block->getPosRelative();
890 std::map<content_t, std::vector<ActiveABM> >::iterator j;
892 if(j == m_aabms.end())
895 for(std::vector<ActiveABM>::iterator
896 i = j->second.begin(); i != j->second.end(); ++i) {
897 if(myrand() % i->chance != 0)
901 if(!i->required_neighbors.empty())
904 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
905 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
906 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
910 MapNode n = map->getNodeNoEx(p1);
911 content_t c = n.getContent();
912 std::set<content_t>::const_iterator k;
913 k = i->required_neighbors.find(c);
914 if(k != i->required_neighbors.end()){
918 // No required neighbor found
923 // Call all the trigger variations
924 i->abm->trigger(m_env, p, n);
925 i->abm->trigger(m_env, p, n,
926 active_object_count, active_object_count_wider);
928 // Count surrounding objects again if the abms added any
929 if(m_env->m_added_objects > 0) {
930 active_object_count = countObjects(block, map, active_object_count_wider);
931 m_env->m_added_objects = 0;
938 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
940 // Reset usage timer immediately, otherwise a block that becomes active
941 // again at around the same time as it would normally be unloaded will
942 // get unloaded incorrectly. (I think this still leaves a small possibility
943 // of a race condition between this and server::AsyncRunStep, which only
944 // some kind of synchronisation will fix, but it at least reduces the window
945 // of opportunity for it to break from seconds to nanoseconds)
946 block->resetUsageTimer();
948 // Get time difference
950 u32 stamp = block->getTimestamp();
951 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
952 dtime_s = m_game_time - stamp;
953 dtime_s += additional_dtime;
955 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
956 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
958 // Remove stored static objects if clearObjects was called since block's timestamp
959 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
960 block->m_static_objects.m_stored.clear();
961 // do not set changed flag to avoid unnecessary mapblock writes
964 // Set current time as timestamp
965 block->setTimestampNoChangedFlag(m_game_time);
967 /*infostream<<"ServerEnvironment::activateBlock(): block is "
968 <<dtime_s<<" seconds old."<<std::endl;*/
970 // Activate stored objects
971 activateObjects(block, dtime_s);
973 /* Handle LoadingBlockModifiers */
974 m_lbm_mgr.applyLBMs(this, block, stamp);
977 std::vector<NodeTimer> elapsed_timers =
978 block->m_node_timers.step((float)dtime_s);
979 if (!elapsed_timers.empty()) {
981 for (std::vector<NodeTimer>::iterator
982 i = elapsed_timers.begin();
983 i != elapsed_timers.end(); ++i){
984 n = block->getNodeNoEx(i->position);
985 v3s16 p = i->position + block->getPosRelative();
986 if (m_script->node_on_timer(p, n, i->elapsed))
987 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
991 /* Handle ActiveBlockModifiers */
992 ABMHandler abmhandler(m_abms, dtime_s, this, false);
993 abmhandler.apply(block);
996 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
998 m_abms.push_back(ABMWithState(abm));
1001 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1003 m_lbm_mgr.addLBMDef(lbm);
1006 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1008 INodeDefManager *ndef = m_gamedef->ndef();
1009 MapNode n_old = m_map->getNodeNoEx(p);
1012 if (ndef->get(n_old).has_on_destruct)
1013 m_script->node_on_destruct(p, n_old);
1016 if (!m_map->addNodeWithEvent(p, n))
1019 // Update active VoxelManipulator if a mapgen thread
1020 m_map->updateVManip(p);
1022 // Call post-destructor
1023 if (ndef->get(n_old).has_after_destruct)
1024 m_script->node_after_destruct(p, n_old);
1027 if (ndef->get(n).has_on_construct)
1028 m_script->node_on_construct(p, n);
1033 bool ServerEnvironment::removeNode(v3s16 p)
1035 INodeDefManager *ndef = m_gamedef->ndef();
1036 MapNode n_old = m_map->getNodeNoEx(p);
1039 if (ndef->get(n_old).has_on_destruct)
1040 m_script->node_on_destruct(p, n_old);
1043 // This is slightly optimized compared to addNodeWithEvent(air)
1044 if (!m_map->removeNodeWithEvent(p))
1047 // Update active VoxelManipulator if a mapgen thread
1048 m_map->updateVManip(p);
1050 // Call post-destructor
1051 if (ndef->get(n_old).has_after_destruct)
1052 m_script->node_after_destruct(p, n_old);
1054 // Air doesn't require constructor
1058 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1060 if (!m_map->addNodeWithEvent(p, n, false))
1063 // Update active VoxelManipulator if a mapgen thread
1064 m_map->updateVManip(p);
1069 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1071 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1072 i != m_active_objects.end(); ++i) {
1073 ServerActiveObject* obj = i->second;
1075 v3f objectpos = obj->getBasePosition();
1076 if (objectpos.getDistanceFrom(pos) > radius)
1078 objects.push_back(id);
1082 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1084 infostream << "ServerEnvironment::clearObjects(): "
1085 << "Removing all active objects" << std::endl;
1086 std::vector<u16> objects_to_remove;
1087 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1088 i != m_active_objects.end(); ++i) {
1089 ServerActiveObject* obj = i->second;
1090 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1093 // Delete static object if block is loaded
1094 if (obj->m_static_exists) {
1095 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1097 block->m_static_objects.remove(id);
1098 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1099 MOD_REASON_CLEAR_ALL_OBJECTS);
1100 obj->m_static_exists = false;
1103 // If known by some client, don't delete immediately
1104 if (obj->m_known_by_count > 0) {
1105 obj->m_pending_deactivation = true;
1106 obj->m_removed = true;
1110 // Tell the object about removal
1111 obj->removingFromEnvironment();
1112 // Deregister in scripting api
1113 m_script->removeObjectReference(obj);
1115 // Delete active object
1116 if (obj->environmentDeletes())
1118 // Id to be removed from m_active_objects
1119 objects_to_remove.push_back(id);
1122 // Remove references from m_active_objects
1123 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1124 i != objects_to_remove.end(); ++i) {
1125 m_active_objects.erase(*i);
1128 // Get list of loaded blocks
1129 std::vector<v3s16> loaded_blocks;
1130 infostream << "ServerEnvironment::clearObjects(): "
1131 << "Listing all loaded blocks" << std::endl;
1132 m_map->listAllLoadedBlocks(loaded_blocks);
1133 infostream << "ServerEnvironment::clearObjects(): "
1134 << "Done listing all loaded blocks: "
1135 << loaded_blocks.size()<<std::endl;
1137 // Get list of loadable blocks
1138 std::vector<v3s16> loadable_blocks;
1139 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1140 infostream << "ServerEnvironment::clearObjects(): "
1141 << "Listing all loadable blocks" << std::endl;
1142 m_map->listAllLoadableBlocks(loadable_blocks);
1143 infostream << "ServerEnvironment::clearObjects(): "
1144 << "Done listing all loadable blocks: "
1145 << loadable_blocks.size() << std::endl;
1147 loadable_blocks = loaded_blocks;
1150 infostream << "ServerEnvironment::clearObjects(): "
1151 << "Now clearing objects in " << loadable_blocks.size()
1152 << " blocks" << std::endl;
1154 // Grab a reference on each loaded block to avoid unloading it
1155 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1156 i != loaded_blocks.end(); ++i) {
1158 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1159 assert(block != NULL);
1163 // Remove objects in all loadable blocks
1164 u32 unload_interval = U32_MAX;
1165 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1166 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1167 unload_interval = MYMAX(unload_interval, 1);
1169 u32 report_interval = loadable_blocks.size() / 10;
1170 u32 num_blocks_checked = 0;
1171 u32 num_blocks_cleared = 0;
1172 u32 num_objs_cleared = 0;
1173 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1174 i != loadable_blocks.end(); ++i) {
1176 MapBlock *block = m_map->emergeBlock(p, false);
1178 errorstream << "ServerEnvironment::clearObjects(): "
1179 << "Failed to emerge block " << PP(p) << std::endl;
1182 u32 num_stored = block->m_static_objects.m_stored.size();
1183 u32 num_active = block->m_static_objects.m_active.size();
1184 if (num_stored != 0 || num_active != 0) {
1185 block->m_static_objects.m_stored.clear();
1186 block->m_static_objects.m_active.clear();
1187 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1188 MOD_REASON_CLEAR_ALL_OBJECTS);
1189 num_objs_cleared += num_stored + num_active;
1190 num_blocks_cleared++;
1192 num_blocks_checked++;
1194 if (report_interval != 0 &&
1195 num_blocks_checked % report_interval == 0) {
1196 float percent = 100.0 * (float)num_blocks_checked /
1197 loadable_blocks.size();
1198 infostream << "ServerEnvironment::clearObjects(): "
1199 << "Cleared " << num_objs_cleared << " objects"
1200 << " in " << num_blocks_cleared << " blocks ("
1201 << percent << "%)" << std::endl;
1203 if (num_blocks_checked % unload_interval == 0) {
1204 m_map->unloadUnreferencedBlocks();
1207 m_map->unloadUnreferencedBlocks();
1209 // Drop references that were added above
1210 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1211 i != loaded_blocks.end(); ++i) {
1213 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1218 m_last_clear_objects_time = m_game_time;
1220 infostream << "ServerEnvironment::clearObjects(): "
1221 << "Finished: Cleared " << num_objs_cleared << " objects"
1222 << " in " << num_blocks_cleared << " blocks" << std::endl;
1225 void ServerEnvironment::step(float dtime)
1227 DSTACK(FUNCTION_NAME);
1229 //TimeTaker timer("ServerEnv step");
1231 /* Step time of day */
1232 stepTimeOfDay(dtime);
1235 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1236 // really matter that much.
1237 static const float server_step = g_settings->getFloat("dedicated_server_step");
1238 m_recommended_send_interval = server_step;
1244 m_game_time_fraction_counter += dtime;
1245 u32 inc_i = (u32)m_game_time_fraction_counter;
1246 m_game_time += inc_i;
1247 m_game_time_fraction_counter -= (float)inc_i;
1254 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1255 for (std::vector<Player*>::iterator i = m_players.begin();
1256 i != m_players.end(); ++i) {
1257 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1260 // Ignore disconnected players
1261 if(player->peer_id == 0)
1265 player->move(dtime, this, 100*BS);
1270 Manage active block list
1272 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1273 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1275 Get player block positions
1277 std::vector<v3s16> players_blockpos;
1278 for (std::vector<Player*>::iterator i = m_players.begin();
1279 i != m_players.end(); ++i) {
1280 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1282 // Ignore disconnected players
1283 if (player->peer_id == 0)
1286 v3s16 blockpos = getNodeBlockPos(
1287 floatToInt(player->getPosition(), BS));
1288 players_blockpos.push_back(blockpos);
1292 Update list of active blocks, collecting changes
1294 static const s16 active_block_range = g_settings->getS16("active_block_range");
1295 std::set<v3s16> blocks_removed;
1296 std::set<v3s16> blocks_added;
1297 m_active_blocks.update(players_blockpos, active_block_range,
1298 blocks_removed, blocks_added);
1301 Handle removed blocks
1304 // Convert active objects that are no more in active blocks to static
1305 deactivateFarObjects(false);
1307 for(std::set<v3s16>::iterator
1308 i = blocks_removed.begin();
1309 i != blocks_removed.end(); ++i) {
1312 /* infostream<<"Server: Block " << PP(p)
1313 << " became inactive"<<std::endl; */
1315 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1319 // Set current time as timestamp (and let it set ChangedFlag)
1320 block->setTimestamp(m_game_time);
1327 for(std::set<v3s16>::iterator
1328 i = blocks_added.begin();
1329 i != blocks_added.end(); ++i)
1333 MapBlock *block = m_map->getBlockOrEmerge(p);
1335 m_active_blocks.m_list.erase(p);
1339 activateBlock(block);
1340 /* infostream<<"Server: Block " << PP(p)
1341 << " became active"<<std::endl; */
1346 Mess around in active blocks
1348 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1349 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1351 float dtime = m_cache_nodetimer_interval;
1353 for(std::set<v3s16>::iterator
1354 i = m_active_blocks.m_list.begin();
1355 i != m_active_blocks.m_list.end(); ++i)
1359 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1360 <<") being handled"<<std::endl;*/
1362 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1366 // Reset block usage timer
1367 block->resetUsageTimer();
1369 // Set current time as timestamp
1370 block->setTimestampNoChangedFlag(m_game_time);
1371 // If time has changed much from the one on disk,
1372 // set block to be saved when it is unloaded
1373 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1374 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1375 MOD_REASON_BLOCK_EXPIRED);
1378 std::vector<NodeTimer> elapsed_timers =
1379 block->m_node_timers.step((float)dtime);
1380 if (!elapsed_timers.empty()) {
1382 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1383 i != elapsed_timers.end(); ++i) {
1384 n = block->getNodeNoEx(i->position);
1385 p = i->position + block->getPosRelative();
1386 if (m_script->node_on_timer(p, n, i->elapsed)) {
1387 block->setNodeTimer(NodeTimer(
1388 i->timeout, 0, i->position));
1395 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1397 if(m_active_block_interval_overload_skip > 0){
1398 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1399 m_active_block_interval_overload_skip--;
1402 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1403 TimeTaker timer("modify in active blocks per interval");
1405 // Initialize handling of ActiveBlockModifiers
1406 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1408 for(std::set<v3s16>::iterator
1409 i = m_active_blocks.m_list.begin();
1410 i != m_active_blocks.m_list.end(); ++i)
1414 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1415 <<") being handled"<<std::endl;*/
1417 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1421 // Set current time as timestamp
1422 block->setTimestampNoChangedFlag(m_game_time);
1424 /* Handle ActiveBlockModifiers */
1425 abmhandler.apply(block);
1428 u32 time_ms = timer.stop(true);
1429 u32 max_time_ms = 200;
1430 if(time_ms > max_time_ms){
1431 warningstream<<"active block modifiers took "
1432 <<time_ms<<"ms (longer than "
1433 <<max_time_ms<<"ms)"<<std::endl;
1434 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1439 Step script environment (run global on_step())
1441 m_script->environment_Step(dtime);
1447 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1448 //TimeTaker timer("Step active objects");
1450 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1452 // This helps the objects to send data at the same time
1453 bool send_recommended = false;
1454 m_send_recommended_timer += dtime;
1455 if(m_send_recommended_timer > getSendRecommendedInterval())
1457 m_send_recommended_timer -= getSendRecommendedInterval();
1458 send_recommended = true;
1461 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1462 i != m_active_objects.end(); ++i) {
1463 ServerActiveObject* obj = i->second;
1464 // Don't step if is to be removed or stored statically
1465 if(obj->m_removed || obj->m_pending_deactivation)
1468 obj->step(dtime, send_recommended);
1469 // Read messages from object
1470 while(!obj->m_messages_out.empty())
1472 m_active_object_messages.push(
1473 obj->m_messages_out.front());
1474 obj->m_messages_out.pop();
1480 Manage active objects
1482 if(m_object_management_interval.step(dtime, 0.5))
1484 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1486 Remove objects that satisfy (m_removed && m_known_by_count==0)
1488 removeRemovedObjects();
1492 Manage particle spawner expiration
1494 if (m_particle_management_interval.step(dtime, 1.0)) {
1495 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1496 i != m_particle_spawners.end(); ) {
1497 //non expiring spawners
1498 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1504 if (i->second <= 0.f)
1505 m_particle_spawners.erase(i++);
1512 u32 ServerEnvironment::addParticleSpawner(float exptime)
1514 // Timers with lifetime 0 do not expire
1515 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1518 for (;;) { // look for unused particlespawner id
1520 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1521 if (f == m_particle_spawners.end()) {
1522 m_particle_spawners[id] = time;
1529 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1531 ActiveObjectMap::iterator n = m_active_objects.find(id);
1532 return (n != m_active_objects.end() ? n->second : NULL);
1535 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1540 return objects.find(id) == objects.end();
1543 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1545 //try to reuse id's as late as possible
1546 static u16 last_used_id = 0;
1547 u16 startid = last_used_id;
1551 if(isFreeServerActiveObjectId(last_used_id, objects))
1552 return last_used_id;
1554 if(last_used_id == startid)
1559 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1561 assert(object); // Pre-condition
1563 u16 id = addActiveObjectRaw(object, true, 0);
1568 Finds out what new objects have been added to
1569 inside a radius around a position
1571 void ServerEnvironment::getAddedActiveObjects(RemotePlayer *player, s16 radius,
1573 std::set<u16> ¤t_objects,
1574 std::queue<u16> &added_objects)
1576 f32 radius_f = radius * BS;
1577 f32 player_radius_f = player_radius * BS;
1579 if (player_radius_f < 0)
1580 player_radius_f = 0;
1583 Go through the object list,
1584 - discard m_removed objects,
1585 - discard objects that are too far away,
1586 - discard objects that are found in current_objects.
1587 - add remaining objects to added_objects
1589 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1590 i != m_active_objects.end(); ++i) {
1594 ServerActiveObject *object = i->second;
1598 // Discard if removed or deactivating
1599 if(object->m_removed || object->m_pending_deactivation)
1602 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1603 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1604 // Discard if too far
1605 if (distance_f > player_radius_f && player_radius_f != 0)
1607 } else if (distance_f > radius_f)
1610 // Discard if already on current_objects
1611 std::set<u16>::iterator n;
1612 n = current_objects.find(id);
1613 if(n != current_objects.end())
1615 // Add to added_objects
1616 added_objects.push(id);
1621 Finds out what objects have been removed from
1622 inside a radius around a position
1624 void ServerEnvironment::getRemovedActiveObjects(RemotePlayer *player, s16 radius,
1626 std::set<u16> ¤t_objects,
1627 std::queue<u16> &removed_objects)
1629 f32 radius_f = radius * BS;
1630 f32 player_radius_f = player_radius * BS;
1632 if (player_radius_f < 0)
1633 player_radius_f = 0;
1636 Go through current_objects; object is removed if:
1637 - object is not found in m_active_objects (this is actually an
1638 error condition; objects should be set m_removed=true and removed
1639 only after all clients have been informed about removal), or
1640 - object has m_removed=true, or
1641 - object is too far away
1643 for(std::set<u16>::iterator
1644 i = current_objects.begin();
1645 i != current_objects.end(); ++i)
1648 ServerActiveObject *object = getActiveObject(id);
1650 if (object == NULL) {
1651 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1652 << " object in current_objects is NULL" << std::endl;
1653 removed_objects.push(id);
1657 if (object->m_removed || object->m_pending_deactivation) {
1658 removed_objects.push(id);
1662 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1663 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1664 if (distance_f <= player_radius_f || player_radius_f == 0)
1666 } else if (distance_f <= radius_f)
1669 // Object is no longer visible
1670 removed_objects.push(id);
1674 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1675 v3s16 blockpos, bool static_exists, v3s16 static_block)
1677 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1681 for (std::map<u16, StaticObject>::iterator
1682 so_it = block->m_static_objects.m_active.begin();
1683 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1684 // Get the ServerActiveObject counterpart to this StaticObject
1685 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1686 if (ao_it == m_active_objects.end()) {
1687 // If this ever happens, there must be some kind of nasty bug.
1688 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1689 "Object from MapBlock::m_static_objects::m_active not found "
1690 "in m_active_objects";
1694 ServerActiveObject *sao = ao_it->second;
1695 sao->m_static_exists = static_exists;
1696 sao->m_static_block = static_block;
1700 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1702 if(m_active_object_messages.empty())
1703 return ActiveObjectMessage(0);
1705 ActiveObjectMessage message = m_active_object_messages.front();
1706 m_active_object_messages.pop();
1711 ************ Private methods *************
1714 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1715 bool set_changed, u32 dtime_s)
1717 assert(object); // Pre-condition
1718 if(object->getId() == 0){
1719 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1722 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1723 <<"no free ids available"<<std::endl;
1724 if(object->environmentDeletes())
1728 object->setId(new_id);
1731 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1732 <<"supplied with id "<<object->getId()<<std::endl;
1735 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1736 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1737 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1738 if(object->environmentDeletes())
1743 if (objectpos_over_limit(object->getBasePosition())) {
1744 v3f p = object->getBasePosition();
1745 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1746 << "object position (" << p.X << "," << p.Y << "," << p.Z
1747 << ") outside maximum range" << std::endl;
1748 if (object->environmentDeletes())
1753 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1754 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1756 m_active_objects[object->getId()] = object;
1758 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1759 <<"Added id="<<object->getId()<<"; there are now "
1760 <<m_active_objects.size()<<" active objects."
1763 // Register reference in scripting api (must be done before post-init)
1764 m_script->addObjectReference(object);
1765 // Post-initialize object
1766 object->addedToEnvironment(dtime_s);
1768 // Add static data to block
1769 if(object->isStaticAllowed())
1771 // Add static object to active static list of the block
1772 v3f objectpos = object->getBasePosition();
1773 std::string staticdata = object->getStaticData();
1774 StaticObject s_obj(object->getType(), objectpos, staticdata);
1775 // Add to the block where the object is located in
1776 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1777 MapBlock *block = m_map->emergeBlock(blockpos);
1779 block->m_static_objects.m_active[object->getId()] = s_obj;
1780 object->m_static_exists = true;
1781 object->m_static_block = blockpos;
1784 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1785 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1787 v3s16 p = floatToInt(objectpos, BS);
1788 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1789 <<"could not emerge block for storing id="<<object->getId()
1790 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1794 return object->getId();
1798 Remove objects that satisfy (m_removed && m_known_by_count==0)
1800 void ServerEnvironment::removeRemovedObjects()
1802 std::vector<u16> objects_to_remove;
1803 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1804 i != m_active_objects.end(); ++i) {
1806 ServerActiveObject* obj = i->second;
1807 // This shouldn't happen but check it
1810 infostream<<"NULL object found in ServerEnvironment"
1811 <<" while finding removed objects. id="<<id<<std::endl;
1812 // Id to be removed from m_active_objects
1813 objects_to_remove.push_back(id);
1818 We will delete objects that are marked as removed or thatare
1819 waiting for deletion after deactivation
1821 if (!obj->m_removed && !obj->m_pending_deactivation)
1825 Delete static data from block if is marked as removed
1827 if(obj->m_static_exists && obj->m_removed)
1829 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1831 block->m_static_objects.remove(id);
1832 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1833 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1834 obj->m_static_exists = false;
1836 infostream<<"Failed to emerge block from which an object to "
1837 <<"be removed was loaded from. id="<<id<<std::endl;
1841 // If m_known_by_count > 0, don't actually remove. On some future
1842 // invocation this will be 0, which is when removal will continue.
1843 if(obj->m_known_by_count > 0)
1847 Move static data from active to stored if not marked as removed
1849 if(obj->m_static_exists && !obj->m_removed){
1850 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1852 std::map<u16, StaticObject>::iterator i =
1853 block->m_static_objects.m_active.find(id);
1854 if(i != block->m_static_objects.m_active.end()){
1855 block->m_static_objects.m_stored.push_back(i->second);
1856 block->m_static_objects.m_active.erase(id);
1857 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1858 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1861 infostream<<"Failed to emerge block from which an object to "
1862 <<"be deactivated was loaded from. id="<<id<<std::endl;
1866 // Tell the object about removal
1867 obj->removingFromEnvironment();
1868 // Deregister in scripting api
1869 m_script->removeObjectReference(obj);
1872 if(obj->environmentDeletes())
1875 // Id to be removed from m_active_objects
1876 objects_to_remove.push_back(id);
1878 // Remove references from m_active_objects
1879 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1880 i != objects_to_remove.end(); ++i) {
1881 m_active_objects.erase(*i);
1885 static void print_hexdump(std::ostream &o, const std::string &data)
1887 const int linelength = 16;
1888 for(int l=0; ; l++){
1889 int i0 = linelength * l;
1890 bool at_end = false;
1891 int thislinelength = linelength;
1892 if(i0 + thislinelength > (int)data.size()){
1893 thislinelength = data.size() - i0;
1896 for(int di=0; di<linelength; di++){
1899 if(di<thislinelength)
1900 snprintf(buf, 4, "%.2x ", data[i]);
1902 snprintf(buf, 4, " ");
1906 for(int di=0; di<thislinelength; di++){
1920 Convert stored objects from blocks near the players to active.
1922 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1927 // Ignore if no stored objects (to not set changed flag)
1928 if(block->m_static_objects.m_stored.empty())
1931 verbosestream<<"ServerEnvironment::activateObjects(): "
1932 <<"activating objects of block "<<PP(block->getPos())
1933 <<" ("<<block->m_static_objects.m_stored.size()
1934 <<" objects)"<<std::endl;
1935 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1937 errorstream<<"suspiciously large amount of objects detected: "
1938 <<block->m_static_objects.m_stored.size()<<" in "
1939 <<PP(block->getPos())
1940 <<"; removing all of them."<<std::endl;
1941 // Clear stored list
1942 block->m_static_objects.m_stored.clear();
1943 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1944 MOD_REASON_TOO_MANY_OBJECTS);
1948 // Activate stored objects
1949 std::vector<StaticObject> new_stored;
1950 for (std::vector<StaticObject>::iterator
1951 i = block->m_static_objects.m_stored.begin();
1952 i != block->m_static_objects.m_stored.end(); ++i) {
1953 StaticObject &s_obj = *i;
1955 // Create an active object from the data
1956 ServerActiveObject *obj = ServerActiveObject::create
1957 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1958 // If couldn't create object, store static data back.
1960 errorstream<<"ServerEnvironment::activateObjects(): "
1961 <<"failed to create active object from static object "
1962 <<"in block "<<PP(s_obj.pos/BS)
1963 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1964 print_hexdump(verbosestream, s_obj.data);
1966 new_stored.push_back(s_obj);
1969 verbosestream<<"ServerEnvironment::activateObjects(): "
1970 <<"activated static object pos="<<PP(s_obj.pos/BS)
1971 <<" type="<<(int)s_obj.type<<std::endl;
1972 // This will also add the object to the active static list
1973 addActiveObjectRaw(obj, false, dtime_s);
1975 // Clear stored list
1976 block->m_static_objects.m_stored.clear();
1977 // Add leftover failed stuff to stored list
1978 for(std::vector<StaticObject>::iterator
1979 i = new_stored.begin();
1980 i != new_stored.end(); ++i) {
1981 StaticObject &s_obj = *i;
1982 block->m_static_objects.m_stored.push_back(s_obj);
1985 // Turn the active counterparts of activated objects not pending for
1987 for(std::map<u16, StaticObject>::iterator
1988 i = block->m_static_objects.m_active.begin();
1989 i != block->m_static_objects.m_active.end(); ++i)
1992 ServerActiveObject *object = getActiveObject(id);
1994 object->m_pending_deactivation = false;
1998 Note: Block hasn't really been modified here.
1999 The objects have just been activated and moved from the stored
2000 static list to the active static list.
2001 As such, the block is essentially the same.
2002 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2003 Otherwise there would be a huge amount of unnecessary I/O.
2008 Convert objects that are not standing inside active blocks to static.
2010 If m_known_by_count != 0, active object is not deleted, but static
2011 data is still updated.
2013 If force_delete is set, active object is deleted nevertheless. It
2014 shall only be set so in the destructor of the environment.
2016 If block wasn't generated (not in memory or on disk),
2018 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2020 std::vector<u16> objects_to_remove;
2021 for(ActiveObjectMap::iterator i = m_active_objects.begin();
2022 i != m_active_objects.end(); ++i) {
2023 ServerActiveObject* obj = i->second;
2026 // Do not deactivate if static data creation not allowed
2027 if(!force_delete && !obj->isStaticAllowed())
2030 // If pending deactivation, let removeRemovedObjects() do it
2031 if(!force_delete && obj->m_pending_deactivation)
2035 v3f objectpos = obj->getBasePosition();
2037 // The block in which the object resides in
2038 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2040 // If object's static data is stored in a deactivated block and object
2041 // is actually located in an active block, re-save to the block in
2042 // which the object is actually located in.
2044 obj->m_static_exists &&
2045 !m_active_blocks.contains(obj->m_static_block) &&
2046 m_active_blocks.contains(blockpos_o))
2048 v3s16 old_static_block = obj->m_static_block;
2050 // Save to block where object is located
2051 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2053 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2054 <<"Could not save object id="<<id
2055 <<" to it's current block "<<PP(blockpos_o)
2059 std::string staticdata_new = obj->getStaticData();
2060 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2061 block->m_static_objects.insert(id, s_obj);
2062 obj->m_static_block = blockpos_o;
2063 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2064 MOD_REASON_STATIC_DATA_ADDED);
2066 // Delete from block where object was located
2067 block = m_map->emergeBlock(old_static_block, false);
2069 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2070 <<"Could not delete object id="<<id
2071 <<" from it's previous block "<<PP(old_static_block)
2075 block->m_static_objects.remove(id);
2076 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2077 MOD_REASON_STATIC_DATA_REMOVED);
2081 // If block is active, don't remove
2082 if(!force_delete && m_active_blocks.contains(blockpos_o))
2085 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2086 <<"deactivating object id="<<id<<" on inactive block "
2087 <<PP(blockpos_o)<<std::endl;
2089 // If known by some client, don't immediately delete.
2090 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2093 Update the static data
2096 if(obj->isStaticAllowed())
2098 // Create new static object
2099 std::string staticdata_new = obj->getStaticData();
2100 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2102 bool stays_in_same_block = false;
2103 bool data_changed = true;
2105 if (obj->m_static_exists) {
2106 if (obj->m_static_block == blockpos_o)
2107 stays_in_same_block = true;
2109 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2112 std::map<u16, StaticObject>::iterator n =
2113 block->m_static_objects.m_active.find(id);
2114 if (n != block->m_static_objects.m_active.end()) {
2115 StaticObject static_old = n->second;
2117 float save_movem = obj->getMinimumSavedMovement();
2119 if (static_old.data == staticdata_new &&
2120 (static_old.pos - objectpos).getLength() < save_movem)
2121 data_changed = false;
2123 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2124 <<"id="<<id<<" m_static_exists=true but "
2125 <<"static data doesn't actually exist in "
2126 <<PP(obj->m_static_block)<<std::endl;
2131 bool shall_be_written = (!stays_in_same_block || data_changed);
2133 // Delete old static object
2134 if(obj->m_static_exists)
2136 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2139 block->m_static_objects.remove(id);
2140 obj->m_static_exists = false;
2141 // Only mark block as modified if data changed considerably
2142 if(shall_be_written)
2143 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2144 MOD_REASON_STATIC_DATA_CHANGED);
2148 // Add to the block where the object is located in
2149 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2150 // Get or generate the block
2151 MapBlock *block = NULL;
2153 block = m_map->emergeBlock(blockpos);
2154 } catch(InvalidPositionException &e){
2155 // Handled via NULL pointer
2156 // NOTE: emergeBlock's failure is usually determined by it
2157 // actually returning NULL
2162 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2163 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2164 <<" statically but block "<<PP(blockpos)
2165 <<" already contains "
2166 <<block->m_static_objects.m_stored.size()
2168 <<" Forcing delete."<<std::endl;
2169 force_delete = true;
2171 // If static counterpart already exists in target block,
2173 // This shouldn't happen because the object is removed from
2174 // the previous block before this according to
2175 // obj->m_static_block, but happens rarely for some unknown
2176 // reason. Unsuccessful attempts have been made to find
2178 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2179 warningstream<<"ServerEnv: Performing hack #83274"
2181 block->m_static_objects.remove(id);
2183 // Store static data
2184 u16 store_id = pending_delete ? id : 0;
2185 block->m_static_objects.insert(store_id, s_obj);
2187 // Only mark block as modified if data changed considerably
2188 if(shall_be_written)
2189 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2190 MOD_REASON_STATIC_DATA_CHANGED);
2192 obj->m_static_exists = true;
2193 obj->m_static_block = block->getPos();
2198 v3s16 p = floatToInt(objectpos, BS);
2199 errorstream<<"ServerEnv: Could not find or generate "
2200 <<"a block for storing id="<<obj->getId()
2201 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2208 If known by some client, set pending deactivation.
2209 Otherwise delete it immediately.
2212 if(pending_delete && !force_delete)
2214 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2215 <<"object id="<<id<<" is known by clients"
2216 <<"; not deleting yet"<<std::endl;
2218 obj->m_pending_deactivation = true;
2222 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2223 <<"object id="<<id<<" is not known by clients"
2224 <<"; deleting"<<std::endl;
2226 // Tell the object about removal
2227 obj->removingFromEnvironment();
2228 // Deregister in scripting api
2229 m_script->removeObjectReference(obj);
2231 // Delete active object
2232 if(obj->environmentDeletes())
2234 // Id to be removed from m_active_objects
2235 objects_to_remove.push_back(id);
2238 // Remove references from m_active_objects
2239 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2240 i != objects_to_remove.end(); ++i) {
2241 m_active_objects.erase(*i);
2247 #include "clientsimpleobject.h"
2253 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2254 ITextureSource *texturesource, IGameDef *gamedef,
2255 IrrlichtDevice *irr):
2258 m_texturesource(texturesource),
2263 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2266 ClientEnvironment::~ClientEnvironment()
2268 // delete active objects
2269 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2270 i != m_active_objects.end(); ++i) {
2274 for(std::vector<ClientSimpleObject*>::iterator
2275 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2283 Map & ClientEnvironment::getMap()
2288 ClientMap & ClientEnvironment::getClientMap()
2293 LocalPlayer *ClientEnvironment::getPlayer(const u16 peer_id)
2295 return dynamic_cast<LocalPlayer *>(Environment::getPlayer(peer_id));
2298 LocalPlayer *ClientEnvironment::getPlayer(const char* name)
2300 return dynamic_cast<LocalPlayer *>(Environment::getPlayer(name));
2303 void ClientEnvironment::addPlayer(Player *player)
2305 DSTACK(FUNCTION_NAME);
2307 It is a failure if player is local and there already is a local
2310 FATAL_ERROR_IF(player->isLocal() && getLocalPlayer() != NULL,
2311 "Player is local but there is already a local player");
2313 Environment::addPlayer(player);
2316 LocalPlayer *ClientEnvironment::getLocalPlayer()
2318 for(std::vector<Player*>::iterator i = m_players.begin();
2319 i != m_players.end(); ++i) {
2320 Player *player = *i;
2321 if (player->isLocal())
2322 return (LocalPlayer*)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);
2562 Stuff that can be done in an arbitarily large dtime
2564 for (std::vector<Player*>::iterator i = m_players.begin();
2565 i != m_players.end(); ++i) {
2566 LocalPlayer *player = dynamic_cast<LocalPlayer *>(*i);
2570 Handle non-local players
2572 if (!player->isLocal()) {
2574 player->move(dtime, this, 100*BS);
2579 // Update lighting on local player (used for wield item)
2580 u32 day_night_ratio = getDayNightRatio();
2584 // On InvalidPositionException, use this as default
2585 // (day: LIGHT_SUN, night: 0)
2586 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2588 v3s16 p = lplayer->getLightPosition();
2589 node_at_lplayer = m_map->getNodeNoEx(p);
2591 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2592 u8 day = light & 0xff;
2593 u8 night = (light >> 8) & 0xff;
2594 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2598 Step active objects and update lighting of them
2601 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2602 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2603 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2604 i != m_active_objects.end(); ++i) {
2605 ClientActiveObject* obj = i->second;
2607 obj->step(dtime, this);
2616 v3s16 p = obj->getLightPosition();
2617 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2619 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2621 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2623 obj->updateLight(light);
2628 Step and handle simple objects
2630 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2631 for(std::vector<ClientSimpleObject*>::iterator
2632 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2633 std::vector<ClientSimpleObject*>::iterator cur = i;
2634 ClientSimpleObject *simple = *cur;
2636 simple->step(dtime);
2637 if(simple->m_to_be_removed) {
2639 i = m_simple_objects.erase(cur);
2647 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2649 m_simple_objects.push_back(simple);
2652 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2654 ClientActiveObject *obj = getActiveObject(id);
2655 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2656 return (GenericCAO*) obj;
2661 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2663 UNORDERED_MAP<u16, ClientActiveObject*>::iterator n = m_active_objects.find(id);
2664 if (n == m_active_objects.end())
2669 bool isFreeClientActiveObjectId(const u16 id,
2670 UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2675 return objects.find(id) == objects.end();
2678 u16 getFreeClientActiveObjectId(UNORDERED_MAP<u16, ClientActiveObject*> &objects)
2680 //try to reuse id's as late as possible
2681 static u16 last_used_id = 0;
2682 u16 startid = last_used_id;
2685 if (isFreeClientActiveObjectId(last_used_id, objects))
2686 return last_used_id;
2688 if (last_used_id == startid)
2693 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2695 assert(object); // Pre-condition
2696 if(object->getId() == 0)
2698 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2701 infostream<<"ClientEnvironment::addActiveObject(): "
2702 <<"no free ids available"<<std::endl;
2706 object->setId(new_id);
2708 if(!isFreeClientActiveObjectId(object->getId(), m_active_objects)) {
2709 infostream<<"ClientEnvironment::addActiveObject(): "
2710 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2714 infostream<<"ClientEnvironment::addActiveObject(): "
2715 <<"added (id="<<object->getId()<<")"<<std::endl;
2716 m_active_objects[object->getId()] = object;
2717 object->addToScene(m_smgr, m_texturesource, m_irr);
2718 { // Update lighting immediately
2723 v3s16 p = object->getLightPosition();
2724 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2726 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2728 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2730 object->updateLight(light);
2732 return object->getId();
2735 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2736 const std::string &init_data)
2738 ClientActiveObject* obj =
2739 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2742 infostream<<"ClientEnvironment::addActiveObject(): "
2743 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2752 obj->initialize(init_data);
2754 catch(SerializationError &e)
2756 errorstream<<"ClientEnvironment::addActiveObject():"
2757 <<" id="<<id<<" type="<<type
2758 <<": SerializationError in initialize(): "
2760 <<": init_data="<<serializeJsonString(init_data)
2764 addActiveObject(obj);
2767 void ClientEnvironment::removeActiveObject(u16 id)
2769 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2770 <<"id="<<id<<std::endl;
2771 ClientActiveObject* obj = getActiveObject(id);
2773 infostream<<"ClientEnvironment::removeActiveObject(): "
2774 <<"id="<<id<<" not found"<<std::endl;
2777 obj->removeFromScene(true);
2779 m_active_objects.erase(id);
2782 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2784 ClientActiveObject *obj = getActiveObject(id);
2786 infostream << "ClientEnvironment::processActiveObjectMessage():"
2787 << " got message for id=" << id << ", which doesn't exist."
2793 obj->processMessage(data);
2794 } catch (SerializationError &e) {
2795 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2796 << " id=" << id << " type=" << obj->getType()
2797 << " SerializationError in processMessage(): " << e.what()
2803 Callbacks for activeobjects
2806 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2808 LocalPlayer *lplayer = getLocalPlayer();
2812 if (lplayer->hp > damage)
2813 lplayer->hp -= damage;
2818 ClientEnvEvent event;
2819 event.type = CEE_PLAYER_DAMAGE;
2820 event.player_damage.amount = damage;
2821 event.player_damage.send_to_server = handle_hp;
2822 m_client_event_queue.push(event);
2825 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2827 ClientEnvEvent event;
2828 event.type = CEE_PLAYER_BREATH;
2829 event.player_breath.amount = breath;
2830 m_client_event_queue.push(event);
2834 Client likes to call these
2837 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2838 std::vector<DistanceSortedActiveObject> &dest)
2840 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2841 i != m_active_objects.end(); ++i) {
2842 ClientActiveObject* obj = i->second;
2844 f32 d = (obj->getPosition() - origin).getLength();
2849 DistanceSortedActiveObject dso(obj, d);
2851 dest.push_back(dso);
2855 ClientEnvEvent ClientEnvironment::getClientEvent()
2857 ClientEnvEvent event;
2858 if(m_client_event_queue.empty())
2859 event.type = CEE_NONE;
2861 event = m_client_event_queue.front();
2862 m_client_event_queue.pop();
2867 #endif // #ifndef SERVER