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 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
607 ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id,
608 player->protocol_version, reason, str_reason, reconnect);
612 void ServerEnvironment::saveLoadedPlayers()
614 std::string players_path = m_path_world + DIR_DELIM "players";
615 fs::CreateDir(players_path);
617 for (std::vector<Player*>::iterator it = m_players.begin();
618 it != m_players.end();
620 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
621 if (player->checkModified()) {
622 player->save(players_path, m_gamedef);
627 void ServerEnvironment::savePlayer(RemotePlayer *player)
629 std::string players_path = m_path_world + DIR_DELIM "players";
630 fs::CreateDir(players_path);
632 player->save(players_path, m_gamedef);
635 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername)
637 bool newplayer = false;
639 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
640 std::string path = players_path + playername;
642 RemotePlayer *player = getPlayer(playername.c_str());
644 player = new RemotePlayer("", m_gamedef->idef());
648 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
649 //// Open file and deserialize
650 std::ifstream is(path.c_str(), std::ios_base::binary);
653 player->deSerialize(is, path);
656 if (player->getName() == playername) {
661 path = players_path + playername + itos(i);
665 infostream << "Player file for player " << playername
666 << " not found" << std::endl;
674 player->setModified(false);
678 void ServerEnvironment::saveMeta()
680 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
682 // Open file and serialize
683 std::ostringstream ss(std::ios_base::binary);
686 args.setU64("game_time", m_game_time);
687 args.setU64("time_of_day", getTimeOfDay());
688 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
689 args.setU64("lbm_introduction_times_version", 1);
690 args.set("lbm_introduction_times",
691 m_lbm_mgr.createIntroductionTimesString());
692 args.setU64("day_count", m_day_count);
696 if(!fs::safeWriteToFile(path, ss.str()))
698 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
700 throw SerializationError("Couldn't save env meta");
704 void ServerEnvironment::loadMeta()
706 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
708 // Open file and deserialize
709 std::ifstream is(path.c_str(), std::ios_base::binary);
711 infostream << "ServerEnvironment::loadMeta(): Failed to open "
712 << path << std::endl;
713 throw SerializationError("Couldn't load env meta");
718 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
719 throw SerializationError("ServerEnvironment::loadMeta(): "
720 "EnvArgsEnd not found!");
724 m_game_time = args.getU64("game_time");
725 } catch (SettingNotFoundException &e) {
726 // Getting this is crucial, otherwise timestamps are useless
727 throw SerializationError("Couldn't load env meta game_time");
730 setTimeOfDay(args.exists("time_of_day") ?
731 // set day to morning by default
732 args.getU64("time_of_day") : 9000);
734 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
735 // If missing, do as if clearObjects was never called
736 args.getU64("last_clear_objects_time") : 0;
738 std::string lbm_introduction_times = "";
740 u64 ver = args.getU64("lbm_introduction_times_version");
742 lbm_introduction_times = args.get("lbm_introduction_times");
744 infostream << "ServerEnvironment::loadMeta(): Non-supported"
745 << " introduction time version " << ver << std::endl;
747 } catch (SettingNotFoundException &e) {
748 // No problem, this is expected. Just continue with an empty string
750 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
752 m_day_count = args.exists("day_count") ?
753 args.getU64("day_count") : 0;
756 void ServerEnvironment::loadDefaultMeta()
758 m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
763 ActiveBlockModifier *abm;
765 std::set<content_t> required_neighbors;
771 ServerEnvironment *m_env;
772 std::map<content_t, std::vector<ActiveABM> > m_aabms;
774 ABMHandler(std::vector<ABMWithState> &abms,
775 float dtime_s, ServerEnvironment *env,
781 INodeDefManager *ndef = env->getGameDef()->ndef();
782 for(std::vector<ABMWithState>::iterator
783 i = abms.begin(); i != abms.end(); ++i) {
784 ActiveBlockModifier *abm = i->abm;
785 float trigger_interval = abm->getTriggerInterval();
786 if(trigger_interval < 0.001)
787 trigger_interval = 0.001;
788 float actual_interval = dtime_s;
791 if(i->timer < trigger_interval)
793 i->timer -= trigger_interval;
794 actual_interval = trigger_interval;
796 float chance = abm->getTriggerChance();
801 if(abm->getSimpleCatchUp()) {
802 float intervals = actual_interval / trigger_interval;
805 aabm.chance = chance / intervals;
809 aabm.chance = chance;
812 std::set<std::string> required_neighbors_s
813 = abm->getRequiredNeighbors();
814 for(std::set<std::string>::iterator
815 i = required_neighbors_s.begin();
816 i != required_neighbors_s.end(); ++i)
818 ndef->getIds(*i, aabm.required_neighbors);
821 std::set<std::string> contents_s = abm->getTriggerContents();
822 for(std::set<std::string>::iterator
823 i = contents_s.begin(); i != contents_s.end(); ++i)
825 std::set<content_t> ids;
826 ndef->getIds(*i, ids);
827 for(std::set<content_t>::const_iterator k = ids.begin();
831 std::map<content_t, std::vector<ActiveABM> >::iterator j;
833 if(j == m_aabms.end()){
834 std::vector<ActiveABM> aabmlist;
835 m_aabms[c] = aabmlist;
838 j->second.push_back(aabm);
843 // Find out how many objects the given block and its neighbours contain.
844 // Returns the number of objects in the block, and also in 'wider' the
845 // number of objects in the block and all its neighbours. The latter
846 // may an estimate if any neighbours are unloaded.
847 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
850 u32 wider_unknown_count = 0;
851 for(s16 x=-1; x<=1; x++)
852 for(s16 y=-1; y<=1; y++)
853 for(s16 z=-1; z<=1; z++)
855 MapBlock *block2 = map->getBlockNoCreateNoEx(
856 block->getPos() + v3s16(x,y,z));
858 wider_unknown_count++;
861 wider += block2->m_static_objects.m_active.size()
862 + block2->m_static_objects.m_stored.size();
865 u32 active_object_count = block->m_static_objects.m_active.size();
866 u32 wider_known_count = 3*3*3 - wider_unknown_count;
867 wider += wider_unknown_count * wider / wider_known_count;
868 return active_object_count;
871 void apply(MapBlock *block)
876 ServerMap *map = &m_env->getServerMap();
878 u32 active_object_count_wider;
879 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
880 m_env->m_added_objects = 0;
883 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
884 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
885 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
887 MapNode n = block->getNodeNoEx(p0);
888 content_t c = n.getContent();
889 v3s16 p = p0 + block->getPosRelative();
891 std::map<content_t, std::vector<ActiveABM> >::iterator j;
893 if(j == m_aabms.end())
896 for(std::vector<ActiveABM>::iterator
897 i = j->second.begin(); i != j->second.end(); ++i) {
898 if(myrand() % i->chance != 0)
902 if(!i->required_neighbors.empty())
905 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
906 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
907 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
911 MapNode n = map->getNodeNoEx(p1);
912 content_t c = n.getContent();
913 std::set<content_t>::const_iterator k;
914 k = i->required_neighbors.find(c);
915 if(k != i->required_neighbors.end()){
919 // No required neighbor found
924 // Call all the trigger variations
925 i->abm->trigger(m_env, p, n);
926 i->abm->trigger(m_env, p, n,
927 active_object_count, active_object_count_wider);
929 // Count surrounding objects again if the abms added any
930 if(m_env->m_added_objects > 0) {
931 active_object_count = countObjects(block, map, active_object_count_wider);
932 m_env->m_added_objects = 0;
939 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
941 // Reset usage timer immediately, otherwise a block that becomes active
942 // again at around the same time as it would normally be unloaded will
943 // get unloaded incorrectly. (I think this still leaves a small possibility
944 // of a race condition between this and server::AsyncRunStep, which only
945 // some kind of synchronisation will fix, but it at least reduces the window
946 // of opportunity for it to break from seconds to nanoseconds)
947 block->resetUsageTimer();
949 // Get time difference
951 u32 stamp = block->getTimestamp();
952 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
953 dtime_s = m_game_time - stamp;
954 dtime_s += additional_dtime;
956 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
957 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
959 // Remove stored static objects if clearObjects was called since block's timestamp
960 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
961 block->m_static_objects.m_stored.clear();
962 // do not set changed flag to avoid unnecessary mapblock writes
965 // Set current time as timestamp
966 block->setTimestampNoChangedFlag(m_game_time);
968 /*infostream<<"ServerEnvironment::activateBlock(): block is "
969 <<dtime_s<<" seconds old."<<std::endl;*/
971 // Activate stored objects
972 activateObjects(block, dtime_s);
974 /* Handle LoadingBlockModifiers */
975 m_lbm_mgr.applyLBMs(this, block, stamp);
978 std::vector<NodeTimer> elapsed_timers =
979 block->m_node_timers.step((float)dtime_s);
980 if (!elapsed_timers.empty()) {
982 for (std::vector<NodeTimer>::iterator
983 i = elapsed_timers.begin();
984 i != elapsed_timers.end(); ++i){
985 n = block->getNodeNoEx(i->position);
986 v3s16 p = i->position + block->getPosRelative();
987 if (m_script->node_on_timer(p, n, i->elapsed))
988 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
992 /* Handle ActiveBlockModifiers */
993 ABMHandler abmhandler(m_abms, dtime_s, this, false);
994 abmhandler.apply(block);
997 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
999 m_abms.push_back(ABMWithState(abm));
1002 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1004 m_lbm_mgr.addLBMDef(lbm);
1007 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1009 INodeDefManager *ndef = m_gamedef->ndef();
1010 MapNode n_old = m_map->getNodeNoEx(p);
1013 if (ndef->get(n_old).has_on_destruct)
1014 m_script->node_on_destruct(p, n_old);
1017 if (!m_map->addNodeWithEvent(p, n))
1020 // Update active VoxelManipulator if a mapgen thread
1021 m_map->updateVManip(p);
1023 // Call post-destructor
1024 if (ndef->get(n_old).has_after_destruct)
1025 m_script->node_after_destruct(p, n_old);
1028 if (ndef->get(n).has_on_construct)
1029 m_script->node_on_construct(p, n);
1034 bool ServerEnvironment::removeNode(v3s16 p)
1036 INodeDefManager *ndef = m_gamedef->ndef();
1037 MapNode n_old = m_map->getNodeNoEx(p);
1040 if (ndef->get(n_old).has_on_destruct)
1041 m_script->node_on_destruct(p, n_old);
1044 // This is slightly optimized compared to addNodeWithEvent(air)
1045 if (!m_map->removeNodeWithEvent(p))
1048 // Update active VoxelManipulator if a mapgen thread
1049 m_map->updateVManip(p);
1051 // Call post-destructor
1052 if (ndef->get(n_old).has_after_destruct)
1053 m_script->node_after_destruct(p, n_old);
1055 // Air doesn't require constructor
1059 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1061 if (!m_map->addNodeWithEvent(p, n, false))
1064 // Update active VoxelManipulator if a mapgen thread
1065 m_map->updateVManip(p);
1070 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1072 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1073 i != m_active_objects.end(); ++i) {
1074 ServerActiveObject* obj = i->second;
1076 v3f objectpos = obj->getBasePosition();
1077 if (objectpos.getDistanceFrom(pos) > radius)
1079 objects.push_back(id);
1083 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1085 infostream << "ServerEnvironment::clearObjects(): "
1086 << "Removing all active objects" << std::endl;
1087 std::vector<u16> objects_to_remove;
1088 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1089 i != m_active_objects.end(); ++i) {
1090 ServerActiveObject* obj = i->second;
1091 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1094 // Delete static object if block is loaded
1095 if (obj->m_static_exists) {
1096 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1098 block->m_static_objects.remove(id);
1099 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1100 MOD_REASON_CLEAR_ALL_OBJECTS);
1101 obj->m_static_exists = false;
1104 // If known by some client, don't delete immediately
1105 if (obj->m_known_by_count > 0) {
1106 obj->m_pending_deactivation = true;
1107 obj->m_removed = true;
1111 // Tell the object about removal
1112 obj->removingFromEnvironment();
1113 // Deregister in scripting api
1114 m_script->removeObjectReference(obj);
1116 // Delete active object
1117 if (obj->environmentDeletes())
1119 // Id to be removed from m_active_objects
1120 objects_to_remove.push_back(id);
1123 // Remove references from m_active_objects
1124 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1125 i != objects_to_remove.end(); ++i) {
1126 m_active_objects.erase(*i);
1129 // Get list of loaded blocks
1130 std::vector<v3s16> loaded_blocks;
1131 infostream << "ServerEnvironment::clearObjects(): "
1132 << "Listing all loaded blocks" << std::endl;
1133 m_map->listAllLoadedBlocks(loaded_blocks);
1134 infostream << "ServerEnvironment::clearObjects(): "
1135 << "Done listing all loaded blocks: "
1136 << loaded_blocks.size()<<std::endl;
1138 // Get list of loadable blocks
1139 std::vector<v3s16> loadable_blocks;
1140 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1141 infostream << "ServerEnvironment::clearObjects(): "
1142 << "Listing all loadable blocks" << std::endl;
1143 m_map->listAllLoadableBlocks(loadable_blocks);
1144 infostream << "ServerEnvironment::clearObjects(): "
1145 << "Done listing all loadable blocks: "
1146 << loadable_blocks.size() << std::endl;
1148 loadable_blocks = loaded_blocks;
1151 infostream << "ServerEnvironment::clearObjects(): "
1152 << "Now clearing objects in " << loadable_blocks.size()
1153 << " blocks" << std::endl;
1155 // Grab a reference on each loaded block to avoid unloading it
1156 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1157 i != loaded_blocks.end(); ++i) {
1159 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1160 assert(block != NULL);
1164 // Remove objects in all loadable blocks
1165 u32 unload_interval = U32_MAX;
1166 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1167 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1168 unload_interval = MYMAX(unload_interval, 1);
1170 u32 report_interval = loadable_blocks.size() / 10;
1171 u32 num_blocks_checked = 0;
1172 u32 num_blocks_cleared = 0;
1173 u32 num_objs_cleared = 0;
1174 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1175 i != loadable_blocks.end(); ++i) {
1177 MapBlock *block = m_map->emergeBlock(p, false);
1179 errorstream << "ServerEnvironment::clearObjects(): "
1180 << "Failed to emerge block " << PP(p) << std::endl;
1183 u32 num_stored = block->m_static_objects.m_stored.size();
1184 u32 num_active = block->m_static_objects.m_active.size();
1185 if (num_stored != 0 || num_active != 0) {
1186 block->m_static_objects.m_stored.clear();
1187 block->m_static_objects.m_active.clear();
1188 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1189 MOD_REASON_CLEAR_ALL_OBJECTS);
1190 num_objs_cleared += num_stored + num_active;
1191 num_blocks_cleared++;
1193 num_blocks_checked++;
1195 if (report_interval != 0 &&
1196 num_blocks_checked % report_interval == 0) {
1197 float percent = 100.0 * (float)num_blocks_checked /
1198 loadable_blocks.size();
1199 infostream << "ServerEnvironment::clearObjects(): "
1200 << "Cleared " << num_objs_cleared << " objects"
1201 << " in " << num_blocks_cleared << " blocks ("
1202 << percent << "%)" << std::endl;
1204 if (num_blocks_checked % unload_interval == 0) {
1205 m_map->unloadUnreferencedBlocks();
1208 m_map->unloadUnreferencedBlocks();
1210 // Drop references that were added above
1211 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1212 i != loaded_blocks.end(); ++i) {
1214 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1219 m_last_clear_objects_time = m_game_time;
1221 infostream << "ServerEnvironment::clearObjects(): "
1222 << "Finished: Cleared " << num_objs_cleared << " objects"
1223 << " in " << num_blocks_cleared << " blocks" << std::endl;
1226 void ServerEnvironment::step(float dtime)
1228 DSTACK(FUNCTION_NAME);
1230 //TimeTaker timer("ServerEnv step");
1232 /* Step time of day */
1233 stepTimeOfDay(dtime);
1236 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1237 // really matter that much.
1238 static const float server_step = g_settings->getFloat("dedicated_server_step");
1239 m_recommended_send_interval = server_step;
1245 m_game_time_fraction_counter += dtime;
1246 u32 inc_i = (u32)m_game_time_fraction_counter;
1247 m_game_time += inc_i;
1248 m_game_time_fraction_counter -= (float)inc_i;
1255 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1256 for (std::vector<Player*>::iterator i = m_players.begin();
1257 i != m_players.end(); ++i) {
1258 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1261 // Ignore disconnected players
1262 if(player->peer_id == 0)
1266 player->move(dtime, this, 100*BS);
1271 Manage active block list
1273 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1274 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1276 Get player block positions
1278 std::vector<v3s16> players_blockpos;
1279 for (std::vector<Player*>::iterator i = m_players.begin();
1280 i != m_players.end(); ++i) {
1281 RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
1283 // Ignore disconnected players
1284 if (player->peer_id == 0)
1287 v3s16 blockpos = getNodeBlockPos(
1288 floatToInt(player->getPosition(), BS));
1289 players_blockpos.push_back(blockpos);
1293 Update list of active blocks, collecting changes
1295 static const s16 active_block_range = g_settings->getS16("active_block_range");
1296 std::set<v3s16> blocks_removed;
1297 std::set<v3s16> blocks_added;
1298 m_active_blocks.update(players_blockpos, active_block_range,
1299 blocks_removed, blocks_added);
1302 Handle removed blocks
1305 // Convert active objects that are no more in active blocks to static
1306 deactivateFarObjects(false);
1308 for(std::set<v3s16>::iterator
1309 i = blocks_removed.begin();
1310 i != blocks_removed.end(); ++i) {
1313 /* infostream<<"Server: Block " << PP(p)
1314 << " became inactive"<<std::endl; */
1316 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1320 // Set current time as timestamp (and let it set ChangedFlag)
1321 block->setTimestamp(m_game_time);
1328 for(std::set<v3s16>::iterator
1329 i = blocks_added.begin();
1330 i != blocks_added.end(); ++i)
1334 MapBlock *block = m_map->getBlockOrEmerge(p);
1336 m_active_blocks.m_list.erase(p);
1340 activateBlock(block);
1341 /* infostream<<"Server: Block " << PP(p)
1342 << " became active"<<std::endl; */
1347 Mess around in active blocks
1349 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1350 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1352 float dtime = m_cache_nodetimer_interval;
1354 for(std::set<v3s16>::iterator
1355 i = m_active_blocks.m_list.begin();
1356 i != m_active_blocks.m_list.end(); ++i)
1360 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1361 <<") being handled"<<std::endl;*/
1363 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1367 // Reset block usage timer
1368 block->resetUsageTimer();
1370 // Set current time as timestamp
1371 block->setTimestampNoChangedFlag(m_game_time);
1372 // If time has changed much from the one on disk,
1373 // set block to be saved when it is unloaded
1374 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1375 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1376 MOD_REASON_BLOCK_EXPIRED);
1379 std::vector<NodeTimer> elapsed_timers =
1380 block->m_node_timers.step((float)dtime);
1381 if (!elapsed_timers.empty()) {
1383 for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
1384 i != elapsed_timers.end(); ++i) {
1385 n = block->getNodeNoEx(i->position);
1386 p = i->position + block->getPosRelative();
1387 if (m_script->node_on_timer(p, n, i->elapsed)) {
1388 block->setNodeTimer(NodeTimer(
1389 i->timeout, 0, i->position));
1396 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1398 if(m_active_block_interval_overload_skip > 0){
1399 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1400 m_active_block_interval_overload_skip--;
1403 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1404 TimeTaker timer("modify in active blocks per interval");
1406 // Initialize handling of ActiveBlockModifiers
1407 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
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 // Set current time as timestamp
1423 block->setTimestampNoChangedFlag(m_game_time);
1425 /* Handle ActiveBlockModifiers */
1426 abmhandler.apply(block);
1429 u32 time_ms = timer.stop(true);
1430 u32 max_time_ms = 200;
1431 if(time_ms > max_time_ms){
1432 warningstream<<"active block modifiers took "
1433 <<time_ms<<"ms (longer than "
1434 <<max_time_ms<<"ms)"<<std::endl;
1435 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1440 Step script environment (run global on_step())
1442 m_script->environment_Step(dtime);
1448 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1449 //TimeTaker timer("Step active objects");
1451 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1453 // This helps the objects to send data at the same time
1454 bool send_recommended = false;
1455 m_send_recommended_timer += dtime;
1456 if(m_send_recommended_timer > getSendRecommendedInterval())
1458 m_send_recommended_timer -= getSendRecommendedInterval();
1459 send_recommended = true;
1462 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1463 i != m_active_objects.end(); ++i) {
1464 ServerActiveObject* obj = i->second;
1465 // Don't step if is to be removed or stored statically
1466 if(obj->m_removed || obj->m_pending_deactivation)
1469 obj->step(dtime, send_recommended);
1470 // Read messages from object
1471 while(!obj->m_messages_out.empty())
1473 m_active_object_messages.push(
1474 obj->m_messages_out.front());
1475 obj->m_messages_out.pop();
1481 Manage active objects
1483 if(m_object_management_interval.step(dtime, 0.5))
1485 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1487 Remove objects that satisfy (m_removed && m_known_by_count==0)
1489 removeRemovedObjects();
1493 Manage particle spawner expiration
1495 if (m_particle_management_interval.step(dtime, 1.0)) {
1496 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1497 i != m_particle_spawners.end(); ) {
1498 //non expiring spawners
1499 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1505 if (i->second <= 0.f)
1506 m_particle_spawners.erase(i++);
1513 u32 ServerEnvironment::addParticleSpawner(float exptime)
1515 // Timers with lifetime 0 do not expire
1516 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1519 for (;;) { // look for unused particlespawner id
1521 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1522 if (f == m_particle_spawners.end()) {
1523 m_particle_spawners[id] = time;
1530 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1532 ActiveObjectMap::iterator n = m_active_objects.find(id);
1533 return (n != m_active_objects.end() ? n->second : NULL);
1536 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1541 return objects.find(id) == objects.end();
1544 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1546 //try to reuse id's as late as possible
1547 static u16 last_used_id = 0;
1548 u16 startid = last_used_id;
1552 if(isFreeServerActiveObjectId(last_used_id, objects))
1553 return last_used_id;
1555 if(last_used_id == startid)
1560 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1562 assert(object); // Pre-condition
1564 u16 id = addActiveObjectRaw(object, true, 0);
1569 Finds out what new objects have been added to
1570 inside a radius around a position
1572 void ServerEnvironment::getAddedActiveObjects(RemotePlayer *player, s16 radius,
1574 std::set<u16> ¤t_objects,
1575 std::queue<u16> &added_objects)
1577 f32 radius_f = radius * BS;
1578 f32 player_radius_f = player_radius * BS;
1580 if (player_radius_f < 0)
1581 player_radius_f = 0;
1584 Go through the object list,
1585 - discard m_removed objects,
1586 - discard objects that are too far away,
1587 - discard objects that are found in current_objects.
1588 - add remaining objects to added_objects
1590 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1591 i != m_active_objects.end(); ++i) {
1595 ServerActiveObject *object = i->second;
1599 // Discard if removed or deactivating
1600 if(object->m_removed || object->m_pending_deactivation)
1603 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1604 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1605 // Discard if too far
1606 if (distance_f > player_radius_f && player_radius_f != 0)
1608 } else if (distance_f > radius_f)
1611 // Discard if already on current_objects
1612 std::set<u16>::iterator n;
1613 n = current_objects.find(id);
1614 if(n != current_objects.end())
1616 // Add to added_objects
1617 added_objects.push(id);
1622 Finds out what objects have been removed from
1623 inside a radius around a position
1625 void ServerEnvironment::getRemovedActiveObjects(RemotePlayer *player, s16 radius,
1627 std::set<u16> ¤t_objects,
1628 std::queue<u16> &removed_objects)
1630 f32 radius_f = radius * BS;
1631 f32 player_radius_f = player_radius * BS;
1633 if (player_radius_f < 0)
1634 player_radius_f = 0;
1637 Go through current_objects; object is removed if:
1638 - object is not found in m_active_objects (this is actually an
1639 error condition; objects should be set m_removed=true and removed
1640 only after all clients have been informed about removal), or
1641 - object has m_removed=true, or
1642 - object is too far away
1644 for(std::set<u16>::iterator
1645 i = current_objects.begin();
1646 i != current_objects.end(); ++i)
1649 ServerActiveObject *object = getActiveObject(id);
1651 if (object == NULL) {
1652 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1653 << " object in current_objects is NULL" << std::endl;
1654 removed_objects.push(id);
1658 if (object->m_removed || object->m_pending_deactivation) {
1659 removed_objects.push(id);
1663 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1664 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1665 if (distance_f <= player_radius_f || player_radius_f == 0)
1667 } else if (distance_f <= radius_f)
1670 // Object is no longer visible
1671 removed_objects.push(id);
1675 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1676 v3s16 blockpos, bool static_exists, v3s16 static_block)
1678 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1682 for (std::map<u16, StaticObject>::iterator
1683 so_it = block->m_static_objects.m_active.begin();
1684 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1685 // Get the ServerActiveObject counterpart to this StaticObject
1686 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1687 if (ao_it == m_active_objects.end()) {
1688 // If this ever happens, there must be some kind of nasty bug.
1689 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1690 "Object from MapBlock::m_static_objects::m_active not found "
1691 "in m_active_objects";
1695 ServerActiveObject *sao = ao_it->second;
1696 sao->m_static_exists = static_exists;
1697 sao->m_static_block = static_block;
1701 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1703 if(m_active_object_messages.empty())
1704 return ActiveObjectMessage(0);
1706 ActiveObjectMessage message = m_active_object_messages.front();
1707 m_active_object_messages.pop();
1712 ************ Private methods *************
1715 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1716 bool set_changed, u32 dtime_s)
1718 assert(object); // Pre-condition
1719 if(object->getId() == 0){
1720 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1723 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1724 <<"no free ids available"<<std::endl;
1725 if(object->environmentDeletes())
1729 object->setId(new_id);
1732 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1733 <<"supplied with id "<<object->getId()<<std::endl;
1736 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1737 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1738 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1739 if(object->environmentDeletes())
1744 if (objectpos_over_limit(object->getBasePosition())) {
1745 v3f p = object->getBasePosition();
1746 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1747 << "object position (" << p.X << "," << p.Y << "," << p.Z
1748 << ") outside maximum range" << std::endl;
1749 if (object->environmentDeletes())
1754 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1755 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1757 m_active_objects[object->getId()] = object;
1759 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1760 <<"Added id="<<object->getId()<<"; there are now "
1761 <<m_active_objects.size()<<" active objects."
1764 // Register reference in scripting api (must be done before post-init)
1765 m_script->addObjectReference(object);
1766 // Post-initialize object
1767 object->addedToEnvironment(dtime_s);
1769 // Add static data to block
1770 if(object->isStaticAllowed())
1772 // Add static object to active static list of the block
1773 v3f objectpos = object->getBasePosition();
1774 std::string staticdata = object->getStaticData();
1775 StaticObject s_obj(object->getType(), objectpos, staticdata);
1776 // Add to the block where the object is located in
1777 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1778 MapBlock *block = m_map->emergeBlock(blockpos);
1780 block->m_static_objects.m_active[object->getId()] = s_obj;
1781 object->m_static_exists = true;
1782 object->m_static_block = blockpos;
1785 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1786 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1788 v3s16 p = floatToInt(objectpos, BS);
1789 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1790 <<"could not emerge block for storing id="<<object->getId()
1791 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1795 return object->getId();
1799 Remove objects that satisfy (m_removed && m_known_by_count==0)
1801 void ServerEnvironment::removeRemovedObjects()
1803 std::vector<u16> objects_to_remove;
1804 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1805 i != m_active_objects.end(); ++i) {
1807 ServerActiveObject* obj = i->second;
1808 // This shouldn't happen but check it
1811 infostream<<"NULL object found in ServerEnvironment"
1812 <<" while finding removed objects. id="<<id<<std::endl;
1813 // Id to be removed from m_active_objects
1814 objects_to_remove.push_back(id);
1819 We will delete objects that are marked as removed or thatare
1820 waiting for deletion after deactivation
1822 if (!obj->m_removed && !obj->m_pending_deactivation)
1826 Delete static data from block if is marked as removed
1828 if(obj->m_static_exists && obj->m_removed)
1830 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1832 block->m_static_objects.remove(id);
1833 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1834 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1835 obj->m_static_exists = false;
1837 infostream<<"Failed to emerge block from which an object to "
1838 <<"be removed was loaded from. id="<<id<<std::endl;
1842 // If m_known_by_count > 0, don't actually remove. On some future
1843 // invocation this will be 0, which is when removal will continue.
1844 if(obj->m_known_by_count > 0)
1848 Move static data from active to stored if not marked as removed
1850 if(obj->m_static_exists && !obj->m_removed){
1851 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1853 std::map<u16, StaticObject>::iterator i =
1854 block->m_static_objects.m_active.find(id);
1855 if(i != block->m_static_objects.m_active.end()){
1856 block->m_static_objects.m_stored.push_back(i->second);
1857 block->m_static_objects.m_active.erase(id);
1858 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1859 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1862 infostream<<"Failed to emerge block from which an object to "
1863 <<"be deactivated was loaded from. id="<<id<<std::endl;
1867 // Tell the object about removal
1868 obj->removingFromEnvironment();
1869 // Deregister in scripting api
1870 m_script->removeObjectReference(obj);
1873 if(obj->environmentDeletes())
1876 // Id to be removed from m_active_objects
1877 objects_to_remove.push_back(id);
1879 // Remove references from m_active_objects
1880 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1881 i != objects_to_remove.end(); ++i) {
1882 m_active_objects.erase(*i);
1886 static void print_hexdump(std::ostream &o, const std::string &data)
1888 const int linelength = 16;
1889 for(int l=0; ; l++){
1890 int i0 = linelength * l;
1891 bool at_end = false;
1892 int thislinelength = linelength;
1893 if(i0 + thislinelength > (int)data.size()){
1894 thislinelength = data.size() - i0;
1897 for(int di=0; di<linelength; di++){
1900 if(di<thislinelength)
1901 snprintf(buf, 4, "%.2x ", data[i]);
1903 snprintf(buf, 4, " ");
1907 for(int di=0; di<thislinelength; di++){
1921 Convert stored objects from blocks near the players to active.
1923 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1928 // Ignore if no stored objects (to not set changed flag)
1929 if(block->m_static_objects.m_stored.empty())
1932 verbosestream<<"ServerEnvironment::activateObjects(): "
1933 <<"activating objects of block "<<PP(block->getPos())
1934 <<" ("<<block->m_static_objects.m_stored.size()
1935 <<" objects)"<<std::endl;
1936 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1938 errorstream<<"suspiciously large amount of objects detected: "
1939 <<block->m_static_objects.m_stored.size()<<" in "
1940 <<PP(block->getPos())
1941 <<"; removing all of them."<<std::endl;
1942 // Clear stored list
1943 block->m_static_objects.m_stored.clear();
1944 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1945 MOD_REASON_TOO_MANY_OBJECTS);
1949 // Activate stored objects
1950 std::vector<StaticObject> new_stored;
1951 for (std::vector<StaticObject>::iterator
1952 i = block->m_static_objects.m_stored.begin();
1953 i != block->m_static_objects.m_stored.end(); ++i) {
1954 StaticObject &s_obj = *i;
1956 // Create an active object from the data
1957 ServerActiveObject *obj = ServerActiveObject::create
1958 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1959 // If couldn't create object, store static data back.
1961 errorstream<<"ServerEnvironment::activateObjects(): "
1962 <<"failed to create active object from static object "
1963 <<"in block "<<PP(s_obj.pos/BS)
1964 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1965 print_hexdump(verbosestream, s_obj.data);
1967 new_stored.push_back(s_obj);
1970 verbosestream<<"ServerEnvironment::activateObjects(): "
1971 <<"activated static object pos="<<PP(s_obj.pos/BS)
1972 <<" type="<<(int)s_obj.type<<std::endl;
1973 // This will also add the object to the active static list
1974 addActiveObjectRaw(obj, false, dtime_s);
1976 // Clear stored list
1977 block->m_static_objects.m_stored.clear();
1978 // Add leftover failed stuff to stored list
1979 for(std::vector<StaticObject>::iterator
1980 i = new_stored.begin();
1981 i != new_stored.end(); ++i) {
1982 StaticObject &s_obj = *i;
1983 block->m_static_objects.m_stored.push_back(s_obj);
1986 // Turn the active counterparts of activated objects not pending for
1988 for(std::map<u16, StaticObject>::iterator
1989 i = block->m_static_objects.m_active.begin();
1990 i != block->m_static_objects.m_active.end(); ++i)
1993 ServerActiveObject *object = getActiveObject(id);
1995 object->m_pending_deactivation = false;
1999 Note: Block hasn't really been modified here.
2000 The objects have just been activated and moved from the stored
2001 static list to the active static list.
2002 As such, the block is essentially the same.
2003 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2004 Otherwise there would be a huge amount of unnecessary I/O.
2009 Convert objects that are not standing inside active blocks to static.
2011 If m_known_by_count != 0, active object is not deleted, but static
2012 data is still updated.
2014 If force_delete is set, active object is deleted nevertheless. It
2015 shall only be set so in the destructor of the environment.
2017 If block wasn't generated (not in memory or on disk),
2019 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2021 std::vector<u16> objects_to_remove;
2022 for(ActiveObjectMap::iterator i = m_active_objects.begin();
2023 i != m_active_objects.end(); ++i) {
2024 ServerActiveObject* obj = i->second;
2027 // Do not deactivate if static data creation not allowed
2028 if(!force_delete && !obj->isStaticAllowed())
2031 // If pending deactivation, let removeRemovedObjects() do it
2032 if(!force_delete && obj->m_pending_deactivation)
2036 v3f objectpos = obj->getBasePosition();
2038 // The block in which the object resides in
2039 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2041 // If object's static data is stored in a deactivated block and object
2042 // is actually located in an active block, re-save to the block in
2043 // which the object is actually located in.
2045 obj->m_static_exists &&
2046 !m_active_blocks.contains(obj->m_static_block) &&
2047 m_active_blocks.contains(blockpos_o))
2049 v3s16 old_static_block = obj->m_static_block;
2051 // Save to block where object is located
2052 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2054 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2055 <<"Could not save object id="<<id
2056 <<" to it's current block "<<PP(blockpos_o)
2060 std::string staticdata_new = obj->getStaticData();
2061 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2062 block->m_static_objects.insert(id, s_obj);
2063 obj->m_static_block = blockpos_o;
2064 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2065 MOD_REASON_STATIC_DATA_ADDED);
2067 // Delete from block where object was located
2068 block = m_map->emergeBlock(old_static_block, false);
2070 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2071 <<"Could not delete object id="<<id
2072 <<" from it's previous block "<<PP(old_static_block)
2076 block->m_static_objects.remove(id);
2077 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2078 MOD_REASON_STATIC_DATA_REMOVED);
2082 // If block is active, don't remove
2083 if(!force_delete && m_active_blocks.contains(blockpos_o))
2086 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2087 <<"deactivating object id="<<id<<" on inactive block "
2088 <<PP(blockpos_o)<<std::endl;
2090 // If known by some client, don't immediately delete.
2091 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2094 Update the static data
2097 if(obj->isStaticAllowed())
2099 // Create new static object
2100 std::string staticdata_new = obj->getStaticData();
2101 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2103 bool stays_in_same_block = false;
2104 bool data_changed = true;
2106 if (obj->m_static_exists) {
2107 if (obj->m_static_block == blockpos_o)
2108 stays_in_same_block = true;
2110 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2113 std::map<u16, StaticObject>::iterator n =
2114 block->m_static_objects.m_active.find(id);
2115 if (n != block->m_static_objects.m_active.end()) {
2116 StaticObject static_old = n->second;
2118 float save_movem = obj->getMinimumSavedMovement();
2120 if (static_old.data == staticdata_new &&
2121 (static_old.pos - objectpos).getLength() < save_movem)
2122 data_changed = false;
2124 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2125 <<"id="<<id<<" m_static_exists=true but "
2126 <<"static data doesn't actually exist in "
2127 <<PP(obj->m_static_block)<<std::endl;
2132 bool shall_be_written = (!stays_in_same_block || data_changed);
2134 // Delete old static object
2135 if(obj->m_static_exists)
2137 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2140 block->m_static_objects.remove(id);
2141 obj->m_static_exists = false;
2142 // Only mark block as modified if data changed considerably
2143 if(shall_be_written)
2144 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2145 MOD_REASON_STATIC_DATA_CHANGED);
2149 // Add to the block where the object is located in
2150 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2151 // Get or generate the block
2152 MapBlock *block = NULL;
2154 block = m_map->emergeBlock(blockpos);
2155 } catch(InvalidPositionException &e){
2156 // Handled via NULL pointer
2157 // NOTE: emergeBlock's failure is usually determined by it
2158 // actually returning NULL
2163 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2164 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2165 <<" statically but block "<<PP(blockpos)
2166 <<" already contains "
2167 <<block->m_static_objects.m_stored.size()
2169 <<" Forcing delete."<<std::endl;
2170 force_delete = true;
2172 // If static counterpart already exists in target block,
2174 // This shouldn't happen because the object is removed from
2175 // the previous block before this according to
2176 // obj->m_static_block, but happens rarely for some unknown
2177 // reason. Unsuccessful attempts have been made to find
2179 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2180 warningstream<<"ServerEnv: Performing hack #83274"
2182 block->m_static_objects.remove(id);
2184 // Store static data
2185 u16 store_id = pending_delete ? id : 0;
2186 block->m_static_objects.insert(store_id, s_obj);
2188 // Only mark block as modified if data changed considerably
2189 if(shall_be_written)
2190 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2191 MOD_REASON_STATIC_DATA_CHANGED);
2193 obj->m_static_exists = true;
2194 obj->m_static_block = block->getPos();
2199 v3s16 p = floatToInt(objectpos, BS);
2200 errorstream<<"ServerEnv: Could not find or generate "
2201 <<"a block for storing id="<<obj->getId()
2202 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2209 If known by some client, set pending deactivation.
2210 Otherwise delete it immediately.
2213 if(pending_delete && !force_delete)
2215 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2216 <<"object id="<<id<<" is known by clients"
2217 <<"; not deleting yet"<<std::endl;
2219 obj->m_pending_deactivation = true;
2223 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2224 <<"object id="<<id<<" is not known by clients"
2225 <<"; deleting"<<std::endl;
2227 // Tell the object about removal
2228 obj->removingFromEnvironment();
2229 // Deregister in scripting api
2230 m_script->removeObjectReference(obj);
2232 // Delete active object
2233 if(obj->environmentDeletes())
2235 // Id to be removed from m_active_objects
2236 objects_to_remove.push_back(id);
2239 // Remove references from m_active_objects
2240 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2241 i != objects_to_remove.end(); ++i) {
2242 m_active_objects.erase(*i);
2248 #include "clientsimpleobject.h"
2254 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2255 ITextureSource *texturesource, IGameDef *gamedef,
2256 IrrlichtDevice *irr):
2259 m_texturesource(texturesource),
2264 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2267 ClientEnvironment::~ClientEnvironment()
2269 // delete active objects
2270 for (UNORDERED_MAP<u16, ClientActiveObject*>::iterator i = m_active_objects.begin();
2271 i != m_active_objects.end(); ++i) {
2275 for(std::vector<ClientSimpleObject*>::iterator
2276 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2284 Map & ClientEnvironment::getMap()
2289 ClientMap & ClientEnvironment::getClientMap()
2294 LocalPlayer *ClientEnvironment::getPlayer(const u16 peer_id)
2296 return dynamic_cast<LocalPlayer *>(Environment::getPlayer(peer_id));
2299 LocalPlayer *ClientEnvironment::getPlayer(const char* name)
2301 return dynamic_cast<LocalPlayer *>(Environment::getPlayer(name));
2304 void ClientEnvironment::addPlayer(LocalPlayer *player)
2306 DSTACK(FUNCTION_NAME);
2308 It is a failure if already is a local player
2310 FATAL_ERROR_IF(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 Player *player = *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