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 Environment::Environment():
54 m_time_of_day_speed(0),
56 m_time_of_day_f(9000./24000),
57 m_time_conversion_skew(0.0f),
58 m_enable_day_night_ratio_override(false),
59 m_day_night_ratio_override(0.0f)
61 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
62 m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval");
63 m_cache_abm_interval = g_settings->getFloat("abm_interval");
64 m_cache_nodetimer_interval = g_settings->getFloat("nodetimer_interval");
67 Environment::~Environment()
70 for(std::vector<Player*>::iterator i = m_players.begin();
71 i != m_players.end(); ++i) {
76 void Environment::addPlayer(Player *player)
78 DSTACK(FUNCTION_NAME);
80 Check that peer_ids are unique.
81 Also check that names are unique.
82 Exception: there can be multiple players with peer_id=0
84 // If peer id is non-zero, it has to be unique.
85 if(player->peer_id != 0)
86 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
87 // Name has to be unique.
88 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
90 m_players.push_back(player);
93 void Environment::removePlayer(Player* player)
95 for (std::vector<Player*>::iterator it = m_players.begin();
96 it != m_players.end(); ++it) {
97 if ((*it) == player) {
105 Player * Environment::getPlayer(u16 peer_id)
107 for(std::vector<Player*>::iterator i = m_players.begin();
108 i != m_players.end(); ++i) {
110 if(player->peer_id == peer_id)
116 Player * Environment::getPlayer(const char *name)
118 for(std::vector<Player*>::iterator i = m_players.begin();
119 i != m_players.end(); ++i) {
121 if(strcmp(player->getName(), name) == 0)
127 Player * Environment::getRandomConnectedPlayer()
129 std::vector<Player*> connected_players = getPlayers(true);
130 u32 chosen_one = myrand() % connected_players.size();
132 for(std::vector<Player*>::iterator
133 i = connected_players.begin();
134 i != connected_players.end(); ++i) {
135 if(j == chosen_one) {
144 Player * Environment::getNearestConnectedPlayer(v3f pos)
146 std::vector<Player*> connected_players = getPlayers(true);
148 Player *nearest_player = NULL;
149 for(std::vector<Player*>::iterator
150 i = connected_players.begin();
151 i != connected_players.end(); ++i) {
153 f32 d = player->getPosition().getDistanceFrom(pos);
154 if(d < nearest_d || nearest_player == NULL) {
156 nearest_player = player;
159 return nearest_player;
162 std::vector<Player*> Environment::getPlayers()
167 std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
169 std::vector<Player*> newlist;
170 for(std::vector<Player*>::iterator
171 i = m_players.begin();
172 i != m_players.end(); ++i) {
175 if(ignore_disconnected) {
176 // Ignore disconnected players
177 if(player->peer_id == 0)
181 newlist.push_back(player);
186 u32 Environment::getDayNightRatio()
188 MutexAutoLock lock(this->m_time_lock);
189 if (m_enable_day_night_ratio_override)
190 return m_day_night_ratio_override;
191 return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
194 void Environment::setTimeOfDaySpeed(float speed)
196 m_time_of_day_speed = speed;
199 float Environment::getTimeOfDaySpeed()
201 return m_time_of_day_speed;
204 void Environment::setDayNightRatioOverride(bool enable, u32 value)
206 MutexAutoLock lock(this->m_time_lock);
207 m_enable_day_night_ratio_override = enable;
208 m_day_night_ratio_override = value;
211 void Environment::setTimeOfDay(u32 time)
213 MutexAutoLock lock(this->m_time_lock);
214 if (m_time_of_day > time)
216 m_time_of_day = time;
217 m_time_of_day_f = (float)time / 24000.0;
220 u32 Environment::getTimeOfDay()
222 MutexAutoLock lock(this->m_time_lock);
223 return m_time_of_day;
226 float Environment::getTimeOfDayF()
228 MutexAutoLock lock(this->m_time_lock);
229 return m_time_of_day_f;
232 void Environment::stepTimeOfDay(float dtime)
234 MutexAutoLock lock(this->m_time_lock);
236 // Cached in order to prevent the two reads we do to give
237 // different results (can be written by code not under the lock)
238 f32 cached_time_of_day_speed = m_time_of_day_speed;
240 f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
241 m_time_conversion_skew += dtime;
242 u32 units = (u32)(m_time_conversion_skew * speed);
246 if (m_time_of_day + units >= 24000) {
250 m_time_of_day = (m_time_of_day + units) % 24000;
252 m_time_of_day_f = (float)m_time_of_day / 24000.0;
255 m_time_conversion_skew -= (f32)units / speed;
258 m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
259 if (m_time_of_day_f > 1.0)
260 m_time_of_day_f -= 1.0;
261 if (m_time_of_day_f < 0.0)
262 m_time_of_day_f += 1.0;
266 u32 Environment::getDayCount()
268 // Atomic<u32> counter
277 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
281 // Initialize timer to random value to spread processing
282 float itv = abm->getTriggerInterval();
283 itv = MYMAX(0.001, itv); // No less than 1ms
284 int minval = MYMAX(-0.51*itv, -60); // Clamp to
285 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
286 timer = myrand_range(minval, maxval);
293 void LBMContentMapping::deleteContents()
295 for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
296 it != lbm_list.end(); ++it) {
301 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
303 // Add the lbm_def to the LBMContentMapping.
304 // Unknown names get added to the global NameIdMapping.
305 INodeDefManager *nodedef = gamedef->ndef();
307 lbm_list.push_back(lbm_def);
309 for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
310 it != lbm_def->trigger_contents.end(); ++it) {
311 std::set<content_t> c_ids;
312 bool found = nodedef->getIds(*it, c_ids);
314 content_t c_id = gamedef->allocateUnknownNodeId(*it);
315 if (c_id == CONTENT_IGNORE) {
316 // Seems it can't be allocated.
317 warningstream << "Could not internalize node name \"" << *it
318 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
324 for (std::set<content_t>::const_iterator iit =
325 c_ids.begin(); iit != c_ids.end(); ++iit) {
326 content_t c_id = *iit;
327 map[c_id].push_back(lbm_def);
332 const std::vector<LoadingBlockModifierDef *> *
333 LBMContentMapping::lookup(content_t c) const
335 container_map::const_iterator it = map.find(c);
338 // This first dereferences the iterator, returning
339 // a std::vector<LoadingBlockModifierDef *>
340 // reference, then we convert it to a pointer.
341 return &(it->second);
344 LBMManager::~LBMManager()
346 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
347 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
350 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
351 it != m_lbm_lookup.end(); ++it) {
352 (it->second).deleteContents();
356 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
358 // Precondition, in query mode the map isn't used anymore
359 FATAL_ERROR_IF(m_query_mode == true,
360 "attempted to modify LBMManager in query mode");
362 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
363 throw ModError("Error adding LBM \"" + lbm_def->name +
364 "\": Does not follow naming conventions: "
365 "Only chararacters [a-z0-9_:] are allowed.");
368 m_lbm_defs[lbm_def->name] = lbm_def;
371 void LBMManager::loadIntroductionTimes(const std::string ×,
372 IGameDef *gamedef, u32 now)
377 // Storing it in a map first instead of
378 // handling the stuff directly in the loop
379 // removes all duplicate entries.
380 // TODO make this std::unordered_map
381 std::map<std::string, u32> introduction_times;
384 The introduction times string consists of name~time entries,
385 with each entry terminated by a semicolon. The time is decimal.
390 while ((idx_new = times.find(";", idx)) != std::string::npos) {
391 std::string entry = times.substr(idx, idx_new - idx);
392 std::vector<std::string> components = str_split(entry, '~');
393 if (components.size() != 2)
394 throw SerializationError("Introduction times entry \""
395 + entry + "\" requires exactly one '~'!");
396 const std::string &name = components[0];
397 u32 time = from_string<u32>(components[1]);
398 introduction_times[name] = time;
402 // Put stuff from introduction_times into m_lbm_lookup
403 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
404 it != introduction_times.end(); ++it) {
405 const std::string &name = it->first;
406 u32 time = it->second;
408 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
409 m_lbm_defs.find(name);
410 if (def_it == m_lbm_defs.end()) {
411 // This seems to be an LBM entry for
412 // an LBM we haven't loaded. Discard it.
415 LoadingBlockModifierDef *lbm_def = def_it->second;
416 if (lbm_def->run_at_every_load) {
417 // This seems to be an LBM entry for
418 // an LBM that runs at every load.
419 // Don't add it just yet.
423 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
425 // Erase the entry so that we know later
426 // what elements didn't get put into m_lbm_lookup
427 m_lbm_defs.erase(name);
430 // Now also add the elements from m_lbm_defs to m_lbm_lookup
431 // that weren't added in the previous step.
432 // They are introduced first time to this world,
433 // or are run at every load (introducement time hardcoded to U32_MAX).
435 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
436 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
438 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
439 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
440 if (it->second->run_at_every_load) {
441 lbms_running_always.addLBM(it->second, gamedef);
443 lbms_we_introduce_now.addLBM(it->second, gamedef);
447 // Clear the list, so that we don't delete remaining elements
448 // twice in the destructor
452 std::string LBMManager::createIntroductionTimesString()
454 // Precondition, we must be in query mode
455 FATAL_ERROR_IF(m_query_mode == false,
456 "attempted to query on non fully set up LBMManager");
458 std::ostringstream oss;
459 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
460 it != m_lbm_lookup.end(); ++it) {
461 u32 time = it->first;
462 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
463 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
464 iit != lbm_list.end(); ++iit) {
465 // Don't add if the LBM runs at every load,
466 // then introducement time is hardcoded
467 // and doesn't need to be stored
468 if ((*iit)->run_at_every_load)
470 oss << (*iit)->name << "~" << time << ";";
476 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
478 // Precondition, we need m_lbm_lookup to be initialized
479 FATAL_ERROR_IF(m_query_mode == false,
480 "attempted to query on non fully set up LBMManager");
481 v3s16 pos_of_block = block->getPosRelative();
485 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
486 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
487 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
488 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
490 n = block->getNodeNoEx(pos);
492 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
493 iit != m_lbm_lookup.end(); ++iit) {
494 const std::vector<LoadingBlockModifierDef *> *lbm_list =
495 iit->second.lookup(c);
498 for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
499 lbm_list->begin(); iit != lbm_list->end(); ++iit) {
500 (*iit)->trigger(env, pos + pos_of_block, n);
510 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
513 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
514 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
515 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
522 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
524 std::set<v3s16> &blocks_removed,
525 std::set<v3s16> &blocks_added)
530 std::set<v3s16> newlist = m_forceloaded_list;
531 for(std::vector<v3s16>::iterator i = active_positions.begin();
532 i != active_positions.end(); ++i)
534 fillRadiusBlock(*i, radius, newlist);
538 Find out which blocks on the old list are not on the new list
540 // Go through old list
541 for(std::set<v3s16>::iterator i = m_list.begin();
542 i != m_list.end(); ++i)
545 // If not on new list, it's been removed
546 if(newlist.find(p) == newlist.end())
547 blocks_removed.insert(p);
551 Find out which blocks on the new list are not on the old list
553 // Go through new list
554 for(std::set<v3s16>::iterator i = newlist.begin();
555 i != newlist.end(); ++i)
558 // If not on old list, it's been added
559 if(m_list.find(p) == m_list.end())
560 blocks_added.insert(p);
567 for(std::set<v3s16>::iterator i = newlist.begin();
568 i != newlist.end(); ++i)
579 ServerEnvironment::ServerEnvironment(ServerMap *map,
580 GameScripting *scriptIface, IGameDef *gamedef,
581 const std::string &path_world) :
583 m_script(scriptIface),
585 m_path_world(path_world),
586 m_send_recommended_timer(0),
587 m_active_block_interval_overload_skip(0),
589 m_game_time_fraction_counter(0),
590 m_last_clear_objects_time(0),
591 m_recommended_send_interval(0.1),
592 m_max_lag_estimate(0.1)
596 ServerEnvironment::~ServerEnvironment()
598 // Clear active block list.
599 // This makes the next one delete all active objects.
600 m_active_blocks.clear();
602 // Convert all objects to static and delete the active objects
603 deactivateFarObjects(true);
608 // Delete ActiveBlockModifiers
609 for(std::vector<ABMWithState>::iterator
610 i = m_abms.begin(); i != m_abms.end(); ++i){
615 Map & ServerEnvironment::getMap()
620 ServerMap & ServerEnvironment::getServerMap()
625 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
627 float distance = pos1.getDistanceFrom(pos2);
629 //calculate normalized direction vector
630 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
631 (pos2.Y - pos1.Y)/distance,
632 (pos2.Z - pos1.Z)/distance);
634 //find out if there's a node on path between pos1 and pos2
635 for (float i = 1; i < distance; i += stepsize) {
636 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
637 normalized_vector.Y * i,
638 normalized_vector.Z * i) +pos1,BS);
640 MapNode n = getMap().getNodeNoEx(pos);
642 if(n.param0 != CONTENT_AIR) {
652 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
653 const std::string &str_reason, bool reconnect)
655 for (std::vector<Player*>::iterator it = m_players.begin();
656 it != m_players.end();
658 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
659 (*it)->protocol_version, (AccessDeniedCode)reason,
660 str_reason, reconnect);
664 void ServerEnvironment::saveLoadedPlayers()
666 std::string players_path = m_path_world + DIR_DELIM "players";
667 fs::CreateDir(players_path);
669 for (std::vector<Player*>::iterator it = m_players.begin();
670 it != m_players.end();
672 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
673 if (player->checkModified()) {
674 player->save(players_path);
679 void ServerEnvironment::savePlayer(RemotePlayer *player)
681 std::string players_path = m_path_world + DIR_DELIM "players";
682 fs::CreateDir(players_path);
684 player->save(players_path);
687 Player *ServerEnvironment::loadPlayer(const std::string &playername)
689 bool newplayer = false;
691 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
692 std::string path = players_path + playername;
694 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
696 player = new RemotePlayer(m_gamedef, "");
700 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
701 //// Open file and deserialize
702 std::ifstream is(path.c_str(), std::ios_base::binary);
705 player->deSerialize(is, path);
708 if (player->getName() == playername) {
713 path = players_path + playername + itos(i);
717 infostream << "Player file for player " << playername
718 << " not found" << std::endl;
726 player->setModified(false);
730 void ServerEnvironment::saveMeta()
732 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
734 // Open file and serialize
735 std::ostringstream ss(std::ios_base::binary);
738 args.setU64("game_time", m_game_time);
739 args.setU64("time_of_day", getTimeOfDay());
740 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
741 args.setU64("lbm_introduction_times_version", 1);
742 args.set("lbm_introduction_times",
743 m_lbm_mgr.createIntroductionTimesString());
744 args.setU64("day_count", m_day_count);
748 if(!fs::safeWriteToFile(path, ss.str()))
750 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
752 throw SerializationError("Couldn't save env meta");
756 void ServerEnvironment::loadMeta()
758 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
760 // Open file and deserialize
761 std::ifstream is(path.c_str(), std::ios_base::binary);
763 infostream << "ServerEnvironment::loadMeta(): Failed to open "
764 << path << std::endl;
765 throw SerializationError("Couldn't load env meta");
770 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
771 throw SerializationError("ServerEnvironment::loadMeta(): "
772 "EnvArgsEnd not found!");
776 m_game_time = args.getU64("game_time");
777 } catch (SettingNotFoundException &e) {
778 // Getting this is crucial, otherwise timestamps are useless
779 throw SerializationError("Couldn't load env meta game_time");
782 setTimeOfDay(args.exists("time_of_day") ?
783 // set day to morning by default
784 args.getU64("time_of_day") : 9000);
786 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
787 // If missing, do as if clearObjects was never called
788 args.getU64("last_clear_objects_time") : 0;
790 std::string lbm_introduction_times = "";
792 u64 ver = args.getU64("lbm_introduction_times_version");
794 lbm_introduction_times = args.get("lbm_introduction_times");
796 infostream << "ServerEnvironment::loadMeta(): Non-supported"
797 << " introduction time version " << ver << std::endl;
799 } catch (SettingNotFoundException &e) {
800 // No problem, this is expected. Just continue with an empty string
802 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
804 m_day_count = args.exists("day_count") ?
805 args.getU64("day_count") : 0;
808 void ServerEnvironment::loadDefaultMeta()
810 m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
815 ActiveBlockModifier *abm;
817 std::set<content_t> required_neighbors;
823 ServerEnvironment *m_env;
824 std::map<content_t, std::vector<ActiveABM> > m_aabms;
826 ABMHandler(std::vector<ABMWithState> &abms,
827 float dtime_s, ServerEnvironment *env,
833 INodeDefManager *ndef = env->getGameDef()->ndef();
834 for(std::vector<ABMWithState>::iterator
835 i = abms.begin(); i != abms.end(); ++i) {
836 ActiveBlockModifier *abm = i->abm;
837 float trigger_interval = abm->getTriggerInterval();
838 if(trigger_interval < 0.001)
839 trigger_interval = 0.001;
840 float actual_interval = dtime_s;
843 if(i->timer < trigger_interval)
845 i->timer -= trigger_interval;
846 actual_interval = trigger_interval;
848 float chance = abm->getTriggerChance();
853 if(abm->getSimpleCatchUp()) {
854 float intervals = actual_interval / trigger_interval;
857 aabm.chance = chance / intervals;
861 aabm.chance = chance;
864 std::set<std::string> required_neighbors_s
865 = abm->getRequiredNeighbors();
866 for(std::set<std::string>::iterator
867 i = required_neighbors_s.begin();
868 i != required_neighbors_s.end(); ++i)
870 ndef->getIds(*i, aabm.required_neighbors);
873 std::set<std::string> contents_s = abm->getTriggerContents();
874 for(std::set<std::string>::iterator
875 i = contents_s.begin(); i != contents_s.end(); ++i)
877 std::set<content_t> ids;
878 ndef->getIds(*i, ids);
879 for(std::set<content_t>::const_iterator k = ids.begin();
883 std::map<content_t, std::vector<ActiveABM> >::iterator j;
885 if(j == m_aabms.end()){
886 std::vector<ActiveABM> aabmlist;
887 m_aabms[c] = aabmlist;
890 j->second.push_back(aabm);
895 // Find out how many objects the given block and its neighbours contain.
896 // Returns the number of objects in the block, and also in 'wider' the
897 // number of objects in the block and all its neighbours. The latter
898 // may an estimate if any neighbours are unloaded.
899 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
902 u32 wider_unknown_count = 0;
903 for(s16 x=-1; x<=1; x++)
904 for(s16 y=-1; y<=1; y++)
905 for(s16 z=-1; z<=1; z++)
907 MapBlock *block2 = map->getBlockNoCreateNoEx(
908 block->getPos() + v3s16(x,y,z));
910 wider_unknown_count++;
913 wider += block2->m_static_objects.m_active.size()
914 + block2->m_static_objects.m_stored.size();
917 u32 active_object_count = block->m_static_objects.m_active.size();
918 u32 wider_known_count = 3*3*3 - wider_unknown_count;
919 wider += wider_unknown_count * wider / wider_known_count;
920 return active_object_count;
923 void apply(MapBlock *block)
928 ServerMap *map = &m_env->getServerMap();
930 u32 active_object_count_wider;
931 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
932 m_env->m_added_objects = 0;
935 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
936 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
937 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
939 MapNode n = block->getNodeNoEx(p0);
940 content_t c = n.getContent();
941 v3s16 p = p0 + block->getPosRelative();
943 std::map<content_t, std::vector<ActiveABM> >::iterator j;
945 if(j == m_aabms.end())
948 for(std::vector<ActiveABM>::iterator
949 i = j->second.begin(); i != j->second.end(); ++i) {
950 if(myrand() % i->chance != 0)
954 if(!i->required_neighbors.empty())
957 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
958 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
959 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
963 MapNode n = map->getNodeNoEx(p1);
964 content_t c = n.getContent();
965 std::set<content_t>::const_iterator k;
966 k = i->required_neighbors.find(c);
967 if(k != i->required_neighbors.end()){
971 // No required neighbor found
976 // Call all the trigger variations
977 i->abm->trigger(m_env, p, n);
978 i->abm->trigger(m_env, p, n,
979 active_object_count, active_object_count_wider);
981 // Count surrounding objects again if the abms added any
982 if(m_env->m_added_objects > 0) {
983 active_object_count = countObjects(block, map, active_object_count_wider);
984 m_env->m_added_objects = 0;
991 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
993 // Reset usage timer immediately, otherwise a block that becomes active
994 // again at around the same time as it would normally be unloaded will
995 // get unloaded incorrectly. (I think this still leaves a small possibility
996 // of a race condition between this and server::AsyncRunStep, which only
997 // some kind of synchronisation will fix, but it at least reduces the window
998 // of opportunity for it to break from seconds to nanoseconds)
999 block->resetUsageTimer();
1001 // Get time difference
1003 u32 stamp = block->getTimestamp();
1004 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
1005 dtime_s = m_game_time - stamp;
1006 dtime_s += additional_dtime;
1008 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
1009 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
1011 // Remove stored static objects if clearObjects was called since block's timestamp
1012 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
1013 block->m_static_objects.m_stored.clear();
1014 // do not set changed flag to avoid unnecessary mapblock writes
1017 // Set current time as timestamp
1018 block->setTimestampNoChangedFlag(m_game_time);
1020 /*infostream<<"ServerEnvironment::activateBlock(): block is "
1021 <<dtime_s<<" seconds old."<<std::endl;*/
1023 // Activate stored objects
1024 activateObjects(block, dtime_s);
1026 /* Handle LoadingBlockModifiers */
1027 m_lbm_mgr.applyLBMs(this, block, stamp);
1030 std::map<v3s16, NodeTimer> elapsed_timers =
1031 block->m_node_timers.step((float)dtime_s);
1032 if(!elapsed_timers.empty()){
1034 for(std::map<v3s16, NodeTimer>::iterator
1035 i = elapsed_timers.begin();
1036 i != elapsed_timers.end(); ++i){
1037 n = block->getNodeNoEx(i->first);
1038 v3s16 p = i->first + block->getPosRelative();
1039 if(m_script->node_on_timer(p,n,i->second.elapsed))
1040 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1044 /* Handle ActiveBlockModifiers */
1045 ABMHandler abmhandler(m_abms, dtime_s, this, false);
1046 abmhandler.apply(block);
1049 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
1051 m_abms.push_back(ABMWithState(abm));
1054 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1056 m_lbm_mgr.addLBMDef(lbm);
1059 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1061 INodeDefManager *ndef = m_gamedef->ndef();
1062 MapNode n_old = m_map->getNodeNoEx(p);
1065 if (ndef->get(n_old).has_on_destruct)
1066 m_script->node_on_destruct(p, n_old);
1069 if (!m_map->addNodeWithEvent(p, n))
1072 // Update active VoxelManipulator if a mapgen thread
1073 m_map->updateVManip(p);
1075 // Call post-destructor
1076 if (ndef->get(n_old).has_after_destruct)
1077 m_script->node_after_destruct(p, n_old);
1080 if (ndef->get(n).has_on_construct)
1081 m_script->node_on_construct(p, n);
1086 bool ServerEnvironment::removeNode(v3s16 p)
1088 INodeDefManager *ndef = m_gamedef->ndef();
1089 MapNode n_old = m_map->getNodeNoEx(p);
1092 if (ndef->get(n_old).has_on_destruct)
1093 m_script->node_on_destruct(p, n_old);
1096 // This is slightly optimized compared to addNodeWithEvent(air)
1097 if (!m_map->removeNodeWithEvent(p))
1100 // Update active VoxelManipulator if a mapgen thread
1101 m_map->updateVManip(p);
1103 // Call post-destructor
1104 if (ndef->get(n_old).has_after_destruct)
1105 m_script->node_after_destruct(p, n_old);
1107 // Air doesn't require constructor
1111 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1113 if (!m_map->addNodeWithEvent(p, n, false))
1116 // Update active VoxelManipulator if a mapgen thread
1117 m_map->updateVManip(p);
1122 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1124 for(std::map<u16, ServerActiveObject*>::iterator
1125 i = m_active_objects.begin();
1126 i != m_active_objects.end(); ++i)
1128 ServerActiveObject* obj = i->second;
1130 v3f objectpos = obj->getBasePosition();
1131 if(objectpos.getDistanceFrom(pos) > radius)
1133 objects.push_back(id);
1137 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1139 infostream << "ServerEnvironment::clearObjects(): "
1140 << "Removing all active objects" << std::endl;
1141 std::vector<u16> objects_to_remove;
1142 for (std::map<u16, ServerActiveObject*>::iterator
1143 i = m_active_objects.begin();
1144 i != m_active_objects.end(); ++i) {
1145 ServerActiveObject* obj = i->second;
1146 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1149 // Delete static object if block is loaded
1150 if (obj->m_static_exists) {
1151 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1153 block->m_static_objects.remove(id);
1154 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1155 MOD_REASON_CLEAR_ALL_OBJECTS);
1156 obj->m_static_exists = false;
1159 // If known by some client, don't delete immediately
1160 if (obj->m_known_by_count > 0) {
1161 obj->m_pending_deactivation = true;
1162 obj->m_removed = true;
1166 // Tell the object about removal
1167 obj->removingFromEnvironment();
1168 // Deregister in scripting api
1169 m_script->removeObjectReference(obj);
1171 // Delete active object
1172 if (obj->environmentDeletes())
1174 // Id to be removed from m_active_objects
1175 objects_to_remove.push_back(id);
1178 // Remove references from m_active_objects
1179 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1180 i != objects_to_remove.end(); ++i) {
1181 m_active_objects.erase(*i);
1184 // Get list of loaded blocks
1185 std::vector<v3s16> loaded_blocks;
1186 infostream << "ServerEnvironment::clearObjects(): "
1187 << "Listing all loaded blocks" << std::endl;
1188 m_map->listAllLoadedBlocks(loaded_blocks);
1189 infostream << "ServerEnvironment::clearObjects(): "
1190 << "Done listing all loaded blocks: "
1191 << loaded_blocks.size()<<std::endl;
1193 // Get list of loadable blocks
1194 std::vector<v3s16> loadable_blocks;
1195 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1196 infostream << "ServerEnvironment::clearObjects(): "
1197 << "Listing all loadable blocks" << std::endl;
1198 m_map->listAllLoadableBlocks(loadable_blocks);
1199 infostream << "ServerEnvironment::clearObjects(): "
1200 << "Done listing all loadable blocks: "
1201 << loadable_blocks.size() << std::endl;
1203 loadable_blocks = loaded_blocks;
1206 infostream << "ServerEnvironment::clearObjects(): "
1207 << "Now clearing objects in " << loadable_blocks.size()
1208 << " blocks" << std::endl;
1210 // Grab a reference on each loaded block to avoid unloading it
1211 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1212 i != loaded_blocks.end(); ++i) {
1214 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1215 assert(block != NULL);
1219 // Remove objects in all loadable blocks
1220 u32 unload_interval = U32_MAX;
1221 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1222 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1223 unload_interval = MYMAX(unload_interval, 1);
1225 u32 report_interval = loadable_blocks.size() / 10;
1226 u32 num_blocks_checked = 0;
1227 u32 num_blocks_cleared = 0;
1228 u32 num_objs_cleared = 0;
1229 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1230 i != loadable_blocks.end(); ++i) {
1232 MapBlock *block = m_map->emergeBlock(p, false);
1234 errorstream << "ServerEnvironment::clearObjects(): "
1235 << "Failed to emerge block " << PP(p) << std::endl;
1238 u32 num_stored = block->m_static_objects.m_stored.size();
1239 u32 num_active = block->m_static_objects.m_active.size();
1240 if (num_stored != 0 || num_active != 0) {
1241 block->m_static_objects.m_stored.clear();
1242 block->m_static_objects.m_active.clear();
1243 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1244 MOD_REASON_CLEAR_ALL_OBJECTS);
1245 num_objs_cleared += num_stored + num_active;
1246 num_blocks_cleared++;
1248 num_blocks_checked++;
1250 if (report_interval != 0 &&
1251 num_blocks_checked % report_interval == 0) {
1252 float percent = 100.0 * (float)num_blocks_checked /
1253 loadable_blocks.size();
1254 infostream << "ServerEnvironment::clearObjects(): "
1255 << "Cleared " << num_objs_cleared << " objects"
1256 << " in " << num_blocks_cleared << " blocks ("
1257 << percent << "%)" << std::endl;
1259 if (num_blocks_checked % unload_interval == 0) {
1260 m_map->unloadUnreferencedBlocks();
1263 m_map->unloadUnreferencedBlocks();
1265 // Drop references that were added above
1266 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1267 i != loaded_blocks.end(); ++i) {
1269 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1274 m_last_clear_objects_time = m_game_time;
1276 infostream << "ServerEnvironment::clearObjects(): "
1277 << "Finished: Cleared " << num_objs_cleared << " objects"
1278 << " in " << num_blocks_cleared << " blocks" << std::endl;
1281 void ServerEnvironment::step(float dtime)
1283 DSTACK(FUNCTION_NAME);
1285 //TimeTaker timer("ServerEnv step");
1287 /* Step time of day */
1288 stepTimeOfDay(dtime);
1291 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1292 // really matter that much.
1293 static const float server_step = g_settings->getFloat("dedicated_server_step");
1294 m_recommended_send_interval = server_step;
1300 m_game_time_fraction_counter += dtime;
1301 u32 inc_i = (u32)m_game_time_fraction_counter;
1302 m_game_time += inc_i;
1303 m_game_time_fraction_counter -= (float)inc_i;
1310 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1311 for(std::vector<Player*>::iterator i = m_players.begin();
1312 i != m_players.end(); ++i)
1314 Player *player = *i;
1316 // Ignore disconnected players
1317 if(player->peer_id == 0)
1321 player->move(dtime, this, 100*BS);
1326 Manage active block list
1328 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1329 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1331 Get player block positions
1333 std::vector<v3s16> players_blockpos;
1334 for(std::vector<Player*>::iterator
1335 i = m_players.begin();
1336 i != m_players.end(); ++i) {
1337 Player *player = *i;
1338 // Ignore disconnected players
1339 if(player->peer_id == 0)
1342 v3s16 blockpos = getNodeBlockPos(
1343 floatToInt(player->getPosition(), BS));
1344 players_blockpos.push_back(blockpos);
1348 Update list of active blocks, collecting changes
1350 static const s16 active_block_range = g_settings->getS16("active_block_range");
1351 std::set<v3s16> blocks_removed;
1352 std::set<v3s16> blocks_added;
1353 m_active_blocks.update(players_blockpos, active_block_range,
1354 blocks_removed, blocks_added);
1357 Handle removed blocks
1360 // Convert active objects that are no more in active blocks to static
1361 deactivateFarObjects(false);
1363 for(std::set<v3s16>::iterator
1364 i = blocks_removed.begin();
1365 i != blocks_removed.end(); ++i) {
1368 /* infostream<<"Server: Block " << PP(p)
1369 << " became inactive"<<std::endl; */
1371 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1375 // Set current time as timestamp (and let it set ChangedFlag)
1376 block->setTimestamp(m_game_time);
1383 for(std::set<v3s16>::iterator
1384 i = blocks_added.begin();
1385 i != blocks_added.end(); ++i)
1389 MapBlock *block = m_map->getBlockOrEmerge(p);
1391 m_active_blocks.m_list.erase(p);
1395 activateBlock(block);
1396 /* infostream<<"Server: Block " << PP(p)
1397 << " became active"<<std::endl; */
1402 Mess around in active blocks
1404 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1405 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1407 float dtime = m_cache_nodetimer_interval;
1409 for(std::set<v3s16>::iterator
1410 i = m_active_blocks.m_list.begin();
1411 i != m_active_blocks.m_list.end(); ++i)
1415 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1416 <<") being handled"<<std::endl;*/
1418 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1422 // Reset block usage timer
1423 block->resetUsageTimer();
1425 // Set current time as timestamp
1426 block->setTimestampNoChangedFlag(m_game_time);
1427 // If time has changed much from the one on disk,
1428 // set block to be saved when it is unloaded
1429 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1430 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1431 MOD_REASON_BLOCK_EXPIRED);
1434 std::map<v3s16, NodeTimer> elapsed_timers =
1435 block->m_node_timers.step((float)dtime);
1436 if(!elapsed_timers.empty()){
1438 for(std::map<v3s16, NodeTimer>::iterator
1439 i = elapsed_timers.begin();
1440 i != elapsed_timers.end(); ++i){
1441 n = block->getNodeNoEx(i->first);
1442 p = i->first + block->getPosRelative();
1443 if(m_script->node_on_timer(p,n,i->second.elapsed))
1444 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1450 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1452 if(m_active_block_interval_overload_skip > 0){
1453 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1454 m_active_block_interval_overload_skip--;
1457 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1458 TimeTaker timer("modify in active blocks per interval");
1460 // Initialize handling of ActiveBlockModifiers
1461 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1463 for(std::set<v3s16>::iterator
1464 i = m_active_blocks.m_list.begin();
1465 i != m_active_blocks.m_list.end(); ++i)
1469 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1470 <<") being handled"<<std::endl;*/
1472 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1476 // Set current time as timestamp
1477 block->setTimestampNoChangedFlag(m_game_time);
1479 /* Handle ActiveBlockModifiers */
1480 abmhandler.apply(block);
1483 u32 time_ms = timer.stop(true);
1484 u32 max_time_ms = 200;
1485 if(time_ms > max_time_ms){
1486 warningstream<<"active block modifiers took "
1487 <<time_ms<<"ms (longer than "
1488 <<max_time_ms<<"ms)"<<std::endl;
1489 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1494 Step script environment (run global on_step())
1496 m_script->environment_Step(dtime);
1502 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1503 //TimeTaker timer("Step active objects");
1505 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1507 // This helps the objects to send data at the same time
1508 bool send_recommended = false;
1509 m_send_recommended_timer += dtime;
1510 if(m_send_recommended_timer > getSendRecommendedInterval())
1512 m_send_recommended_timer -= getSendRecommendedInterval();
1513 send_recommended = true;
1516 for(std::map<u16, ServerActiveObject*>::iterator
1517 i = m_active_objects.begin();
1518 i != m_active_objects.end(); ++i)
1520 ServerActiveObject* obj = i->second;
1521 // Don't step if is to be removed or stored statically
1522 if(obj->m_removed || obj->m_pending_deactivation)
1525 obj->step(dtime, send_recommended);
1526 // Read messages from object
1527 while(!obj->m_messages_out.empty())
1529 m_active_object_messages.push(
1530 obj->m_messages_out.front());
1531 obj->m_messages_out.pop();
1537 Manage active objects
1539 if(m_object_management_interval.step(dtime, 0.5))
1541 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1543 Remove objects that satisfy (m_removed && m_known_by_count==0)
1545 removeRemovedObjects();
1549 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1551 std::map<u16, ServerActiveObject*>::iterator n;
1552 n = m_active_objects.find(id);
1553 if(n == m_active_objects.end())
1558 bool isFreeServerActiveObjectId(u16 id,
1559 std::map<u16, ServerActiveObject*> &objects)
1564 return objects.find(id) == objects.end();
1567 u16 getFreeServerActiveObjectId(
1568 std::map<u16, ServerActiveObject*> &objects)
1570 //try to reuse id's as late as possible
1571 static u16 last_used_id = 0;
1572 u16 startid = last_used_id;
1576 if(isFreeServerActiveObjectId(last_used_id, objects))
1577 return last_used_id;
1579 if(last_used_id == startid)
1584 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1586 assert(object); // Pre-condition
1588 u16 id = addActiveObjectRaw(object, true, 0);
1593 Finds out what new objects have been added to
1594 inside a radius around a position
1596 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1598 std::set<u16> ¤t_objects,
1599 std::queue<u16> &added_objects)
1601 f32 radius_f = radius * BS;
1602 f32 player_radius_f = player_radius * BS;
1604 if (player_radius_f < 0)
1605 player_radius_f = 0;
1608 Go through the object list,
1609 - discard m_removed objects,
1610 - discard objects that are too far away,
1611 - discard objects that are found in current_objects.
1612 - add remaining objects to added_objects
1614 for(std::map<u16, ServerActiveObject*>::iterator
1615 i = m_active_objects.begin();
1616 i != m_active_objects.end(); ++i) {
1620 ServerActiveObject *object = i->second;
1624 // Discard if removed or deactivating
1625 if(object->m_removed || object->m_pending_deactivation)
1628 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1629 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1630 // Discard if too far
1631 if (distance_f > player_radius_f && player_radius_f != 0)
1633 } else if (distance_f > radius_f)
1636 // Discard if already on current_objects
1637 std::set<u16>::iterator n;
1638 n = current_objects.find(id);
1639 if(n != current_objects.end())
1641 // Add to added_objects
1642 added_objects.push(id);
1647 Finds out what objects have been removed from
1648 inside a radius around a position
1650 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1652 std::set<u16> ¤t_objects,
1653 std::queue<u16> &removed_objects)
1655 f32 radius_f = radius * BS;
1656 f32 player_radius_f = player_radius * BS;
1658 if (player_radius_f < 0)
1659 player_radius_f = 0;
1662 Go through current_objects; object is removed if:
1663 - object is not found in m_active_objects (this is actually an
1664 error condition; objects should be set m_removed=true and removed
1665 only after all clients have been informed about removal), or
1666 - object has m_removed=true, or
1667 - object is too far away
1669 for(std::set<u16>::iterator
1670 i = current_objects.begin();
1671 i != current_objects.end(); ++i)
1674 ServerActiveObject *object = getActiveObject(id);
1676 if (object == NULL) {
1677 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1678 << " object in current_objects is NULL" << std::endl;
1679 removed_objects.push(id);
1683 if (object->m_removed || object->m_pending_deactivation) {
1684 removed_objects.push(id);
1688 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1689 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1690 if (distance_f <= player_radius_f || player_radius_f == 0)
1692 } else if (distance_f <= radius_f)
1695 // Object is no longer visible
1696 removed_objects.push(id);
1700 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1701 v3s16 blockpos, bool static_exists, v3s16 static_block)
1703 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1707 for (std::map<u16, StaticObject>::iterator
1708 so_it = block->m_static_objects.m_active.begin();
1709 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1710 // Get the ServerActiveObject counterpart to this StaticObject
1711 std::map<u16, ServerActiveObject *>::iterator ao_it;
1712 ao_it = m_active_objects.find(so_it->first);
1713 if (ao_it == m_active_objects.end()) {
1714 // If this ever happens, there must be some kind of nasty bug.
1715 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1716 "Object from MapBlock::m_static_objects::m_active not found "
1717 "in m_active_objects";
1721 ServerActiveObject *sao = ao_it->second;
1722 sao->m_static_exists = static_exists;
1723 sao->m_static_block = static_block;
1727 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1729 if(m_active_object_messages.empty())
1730 return ActiveObjectMessage(0);
1732 ActiveObjectMessage message = m_active_object_messages.front();
1733 m_active_object_messages.pop();
1738 ************ Private methods *************
1741 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1742 bool set_changed, u32 dtime_s)
1744 assert(object); // Pre-condition
1745 if(object->getId() == 0){
1746 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1749 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1750 <<"no free ids available"<<std::endl;
1751 if(object->environmentDeletes())
1755 object->setId(new_id);
1758 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1759 <<"supplied with id "<<object->getId()<<std::endl;
1761 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1763 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1764 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1765 if(object->environmentDeletes())
1770 if (objectpos_over_limit(object->getBasePosition())) {
1771 v3f p = object->getBasePosition();
1772 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1773 << "object position (" << p.X << "," << p.Y << "," << p.Z
1774 << ") outside maximum range" << std::endl;
1775 if (object->environmentDeletes())
1780 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1781 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1783 m_active_objects[object->getId()] = object;
1785 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1786 <<"Added id="<<object->getId()<<"; there are now "
1787 <<m_active_objects.size()<<" active objects."
1790 // Register reference in scripting api (must be done before post-init)
1791 m_script->addObjectReference(object);
1792 // Post-initialize object
1793 object->addedToEnvironment(dtime_s);
1795 // Add static data to block
1796 if(object->isStaticAllowed())
1798 // Add static object to active static list of the block
1799 v3f objectpos = object->getBasePosition();
1800 std::string staticdata = object->getStaticData();
1801 StaticObject s_obj(object->getType(), objectpos, staticdata);
1802 // Add to the block where the object is located in
1803 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1804 MapBlock *block = m_map->emergeBlock(blockpos);
1806 block->m_static_objects.m_active[object->getId()] = s_obj;
1807 object->m_static_exists = true;
1808 object->m_static_block = blockpos;
1811 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1812 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1814 v3s16 p = floatToInt(objectpos, BS);
1815 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1816 <<"could not emerge block for storing id="<<object->getId()
1817 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1821 return object->getId();
1825 Remove objects that satisfy (m_removed && m_known_by_count==0)
1827 void ServerEnvironment::removeRemovedObjects()
1829 std::vector<u16> objects_to_remove;
1830 for(std::map<u16, ServerActiveObject*>::iterator
1831 i = m_active_objects.begin();
1832 i != m_active_objects.end(); ++i) {
1834 ServerActiveObject* obj = i->second;
1835 // This shouldn't happen but check it
1838 infostream<<"NULL object found in ServerEnvironment"
1839 <<" while finding removed objects. id="<<id<<std::endl;
1840 // Id to be removed from m_active_objects
1841 objects_to_remove.push_back(id);
1846 We will delete objects that are marked as removed or thatare
1847 waiting for deletion after deactivation
1849 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1853 Delete static data from block if is marked as removed
1855 if(obj->m_static_exists && obj->m_removed)
1857 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1859 block->m_static_objects.remove(id);
1860 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1861 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1862 obj->m_static_exists = false;
1864 infostream<<"Failed to emerge block from which an object to "
1865 <<"be removed was loaded from. id="<<id<<std::endl;
1869 // If m_known_by_count > 0, don't actually remove. On some future
1870 // invocation this will be 0, which is when removal will continue.
1871 if(obj->m_known_by_count > 0)
1875 Move static data from active to stored if not marked as removed
1877 if(obj->m_static_exists && !obj->m_removed){
1878 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1880 std::map<u16, StaticObject>::iterator i =
1881 block->m_static_objects.m_active.find(id);
1882 if(i != block->m_static_objects.m_active.end()){
1883 block->m_static_objects.m_stored.push_back(i->second);
1884 block->m_static_objects.m_active.erase(id);
1885 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1886 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1889 infostream<<"Failed to emerge block from which an object to "
1890 <<"be deactivated was loaded from. id="<<id<<std::endl;
1894 // Tell the object about removal
1895 obj->removingFromEnvironment();
1896 // Deregister in scripting api
1897 m_script->removeObjectReference(obj);
1900 if(obj->environmentDeletes())
1903 // Id to be removed from m_active_objects
1904 objects_to_remove.push_back(id);
1906 // Remove references from m_active_objects
1907 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1908 i != objects_to_remove.end(); ++i) {
1909 m_active_objects.erase(*i);
1913 static void print_hexdump(std::ostream &o, const std::string &data)
1915 const int linelength = 16;
1916 for(int l=0; ; l++){
1917 int i0 = linelength * l;
1918 bool at_end = false;
1919 int thislinelength = linelength;
1920 if(i0 + thislinelength > (int)data.size()){
1921 thislinelength = data.size() - i0;
1924 for(int di=0; di<linelength; di++){
1927 if(di<thislinelength)
1928 snprintf(buf, 4, "%.2x ", data[i]);
1930 snprintf(buf, 4, " ");
1934 for(int di=0; di<thislinelength; di++){
1948 Convert stored objects from blocks near the players to active.
1950 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1955 // Ignore if no stored objects (to not set changed flag)
1956 if(block->m_static_objects.m_stored.empty())
1959 verbosestream<<"ServerEnvironment::activateObjects(): "
1960 <<"activating objects of block "<<PP(block->getPos())
1961 <<" ("<<block->m_static_objects.m_stored.size()
1962 <<" objects)"<<std::endl;
1963 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1965 errorstream<<"suspiciously large amount of objects detected: "
1966 <<block->m_static_objects.m_stored.size()<<" in "
1967 <<PP(block->getPos())
1968 <<"; removing all of them."<<std::endl;
1969 // Clear stored list
1970 block->m_static_objects.m_stored.clear();
1971 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1972 MOD_REASON_TOO_MANY_OBJECTS);
1976 // Activate stored objects
1977 std::vector<StaticObject> new_stored;
1978 for (std::vector<StaticObject>::iterator
1979 i = block->m_static_objects.m_stored.begin();
1980 i != block->m_static_objects.m_stored.end(); ++i) {
1981 StaticObject &s_obj = *i;
1983 // Create an active object from the data
1984 ServerActiveObject *obj = ServerActiveObject::create
1985 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1986 // If couldn't create object, store static data back.
1988 errorstream<<"ServerEnvironment::activateObjects(): "
1989 <<"failed to create active object from static object "
1990 <<"in block "<<PP(s_obj.pos/BS)
1991 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1992 print_hexdump(verbosestream, s_obj.data);
1994 new_stored.push_back(s_obj);
1997 verbosestream<<"ServerEnvironment::activateObjects(): "
1998 <<"activated static object pos="<<PP(s_obj.pos/BS)
1999 <<" type="<<(int)s_obj.type<<std::endl;
2000 // This will also add the object to the active static list
2001 addActiveObjectRaw(obj, false, dtime_s);
2003 // Clear stored list
2004 block->m_static_objects.m_stored.clear();
2005 // Add leftover failed stuff to stored list
2006 for(std::vector<StaticObject>::iterator
2007 i = new_stored.begin();
2008 i != new_stored.end(); ++i) {
2009 StaticObject &s_obj = *i;
2010 block->m_static_objects.m_stored.push_back(s_obj);
2013 // Turn the active counterparts of activated objects not pending for
2015 for(std::map<u16, StaticObject>::iterator
2016 i = block->m_static_objects.m_active.begin();
2017 i != block->m_static_objects.m_active.end(); ++i)
2020 ServerActiveObject *object = getActiveObject(id);
2022 object->m_pending_deactivation = false;
2026 Note: Block hasn't really been modified here.
2027 The objects have just been activated and moved from the stored
2028 static list to the active static list.
2029 As such, the block is essentially the same.
2030 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2031 Otherwise there would be a huge amount of unnecessary I/O.
2036 Convert objects that are not standing inside active blocks to static.
2038 If m_known_by_count != 0, active object is not deleted, but static
2039 data is still updated.
2041 If force_delete is set, active object is deleted nevertheless. It
2042 shall only be set so in the destructor of the environment.
2044 If block wasn't generated (not in memory or on disk),
2046 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2048 std::vector<u16> objects_to_remove;
2049 for(std::map<u16, ServerActiveObject*>::iterator
2050 i = m_active_objects.begin();
2051 i != m_active_objects.end(); ++i) {
2052 ServerActiveObject* obj = i->second;
2055 // Do not deactivate if static data creation not allowed
2056 if(!force_delete && !obj->isStaticAllowed())
2059 // If pending deactivation, let removeRemovedObjects() do it
2060 if(!force_delete && obj->m_pending_deactivation)
2064 v3f objectpos = obj->getBasePosition();
2066 // The block in which the object resides in
2067 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2069 // If object's static data is stored in a deactivated block and object
2070 // is actually located in an active block, re-save to the block in
2071 // which the object is actually located in.
2073 obj->m_static_exists &&
2074 !m_active_blocks.contains(obj->m_static_block) &&
2075 m_active_blocks.contains(blockpos_o))
2077 v3s16 old_static_block = obj->m_static_block;
2079 // Save to block where object is located
2080 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2082 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2083 <<"Could not save object id="<<id
2084 <<" to it's current block "<<PP(blockpos_o)
2088 std::string staticdata_new = obj->getStaticData();
2089 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2090 block->m_static_objects.insert(id, s_obj);
2091 obj->m_static_block = blockpos_o;
2092 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2093 MOD_REASON_STATIC_DATA_ADDED);
2095 // Delete from block where object was located
2096 block = m_map->emergeBlock(old_static_block, false);
2098 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2099 <<"Could not delete object id="<<id
2100 <<" from it's previous block "<<PP(old_static_block)
2104 block->m_static_objects.remove(id);
2105 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2106 MOD_REASON_STATIC_DATA_REMOVED);
2110 // If block is active, don't remove
2111 if(!force_delete && m_active_blocks.contains(blockpos_o))
2114 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2115 <<"deactivating object id="<<id<<" on inactive block "
2116 <<PP(blockpos_o)<<std::endl;
2118 // If known by some client, don't immediately delete.
2119 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2122 Update the static data
2125 if(obj->isStaticAllowed())
2127 // Create new static object
2128 std::string staticdata_new = obj->getStaticData();
2129 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2131 bool stays_in_same_block = false;
2132 bool data_changed = true;
2134 if (obj->m_static_exists) {
2135 if (obj->m_static_block == blockpos_o)
2136 stays_in_same_block = true;
2138 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2141 std::map<u16, StaticObject>::iterator n =
2142 block->m_static_objects.m_active.find(id);
2143 if (n != block->m_static_objects.m_active.end()) {
2144 StaticObject static_old = n->second;
2146 float save_movem = obj->getMinimumSavedMovement();
2148 if (static_old.data == staticdata_new &&
2149 (static_old.pos - objectpos).getLength() < save_movem)
2150 data_changed = false;
2152 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2153 <<"id="<<id<<" m_static_exists=true but "
2154 <<"static data doesn't actually exist in "
2155 <<PP(obj->m_static_block)<<std::endl;
2160 bool shall_be_written = (!stays_in_same_block || data_changed);
2162 // Delete old static object
2163 if(obj->m_static_exists)
2165 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2168 block->m_static_objects.remove(id);
2169 obj->m_static_exists = false;
2170 // Only mark block as modified if data changed considerably
2171 if(shall_be_written)
2172 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2173 MOD_REASON_STATIC_DATA_CHANGED);
2177 // Add to the block where the object is located in
2178 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2179 // Get or generate the block
2180 MapBlock *block = NULL;
2182 block = m_map->emergeBlock(blockpos);
2183 } catch(InvalidPositionException &e){
2184 // Handled via NULL pointer
2185 // NOTE: emergeBlock's failure is usually determined by it
2186 // actually returning NULL
2191 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2192 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2193 <<" statically but block "<<PP(blockpos)
2194 <<" already contains "
2195 <<block->m_static_objects.m_stored.size()
2197 <<" Forcing delete."<<std::endl;
2198 force_delete = true;
2200 // If static counterpart already exists in target block,
2202 // This shouldn't happen because the object is removed from
2203 // the previous block before this according to
2204 // obj->m_static_block, but happens rarely for some unknown
2205 // reason. Unsuccessful attempts have been made to find
2207 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2208 warningstream<<"ServerEnv: Performing hack #83274"
2210 block->m_static_objects.remove(id);
2212 // Store static data
2213 u16 store_id = pending_delete ? id : 0;
2214 block->m_static_objects.insert(store_id, s_obj);
2216 // Only mark block as modified if data changed considerably
2217 if(shall_be_written)
2218 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2219 MOD_REASON_STATIC_DATA_CHANGED);
2221 obj->m_static_exists = true;
2222 obj->m_static_block = block->getPos();
2227 v3s16 p = floatToInt(objectpos, BS);
2228 errorstream<<"ServerEnv: Could not find or generate "
2229 <<"a block for storing id="<<obj->getId()
2230 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2237 If known by some client, set pending deactivation.
2238 Otherwise delete it immediately.
2241 if(pending_delete && !force_delete)
2243 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2244 <<"object id="<<id<<" is known by clients"
2245 <<"; not deleting yet"<<std::endl;
2247 obj->m_pending_deactivation = true;
2251 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2252 <<"object id="<<id<<" is not known by clients"
2253 <<"; deleting"<<std::endl;
2255 // Tell the object about removal
2256 obj->removingFromEnvironment();
2257 // Deregister in scripting api
2258 m_script->removeObjectReference(obj);
2260 // Delete active object
2261 if(obj->environmentDeletes())
2263 // Id to be removed from m_active_objects
2264 objects_to_remove.push_back(id);
2267 // Remove references from m_active_objects
2268 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2269 i != objects_to_remove.end(); ++i) {
2270 m_active_objects.erase(*i);
2276 #include "clientsimpleobject.h"
2282 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2283 ITextureSource *texturesource, IGameDef *gamedef,
2284 IrrlichtDevice *irr):
2287 m_texturesource(texturesource),
2292 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2295 ClientEnvironment::~ClientEnvironment()
2297 // delete active objects
2298 for(std::map<u16, ClientActiveObject*>::iterator
2299 i = m_active_objects.begin();
2300 i != m_active_objects.end(); ++i)
2305 for(std::vector<ClientSimpleObject*>::iterator
2306 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2314 Map & ClientEnvironment::getMap()
2319 ClientMap & ClientEnvironment::getClientMap()
2324 void ClientEnvironment::addPlayer(Player *player)
2326 DSTACK(FUNCTION_NAME);
2328 It is a failure if player is local and there already is a local
2331 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2332 "Player is local but there is already a local player");
2334 Environment::addPlayer(player);
2337 LocalPlayer * ClientEnvironment::getLocalPlayer()
2339 for(std::vector<Player*>::iterator i = m_players.begin();
2340 i != m_players.end(); ++i) {
2341 Player *player = *i;
2342 if(player->isLocal())
2343 return (LocalPlayer*)player;
2348 void ClientEnvironment::step(float dtime)
2350 DSTACK(FUNCTION_NAME);
2352 /* Step time of day */
2353 stepTimeOfDay(dtime);
2355 // Get some settings
2356 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2357 bool free_move = fly_allowed && g_settings->getBool("free_move");
2360 LocalPlayer *lplayer = getLocalPlayer();
2362 // collision info queue
2363 std::vector<CollisionInfo> player_collisions;
2366 Get the speed the player is going
2368 bool is_climbing = lplayer->is_climbing;
2370 f32 player_speed = lplayer->getSpeed().getLength();
2373 Maximum position increment
2375 //f32 position_max_increment = 0.05*BS;
2376 f32 position_max_increment = 0.1*BS;
2378 // Maximum time increment (for collision detection etc)
2379 // time = distance / speed
2380 f32 dtime_max_increment = 1;
2381 if(player_speed > 0.001)
2382 dtime_max_increment = position_max_increment / player_speed;
2384 // Maximum time increment is 10ms or lower
2385 if(dtime_max_increment > 0.01)
2386 dtime_max_increment = 0.01;
2388 // Don't allow overly huge dtime
2392 f32 dtime_downcount = dtime;
2395 Stuff that has a maximum time increment
2404 if(dtime_downcount > dtime_max_increment)
2406 dtime_part = dtime_max_increment;
2407 dtime_downcount -= dtime_part;
2411 dtime_part = dtime_downcount;
2413 Setting this to 0 (no -=dtime_part) disables an infinite loop
2414 when dtime_part is so small that dtime_downcount -= dtime_part
2417 dtime_downcount = 0;
2426 if(free_move == false && is_climbing == false)
2429 v3f speed = lplayer->getSpeed();
2430 if(lplayer->in_liquid == false)
2431 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2433 // Liquid floating / sinking
2434 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2435 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2437 // Liquid resistance
2438 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2440 // How much the node's viscosity blocks movement, ranges between 0 and 1
2441 // Should match the scale at which viscosity increase affects other liquid attributes
2442 const f32 viscosity_factor = 0.3;
2444 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2445 f32 dl = d_wanted.getLength();
2446 if(dl > lplayer->movement_liquid_fluidity_smooth)
2447 dl = lplayer->movement_liquid_fluidity_smooth;
2448 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2450 v3f d = d_wanted.normalize() * dl;
2454 lplayer->setSpeed(speed);
2459 This also does collision detection.
2461 lplayer->move(dtime_part, this, position_max_increment,
2462 &player_collisions);
2465 while(dtime_downcount > 0.001);
2467 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2469 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2470 i != player_collisions.end(); ++i) {
2471 CollisionInfo &info = *i;
2472 v3f speed_diff = info.new_speed - info.old_speed;;
2473 // Handle only fall damage
2474 // (because otherwise walking against something in fast_move kills you)
2475 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2477 // Get rid of other components
2480 f32 pre_factor = 1; // 1 hp per node/s
2481 f32 tolerance = BS*14; // 5 without damage
2482 f32 post_factor = 1; // 1 hp per node/s
2483 if(info.type == COLLISION_NODE)
2485 const ContentFeatures &f = m_gamedef->ndef()->
2486 get(m_map->getNodeNoEx(info.node_p));
2487 // Determine fall damage multiplier
2488 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2489 pre_factor = 1.0 + (float)addp/100.0;
2491 float speed = pre_factor * speed_diff.getLength();
2492 if(speed > tolerance)
2494 f32 damage_f = (speed - tolerance)/BS * post_factor;
2495 u16 damage = (u16)(damage_f+0.5);
2497 damageLocalPlayer(damage, true);
2498 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2499 m_gamedef->event()->put(e);
2505 A quick draft of lava damage
2507 if(m_lava_hurt_interval.step(dtime, 1.0))
2509 v3f pf = lplayer->getPosition();
2511 // Feet, middle and head
2512 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2513 MapNode n1 = m_map->getNodeNoEx(p1);
2514 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2515 MapNode n2 = m_map->getNodeNoEx(p2);
2516 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2517 MapNode n3 = m_map->getNodeNoEx(p3);
2519 u32 damage_per_second = 0;
2520 damage_per_second = MYMAX(damage_per_second,
2521 m_gamedef->ndef()->get(n1).damage_per_second);
2522 damage_per_second = MYMAX(damage_per_second,
2523 m_gamedef->ndef()->get(n2).damage_per_second);
2524 damage_per_second = MYMAX(damage_per_second,
2525 m_gamedef->ndef()->get(n3).damage_per_second);
2527 if(damage_per_second != 0)
2529 damageLocalPlayer(damage_per_second, true);
2536 if(m_drowning_interval.step(dtime, 2.0))
2538 v3f pf = lplayer->getPosition();
2541 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2542 MapNode n = m_map->getNodeNoEx(p);
2543 ContentFeatures c = m_gamedef->ndef()->get(n);
2544 u8 drowning_damage = c.drowning;
2545 if(drowning_damage > 0 && lplayer->hp > 0){
2546 u16 breath = lplayer->getBreath();
2553 lplayer->setBreath(breath);
2554 updateLocalPlayerBreath(breath);
2557 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2558 damageLocalPlayer(drowning_damage, true);
2561 if(m_breathing_interval.step(dtime, 0.5))
2563 v3f pf = lplayer->getPosition();
2566 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2567 MapNode n = m_map->getNodeNoEx(p);
2568 ContentFeatures c = m_gamedef->ndef()->get(n);
2570 lplayer->setBreath(11);
2572 else if(c.drowning == 0){
2573 u16 breath = lplayer->getBreath();
2576 lplayer->setBreath(breath);
2577 updateLocalPlayerBreath(breath);
2583 Stuff that can be done in an arbitarily large dtime
2585 for(std::vector<Player*>::iterator i = m_players.begin();
2586 i != m_players.end(); ++i) {
2587 Player *player = *i;
2590 Handle non-local players
2592 if(player->isLocal() == false) {
2594 player->move(dtime, this, 100*BS);
2599 // Update lighting on local player (used for wield item)
2600 u32 day_night_ratio = getDayNightRatio();
2604 // On InvalidPositionException, use this as default
2605 // (day: LIGHT_SUN, night: 0)
2606 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2608 v3s16 p = lplayer->getLightPosition();
2609 node_at_lplayer = m_map->getNodeNoEx(p);
2611 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2612 u8 day = light & 0xff;
2613 u8 night = (light >> 8) & 0xff;
2614 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2618 Step active objects and update lighting of them
2621 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2622 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2623 for(std::map<u16, ClientActiveObject*>::iterator
2624 i = m_active_objects.begin();
2625 i != m_active_objects.end(); ++i)
2627 ClientActiveObject* obj = i->second;
2629 obj->step(dtime, this);
2638 v3s16 p = obj->getLightPosition();
2639 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2641 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2643 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2645 obj->updateLight(light);
2650 Step and handle simple objects
2652 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2653 for(std::vector<ClientSimpleObject*>::iterator
2654 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2655 std::vector<ClientSimpleObject*>::iterator cur = i;
2656 ClientSimpleObject *simple = *cur;
2658 simple->step(dtime);
2659 if(simple->m_to_be_removed) {
2661 i = m_simple_objects.erase(cur);
2669 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2671 m_simple_objects.push_back(simple);
2674 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2676 ClientActiveObject *obj = getActiveObject(id);
2677 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2678 return (GenericCAO*) obj;
2683 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2685 std::map<u16, ClientActiveObject*>::iterator n;
2686 n = m_active_objects.find(id);
2687 if(n == m_active_objects.end())
2692 bool isFreeClientActiveObjectId(u16 id,
2693 std::map<u16, ClientActiveObject*> &objects)
2698 return objects.find(id) == objects.end();
2701 u16 getFreeClientActiveObjectId(
2702 std::map<u16, ClientActiveObject*> &objects)
2704 //try to reuse id's as late as possible
2705 static u16 last_used_id = 0;
2706 u16 startid = last_used_id;
2710 if(isFreeClientActiveObjectId(last_used_id, objects))
2711 return last_used_id;
2713 if(last_used_id == startid)
2718 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2720 assert(object); // Pre-condition
2721 if(object->getId() == 0)
2723 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2726 infostream<<"ClientEnvironment::addActiveObject(): "
2727 <<"no free ids available"<<std::endl;
2731 object->setId(new_id);
2733 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2735 infostream<<"ClientEnvironment::addActiveObject(): "
2736 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2740 infostream<<"ClientEnvironment::addActiveObject(): "
2741 <<"added (id="<<object->getId()<<")"<<std::endl;
2742 m_active_objects[object->getId()] = object;
2743 object->addToScene(m_smgr, m_texturesource, m_irr);
2744 { // Update lighting immediately
2749 v3s16 p = object->getLightPosition();
2750 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2752 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2754 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2756 object->updateLight(light);
2758 return object->getId();
2761 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2762 const std::string &init_data)
2764 ClientActiveObject* obj =
2765 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2768 infostream<<"ClientEnvironment::addActiveObject(): "
2769 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2778 obj->initialize(init_data);
2780 catch(SerializationError &e)
2782 errorstream<<"ClientEnvironment::addActiveObject():"
2783 <<" id="<<id<<" type="<<type
2784 <<": SerializationError in initialize(): "
2786 <<": init_data="<<serializeJsonString(init_data)
2790 addActiveObject(obj);
2793 void ClientEnvironment::removeActiveObject(u16 id)
2795 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2796 <<"id="<<id<<std::endl;
2797 ClientActiveObject* obj = getActiveObject(id);
2800 infostream<<"ClientEnvironment::removeActiveObject(): "
2801 <<"id="<<id<<" not found"<<std::endl;
2804 obj->removeFromScene(true);
2806 m_active_objects.erase(id);
2809 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2811 ClientActiveObject *obj = getActiveObject(id);
2813 infostream << "ClientEnvironment::processActiveObjectMessage():"
2814 << " got message for id=" << id << ", which doesn't exist."
2820 obj->processMessage(data);
2821 } catch (SerializationError &e) {
2822 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2823 << " id=" << id << " type=" << obj->getType()
2824 << " SerializationError in processMessage(): " << e.what()
2830 Callbacks for activeobjects
2833 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2835 LocalPlayer *lplayer = getLocalPlayer();
2839 if (lplayer->hp > damage)
2840 lplayer->hp -= damage;
2845 ClientEnvEvent event;
2846 event.type = CEE_PLAYER_DAMAGE;
2847 event.player_damage.amount = damage;
2848 event.player_damage.send_to_server = handle_hp;
2849 m_client_event_queue.push(event);
2852 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2854 ClientEnvEvent event;
2855 event.type = CEE_PLAYER_BREATH;
2856 event.player_breath.amount = breath;
2857 m_client_event_queue.push(event);
2861 Client likes to call these
2864 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2865 std::vector<DistanceSortedActiveObject> &dest)
2867 for(std::map<u16, ClientActiveObject*>::iterator
2868 i = m_active_objects.begin();
2869 i != m_active_objects.end(); ++i)
2871 ClientActiveObject* obj = i->second;
2873 f32 d = (obj->getPosition() - origin).getLength();
2878 DistanceSortedActiveObject dso(obj, d);
2880 dest.push_back(dso);
2884 ClientEnvEvent ClientEnvironment::getClientEvent()
2886 ClientEnvEvent event;
2887 if(m_client_event_queue.empty())
2888 event.type = CEE_NONE;
2890 event = m_client_event_queue.front();
2891 m_client_event_queue.pop();
2896 #endif // #ifndef SERVER