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();
607 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
608 (*it)->protocol_version, (AccessDeniedCode)reason,
609 str_reason, reconnect);
613 void ServerEnvironment::saveLoadedPlayers()
615 std::string players_path = m_path_world + DIR_DELIM "players";
616 fs::CreateDir(players_path);
618 for (std::vector<Player*>::iterator it = m_players.begin();
619 it != m_players.end();
621 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
622 if (player->checkModified()) {
623 player->save(players_path);
628 void ServerEnvironment::savePlayer(RemotePlayer *player)
630 std::string players_path = m_path_world + DIR_DELIM "players";
631 fs::CreateDir(players_path);
633 player->save(players_path);
636 Player *ServerEnvironment::loadPlayer(const std::string &playername)
638 bool newplayer = false;
640 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
641 std::string path = players_path + playername;
643 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
645 player = new RemotePlayer(m_gamedef, "");
649 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
650 //// Open file and deserialize
651 std::ifstream is(path.c_str(), std::ios_base::binary);
654 player->deSerialize(is, path);
657 if (player->getName() == playername) {
662 path = players_path + playername + itos(i);
666 infostream << "Player file for player " << playername
667 << " not found" << std::endl;
675 player->setModified(false);
679 void ServerEnvironment::saveMeta()
681 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
683 // Open file and serialize
684 std::ostringstream ss(std::ios_base::binary);
687 args.setU64("game_time", m_game_time);
688 args.setU64("time_of_day", getTimeOfDay());
689 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
690 args.setU64("lbm_introduction_times_version", 1);
691 args.set("lbm_introduction_times",
692 m_lbm_mgr.createIntroductionTimesString());
693 args.setU64("day_count", m_day_count);
697 if(!fs::safeWriteToFile(path, ss.str()))
699 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
701 throw SerializationError("Couldn't save env meta");
705 void ServerEnvironment::loadMeta()
707 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
709 // Open file and deserialize
710 std::ifstream is(path.c_str(), std::ios_base::binary);
712 infostream << "ServerEnvironment::loadMeta(): Failed to open "
713 << path << std::endl;
714 throw SerializationError("Couldn't load env meta");
719 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
720 throw SerializationError("ServerEnvironment::loadMeta(): "
721 "EnvArgsEnd not found!");
725 m_game_time = args.getU64("game_time");
726 } catch (SettingNotFoundException &e) {
727 // Getting this is crucial, otherwise timestamps are useless
728 throw SerializationError("Couldn't load env meta game_time");
731 setTimeOfDay(args.exists("time_of_day") ?
732 // set day to morning by default
733 args.getU64("time_of_day") : 9000);
735 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
736 // If missing, do as if clearObjects was never called
737 args.getU64("last_clear_objects_time") : 0;
739 std::string lbm_introduction_times = "";
741 u64 ver = args.getU64("lbm_introduction_times_version");
743 lbm_introduction_times = args.get("lbm_introduction_times");
745 infostream << "ServerEnvironment::loadMeta(): Non-supported"
746 << " introduction time version " << ver << std::endl;
748 } catch (SettingNotFoundException &e) {
749 // No problem, this is expected. Just continue with an empty string
751 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
753 m_day_count = args.exists("day_count") ?
754 args.getU64("day_count") : 0;
757 void ServerEnvironment::loadDefaultMeta()
759 m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
764 ActiveBlockModifier *abm;
766 std::set<content_t> required_neighbors;
772 ServerEnvironment *m_env;
773 std::map<content_t, std::vector<ActiveABM> > m_aabms;
775 ABMHandler(std::vector<ABMWithState> &abms,
776 float dtime_s, ServerEnvironment *env,
782 INodeDefManager *ndef = env->getGameDef()->ndef();
783 for(std::vector<ABMWithState>::iterator
784 i = abms.begin(); i != abms.end(); ++i) {
785 ActiveBlockModifier *abm = i->abm;
786 float trigger_interval = abm->getTriggerInterval();
787 if(trigger_interval < 0.001)
788 trigger_interval = 0.001;
789 float actual_interval = dtime_s;
792 if(i->timer < trigger_interval)
794 i->timer -= trigger_interval;
795 actual_interval = trigger_interval;
797 float chance = abm->getTriggerChance();
802 if(abm->getSimpleCatchUp()) {
803 float intervals = actual_interval / trigger_interval;
806 aabm.chance = chance / intervals;
810 aabm.chance = chance;
813 std::set<std::string> required_neighbors_s
814 = abm->getRequiredNeighbors();
815 for(std::set<std::string>::iterator
816 i = required_neighbors_s.begin();
817 i != required_neighbors_s.end(); ++i)
819 ndef->getIds(*i, aabm.required_neighbors);
822 std::set<std::string> contents_s = abm->getTriggerContents();
823 for(std::set<std::string>::iterator
824 i = contents_s.begin(); i != contents_s.end(); ++i)
826 std::set<content_t> ids;
827 ndef->getIds(*i, ids);
828 for(std::set<content_t>::const_iterator k = ids.begin();
832 std::map<content_t, std::vector<ActiveABM> >::iterator j;
834 if(j == m_aabms.end()){
835 std::vector<ActiveABM> aabmlist;
836 m_aabms[c] = aabmlist;
839 j->second.push_back(aabm);
844 // Find out how many objects the given block and its neighbours contain.
845 // Returns the number of objects in the block, and also in 'wider' the
846 // number of objects in the block and all its neighbours. The latter
847 // may an estimate if any neighbours are unloaded.
848 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
851 u32 wider_unknown_count = 0;
852 for(s16 x=-1; x<=1; x++)
853 for(s16 y=-1; y<=1; y++)
854 for(s16 z=-1; z<=1; z++)
856 MapBlock *block2 = map->getBlockNoCreateNoEx(
857 block->getPos() + v3s16(x,y,z));
859 wider_unknown_count++;
862 wider += block2->m_static_objects.m_active.size()
863 + block2->m_static_objects.m_stored.size();
866 u32 active_object_count = block->m_static_objects.m_active.size();
867 u32 wider_known_count = 3*3*3 - wider_unknown_count;
868 wider += wider_unknown_count * wider / wider_known_count;
869 return active_object_count;
872 void apply(MapBlock *block)
877 ServerMap *map = &m_env->getServerMap();
879 u32 active_object_count_wider;
880 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
881 m_env->m_added_objects = 0;
884 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
885 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
886 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
888 MapNode n = block->getNodeNoEx(p0);
889 content_t c = n.getContent();
890 v3s16 p = p0 + block->getPosRelative();
892 std::map<content_t, std::vector<ActiveABM> >::iterator j;
894 if(j == m_aabms.end())
897 for(std::vector<ActiveABM>::iterator
898 i = j->second.begin(); i != j->second.end(); ++i) {
899 if(myrand() % i->chance != 0)
903 if(!i->required_neighbors.empty())
906 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
907 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
908 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
912 MapNode n = map->getNodeNoEx(p1);
913 content_t c = n.getContent();
914 std::set<content_t>::const_iterator k;
915 k = i->required_neighbors.find(c);
916 if(k != i->required_neighbors.end()){
920 // No required neighbor found
925 // Call all the trigger variations
926 i->abm->trigger(m_env, p, n);
927 i->abm->trigger(m_env, p, n,
928 active_object_count, active_object_count_wider);
930 // Count surrounding objects again if the abms added any
931 if(m_env->m_added_objects > 0) {
932 active_object_count = countObjects(block, map, active_object_count_wider);
933 m_env->m_added_objects = 0;
940 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
942 // Reset usage timer immediately, otherwise a block that becomes active
943 // again at around the same time as it would normally be unloaded will
944 // get unloaded incorrectly. (I think this still leaves a small possibility
945 // of a race condition between this and server::AsyncRunStep, which only
946 // some kind of synchronisation will fix, but it at least reduces the window
947 // of opportunity for it to break from seconds to nanoseconds)
948 block->resetUsageTimer();
950 // Get time difference
952 u32 stamp = block->getTimestamp();
953 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
954 dtime_s = m_game_time - stamp;
955 dtime_s += additional_dtime;
957 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
958 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
960 // Remove stored static objects if clearObjects was called since block's timestamp
961 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
962 block->m_static_objects.m_stored.clear();
963 // do not set changed flag to avoid unnecessary mapblock writes
966 // Set current time as timestamp
967 block->setTimestampNoChangedFlag(m_game_time);
969 /*infostream<<"ServerEnvironment::activateBlock(): block is "
970 <<dtime_s<<" seconds old."<<std::endl;*/
972 // Activate stored objects
973 activateObjects(block, dtime_s);
975 /* Handle LoadingBlockModifiers */
976 m_lbm_mgr.applyLBMs(this, block, stamp);
979 std::vector<NodeTimer> elapsed_timers =
980 block->m_node_timers.step((float)dtime_s);
981 if (!elapsed_timers.empty()) {
983 for (std::vector<NodeTimer>::iterator
984 i = elapsed_timers.begin();
985 i != elapsed_timers.end(); ++i){
986 n = block->getNodeNoEx(i->position);
987 v3s16 p = i->position + block->getPosRelative();
988 if (m_script->node_on_timer(p, n, i->elapsed))
989 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
993 /* Handle ActiveBlockModifiers */
994 ABMHandler abmhandler(m_abms, dtime_s, this, false);
995 abmhandler.apply(block);
998 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
1000 m_abms.push_back(ABMWithState(abm));
1003 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1005 m_lbm_mgr.addLBMDef(lbm);
1008 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1010 INodeDefManager *ndef = m_gamedef->ndef();
1011 MapNode n_old = m_map->getNodeNoEx(p);
1014 if (ndef->get(n_old).has_on_destruct)
1015 m_script->node_on_destruct(p, n_old);
1018 if (!m_map->addNodeWithEvent(p, n))
1021 // Update active VoxelManipulator if a mapgen thread
1022 m_map->updateVManip(p);
1024 // Call post-destructor
1025 if (ndef->get(n_old).has_after_destruct)
1026 m_script->node_after_destruct(p, n_old);
1029 if (ndef->get(n).has_on_construct)
1030 m_script->node_on_construct(p, n);
1035 bool ServerEnvironment::removeNode(v3s16 p)
1037 INodeDefManager *ndef = m_gamedef->ndef();
1038 MapNode n_old = m_map->getNodeNoEx(p);
1041 if (ndef->get(n_old).has_on_destruct)
1042 m_script->node_on_destruct(p, n_old);
1045 // This is slightly optimized compared to addNodeWithEvent(air)
1046 if (!m_map->removeNodeWithEvent(p))
1049 // Update active VoxelManipulator if a mapgen thread
1050 m_map->updateVManip(p);
1052 // Call post-destructor
1053 if (ndef->get(n_old).has_after_destruct)
1054 m_script->node_after_destruct(p, n_old);
1056 // Air doesn't require constructor
1060 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1062 if (!m_map->addNodeWithEvent(p, n, false))
1065 // Update active VoxelManipulator if a mapgen thread
1066 m_map->updateVManip(p);
1071 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1073 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1074 i != m_active_objects.end(); ++i) {
1075 ServerActiveObject* obj = i->second;
1077 v3f objectpos = obj->getBasePosition();
1078 if (objectpos.getDistanceFrom(pos) > radius)
1080 objects.push_back(id);
1084 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1086 infostream << "ServerEnvironment::clearObjects(): "
1087 << "Removing all active objects" << std::endl;
1088 std::vector<u16> objects_to_remove;
1089 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1090 i != m_active_objects.end(); ++i) {
1091 ServerActiveObject* obj = i->second;
1092 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1095 // Delete static object if block is loaded
1096 if (obj->m_static_exists) {
1097 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1099 block->m_static_objects.remove(id);
1100 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1101 MOD_REASON_CLEAR_ALL_OBJECTS);
1102 obj->m_static_exists = false;
1105 // If known by some client, don't delete immediately
1106 if (obj->m_known_by_count > 0) {
1107 obj->m_pending_deactivation = true;
1108 obj->m_removed = true;
1112 // Tell the object about removal
1113 obj->removingFromEnvironment();
1114 // Deregister in scripting api
1115 m_script->removeObjectReference(obj);
1117 // Delete active object
1118 if (obj->environmentDeletes())
1120 // Id to be removed from m_active_objects
1121 objects_to_remove.push_back(id);
1124 // Remove references from m_active_objects
1125 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1126 i != objects_to_remove.end(); ++i) {
1127 m_active_objects.erase(*i);
1130 // Get list of loaded blocks
1131 std::vector<v3s16> loaded_blocks;
1132 infostream << "ServerEnvironment::clearObjects(): "
1133 << "Listing all loaded blocks" << std::endl;
1134 m_map->listAllLoadedBlocks(loaded_blocks);
1135 infostream << "ServerEnvironment::clearObjects(): "
1136 << "Done listing all loaded blocks: "
1137 << loaded_blocks.size()<<std::endl;
1139 // Get list of loadable blocks
1140 std::vector<v3s16> loadable_blocks;
1141 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1142 infostream << "ServerEnvironment::clearObjects(): "
1143 << "Listing all loadable blocks" << std::endl;
1144 m_map->listAllLoadableBlocks(loadable_blocks);
1145 infostream << "ServerEnvironment::clearObjects(): "
1146 << "Done listing all loadable blocks: "
1147 << loadable_blocks.size() << std::endl;
1149 loadable_blocks = loaded_blocks;
1152 infostream << "ServerEnvironment::clearObjects(): "
1153 << "Now clearing objects in " << loadable_blocks.size()
1154 << " blocks" << std::endl;
1156 // Grab a reference on each loaded block to avoid unloading it
1157 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1158 i != loaded_blocks.end(); ++i) {
1160 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1161 assert(block != NULL);
1165 // Remove objects in all loadable blocks
1166 u32 unload_interval = U32_MAX;
1167 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1168 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1169 unload_interval = MYMAX(unload_interval, 1);
1171 u32 report_interval = loadable_blocks.size() / 10;
1172 u32 num_blocks_checked = 0;
1173 u32 num_blocks_cleared = 0;
1174 u32 num_objs_cleared = 0;
1175 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1176 i != loadable_blocks.end(); ++i) {
1178 MapBlock *block = m_map->emergeBlock(p, false);
1180 errorstream << "ServerEnvironment::clearObjects(): "
1181 << "Failed to emerge block " << PP(p) << std::endl;
1184 u32 num_stored = block->m_static_objects.m_stored.size();
1185 u32 num_active = block->m_static_objects.m_active.size();
1186 if (num_stored != 0 || num_active != 0) {
1187 block->m_static_objects.m_stored.clear();
1188 block->m_static_objects.m_active.clear();
1189 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1190 MOD_REASON_CLEAR_ALL_OBJECTS);
1191 num_objs_cleared += num_stored + num_active;
1192 num_blocks_cleared++;
1194 num_blocks_checked++;
1196 if (report_interval != 0 &&
1197 num_blocks_checked % report_interval == 0) {
1198 float percent = 100.0 * (float)num_blocks_checked /
1199 loadable_blocks.size();
1200 infostream << "ServerEnvironment::clearObjects(): "
1201 << "Cleared " << num_objs_cleared << " objects"
1202 << " in " << num_blocks_cleared << " blocks ("
1203 << percent << "%)" << std::endl;
1205 if (num_blocks_checked % unload_interval == 0) {
1206 m_map->unloadUnreferencedBlocks();
1209 m_map->unloadUnreferencedBlocks();
1211 // Drop references that were added above
1212 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1213 i != loaded_blocks.end(); ++i) {
1215 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1220 m_last_clear_objects_time = m_game_time;
1222 infostream << "ServerEnvironment::clearObjects(): "
1223 << "Finished: Cleared " << num_objs_cleared << " objects"
1224 << " in " << num_blocks_cleared << " blocks" << std::endl;
1227 void ServerEnvironment::step(float dtime)
1229 DSTACK(FUNCTION_NAME);
1231 //TimeTaker timer("ServerEnv step");
1233 /* Step time of day */
1234 stepTimeOfDay(dtime);
1237 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1238 // really matter that much.
1239 static const float server_step = g_settings->getFloat("dedicated_server_step");
1240 m_recommended_send_interval = server_step;
1246 m_game_time_fraction_counter += dtime;
1247 u32 inc_i = (u32)m_game_time_fraction_counter;
1248 m_game_time += inc_i;
1249 m_game_time_fraction_counter -= (float)inc_i;
1256 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1257 for(std::vector<Player*>::iterator i = m_players.begin();
1258 i != m_players.end(); ++i)
1260 Player *player = *i;
1262 // Ignore disconnected players
1263 if(player->peer_id == 0)
1267 player->move(dtime, this, 100*BS);
1272 Manage active block list
1274 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1275 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1277 Get player block positions
1279 std::vector<v3s16> players_blockpos;
1280 for(std::vector<Player*>::iterator
1281 i = m_players.begin();
1282 i != m_players.end(); ++i) {
1283 Player *player = *i;
1284 // Ignore disconnected players
1285 if(player->peer_id == 0)
1288 v3s16 blockpos = getNodeBlockPos(
1289 floatToInt(player->getPosition(), BS));
1290 players_blockpos.push_back(blockpos);
1294 Update list of active blocks, collecting changes
1296 static const s16 active_block_range = g_settings->getS16("active_block_range");
1297 std::set<v3s16> blocks_removed;
1298 std::set<v3s16> blocks_added;
1299 m_active_blocks.update(players_blockpos, active_block_range,
1300 blocks_removed, blocks_added);
1303 Handle removed blocks
1306 // Convert active objects that are no more in active blocks to static
1307 deactivateFarObjects(false);
1309 for(std::set<v3s16>::iterator
1310 i = blocks_removed.begin();
1311 i != blocks_removed.end(); ++i) {
1314 /* infostream<<"Server: Block " << PP(p)
1315 << " became inactive"<<std::endl; */
1317 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1321 // Set current time as timestamp (and let it set ChangedFlag)
1322 block->setTimestamp(m_game_time);
1329 for(std::set<v3s16>::iterator
1330 i = blocks_added.begin();
1331 i != blocks_added.end(); ++i)
1335 MapBlock *block = m_map->getBlockOrEmerge(p);
1337 m_active_blocks.m_list.erase(p);
1341 activateBlock(block);
1342 /* infostream<<"Server: Block " << PP(p)
1343 << " became active"<<std::endl; */
1348 Mess around in active blocks
1350 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1351 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1353 float dtime = m_cache_nodetimer_interval;
1355 for(std::set<v3s16>::iterator
1356 i = m_active_blocks.m_list.begin();
1357 i != m_active_blocks.m_list.end(); ++i)
1361 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1362 <<") being handled"<<std::endl;*/
1364 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1368 // Reset block usage timer
1369 block->resetUsageTimer();
1371 // Set current time as timestamp
1372 block->setTimestampNoChangedFlag(m_game_time);
1373 // If time has changed much from the one on disk,
1374 // set block to be saved when it is unloaded
1375 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1376 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1377 MOD_REASON_BLOCK_EXPIRED);
1380 std::vector<NodeTimer> elapsed_timers =
1381 block->m_node_timers.step((float)dtime);
1382 if (!elapsed_timers.empty()) {
1384 for (std::vector<NodeTimer>::iterator
1385 i = elapsed_timers.begin();
1386 i != elapsed_timers.end(); ++i) {
1387 n = block->getNodeNoEx(i->position);
1388 p = i->position + block->getPosRelative();
1389 if (m_script->node_on_timer(p, n, i->elapsed)) {
1390 block->setNodeTimer(NodeTimer(
1391 i->timeout, 0, i->position));
1398 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1400 if(m_active_block_interval_overload_skip > 0){
1401 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1402 m_active_block_interval_overload_skip--;
1405 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1406 TimeTaker timer("modify in active blocks per interval");
1408 // Initialize handling of ActiveBlockModifiers
1409 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1411 for(std::set<v3s16>::iterator
1412 i = m_active_blocks.m_list.begin();
1413 i != m_active_blocks.m_list.end(); ++i)
1417 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1418 <<") being handled"<<std::endl;*/
1420 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1424 // Set current time as timestamp
1425 block->setTimestampNoChangedFlag(m_game_time);
1427 /* Handle ActiveBlockModifiers */
1428 abmhandler.apply(block);
1431 u32 time_ms = timer.stop(true);
1432 u32 max_time_ms = 200;
1433 if(time_ms > max_time_ms){
1434 warningstream<<"active block modifiers took "
1435 <<time_ms<<"ms (longer than "
1436 <<max_time_ms<<"ms)"<<std::endl;
1437 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1442 Step script environment (run global on_step())
1444 m_script->environment_Step(dtime);
1450 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1451 //TimeTaker timer("Step active objects");
1453 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1455 // This helps the objects to send data at the same time
1456 bool send_recommended = false;
1457 m_send_recommended_timer += dtime;
1458 if(m_send_recommended_timer > getSendRecommendedInterval())
1460 m_send_recommended_timer -= getSendRecommendedInterval();
1461 send_recommended = true;
1464 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1465 i != m_active_objects.end(); ++i) {
1466 ServerActiveObject* obj = i->second;
1467 // Don't step if is to be removed or stored statically
1468 if(obj->m_removed || obj->m_pending_deactivation)
1471 obj->step(dtime, send_recommended);
1472 // Read messages from object
1473 while(!obj->m_messages_out.empty())
1475 m_active_object_messages.push(
1476 obj->m_messages_out.front());
1477 obj->m_messages_out.pop();
1483 Manage active objects
1485 if(m_object_management_interval.step(dtime, 0.5))
1487 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1489 Remove objects that satisfy (m_removed && m_known_by_count==0)
1491 removeRemovedObjects();
1495 Manage particle spawner expiration
1497 if (m_particle_management_interval.step(dtime, 1.0)) {
1498 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1499 i != m_particle_spawners.end(); ) {
1500 //non expiring spawners
1501 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1507 if (i->second <= 0.f)
1508 m_particle_spawners.erase(i++);
1515 u32 ServerEnvironment::addParticleSpawner(float exptime)
1517 // Timers with lifetime 0 do not expire
1518 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1521 for (;;) { // look for unused particlespawner id
1523 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1524 if (f == m_particle_spawners.end()) {
1525 m_particle_spawners[id] = time;
1532 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1534 ActiveObjectMap::iterator n = m_active_objects.find(id);
1535 return (n != m_active_objects.end() ? n->second : NULL);
1538 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1543 return objects.find(id) == objects.end();
1546 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1548 //try to reuse id's as late as possible
1549 static u16 last_used_id = 0;
1550 u16 startid = last_used_id;
1554 if(isFreeServerActiveObjectId(last_used_id, objects))
1555 return last_used_id;
1557 if(last_used_id == startid)
1562 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1564 assert(object); // Pre-condition
1566 u16 id = addActiveObjectRaw(object, true, 0);
1571 Finds out what new objects have been added to
1572 inside a radius around a position
1574 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1576 std::set<u16> ¤t_objects,
1577 std::queue<u16> &added_objects)
1579 f32 radius_f = radius * BS;
1580 f32 player_radius_f = player_radius * BS;
1582 if (player_radius_f < 0)
1583 player_radius_f = 0;
1586 Go through the object list,
1587 - discard m_removed objects,
1588 - discard objects that are too far away,
1589 - discard objects that are found in current_objects.
1590 - add remaining objects to added_objects
1592 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1593 i != m_active_objects.end(); ++i) {
1597 ServerActiveObject *object = i->second;
1601 // Discard if removed or deactivating
1602 if(object->m_removed || object->m_pending_deactivation)
1605 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1606 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1607 // Discard if too far
1608 if (distance_f > player_radius_f && player_radius_f != 0)
1610 } else if (distance_f > radius_f)
1613 // Discard if already on current_objects
1614 std::set<u16>::iterator n;
1615 n = current_objects.find(id);
1616 if(n != current_objects.end())
1618 // Add to added_objects
1619 added_objects.push(id);
1624 Finds out what objects have been removed from
1625 inside a radius around a position
1627 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1629 std::set<u16> ¤t_objects,
1630 std::queue<u16> &removed_objects)
1632 f32 radius_f = radius * BS;
1633 f32 player_radius_f = player_radius * BS;
1635 if (player_radius_f < 0)
1636 player_radius_f = 0;
1639 Go through current_objects; object is removed if:
1640 - object is not found in m_active_objects (this is actually an
1641 error condition; objects should be set m_removed=true and removed
1642 only after all clients have been informed about removal), or
1643 - object has m_removed=true, or
1644 - object is too far away
1646 for(std::set<u16>::iterator
1647 i = current_objects.begin();
1648 i != current_objects.end(); ++i)
1651 ServerActiveObject *object = getActiveObject(id);
1653 if (object == NULL) {
1654 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1655 << " object in current_objects is NULL" << std::endl;
1656 removed_objects.push(id);
1660 if (object->m_removed || object->m_pending_deactivation) {
1661 removed_objects.push(id);
1665 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1666 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1667 if (distance_f <= player_radius_f || player_radius_f == 0)
1669 } else if (distance_f <= radius_f)
1672 // Object is no longer visible
1673 removed_objects.push(id);
1677 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1678 v3s16 blockpos, bool static_exists, v3s16 static_block)
1680 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1684 for (std::map<u16, StaticObject>::iterator
1685 so_it = block->m_static_objects.m_active.begin();
1686 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1687 // Get the ServerActiveObject counterpart to this StaticObject
1688 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1689 if (ao_it == m_active_objects.end()) {
1690 // If this ever happens, there must be some kind of nasty bug.
1691 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1692 "Object from MapBlock::m_static_objects::m_active not found "
1693 "in m_active_objects";
1697 ServerActiveObject *sao = ao_it->second;
1698 sao->m_static_exists = static_exists;
1699 sao->m_static_block = static_block;
1703 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1705 if(m_active_object_messages.empty())
1706 return ActiveObjectMessage(0);
1708 ActiveObjectMessage message = m_active_object_messages.front();
1709 m_active_object_messages.pop();
1714 ************ Private methods *************
1717 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1718 bool set_changed, u32 dtime_s)
1720 assert(object); // Pre-condition
1721 if(object->getId() == 0){
1722 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1725 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1726 <<"no free ids available"<<std::endl;
1727 if(object->environmentDeletes())
1731 object->setId(new_id);
1734 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1735 <<"supplied with id "<<object->getId()<<std::endl;
1738 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1739 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1740 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1741 if(object->environmentDeletes())
1746 if (objectpos_over_limit(object->getBasePosition())) {
1747 v3f p = object->getBasePosition();
1748 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1749 << "object position (" << p.X << "," << p.Y << "," << p.Z
1750 << ") outside maximum range" << std::endl;
1751 if (object->environmentDeletes())
1756 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1757 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1759 m_active_objects[object->getId()] = object;
1761 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1762 <<"Added id="<<object->getId()<<"; there are now "
1763 <<m_active_objects.size()<<" active objects."
1766 // Register reference in scripting api (must be done before post-init)
1767 m_script->addObjectReference(object);
1768 // Post-initialize object
1769 object->addedToEnvironment(dtime_s);
1771 // Add static data to block
1772 if(object->isStaticAllowed())
1774 // Add static object to active static list of the block
1775 v3f objectpos = object->getBasePosition();
1776 std::string staticdata = object->getStaticData();
1777 StaticObject s_obj(object->getType(), objectpos, staticdata);
1778 // Add to the block where the object is located in
1779 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1780 MapBlock *block = m_map->emergeBlock(blockpos);
1782 block->m_static_objects.m_active[object->getId()] = s_obj;
1783 object->m_static_exists = true;
1784 object->m_static_block = blockpos;
1787 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1788 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1790 v3s16 p = floatToInt(objectpos, BS);
1791 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1792 <<"could not emerge block for storing id="<<object->getId()
1793 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1797 return object->getId();
1801 Remove objects that satisfy (m_removed && m_known_by_count==0)
1803 void ServerEnvironment::removeRemovedObjects()
1805 std::vector<u16> objects_to_remove;
1806 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1807 i != m_active_objects.end(); ++i) {
1809 ServerActiveObject* obj = i->second;
1810 // This shouldn't happen but check it
1813 infostream<<"NULL object found in ServerEnvironment"
1814 <<" while finding removed objects. id="<<id<<std::endl;
1815 // Id to be removed from m_active_objects
1816 objects_to_remove.push_back(id);
1821 We will delete objects that are marked as removed or thatare
1822 waiting for deletion after deactivation
1824 if (!obj->m_removed && !obj->m_pending_deactivation)
1828 Delete static data from block if is marked as removed
1830 if(obj->m_static_exists && obj->m_removed)
1832 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1834 block->m_static_objects.remove(id);
1835 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1836 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1837 obj->m_static_exists = false;
1839 infostream<<"Failed to emerge block from which an object to "
1840 <<"be removed was loaded from. id="<<id<<std::endl;
1844 // If m_known_by_count > 0, don't actually remove. On some future
1845 // invocation this will be 0, which is when removal will continue.
1846 if(obj->m_known_by_count > 0)
1850 Move static data from active to stored if not marked as removed
1852 if(obj->m_static_exists && !obj->m_removed){
1853 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1855 std::map<u16, StaticObject>::iterator i =
1856 block->m_static_objects.m_active.find(id);
1857 if(i != block->m_static_objects.m_active.end()){
1858 block->m_static_objects.m_stored.push_back(i->second);
1859 block->m_static_objects.m_active.erase(id);
1860 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1861 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1864 infostream<<"Failed to emerge block from which an object to "
1865 <<"be deactivated was loaded from. id="<<id<<std::endl;
1869 // Tell the object about removal
1870 obj->removingFromEnvironment();
1871 // Deregister in scripting api
1872 m_script->removeObjectReference(obj);
1875 if(obj->environmentDeletes())
1878 // Id to be removed from m_active_objects
1879 objects_to_remove.push_back(id);
1881 // Remove references from m_active_objects
1882 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1883 i != objects_to_remove.end(); ++i) {
1884 m_active_objects.erase(*i);
1888 static void print_hexdump(std::ostream &o, const std::string &data)
1890 const int linelength = 16;
1891 for(int l=0; ; l++){
1892 int i0 = linelength * l;
1893 bool at_end = false;
1894 int thislinelength = linelength;
1895 if(i0 + thislinelength > (int)data.size()){
1896 thislinelength = data.size() - i0;
1899 for(int di=0; di<linelength; di++){
1902 if(di<thislinelength)
1903 snprintf(buf, 4, "%.2x ", data[i]);
1905 snprintf(buf, 4, " ");
1909 for(int di=0; di<thislinelength; di++){
1923 Convert stored objects from blocks near the players to active.
1925 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1930 // Ignore if no stored objects (to not set changed flag)
1931 if(block->m_static_objects.m_stored.empty())
1934 verbosestream<<"ServerEnvironment::activateObjects(): "
1935 <<"activating objects of block "<<PP(block->getPos())
1936 <<" ("<<block->m_static_objects.m_stored.size()
1937 <<" objects)"<<std::endl;
1938 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1940 errorstream<<"suspiciously large amount of objects detected: "
1941 <<block->m_static_objects.m_stored.size()<<" in "
1942 <<PP(block->getPos())
1943 <<"; removing all of them."<<std::endl;
1944 // Clear stored list
1945 block->m_static_objects.m_stored.clear();
1946 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1947 MOD_REASON_TOO_MANY_OBJECTS);
1951 // Activate stored objects
1952 std::vector<StaticObject> new_stored;
1953 for (std::vector<StaticObject>::iterator
1954 i = block->m_static_objects.m_stored.begin();
1955 i != block->m_static_objects.m_stored.end(); ++i) {
1956 StaticObject &s_obj = *i;
1958 // Create an active object from the data
1959 ServerActiveObject *obj = ServerActiveObject::create
1960 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1961 // If couldn't create object, store static data back.
1963 errorstream<<"ServerEnvironment::activateObjects(): "
1964 <<"failed to create active object from static object "
1965 <<"in block "<<PP(s_obj.pos/BS)
1966 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1967 print_hexdump(verbosestream, s_obj.data);
1969 new_stored.push_back(s_obj);
1972 verbosestream<<"ServerEnvironment::activateObjects(): "
1973 <<"activated static object pos="<<PP(s_obj.pos/BS)
1974 <<" type="<<(int)s_obj.type<<std::endl;
1975 // This will also add the object to the active static list
1976 addActiveObjectRaw(obj, false, dtime_s);
1978 // Clear stored list
1979 block->m_static_objects.m_stored.clear();
1980 // Add leftover failed stuff to stored list
1981 for(std::vector<StaticObject>::iterator
1982 i = new_stored.begin();
1983 i != new_stored.end(); ++i) {
1984 StaticObject &s_obj = *i;
1985 block->m_static_objects.m_stored.push_back(s_obj);
1988 // Turn the active counterparts of activated objects not pending for
1990 for(std::map<u16, StaticObject>::iterator
1991 i = block->m_static_objects.m_active.begin();
1992 i != block->m_static_objects.m_active.end(); ++i)
1995 ServerActiveObject *object = getActiveObject(id);
1997 object->m_pending_deactivation = false;
2001 Note: Block hasn't really been modified here.
2002 The objects have just been activated and moved from the stored
2003 static list to the active static list.
2004 As such, the block is essentially the same.
2005 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2006 Otherwise there would be a huge amount of unnecessary I/O.
2011 Convert objects that are not standing inside active blocks to static.
2013 If m_known_by_count != 0, active object is not deleted, but static
2014 data is still updated.
2016 If force_delete is set, active object is deleted nevertheless. It
2017 shall only be set so in the destructor of the environment.
2019 If block wasn't generated (not in memory or on disk),
2021 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2023 std::vector<u16> objects_to_remove;
2024 for(ActiveObjectMap::iterator i = m_active_objects.begin();
2025 i != m_active_objects.end(); ++i) {
2026 ServerActiveObject* obj = i->second;
2029 // Do not deactivate if static data creation not allowed
2030 if(!force_delete && !obj->isStaticAllowed())
2033 // If pending deactivation, let removeRemovedObjects() do it
2034 if(!force_delete && obj->m_pending_deactivation)
2038 v3f objectpos = obj->getBasePosition();
2040 // The block in which the object resides in
2041 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2043 // If object's static data is stored in a deactivated block and object
2044 // is actually located in an active block, re-save to the block in
2045 // which the object is actually located in.
2047 obj->m_static_exists &&
2048 !m_active_blocks.contains(obj->m_static_block) &&
2049 m_active_blocks.contains(blockpos_o))
2051 v3s16 old_static_block = obj->m_static_block;
2053 // Save to block where object is located
2054 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2056 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2057 <<"Could not save object id="<<id
2058 <<" to it's current block "<<PP(blockpos_o)
2062 std::string staticdata_new = obj->getStaticData();
2063 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2064 block->m_static_objects.insert(id, s_obj);
2065 obj->m_static_block = blockpos_o;
2066 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2067 MOD_REASON_STATIC_DATA_ADDED);
2069 // Delete from block where object was located
2070 block = m_map->emergeBlock(old_static_block, false);
2072 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2073 <<"Could not delete object id="<<id
2074 <<" from it's previous block "<<PP(old_static_block)
2078 block->m_static_objects.remove(id);
2079 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2080 MOD_REASON_STATIC_DATA_REMOVED);
2084 // If block is active, don't remove
2085 if(!force_delete && m_active_blocks.contains(blockpos_o))
2088 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2089 <<"deactivating object id="<<id<<" on inactive block "
2090 <<PP(blockpos_o)<<std::endl;
2092 // If known by some client, don't immediately delete.
2093 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2096 Update the static data
2099 if(obj->isStaticAllowed())
2101 // Create new static object
2102 std::string staticdata_new = obj->getStaticData();
2103 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2105 bool stays_in_same_block = false;
2106 bool data_changed = true;
2108 if (obj->m_static_exists) {
2109 if (obj->m_static_block == blockpos_o)
2110 stays_in_same_block = true;
2112 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2115 std::map<u16, StaticObject>::iterator n =
2116 block->m_static_objects.m_active.find(id);
2117 if (n != block->m_static_objects.m_active.end()) {
2118 StaticObject static_old = n->second;
2120 float save_movem = obj->getMinimumSavedMovement();
2122 if (static_old.data == staticdata_new &&
2123 (static_old.pos - objectpos).getLength() < save_movem)
2124 data_changed = false;
2126 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2127 <<"id="<<id<<" m_static_exists=true but "
2128 <<"static data doesn't actually exist in "
2129 <<PP(obj->m_static_block)<<std::endl;
2134 bool shall_be_written = (!stays_in_same_block || data_changed);
2136 // Delete old static object
2137 if(obj->m_static_exists)
2139 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2142 block->m_static_objects.remove(id);
2143 obj->m_static_exists = false;
2144 // Only mark block as modified if data changed considerably
2145 if(shall_be_written)
2146 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2147 MOD_REASON_STATIC_DATA_CHANGED);
2151 // Add to the block where the object is located in
2152 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2153 // Get or generate the block
2154 MapBlock *block = NULL;
2156 block = m_map->emergeBlock(blockpos);
2157 } catch(InvalidPositionException &e){
2158 // Handled via NULL pointer
2159 // NOTE: emergeBlock's failure is usually determined by it
2160 // actually returning NULL
2165 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2166 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2167 <<" statically but block "<<PP(blockpos)
2168 <<" already contains "
2169 <<block->m_static_objects.m_stored.size()
2171 <<" Forcing delete."<<std::endl;
2172 force_delete = true;
2174 // If static counterpart already exists in target block,
2176 // This shouldn't happen because the object is removed from
2177 // the previous block before this according to
2178 // obj->m_static_block, but happens rarely for some unknown
2179 // reason. Unsuccessful attempts have been made to find
2181 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2182 warningstream<<"ServerEnv: Performing hack #83274"
2184 block->m_static_objects.remove(id);
2186 // Store static data
2187 u16 store_id = pending_delete ? id : 0;
2188 block->m_static_objects.insert(store_id, s_obj);
2190 // Only mark block as modified if data changed considerably
2191 if(shall_be_written)
2192 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2193 MOD_REASON_STATIC_DATA_CHANGED);
2195 obj->m_static_exists = true;
2196 obj->m_static_block = block->getPos();
2201 v3s16 p = floatToInt(objectpos, BS);
2202 errorstream<<"ServerEnv: Could not find or generate "
2203 <<"a block for storing id="<<obj->getId()
2204 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2211 If known by some client, set pending deactivation.
2212 Otherwise delete it immediately.
2215 if(pending_delete && !force_delete)
2217 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2218 <<"object id="<<id<<" is known by clients"
2219 <<"; not deleting yet"<<std::endl;
2221 obj->m_pending_deactivation = true;
2225 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2226 <<"object id="<<id<<" is not known by clients"
2227 <<"; deleting"<<std::endl;
2229 // Tell the object about removal
2230 obj->removingFromEnvironment();
2231 // Deregister in scripting api
2232 m_script->removeObjectReference(obj);
2234 // Delete active object
2235 if(obj->environmentDeletes())
2237 // Id to be removed from m_active_objects
2238 objects_to_remove.push_back(id);
2241 // Remove references from m_active_objects
2242 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2243 i != objects_to_remove.end(); ++i) {
2244 m_active_objects.erase(*i);
2250 #include "clientsimpleobject.h"
2256 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2257 ITextureSource *texturesource, IGameDef *gamedef,
2258 IrrlichtDevice *irr):
2261 m_texturesource(texturesource),
2266 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2269 ClientEnvironment::~ClientEnvironment()
2271 // delete active objects
2272 for(std::map<u16, ClientActiveObject*>::iterator
2273 i = m_active_objects.begin();
2274 i != m_active_objects.end(); ++i)
2279 for(std::vector<ClientSimpleObject*>::iterator
2280 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2288 Map & ClientEnvironment::getMap()
2293 ClientMap & ClientEnvironment::getClientMap()
2298 LocalPlayer *ClientEnvironment::getPlayer(const u16 peer_id)
2300 return dynamic_cast<LocalPlayer *>(Environment::getPlayer(peer_id));
2303 LocalPlayer *ClientEnvironment::getPlayer(const char* name)
2305 return dynamic_cast<LocalPlayer *>(Environment::getPlayer(name));
2308 void ClientEnvironment::addPlayer(Player *player)
2310 DSTACK(FUNCTION_NAME);
2312 It is a failure if player is local and there already is a local
2315 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2316 "Player is local but there is already a local player");
2318 Environment::addPlayer(player);
2321 LocalPlayer * ClientEnvironment::getLocalPlayer()
2323 for(std::vector<Player*>::iterator i = m_players.begin();
2324 i != m_players.end(); ++i) {
2325 Player *player = *i;
2326 if(player->isLocal())
2327 return (LocalPlayer*)player;
2332 void ClientEnvironment::step(float dtime)
2334 DSTACK(FUNCTION_NAME);
2336 /* Step time of day */
2337 stepTimeOfDay(dtime);
2339 // Get some settings
2340 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2341 bool free_move = fly_allowed && g_settings->getBool("free_move");
2344 LocalPlayer *lplayer = getLocalPlayer();
2346 // collision info queue
2347 std::vector<CollisionInfo> player_collisions;
2350 Get the speed the player is going
2352 bool is_climbing = lplayer->is_climbing;
2354 f32 player_speed = lplayer->getSpeed().getLength();
2357 Maximum position increment
2359 //f32 position_max_increment = 0.05*BS;
2360 f32 position_max_increment = 0.1*BS;
2362 // Maximum time increment (for collision detection etc)
2363 // time = distance / speed
2364 f32 dtime_max_increment = 1;
2365 if(player_speed > 0.001)
2366 dtime_max_increment = position_max_increment / player_speed;
2368 // Maximum time increment is 10ms or lower
2369 if(dtime_max_increment > 0.01)
2370 dtime_max_increment = 0.01;
2372 // Don't allow overly huge dtime
2376 f32 dtime_downcount = dtime;
2379 Stuff that has a maximum time increment
2388 if(dtime_downcount > dtime_max_increment)
2390 dtime_part = dtime_max_increment;
2391 dtime_downcount -= dtime_part;
2395 dtime_part = dtime_downcount;
2397 Setting this to 0 (no -=dtime_part) disables an infinite loop
2398 when dtime_part is so small that dtime_downcount -= dtime_part
2401 dtime_downcount = 0;
2410 if(free_move == false && is_climbing == false)
2413 v3f speed = lplayer->getSpeed();
2414 if(lplayer->in_liquid == false)
2415 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2417 // Liquid floating / sinking
2418 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2419 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2421 // Liquid resistance
2422 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2424 // How much the node's viscosity blocks movement, ranges between 0 and 1
2425 // Should match the scale at which viscosity increase affects other liquid attributes
2426 const f32 viscosity_factor = 0.3;
2428 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2429 f32 dl = d_wanted.getLength();
2430 if(dl > lplayer->movement_liquid_fluidity_smooth)
2431 dl = lplayer->movement_liquid_fluidity_smooth;
2432 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2434 v3f d = d_wanted.normalize() * dl;
2438 lplayer->setSpeed(speed);
2443 This also does collision detection.
2445 lplayer->move(dtime_part, this, position_max_increment,
2446 &player_collisions);
2449 while(dtime_downcount > 0.001);
2451 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2453 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2454 i != player_collisions.end(); ++i) {
2455 CollisionInfo &info = *i;
2456 v3f speed_diff = info.new_speed - info.old_speed;;
2457 // Handle only fall damage
2458 // (because otherwise walking against something in fast_move kills you)
2459 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2461 // Get rid of other components
2464 f32 pre_factor = 1; // 1 hp per node/s
2465 f32 tolerance = BS*14; // 5 without damage
2466 f32 post_factor = 1; // 1 hp per node/s
2467 if(info.type == COLLISION_NODE)
2469 const ContentFeatures &f = m_gamedef->ndef()->
2470 get(m_map->getNodeNoEx(info.node_p));
2471 // Determine fall damage multiplier
2472 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2473 pre_factor = 1.0 + (float)addp/100.0;
2475 float speed = pre_factor * speed_diff.getLength();
2476 if(speed > tolerance)
2478 f32 damage_f = (speed - tolerance)/BS * post_factor;
2479 u16 damage = (u16)(damage_f+0.5);
2481 damageLocalPlayer(damage, true);
2482 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2483 m_gamedef->event()->put(e);
2489 A quick draft of lava damage
2491 if(m_lava_hurt_interval.step(dtime, 1.0))
2493 v3f pf = lplayer->getPosition();
2495 // Feet, middle and head
2496 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2497 MapNode n1 = m_map->getNodeNoEx(p1);
2498 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2499 MapNode n2 = m_map->getNodeNoEx(p2);
2500 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2501 MapNode n3 = m_map->getNodeNoEx(p3);
2503 u32 damage_per_second = 0;
2504 damage_per_second = MYMAX(damage_per_second,
2505 m_gamedef->ndef()->get(n1).damage_per_second);
2506 damage_per_second = MYMAX(damage_per_second,
2507 m_gamedef->ndef()->get(n2).damage_per_second);
2508 damage_per_second = MYMAX(damage_per_second,
2509 m_gamedef->ndef()->get(n3).damage_per_second);
2511 if(damage_per_second != 0)
2513 damageLocalPlayer(damage_per_second, true);
2520 if(m_drowning_interval.step(dtime, 2.0))
2522 v3f pf = lplayer->getPosition();
2525 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2526 MapNode n = m_map->getNodeNoEx(p);
2527 ContentFeatures c = m_gamedef->ndef()->get(n);
2528 u8 drowning_damage = c.drowning;
2529 if(drowning_damage > 0 && lplayer->hp > 0){
2530 u16 breath = lplayer->getBreath();
2537 lplayer->setBreath(breath);
2538 updateLocalPlayerBreath(breath);
2541 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2542 damageLocalPlayer(drowning_damage, true);
2545 if(m_breathing_interval.step(dtime, 0.5))
2547 v3f pf = lplayer->getPosition();
2550 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2551 MapNode n = m_map->getNodeNoEx(p);
2552 ContentFeatures c = m_gamedef->ndef()->get(n);
2554 lplayer->setBreath(11);
2556 else if(c.drowning == 0){
2557 u16 breath = lplayer->getBreath();
2560 lplayer->setBreath(breath);
2561 updateLocalPlayerBreath(breath);
2567 Stuff that can be done in an arbitarily large dtime
2569 for(std::vector<Player*>::iterator i = m_players.begin();
2570 i != m_players.end(); ++i) {
2571 Player *player = *i;
2574 Handle non-local players
2576 if(player->isLocal() == false) {
2578 player->move(dtime, this, 100*BS);
2583 // Update lighting on local player (used for wield item)
2584 u32 day_night_ratio = getDayNightRatio();
2588 // On InvalidPositionException, use this as default
2589 // (day: LIGHT_SUN, night: 0)
2590 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2592 v3s16 p = lplayer->getLightPosition();
2593 node_at_lplayer = m_map->getNodeNoEx(p);
2595 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2596 u8 day = light & 0xff;
2597 u8 night = (light >> 8) & 0xff;
2598 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2602 Step active objects and update lighting of them
2605 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2606 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2607 for(std::map<u16, ClientActiveObject*>::iterator
2608 i = m_active_objects.begin();
2609 i != m_active_objects.end(); ++i)
2611 ClientActiveObject* obj = i->second;
2613 obj->step(dtime, this);
2622 v3s16 p = obj->getLightPosition();
2623 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2625 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2627 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2629 obj->updateLight(light);
2634 Step and handle simple objects
2636 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2637 for(std::vector<ClientSimpleObject*>::iterator
2638 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2639 std::vector<ClientSimpleObject*>::iterator cur = i;
2640 ClientSimpleObject *simple = *cur;
2642 simple->step(dtime);
2643 if(simple->m_to_be_removed) {
2645 i = m_simple_objects.erase(cur);
2653 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2655 m_simple_objects.push_back(simple);
2658 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2660 ClientActiveObject *obj = getActiveObject(id);
2661 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2662 return (GenericCAO*) obj;
2667 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2669 std::map<u16, ClientActiveObject*>::iterator n;
2670 n = m_active_objects.find(id);
2671 if(n == m_active_objects.end())
2676 bool isFreeClientActiveObjectId(u16 id,
2677 std::map<u16, ClientActiveObject*> &objects)
2682 return objects.find(id) == objects.end();
2685 u16 getFreeClientActiveObjectId(
2686 std::map<u16, ClientActiveObject*> &objects)
2688 //try to reuse id's as late as possible
2689 static u16 last_used_id = 0;
2690 u16 startid = last_used_id;
2694 if(isFreeClientActiveObjectId(last_used_id, objects))
2695 return last_used_id;
2697 if(last_used_id == startid)
2702 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2704 assert(object); // Pre-condition
2705 if(object->getId() == 0)
2707 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2710 infostream<<"ClientEnvironment::addActiveObject(): "
2711 <<"no free ids available"<<std::endl;
2715 object->setId(new_id);
2717 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2719 infostream<<"ClientEnvironment::addActiveObject(): "
2720 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2724 infostream<<"ClientEnvironment::addActiveObject(): "
2725 <<"added (id="<<object->getId()<<")"<<std::endl;
2726 m_active_objects[object->getId()] = object;
2727 object->addToScene(m_smgr, m_texturesource, m_irr);
2728 { // Update lighting immediately
2733 v3s16 p = object->getLightPosition();
2734 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2736 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2738 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2740 object->updateLight(light);
2742 return object->getId();
2745 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2746 const std::string &init_data)
2748 ClientActiveObject* obj =
2749 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2752 infostream<<"ClientEnvironment::addActiveObject(): "
2753 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2762 obj->initialize(init_data);
2764 catch(SerializationError &e)
2766 errorstream<<"ClientEnvironment::addActiveObject():"
2767 <<" id="<<id<<" type="<<type
2768 <<": SerializationError in initialize(): "
2770 <<": init_data="<<serializeJsonString(init_data)
2774 addActiveObject(obj);
2777 void ClientEnvironment::removeActiveObject(u16 id)
2779 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2780 <<"id="<<id<<std::endl;
2781 ClientActiveObject* obj = getActiveObject(id);
2784 infostream<<"ClientEnvironment::removeActiveObject(): "
2785 <<"id="<<id<<" not found"<<std::endl;
2788 obj->removeFromScene(true);
2790 m_active_objects.erase(id);
2793 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2795 ClientActiveObject *obj = getActiveObject(id);
2797 infostream << "ClientEnvironment::processActiveObjectMessage():"
2798 << " got message for id=" << id << ", which doesn't exist."
2804 obj->processMessage(data);
2805 } catch (SerializationError &e) {
2806 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2807 << " id=" << id << " type=" << obj->getType()
2808 << " SerializationError in processMessage(): " << e.what()
2814 Callbacks for activeobjects
2817 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2819 LocalPlayer *lplayer = getLocalPlayer();
2823 if (lplayer->hp > damage)
2824 lplayer->hp -= damage;
2829 ClientEnvEvent event;
2830 event.type = CEE_PLAYER_DAMAGE;
2831 event.player_damage.amount = damage;
2832 event.player_damage.send_to_server = handle_hp;
2833 m_client_event_queue.push(event);
2836 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2838 ClientEnvEvent event;
2839 event.type = CEE_PLAYER_BREATH;
2840 event.player_breath.amount = breath;
2841 m_client_event_queue.push(event);
2845 Client likes to call these
2848 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2849 std::vector<DistanceSortedActiveObject> &dest)
2851 for(std::map<u16, ClientActiveObject*>::iterator
2852 i = m_active_objects.begin();
2853 i != m_active_objects.end(); ++i)
2855 ClientActiveObject* obj = i->second;
2857 f32 d = (obj->getPosition() - origin).getLength();
2862 DistanceSortedActiveObject dso(obj, d);
2864 dest.push_back(dso);
2868 ClientEnvEvent ClientEnvironment::getClientEvent()
2870 ClientEnvEvent event;
2871 if(m_client_event_queue.empty())
2872 event.type = CEE_NONE;
2874 event = m_client_event_queue.front();
2875 m_client_event_queue.pop();
2880 #endif // #ifndef SERVER