3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "environment.h"
24 #include "collision.h"
25 #include "content_mapnode.h"
27 #include "serverobject.h"
28 #include "content_sao.h"
32 #include "scripting_game.h"
34 #include "nodemetadata.h"
37 #include "clientmap.h"
38 #include "localplayer.h"
39 #include "mapblock_mesh.h"
43 #include "daynightratio.h"
46 #include "util/serialize.h"
47 #include "threading/mutex_auto_lock.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
53 Environment::Environment():
54 m_time_of_day_speed(0),
56 m_time_of_day_f(9000./24000),
57 m_time_conversion_skew(0.0f),
58 m_enable_day_night_ratio_override(false),
59 m_day_night_ratio_override(0.0f)
61 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
64 Environment::~Environment()
67 for(std::vector<Player*>::iterator i = m_players.begin();
68 i != m_players.end(); ++i) {
73 void Environment::addPlayer(Player *player)
75 DSTACK(FUNCTION_NAME);
77 Check that peer_ids are unique.
78 Also check that names are unique.
79 Exception: there can be multiple players with peer_id=0
81 // If peer id is non-zero, it has to be unique.
82 if(player->peer_id != 0)
83 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
84 // Name has to be unique.
85 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
87 m_players.push_back(player);
90 void Environment::removePlayer(Player* player)
92 for (std::vector<Player*>::iterator it = m_players.begin();
93 it != m_players.end(); ++it) {
94 if ((*it) == player) {
102 Player * Environment::getPlayer(u16 peer_id)
104 for(std::vector<Player*>::iterator i = m_players.begin();
105 i != m_players.end(); ++i) {
107 if(player->peer_id == peer_id)
113 Player * Environment::getPlayer(const char *name)
115 for(std::vector<Player*>::iterator i = m_players.begin();
116 i != m_players.end(); ++i) {
118 if(strcmp(player->getName(), name) == 0)
124 Player * Environment::getRandomConnectedPlayer()
126 std::vector<Player*> connected_players = getPlayers(true);
127 u32 chosen_one = myrand() % connected_players.size();
129 for(std::vector<Player*>::iterator
130 i = connected_players.begin();
131 i != connected_players.end(); ++i) {
132 if(j == chosen_one) {
141 Player * Environment::getNearestConnectedPlayer(v3f pos)
143 std::vector<Player*> connected_players = getPlayers(true);
145 Player *nearest_player = NULL;
146 for(std::vector<Player*>::iterator
147 i = connected_players.begin();
148 i != connected_players.end(); ++i) {
150 f32 d = player->getPosition().getDistanceFrom(pos);
151 if(d < nearest_d || nearest_player == NULL) {
153 nearest_player = player;
156 return nearest_player;
159 std::vector<Player*> Environment::getPlayers()
164 std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
166 std::vector<Player*> newlist;
167 for(std::vector<Player*>::iterator
168 i = m_players.begin();
169 i != m_players.end(); ++i) {
172 if(ignore_disconnected) {
173 // Ignore disconnected players
174 if(player->peer_id == 0)
178 newlist.push_back(player);
183 u32 Environment::getDayNightRatio()
185 MutexAutoLock lock(this->m_time_lock);
186 if (m_enable_day_night_ratio_override)
187 return m_day_night_ratio_override;
188 return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
191 void Environment::setTimeOfDaySpeed(float speed)
193 m_time_of_day_speed = speed;
196 float Environment::getTimeOfDaySpeed()
198 return m_time_of_day_speed;
201 void Environment::setDayNightRatioOverride(bool enable, u32 value)
203 MutexAutoLock lock(this->m_time_lock);
204 m_enable_day_night_ratio_override = enable;
205 m_day_night_ratio_override = value;
208 void Environment::setTimeOfDay(u32 time)
210 MutexAutoLock lock(this->m_time_lock);
211 if (m_time_of_day > time)
213 m_time_of_day = time;
214 m_time_of_day_f = (float)time / 24000.0;
217 u32 Environment::getTimeOfDay()
219 MutexAutoLock lock(this->m_time_lock);
220 return m_time_of_day;
223 float Environment::getTimeOfDayF()
225 MutexAutoLock lock(this->m_time_lock);
226 return m_time_of_day_f;
229 void Environment::stepTimeOfDay(float dtime)
231 MutexAutoLock lock(this->m_time_lock);
233 // Cached in order to prevent the two reads we do to give
234 // different results (can be written by code not under the lock)
235 f32 cached_time_of_day_speed = m_time_of_day_speed;
237 f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
238 m_time_conversion_skew += dtime;
239 u32 units = (u32)(m_time_conversion_skew * speed);
243 if (m_time_of_day + units >= 24000) {
247 m_time_of_day = (m_time_of_day + units) % 24000;
249 m_time_of_day_f = (float)m_time_of_day / 24000.0;
252 m_time_conversion_skew -= (f32)units / speed;
255 m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
256 if (m_time_of_day_f > 1.0)
257 m_time_of_day_f -= 1.0;
258 if (m_time_of_day_f < 0.0)
259 m_time_of_day_f += 1.0;
263 u32 Environment::getDayCount()
265 // Atomic<u32> counter
274 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
278 // Initialize timer to random value to spread processing
279 float itv = abm->getTriggerInterval();
280 itv = MYMAX(0.001, itv); // No less than 1ms
281 int minval = MYMAX(-0.51*itv, -60); // Clamp to
282 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
283 timer = myrand_range(minval, maxval);
290 void LBMContentMapping::deleteContents()
292 for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
293 it != lbm_list.end(); ++it) {
298 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
300 // Add the lbm_def to the LBMContentMapping.
301 // Unknown names get added to the global NameIdMapping.
302 INodeDefManager *nodedef = gamedef->ndef();
304 lbm_list.push_back(lbm_def);
306 for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
307 it != lbm_def->trigger_contents.end(); ++it) {
308 std::set<content_t> c_ids;
309 bool found = nodedef->getIds(*it, c_ids);
311 content_t c_id = gamedef->allocateUnknownNodeId(*it);
312 if (c_id == CONTENT_IGNORE) {
313 // Seems it can't be allocated.
314 warningstream << "Could not internalize node name \"" << *it
315 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
321 for (std::set<content_t>::const_iterator iit =
322 c_ids.begin(); iit != c_ids.end(); ++iit) {
323 content_t c_id = *iit;
324 map[c_id].push_back(lbm_def);
329 const std::vector<LoadingBlockModifierDef *> *
330 LBMContentMapping::lookup(content_t c) const
332 container_map::const_iterator it = map.find(c);
335 // This first dereferences the iterator, returning
336 // a std::vector<LoadingBlockModifierDef *>
337 // reference, then we convert it to a pointer.
338 return &(it->second);
341 LBMManager::~LBMManager()
343 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
344 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
347 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
348 it != m_lbm_lookup.end(); ++it) {
349 (it->second).deleteContents();
353 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
355 // Precondition, in query mode the map isn't used anymore
356 FATAL_ERROR_IF(m_query_mode == true,
357 "attempted to modify LBMManager in query mode");
359 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
360 throw ModError("Error adding LBM \"" + lbm_def->name +
361 "\": Does not follow naming conventions: "
362 "Only chararacters [a-z0-9_:] are allowed.");
365 m_lbm_defs[lbm_def->name] = lbm_def;
368 void LBMManager::loadIntroductionTimes(const std::string ×,
369 IGameDef *gamedef, u32 now)
374 // Storing it in a map first instead of
375 // handling the stuff directly in the loop
376 // removes all duplicate entries.
377 // TODO make this std::unordered_map
378 std::map<std::string, u32> introduction_times;
381 The introduction times string consists of name~time entries,
382 with each entry terminated by a semicolon. The time is decimal.
387 while ((idx_new = times.find(";", idx)) != std::string::npos) {
388 std::string entry = times.substr(idx, idx_new - idx);
389 std::vector<std::string> components = str_split(entry, '~');
390 if (components.size() != 2)
391 throw SerializationError("Introduction times entry \""
392 + entry + "\" requires exactly one '~'!");
393 const std::string &name = components[0];
394 u32 time = from_string<u32>(components[1]);
395 introduction_times[name] = time;
399 // Put stuff from introduction_times into m_lbm_lookup
400 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
401 it != introduction_times.end(); ++it) {
402 const std::string &name = it->first;
403 u32 time = it->second;
405 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
406 m_lbm_defs.find(name);
407 if (def_it == m_lbm_defs.end()) {
408 // This seems to be an LBM entry for
409 // an LBM we haven't loaded. Discard it.
412 LoadingBlockModifierDef *lbm_def = def_it->second;
413 if (lbm_def->run_at_every_load) {
414 // This seems to be an LBM entry for
415 // an LBM that runs at every load.
416 // Don't add it just yet.
420 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
422 // Erase the entry so that we know later
423 // what elements didn't get put into m_lbm_lookup
424 m_lbm_defs.erase(name);
427 // Now also add the elements from m_lbm_defs to m_lbm_lookup
428 // that weren't added in the previous step.
429 // They are introduced first time to this world,
430 // or are run at every load (introducement time hardcoded to U32_MAX).
432 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
433 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
435 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
436 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
437 if (it->second->run_at_every_load) {
438 lbms_running_always.addLBM(it->second, gamedef);
440 lbms_we_introduce_now.addLBM(it->second, gamedef);
444 // Clear the list, so that we don't delete remaining elements
445 // twice in the destructor
449 std::string LBMManager::createIntroductionTimesString()
451 // Precondition, we must be in query mode
452 FATAL_ERROR_IF(m_query_mode == false,
453 "attempted to query on non fully set up LBMManager");
455 std::ostringstream oss;
456 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
457 it != m_lbm_lookup.end(); ++it) {
458 u32 time = it->first;
459 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
460 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
461 iit != lbm_list.end(); ++iit) {
462 // Don't add if the LBM runs at every load,
463 // then introducement time is hardcoded
464 // and doesn't need to be stored
465 if ((*iit)->run_at_every_load)
467 oss << (*iit)->name << "~" << time << ";";
473 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
475 // Precondition, we need m_lbm_lookup to be initialized
476 FATAL_ERROR_IF(m_query_mode == false,
477 "attempted to query on non fully set up LBMManager");
478 v3s16 pos_of_block = block->getPosRelative();
482 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
483 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
484 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
485 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
487 n = block->getNodeNoEx(pos);
489 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
490 iit != m_lbm_lookup.end(); ++iit) {
491 const std::vector<LoadingBlockModifierDef *> *lbm_list =
492 iit->second.lookup(c);
495 for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
496 lbm_list->begin(); iit != lbm_list->end(); ++iit) {
497 (*iit)->trigger(env, pos + pos_of_block, n);
507 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
510 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
511 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
512 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
519 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
521 std::set<v3s16> &blocks_removed,
522 std::set<v3s16> &blocks_added)
527 std::set<v3s16> newlist = m_forceloaded_list;
528 for(std::vector<v3s16>::iterator i = active_positions.begin();
529 i != active_positions.end(); ++i)
531 fillRadiusBlock(*i, radius, newlist);
535 Find out which blocks on the old list are not on the new list
537 // Go through old list
538 for(std::set<v3s16>::iterator i = m_list.begin();
539 i != m_list.end(); ++i)
542 // If not on new list, it's been removed
543 if(newlist.find(p) == newlist.end())
544 blocks_removed.insert(p);
548 Find out which blocks on the new list are not on the old list
550 // Go through new list
551 for(std::set<v3s16>::iterator i = newlist.begin();
552 i != newlist.end(); ++i)
555 // If not on old list, it's been added
556 if(m_list.find(p) == m_list.end())
557 blocks_added.insert(p);
564 for(std::set<v3s16>::iterator i = newlist.begin();
565 i != newlist.end(); ++i)
576 ServerEnvironment::ServerEnvironment(ServerMap *map,
577 GameScripting *scriptIface, IGameDef *gamedef,
578 const std::string &path_world) :
580 m_script(scriptIface),
582 m_path_world(path_world),
583 m_send_recommended_timer(0),
584 m_active_block_interval_overload_skip(0),
586 m_game_time_fraction_counter(0),
587 m_last_clear_objects_time(0),
588 m_recommended_send_interval(0.1),
589 m_max_lag_estimate(0.1)
593 ServerEnvironment::~ServerEnvironment()
595 // Clear active block list.
596 // This makes the next one delete all active objects.
597 m_active_blocks.clear();
599 // Convert all objects to static and delete the active objects
600 deactivateFarObjects(true);
605 // Delete ActiveBlockModifiers
606 for(std::vector<ABMWithState>::iterator
607 i = m_abms.begin(); i != m_abms.end(); ++i){
612 Map & ServerEnvironment::getMap()
617 ServerMap & ServerEnvironment::getServerMap()
622 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
624 float distance = pos1.getDistanceFrom(pos2);
626 //calculate normalized direction vector
627 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
628 (pos2.Y - pos1.Y)/distance,
629 (pos2.Z - pos1.Z)/distance);
631 //find out if there's a node on path between pos1 and pos2
632 for (float i = 1; i < distance; i += stepsize) {
633 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
634 normalized_vector.Y * i,
635 normalized_vector.Z * i) +pos1,BS);
637 MapNode n = getMap().getNodeNoEx(pos);
639 if(n.param0 != CONTENT_AIR) {
649 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
650 const std::string &str_reason, bool reconnect)
652 for (std::vector<Player*>::iterator it = m_players.begin();
653 it != m_players.end();
655 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
656 (*it)->protocol_version, (AccessDeniedCode)reason,
657 str_reason, reconnect);
661 void ServerEnvironment::saveLoadedPlayers()
663 std::string players_path = m_path_world + DIR_DELIM "players";
664 fs::CreateDir(players_path);
666 for (std::vector<Player*>::iterator it = m_players.begin();
667 it != m_players.end();
669 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
670 if (player->checkModified()) {
671 player->save(players_path);
676 void ServerEnvironment::savePlayer(RemotePlayer *player)
678 std::string players_path = m_path_world + DIR_DELIM "players";
679 fs::CreateDir(players_path);
681 player->save(players_path);
684 Player *ServerEnvironment::loadPlayer(const std::string &playername)
686 bool newplayer = false;
688 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
689 std::string path = players_path + playername;
691 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
693 player = new RemotePlayer(m_gamedef, "");
697 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
698 //// Open file and deserialize
699 std::ifstream is(path.c_str(), std::ios_base::binary);
702 player->deSerialize(is, path);
705 if (player->getName() == playername) {
710 path = players_path + playername + itos(i);
714 infostream << "Player file for player " << playername
715 << " not found" << std::endl;
723 player->setModified(false);
727 void ServerEnvironment::saveMeta()
729 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
731 // Open file and serialize
732 std::ostringstream ss(std::ios_base::binary);
735 args.setU64("game_time", m_game_time);
736 args.setU64("time_of_day", getTimeOfDay());
737 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
738 args.setU64("lbm_introduction_times_version", 1);
739 args.set("lbm_introduction_times",
740 m_lbm_mgr.createIntroductionTimesString());
741 args.setU64("day_count", m_day_count);
745 if(!fs::safeWriteToFile(path, ss.str()))
747 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
749 throw SerializationError("Couldn't save env meta");
753 void ServerEnvironment::loadMeta()
755 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
757 // Open file and deserialize
758 std::ifstream is(path.c_str(), std::ios_base::binary);
760 infostream << "ServerEnvironment::loadMeta(): Failed to open "
761 << path << std::endl;
762 throw SerializationError("Couldn't load env meta");
767 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
768 throw SerializationError("ServerEnvironment::loadMeta(): "
769 "EnvArgsEnd not found!");
773 m_game_time = args.getU64("game_time");
774 } catch (SettingNotFoundException &e) {
775 // Getting this is crucial, otherwise timestamps are useless
776 throw SerializationError("Couldn't load env meta game_time");
779 setTimeOfDay(args.exists("time_of_day") ?
780 // set day to morning by default
781 args.getU64("time_of_day") : 9000);
783 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
784 // If missing, do as if clearObjects was never called
785 args.getU64("last_clear_objects_time") : 0;
787 std::string lbm_introduction_times = "";
789 u64 ver = args.getU64("lbm_introduction_times_version");
791 lbm_introduction_times = args.get("lbm_introduction_times");
793 infostream << "ServerEnvironment::loadMeta(): Non-supported"
794 << " introduction time version " << ver << std::endl;
796 } catch (SettingNotFoundException &e) {
797 // No problem, this is expected. Just continue with an empty string
799 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
801 m_day_count = args.exists("day_count") ?
802 args.getU64("day_count") : 0;
805 void ServerEnvironment::loadDefaultMeta()
807 m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
812 ActiveBlockModifier *abm;
814 std::set<content_t> required_neighbors;
820 ServerEnvironment *m_env;
821 std::map<content_t, std::vector<ActiveABM> > m_aabms;
823 ABMHandler(std::vector<ABMWithState> &abms,
824 float dtime_s, ServerEnvironment *env,
830 INodeDefManager *ndef = env->getGameDef()->ndef();
831 for(std::vector<ABMWithState>::iterator
832 i = abms.begin(); i != abms.end(); ++i) {
833 ActiveBlockModifier *abm = i->abm;
834 float trigger_interval = abm->getTriggerInterval();
835 if(trigger_interval < 0.001)
836 trigger_interval = 0.001;
837 float actual_interval = dtime_s;
840 if(i->timer < trigger_interval)
842 i->timer -= trigger_interval;
843 actual_interval = trigger_interval;
845 float chance = abm->getTriggerChance();
850 if(abm->getSimpleCatchUp()) {
851 float intervals = actual_interval / trigger_interval;
854 aabm.chance = chance / intervals;
858 aabm.chance = chance;
861 std::set<std::string> required_neighbors_s
862 = abm->getRequiredNeighbors();
863 for(std::set<std::string>::iterator
864 i = required_neighbors_s.begin();
865 i != required_neighbors_s.end(); ++i)
867 ndef->getIds(*i, aabm.required_neighbors);
870 std::set<std::string> contents_s = abm->getTriggerContents();
871 for(std::set<std::string>::iterator
872 i = contents_s.begin(); i != contents_s.end(); ++i)
874 std::set<content_t> ids;
875 ndef->getIds(*i, ids);
876 for(std::set<content_t>::const_iterator k = ids.begin();
880 std::map<content_t, std::vector<ActiveABM> >::iterator j;
882 if(j == m_aabms.end()){
883 std::vector<ActiveABM> aabmlist;
884 m_aabms[c] = aabmlist;
887 j->second.push_back(aabm);
892 // Find out how many objects the given block and its neighbours contain.
893 // Returns the number of objects in the block, and also in 'wider' the
894 // number of objects in the block and all its neighbours. The latter
895 // may an estimate if any neighbours are unloaded.
896 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
899 u32 wider_unknown_count = 0;
900 for(s16 x=-1; x<=1; x++)
901 for(s16 y=-1; y<=1; y++)
902 for(s16 z=-1; z<=1; z++)
904 MapBlock *block2 = map->getBlockNoCreateNoEx(
905 block->getPos() + v3s16(x,y,z));
907 wider_unknown_count++;
910 wider += block2->m_static_objects.m_active.size()
911 + block2->m_static_objects.m_stored.size();
914 u32 active_object_count = block->m_static_objects.m_active.size();
915 u32 wider_known_count = 3*3*3 - wider_unknown_count;
916 wider += wider_unknown_count * wider / wider_known_count;
917 return active_object_count;
920 void apply(MapBlock *block)
925 ServerMap *map = &m_env->getServerMap();
927 u32 active_object_count_wider;
928 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
929 m_env->m_added_objects = 0;
932 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
933 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
934 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
936 MapNode n = block->getNodeNoEx(p0);
937 content_t c = n.getContent();
938 v3s16 p = p0 + block->getPosRelative();
940 std::map<content_t, std::vector<ActiveABM> >::iterator j;
942 if(j == m_aabms.end())
945 for(std::vector<ActiveABM>::iterator
946 i = j->second.begin(); i != j->second.end(); ++i) {
947 if(myrand() % i->chance != 0)
951 if(!i->required_neighbors.empty())
954 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
955 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
956 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
960 MapNode n = map->getNodeNoEx(p1);
961 content_t c = n.getContent();
962 std::set<content_t>::const_iterator k;
963 k = i->required_neighbors.find(c);
964 if(k != i->required_neighbors.end()){
968 // No required neighbor found
973 // Call all the trigger variations
974 i->abm->trigger(m_env, p, n);
975 i->abm->trigger(m_env, p, n,
976 active_object_count, active_object_count_wider);
978 // Count surrounding objects again if the abms added any
979 if(m_env->m_added_objects > 0) {
980 active_object_count = countObjects(block, map, active_object_count_wider);
981 m_env->m_added_objects = 0;
988 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
990 // Reset usage timer immediately, otherwise a block that becomes active
991 // again at around the same time as it would normally be unloaded will
992 // get unloaded incorrectly. (I think this still leaves a small possibility
993 // of a race condition between this and server::AsyncRunStep, which only
994 // some kind of synchronisation will fix, but it at least reduces the window
995 // of opportunity for it to break from seconds to nanoseconds)
996 block->resetUsageTimer();
998 // Get time difference
1000 u32 stamp = block->getTimestamp();
1001 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
1002 dtime_s = m_game_time - stamp;
1003 dtime_s += additional_dtime;
1005 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
1006 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
1008 // Remove stored static objects if clearObjects was called since block's timestamp
1009 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
1010 block->m_static_objects.m_stored.clear();
1011 // do not set changed flag to avoid unnecessary mapblock writes
1014 // Set current time as timestamp
1015 block->setTimestampNoChangedFlag(m_game_time);
1017 /*infostream<<"ServerEnvironment::activateBlock(): block is "
1018 <<dtime_s<<" seconds old."<<std::endl;*/
1020 // Activate stored objects
1021 activateObjects(block, dtime_s);
1023 /* Handle LoadingBlockModifiers */
1024 m_lbm_mgr.applyLBMs(this, block, stamp);
1027 std::map<v3s16, NodeTimer> elapsed_timers =
1028 block->m_node_timers.step((float)dtime_s);
1029 if(!elapsed_timers.empty()){
1031 for(std::map<v3s16, NodeTimer>::iterator
1032 i = elapsed_timers.begin();
1033 i != elapsed_timers.end(); ++i){
1034 n = block->getNodeNoEx(i->first);
1035 v3s16 p = i->first + block->getPosRelative();
1036 if(m_script->node_on_timer(p,n,i->second.elapsed))
1037 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1041 /* Handle ActiveBlockModifiers */
1042 ABMHandler abmhandler(m_abms, dtime_s, this, false);
1043 abmhandler.apply(block);
1046 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
1048 m_abms.push_back(ABMWithState(abm));
1051 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1053 m_lbm_mgr.addLBMDef(lbm);
1056 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1058 INodeDefManager *ndef = m_gamedef->ndef();
1059 MapNode n_old = m_map->getNodeNoEx(p);
1062 if (ndef->get(n_old).has_on_destruct)
1063 m_script->node_on_destruct(p, n_old);
1066 if (!m_map->addNodeWithEvent(p, n))
1069 // Update active VoxelManipulator if a mapgen thread
1070 m_map->updateVManip(p);
1072 // Call post-destructor
1073 if (ndef->get(n_old).has_after_destruct)
1074 m_script->node_after_destruct(p, n_old);
1077 if (ndef->get(n).has_on_construct)
1078 m_script->node_on_construct(p, n);
1083 bool ServerEnvironment::removeNode(v3s16 p)
1085 INodeDefManager *ndef = m_gamedef->ndef();
1086 MapNode n_old = m_map->getNodeNoEx(p);
1089 if (ndef->get(n_old).has_on_destruct)
1090 m_script->node_on_destruct(p, n_old);
1093 // This is slightly optimized compared to addNodeWithEvent(air)
1094 if (!m_map->removeNodeWithEvent(p))
1097 // Update active VoxelManipulator if a mapgen thread
1098 m_map->updateVManip(p);
1100 // Call post-destructor
1101 if (ndef->get(n_old).has_after_destruct)
1102 m_script->node_after_destruct(p, n_old);
1104 // Air doesn't require constructor
1108 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1110 if (!m_map->addNodeWithEvent(p, n, false))
1113 // Update active VoxelManipulator if a mapgen thread
1114 m_map->updateVManip(p);
1119 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1121 for(std::map<u16, ServerActiveObject*>::iterator
1122 i = m_active_objects.begin();
1123 i != m_active_objects.end(); ++i)
1125 ServerActiveObject* obj = i->second;
1127 v3f objectpos = obj->getBasePosition();
1128 if(objectpos.getDistanceFrom(pos) > radius)
1130 objects.push_back(id);
1134 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1136 infostream << "ServerEnvironment::clearObjects(): "
1137 << "Removing all active objects" << std::endl;
1138 std::vector<u16> objects_to_remove;
1139 for (std::map<u16, ServerActiveObject*>::iterator
1140 i = m_active_objects.begin();
1141 i != m_active_objects.end(); ++i) {
1142 ServerActiveObject* obj = i->second;
1143 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1146 // Delete static object if block is loaded
1147 if (obj->m_static_exists) {
1148 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1150 block->m_static_objects.remove(id);
1151 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1152 MOD_REASON_CLEAR_ALL_OBJECTS);
1153 obj->m_static_exists = false;
1156 // If known by some client, don't delete immediately
1157 if (obj->m_known_by_count > 0) {
1158 obj->m_pending_deactivation = true;
1159 obj->m_removed = true;
1163 // Tell the object about removal
1164 obj->removingFromEnvironment();
1165 // Deregister in scripting api
1166 m_script->removeObjectReference(obj);
1168 // Delete active object
1169 if (obj->environmentDeletes())
1171 // Id to be removed from m_active_objects
1172 objects_to_remove.push_back(id);
1175 // Remove references from m_active_objects
1176 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1177 i != objects_to_remove.end(); ++i) {
1178 m_active_objects.erase(*i);
1181 // Get list of loaded blocks
1182 std::vector<v3s16> loaded_blocks;
1183 infostream << "ServerEnvironment::clearObjects(): "
1184 << "Listing all loaded blocks" << std::endl;
1185 m_map->listAllLoadedBlocks(loaded_blocks);
1186 infostream << "ServerEnvironment::clearObjects(): "
1187 << "Done listing all loaded blocks: "
1188 << loaded_blocks.size()<<std::endl;
1190 // Get list of loadable blocks
1191 std::vector<v3s16> loadable_blocks;
1192 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1193 infostream << "ServerEnvironment::clearObjects(): "
1194 << "Listing all loadable blocks" << std::endl;
1195 m_map->listAllLoadableBlocks(loadable_blocks);
1196 infostream << "ServerEnvironment::clearObjects(): "
1197 << "Done listing all loadable blocks: "
1198 << loadable_blocks.size() << std::endl;
1200 loadable_blocks = loaded_blocks;
1203 infostream << "ServerEnvironment::clearObjects(): "
1204 << "Now clearing objects in " << loadable_blocks.size()
1205 << " blocks" << std::endl;
1207 // Grab a reference on each loaded block to avoid unloading it
1208 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1209 i != loaded_blocks.end(); ++i) {
1211 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1212 assert(block != NULL);
1216 // Remove objects in all loadable blocks
1217 u32 unload_interval = U32_MAX;
1218 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1219 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1220 unload_interval = MYMAX(unload_interval, 1);
1222 u32 report_interval = loadable_blocks.size() / 10;
1223 u32 num_blocks_checked = 0;
1224 u32 num_blocks_cleared = 0;
1225 u32 num_objs_cleared = 0;
1226 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1227 i != loadable_blocks.end(); ++i) {
1229 MapBlock *block = m_map->emergeBlock(p, false);
1231 errorstream << "ServerEnvironment::clearObjects(): "
1232 << "Failed to emerge block " << PP(p) << std::endl;
1235 u32 num_stored = block->m_static_objects.m_stored.size();
1236 u32 num_active = block->m_static_objects.m_active.size();
1237 if (num_stored != 0 || num_active != 0) {
1238 block->m_static_objects.m_stored.clear();
1239 block->m_static_objects.m_active.clear();
1240 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1241 MOD_REASON_CLEAR_ALL_OBJECTS);
1242 num_objs_cleared += num_stored + num_active;
1243 num_blocks_cleared++;
1245 num_blocks_checked++;
1247 if (report_interval != 0 &&
1248 num_blocks_checked % report_interval == 0) {
1249 float percent = 100.0 * (float)num_blocks_checked /
1250 loadable_blocks.size();
1251 infostream << "ServerEnvironment::clearObjects(): "
1252 << "Cleared " << num_objs_cleared << " objects"
1253 << " in " << num_blocks_cleared << " blocks ("
1254 << percent << "%)" << std::endl;
1256 if (num_blocks_checked % unload_interval == 0) {
1257 m_map->unloadUnreferencedBlocks();
1260 m_map->unloadUnreferencedBlocks();
1262 // Drop references that were added above
1263 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1264 i != loaded_blocks.end(); ++i) {
1266 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1271 m_last_clear_objects_time = m_game_time;
1273 infostream << "ServerEnvironment::clearObjects(): "
1274 << "Finished: Cleared " << num_objs_cleared << " objects"
1275 << " in " << num_blocks_cleared << " blocks" << std::endl;
1278 void ServerEnvironment::step(float dtime)
1280 DSTACK(FUNCTION_NAME);
1282 //TimeTaker timer("ServerEnv step");
1284 /* Step time of day */
1285 stepTimeOfDay(dtime);
1288 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1289 // really matter that much.
1290 static const float server_step = g_settings->getFloat("dedicated_server_step");
1291 m_recommended_send_interval = server_step;
1297 m_game_time_fraction_counter += dtime;
1298 u32 inc_i = (u32)m_game_time_fraction_counter;
1299 m_game_time += inc_i;
1300 m_game_time_fraction_counter -= (float)inc_i;
1307 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1308 for(std::vector<Player*>::iterator i = m_players.begin();
1309 i != m_players.end(); ++i)
1311 Player *player = *i;
1313 // Ignore disconnected players
1314 if(player->peer_id == 0)
1318 player->move(dtime, this, 100*BS);
1323 Manage active block list
1325 if(m_active_blocks_management_interval.step(dtime, 2.0))
1327 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1329 Get player block positions
1331 std::vector<v3s16> players_blockpos;
1332 for(std::vector<Player*>::iterator
1333 i = m_players.begin();
1334 i != m_players.end(); ++i) {
1335 Player *player = *i;
1336 // Ignore disconnected players
1337 if(player->peer_id == 0)
1340 v3s16 blockpos = getNodeBlockPos(
1341 floatToInt(player->getPosition(), BS));
1342 players_blockpos.push_back(blockpos);
1346 Update list of active blocks, collecting changes
1348 static const s16 active_block_range = g_settings->getS16("active_block_range");
1349 std::set<v3s16> blocks_removed;
1350 std::set<v3s16> blocks_added;
1351 m_active_blocks.update(players_blockpos, active_block_range,
1352 blocks_removed, blocks_added);
1355 Handle removed blocks
1358 // Convert active objects that are no more in active blocks to static
1359 deactivateFarObjects(false);
1361 for(std::set<v3s16>::iterator
1362 i = blocks_removed.begin();
1363 i != blocks_removed.end(); ++i) {
1366 /* infostream<<"Server: Block " << PP(p)
1367 << " became inactive"<<std::endl; */
1369 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1373 // Set current time as timestamp (and let it set ChangedFlag)
1374 block->setTimestamp(m_game_time);
1381 for(std::set<v3s16>::iterator
1382 i = blocks_added.begin();
1383 i != blocks_added.end(); ++i)
1387 MapBlock *block = m_map->getBlockOrEmerge(p);
1389 m_active_blocks.m_list.erase(p);
1393 activateBlock(block);
1394 /* infostream<<"Server: Block " << PP(p)
1395 << " became active"<<std::endl; */
1400 Mess around in active blocks
1402 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1404 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1408 for(std::set<v3s16>::iterator
1409 i = m_active_blocks.m_list.begin();
1410 i != m_active_blocks.m_list.end(); ++i)
1414 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1415 <<") being handled"<<std::endl;*/
1417 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1421 // Reset block usage timer
1422 block->resetUsageTimer();
1424 // Set current time as timestamp
1425 block->setTimestampNoChangedFlag(m_game_time);
1426 // If time has changed much from the one on disk,
1427 // set block to be saved when it is unloaded
1428 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1429 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1430 MOD_REASON_BLOCK_EXPIRED);
1433 std::map<v3s16, NodeTimer> elapsed_timers =
1434 block->m_node_timers.step((float)dtime);
1435 if(!elapsed_timers.empty()){
1437 for(std::map<v3s16, NodeTimer>::iterator
1438 i = elapsed_timers.begin();
1439 i != elapsed_timers.end(); ++i){
1440 n = block->getNodeNoEx(i->first);
1441 p = i->first + block->getPosRelative();
1442 if(m_script->node_on_timer(p,n,i->second.elapsed))
1443 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1449 const float abm_interval = 1.0;
1450 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1452 if(m_active_block_interval_overload_skip > 0){
1453 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1454 m_active_block_interval_overload_skip--;
1457 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1458 TimeTaker timer("modify in active blocks");
1460 // Initialize handling of ActiveBlockModifiers
1461 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1463 for(std::set<v3s16>::iterator
1464 i = m_active_blocks.m_list.begin();
1465 i != m_active_blocks.m_list.end(); ++i)
1469 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1470 <<") being handled"<<std::endl;*/
1472 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1476 // Set current time as timestamp
1477 block->setTimestampNoChangedFlag(m_game_time);
1479 /* Handle ActiveBlockModifiers */
1480 abmhandler.apply(block);
1483 u32 time_ms = timer.stop(true);
1484 u32 max_time_ms = 200;
1485 if(time_ms > max_time_ms){
1486 warningstream<<"active block modifiers took "
1487 <<time_ms<<"ms (longer than "
1488 <<max_time_ms<<"ms)"<<std::endl;
1489 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1494 Step script environment (run global on_step())
1496 m_script->environment_Step(dtime);
1502 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1503 //TimeTaker timer("Step active objects");
1505 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1507 // This helps the objects to send data at the same time
1508 bool send_recommended = false;
1509 m_send_recommended_timer += dtime;
1510 if(m_send_recommended_timer > getSendRecommendedInterval())
1512 m_send_recommended_timer -= getSendRecommendedInterval();
1513 send_recommended = true;
1516 for(std::map<u16, ServerActiveObject*>::iterator
1517 i = m_active_objects.begin();
1518 i != m_active_objects.end(); ++i)
1520 ServerActiveObject* obj = i->second;
1521 // Don't step if is to be removed or stored statically
1522 if(obj->m_removed || obj->m_pending_deactivation)
1525 obj->step(dtime, send_recommended);
1526 // Read messages from object
1527 while(!obj->m_messages_out.empty())
1529 m_active_object_messages.push(
1530 obj->m_messages_out.front());
1531 obj->m_messages_out.pop();
1537 Manage active objects
1539 if(m_object_management_interval.step(dtime, 0.5))
1541 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1543 Remove objects that satisfy (m_removed && m_known_by_count==0)
1545 removeRemovedObjects();
1549 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1551 std::map<u16, ServerActiveObject*>::iterator n;
1552 n = m_active_objects.find(id);
1553 if(n == m_active_objects.end())
1558 bool isFreeServerActiveObjectId(u16 id,
1559 std::map<u16, ServerActiveObject*> &objects)
1564 return objects.find(id) == objects.end();
1567 u16 getFreeServerActiveObjectId(
1568 std::map<u16, ServerActiveObject*> &objects)
1570 //try to reuse id's as late as possible
1571 static u16 last_used_id = 0;
1572 u16 startid = last_used_id;
1576 if(isFreeServerActiveObjectId(last_used_id, objects))
1577 return last_used_id;
1579 if(last_used_id == startid)
1584 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1586 assert(object); // Pre-condition
1588 u16 id = addActiveObjectRaw(object, true, 0);
1593 Finds out what new objects have been added to
1594 inside a radius around a position
1596 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1598 std::set<u16> ¤t_objects,
1599 std::queue<u16> &added_objects)
1601 f32 radius_f = radius * BS;
1602 f32 player_radius_f = player_radius * BS;
1604 if (player_radius_f < 0)
1605 player_radius_f = 0;
1608 Go through the object list,
1609 - discard m_removed objects,
1610 - discard objects that are too far away,
1611 - discard objects that are found in current_objects.
1612 - add remaining objects to added_objects
1614 for(std::map<u16, ServerActiveObject*>::iterator
1615 i = m_active_objects.begin();
1616 i != m_active_objects.end(); ++i) {
1620 ServerActiveObject *object = i->second;
1624 // Discard if removed or deactivating
1625 if(object->m_removed || object->m_pending_deactivation)
1628 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1629 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1630 // Discard if too far
1631 if (distance_f > player_radius_f && player_radius_f != 0)
1633 } else if (distance_f > radius_f)
1636 // Discard if already on current_objects
1637 std::set<u16>::iterator n;
1638 n = current_objects.find(id);
1639 if(n != current_objects.end())
1641 // Add to added_objects
1642 added_objects.push(id);
1647 Finds out what objects have been removed from
1648 inside a radius around a position
1650 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1652 std::set<u16> ¤t_objects,
1653 std::queue<u16> &removed_objects)
1655 f32 radius_f = radius * BS;
1656 f32 player_radius_f = player_radius * BS;
1658 if (player_radius_f < 0)
1659 player_radius_f = 0;
1662 Go through current_objects; object is removed if:
1663 - object is not found in m_active_objects (this is actually an
1664 error condition; objects should be set m_removed=true and removed
1665 only after all clients have been informed about removal), or
1666 - object has m_removed=true, or
1667 - object is too far away
1669 for(std::set<u16>::iterator
1670 i = current_objects.begin();
1671 i != current_objects.end(); ++i)
1674 ServerActiveObject *object = getActiveObject(id);
1676 if (object == NULL) {
1677 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1678 << " object in current_objects is NULL" << std::endl;
1679 removed_objects.push(id);
1683 if (object->m_removed || object->m_pending_deactivation) {
1684 removed_objects.push(id);
1688 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1689 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1690 if (distance_f <= player_radius_f || player_radius_f == 0)
1692 } else if (distance_f <= radius_f)
1695 // Object is no longer visible
1696 removed_objects.push(id);
1700 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1701 v3s16 blockpos, bool static_exists, v3s16 static_block)
1703 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1707 for (std::map<u16, StaticObject>::iterator
1708 so_it = block->m_static_objects.m_active.begin();
1709 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1710 // Get the ServerActiveObject counterpart to this StaticObject
1711 std::map<u16, ServerActiveObject *>::iterator ao_it;
1712 ao_it = m_active_objects.find(so_it->first);
1713 if (ao_it == m_active_objects.end()) {
1714 // If this ever happens, there must be some kind of nasty bug.
1715 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1716 "Object from MapBlock::m_static_objects::m_active not found "
1717 "in m_active_objects";
1721 ServerActiveObject *sao = ao_it->second;
1722 sao->m_static_exists = static_exists;
1723 sao->m_static_block = static_block;
1727 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1729 if(m_active_object_messages.empty())
1730 return ActiveObjectMessage(0);
1732 ActiveObjectMessage message = m_active_object_messages.front();
1733 m_active_object_messages.pop();
1738 ************ Private methods *************
1741 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1742 bool set_changed, u32 dtime_s)
1744 assert(object); // Pre-condition
1745 if(object->getId() == 0){
1746 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1749 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1750 <<"no free ids available"<<std::endl;
1751 if(object->environmentDeletes())
1755 object->setId(new_id);
1758 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1759 <<"supplied with id "<<object->getId()<<std::endl;
1761 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1763 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1764 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1765 if(object->environmentDeletes())
1770 if (objectpos_over_limit(object->getBasePosition())) {
1771 v3f p = object->getBasePosition();
1772 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1773 << "object position (" << p.X << "," << p.Y << "," << p.Z
1774 << ") outside maximum range" << std::endl;
1775 if (object->environmentDeletes())
1780 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1781 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1783 m_active_objects[object->getId()] = object;
1785 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1786 <<"Added id="<<object->getId()<<"; there are now "
1787 <<m_active_objects.size()<<" active objects."
1790 // Register reference in scripting api (must be done before post-init)
1791 m_script->addObjectReference(object);
1792 // Post-initialize object
1793 object->addedToEnvironment(dtime_s);
1795 // Add static data to block
1796 if(object->isStaticAllowed())
1798 // Add static object to active static list of the block
1799 v3f objectpos = object->getBasePosition();
1800 std::string staticdata = object->getStaticData();
1801 StaticObject s_obj(object->getType(), objectpos, staticdata);
1802 // Add to the block where the object is located in
1803 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1804 MapBlock *block = m_map->emergeBlock(blockpos);
1806 block->m_static_objects.m_active[object->getId()] = s_obj;
1807 object->m_static_exists = true;
1808 object->m_static_block = blockpos;
1811 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1812 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1814 v3s16 p = floatToInt(objectpos, BS);
1815 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1816 <<"could not emerge block for storing id="<<object->getId()
1817 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1821 return object->getId();
1825 Remove objects that satisfy (m_removed && m_known_by_count==0)
1827 void ServerEnvironment::removeRemovedObjects()
1829 std::vector<u16> objects_to_remove;
1830 for(std::map<u16, ServerActiveObject*>::iterator
1831 i = m_active_objects.begin();
1832 i != m_active_objects.end(); ++i) {
1834 ServerActiveObject* obj = i->second;
1835 // This shouldn't happen but check it
1838 infostream<<"NULL object found in ServerEnvironment"
1839 <<" while finding removed objects. id="<<id<<std::endl;
1840 // Id to be removed from m_active_objects
1841 objects_to_remove.push_back(id);
1846 We will delete objects that are marked as removed or thatare
1847 waiting for deletion after deactivation
1849 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1853 Delete static data from block if is marked as removed
1855 if(obj->m_static_exists && obj->m_removed)
1857 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1859 block->m_static_objects.remove(id);
1860 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1861 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1862 obj->m_static_exists = false;
1864 infostream<<"Failed to emerge block from which an object to "
1865 <<"be removed was loaded from. id="<<id<<std::endl;
1869 // If m_known_by_count > 0, don't actually remove. On some future
1870 // invocation this will be 0, which is when removal will continue.
1871 if(obj->m_known_by_count > 0)
1875 Move static data from active to stored if not marked as removed
1877 if(obj->m_static_exists && !obj->m_removed){
1878 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1880 std::map<u16, StaticObject>::iterator i =
1881 block->m_static_objects.m_active.find(id);
1882 if(i != block->m_static_objects.m_active.end()){
1883 block->m_static_objects.m_stored.push_back(i->second);
1884 block->m_static_objects.m_active.erase(id);
1885 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1886 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1889 infostream<<"Failed to emerge block from which an object to "
1890 <<"be deactivated was loaded from. id="<<id<<std::endl;
1894 // Tell the object about removal
1895 obj->removingFromEnvironment();
1896 // Deregister in scripting api
1897 m_script->removeObjectReference(obj);
1900 if(obj->environmentDeletes())
1903 // Id to be removed from m_active_objects
1904 objects_to_remove.push_back(id);
1906 // Remove references from m_active_objects
1907 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1908 i != objects_to_remove.end(); ++i) {
1909 m_active_objects.erase(*i);
1913 static void print_hexdump(std::ostream &o, const std::string &data)
1915 const int linelength = 16;
1916 for(int l=0; ; l++){
1917 int i0 = linelength * l;
1918 bool at_end = false;
1919 int thislinelength = linelength;
1920 if(i0 + thislinelength > (int)data.size()){
1921 thislinelength = data.size() - i0;
1924 for(int di=0; di<linelength; di++){
1927 if(di<thislinelength)
1928 snprintf(buf, 4, "%.2x ", data[i]);
1930 snprintf(buf, 4, " ");
1934 for(int di=0; di<thislinelength; di++){
1948 Convert stored objects from blocks near the players to active.
1950 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1955 // Ignore if no stored objects (to not set changed flag)
1956 if(block->m_static_objects.m_stored.empty())
1959 verbosestream<<"ServerEnvironment::activateObjects(): "
1960 <<"activating objects of block "<<PP(block->getPos())
1961 <<" ("<<block->m_static_objects.m_stored.size()
1962 <<" objects)"<<std::endl;
1963 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1965 errorstream<<"suspiciously large amount of objects detected: "
1966 <<block->m_static_objects.m_stored.size()<<" in "
1967 <<PP(block->getPos())
1968 <<"; removing all of them."<<std::endl;
1969 // Clear stored list
1970 block->m_static_objects.m_stored.clear();
1971 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1972 MOD_REASON_TOO_MANY_OBJECTS);
1976 // Activate stored objects
1977 std::vector<StaticObject> new_stored;
1978 for (std::vector<StaticObject>::iterator
1979 i = block->m_static_objects.m_stored.begin();
1980 i != block->m_static_objects.m_stored.end(); ++i) {
1981 StaticObject &s_obj = *i;
1983 // Create an active object from the data
1984 ServerActiveObject *obj = ServerActiveObject::create
1985 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1986 // If couldn't create object, store static data back.
1988 errorstream<<"ServerEnvironment::activateObjects(): "
1989 <<"failed to create active object from static object "
1990 <<"in block "<<PP(s_obj.pos/BS)
1991 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1992 print_hexdump(verbosestream, s_obj.data);
1994 new_stored.push_back(s_obj);
1997 verbosestream<<"ServerEnvironment::activateObjects(): "
1998 <<"activated static object pos="<<PP(s_obj.pos/BS)
1999 <<" type="<<(int)s_obj.type<<std::endl;
2000 // This will also add the object to the active static list
2001 addActiveObjectRaw(obj, false, dtime_s);
2003 // Clear stored list
2004 block->m_static_objects.m_stored.clear();
2005 // Add leftover failed stuff to stored list
2006 for(std::vector<StaticObject>::iterator
2007 i = new_stored.begin();
2008 i != new_stored.end(); ++i) {
2009 StaticObject &s_obj = *i;
2010 block->m_static_objects.m_stored.push_back(s_obj);
2013 // Turn the active counterparts of activated objects not pending for
2015 for(std::map<u16, StaticObject>::iterator
2016 i = block->m_static_objects.m_active.begin();
2017 i != block->m_static_objects.m_active.end(); ++i)
2020 ServerActiveObject *object = getActiveObject(id);
2022 object->m_pending_deactivation = false;
2026 Note: Block hasn't really been modified here.
2027 The objects have just been activated and moved from the stored
2028 static list to the active static list.
2029 As such, the block is essentially the same.
2030 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2031 Otherwise there would be a huge amount of unnecessary I/O.
2036 Convert objects that are not standing inside active blocks to static.
2038 If m_known_by_count != 0, active object is not deleted, but static
2039 data is still updated.
2041 If force_delete is set, active object is deleted nevertheless. It
2042 shall only be set so in the destructor of the environment.
2044 If block wasn't generated (not in memory or on disk),
2046 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2048 std::vector<u16> objects_to_remove;
2049 for(std::map<u16, ServerActiveObject*>::iterator
2050 i = m_active_objects.begin();
2051 i != m_active_objects.end(); ++i) {
2052 ServerActiveObject* obj = i->second;
2055 // Do not deactivate if static data creation not allowed
2056 if(!force_delete && !obj->isStaticAllowed())
2059 // If pending deactivation, let removeRemovedObjects() do it
2060 if(!force_delete && obj->m_pending_deactivation)
2064 v3f objectpos = obj->getBasePosition();
2066 // The block in which the object resides in
2067 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2069 // If object's static data is stored in a deactivated block and object
2070 // is actually located in an active block, re-save to the block in
2071 // which the object is actually located in.
2073 obj->m_static_exists &&
2074 !m_active_blocks.contains(obj->m_static_block) &&
2075 m_active_blocks.contains(blockpos_o))
2077 v3s16 old_static_block = obj->m_static_block;
2079 // Save to block where object is located
2080 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2082 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2083 <<"Could not save object id="<<id
2084 <<" to it's current block "<<PP(blockpos_o)
2088 std::string staticdata_new = obj->getStaticData();
2089 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2090 block->m_static_objects.insert(id, s_obj);
2091 obj->m_static_block = blockpos_o;
2092 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2093 MOD_REASON_STATIC_DATA_ADDED);
2095 // Delete from block where object was located
2096 block = m_map->emergeBlock(old_static_block, false);
2098 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2099 <<"Could not delete object id="<<id
2100 <<" from it's previous block "<<PP(old_static_block)
2104 block->m_static_objects.remove(id);
2105 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2106 MOD_REASON_STATIC_DATA_REMOVED);
2110 // If block is active, don't remove
2111 if(!force_delete && m_active_blocks.contains(blockpos_o))
2114 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2115 <<"deactivating object id="<<id<<" on inactive block "
2116 <<PP(blockpos_o)<<std::endl;
2118 // If known by some client, don't immediately delete.
2119 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2122 Update the static data
2125 if(obj->isStaticAllowed())
2127 // Create new static object
2128 std::string staticdata_new = obj->getStaticData();
2129 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2131 bool stays_in_same_block = false;
2132 bool data_changed = true;
2134 if (obj->m_static_exists) {
2135 if (obj->m_static_block == blockpos_o)
2136 stays_in_same_block = true;
2138 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2141 std::map<u16, StaticObject>::iterator n =
2142 block->m_static_objects.m_active.find(id);
2143 if (n != block->m_static_objects.m_active.end()) {
2144 StaticObject static_old = n->second;
2146 float save_movem = obj->getMinimumSavedMovement();
2148 if (static_old.data == staticdata_new &&
2149 (static_old.pos - objectpos).getLength() < save_movem)
2150 data_changed = false;
2152 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2153 <<"id="<<id<<" m_static_exists=true but "
2154 <<"static data doesn't actually exist in "
2155 <<PP(obj->m_static_block)<<std::endl;
2160 bool shall_be_written = (!stays_in_same_block || data_changed);
2162 // Delete old static object
2163 if(obj->m_static_exists)
2165 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2168 block->m_static_objects.remove(id);
2169 obj->m_static_exists = false;
2170 // Only mark block as modified if data changed considerably
2171 if(shall_be_written)
2172 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2173 MOD_REASON_STATIC_DATA_CHANGED);
2177 // Add to the block where the object is located in
2178 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2179 // Get or generate the block
2180 MapBlock *block = NULL;
2182 block = m_map->emergeBlock(blockpos);
2183 } catch(InvalidPositionException &e){
2184 // Handled via NULL pointer
2185 // NOTE: emergeBlock's failure is usually determined by it
2186 // actually returning NULL
2191 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2192 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2193 <<" statically but block "<<PP(blockpos)
2194 <<" already contains "
2195 <<block->m_static_objects.m_stored.size()
2197 <<" Forcing delete."<<std::endl;
2198 force_delete = true;
2200 // If static counterpart already exists in target block,
2202 // This shouldn't happen because the object is removed from
2203 // the previous block before this according to
2204 // obj->m_static_block, but happens rarely for some unknown
2205 // reason. Unsuccessful attempts have been made to find
2207 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2208 warningstream<<"ServerEnv: Performing hack #83274"
2210 block->m_static_objects.remove(id);
2212 // Store static data
2213 u16 store_id = pending_delete ? id : 0;
2214 block->m_static_objects.insert(store_id, s_obj);
2216 // Only mark block as modified if data changed considerably
2217 if(shall_be_written)
2218 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2219 MOD_REASON_STATIC_DATA_CHANGED);
2221 obj->m_static_exists = true;
2222 obj->m_static_block = block->getPos();
2227 v3s16 p = floatToInt(objectpos, BS);
2228 errorstream<<"ServerEnv: Could not find or generate "
2229 <<"a block for storing id="<<obj->getId()
2230 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2237 If known by some client, set pending deactivation.
2238 Otherwise delete it immediately.
2241 if(pending_delete && !force_delete)
2243 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2244 <<"object id="<<id<<" is known by clients"
2245 <<"; not deleting yet"<<std::endl;
2247 obj->m_pending_deactivation = true;
2251 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2252 <<"object id="<<id<<" is not known by clients"
2253 <<"; deleting"<<std::endl;
2255 // Tell the object about removal
2256 obj->removingFromEnvironment();
2257 // Deregister in scripting api
2258 m_script->removeObjectReference(obj);
2260 // Delete active object
2261 if(obj->environmentDeletes())
2263 // Id to be removed from m_active_objects
2264 objects_to_remove.push_back(id);
2267 // Remove references from m_active_objects
2268 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2269 i != objects_to_remove.end(); ++i) {
2270 m_active_objects.erase(*i);
2276 #include "clientsimpleobject.h"
2282 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2283 ITextureSource *texturesource, IGameDef *gamedef,
2284 IrrlichtDevice *irr):
2287 m_texturesource(texturesource),
2292 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2295 ClientEnvironment::~ClientEnvironment()
2297 // delete active objects
2298 for(std::map<u16, ClientActiveObject*>::iterator
2299 i = m_active_objects.begin();
2300 i != m_active_objects.end(); ++i)
2305 for(std::vector<ClientSimpleObject*>::iterator
2306 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2314 Map & ClientEnvironment::getMap()
2319 ClientMap & ClientEnvironment::getClientMap()
2324 void ClientEnvironment::addPlayer(Player *player)
2326 DSTACK(FUNCTION_NAME);
2328 It is a failure if player is local and there already is a local
2331 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2332 "Player is local but there is already a local player");
2334 Environment::addPlayer(player);
2337 LocalPlayer * ClientEnvironment::getLocalPlayer()
2339 for(std::vector<Player*>::iterator i = m_players.begin();
2340 i != m_players.end(); ++i) {
2341 Player *player = *i;
2342 if(player->isLocal())
2343 return (LocalPlayer*)player;
2348 void ClientEnvironment::step(float dtime)
2350 DSTACK(FUNCTION_NAME);
2352 /* Step time of day */
2353 stepTimeOfDay(dtime);
2355 // Get some settings
2356 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2357 bool free_move = fly_allowed && g_settings->getBool("free_move");
2360 LocalPlayer *lplayer = getLocalPlayer();
2362 // collision info queue
2363 std::vector<CollisionInfo> player_collisions;
2366 Get the speed the player is going
2368 bool is_climbing = lplayer->is_climbing;
2370 f32 player_speed = lplayer->getSpeed().getLength();
2373 Maximum position increment
2375 //f32 position_max_increment = 0.05*BS;
2376 f32 position_max_increment = 0.1*BS;
2378 // Maximum time increment (for collision detection etc)
2379 // time = distance / speed
2380 f32 dtime_max_increment = 1;
2381 if(player_speed > 0.001)
2382 dtime_max_increment = position_max_increment / player_speed;
2384 // Maximum time increment is 10ms or lower
2385 if(dtime_max_increment > 0.01)
2386 dtime_max_increment = 0.01;
2388 // Don't allow overly huge dtime
2392 f32 dtime_downcount = dtime;
2395 Stuff that has a maximum time increment
2404 if(dtime_downcount > dtime_max_increment)
2406 dtime_part = dtime_max_increment;
2407 dtime_downcount -= dtime_part;
2411 dtime_part = dtime_downcount;
2413 Setting this to 0 (no -=dtime_part) disables an infinite loop
2414 when dtime_part is so small that dtime_downcount -= dtime_part
2417 dtime_downcount = 0;
2426 if(free_move == false && is_climbing == false)
2429 v3f speed = lplayer->getSpeed();
2430 if(lplayer->in_liquid == false)
2431 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2433 // Liquid floating / sinking
2434 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2435 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2437 // Liquid resistance
2438 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2440 // How much the node's viscosity blocks movement, ranges between 0 and 1
2441 // Should match the scale at which viscosity increase affects other liquid attributes
2442 const f32 viscosity_factor = 0.3;
2444 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2445 f32 dl = d_wanted.getLength();
2446 if(dl > lplayer->movement_liquid_fluidity_smooth)
2447 dl = lplayer->movement_liquid_fluidity_smooth;
2448 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2450 v3f d = d_wanted.normalize() * dl;
2454 lplayer->setSpeed(speed);
2459 This also does collision detection.
2461 lplayer->move(dtime_part, this, position_max_increment,
2462 &player_collisions);
2465 while(dtime_downcount > 0.001);
2467 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2469 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2470 i != player_collisions.end(); ++i) {
2471 CollisionInfo &info = *i;
2472 v3f speed_diff = info.new_speed - info.old_speed;;
2473 // Handle only fall damage
2474 // (because otherwise walking against something in fast_move kills you)
2475 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2477 // Get rid of other components
2480 f32 pre_factor = 1; // 1 hp per node/s
2481 f32 tolerance = BS*14; // 5 without damage
2482 f32 post_factor = 1; // 1 hp per node/s
2483 if(info.type == COLLISION_NODE)
2485 const ContentFeatures &f = m_gamedef->ndef()->
2486 get(m_map->getNodeNoEx(info.node_p));
2487 // Determine fall damage multiplier
2488 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2489 pre_factor = 1.0 + (float)addp/100.0;
2491 float speed = pre_factor * speed_diff.getLength();
2492 if(speed > tolerance)
2494 f32 damage_f = (speed - tolerance)/BS * post_factor;
2495 u16 damage = (u16)(damage_f+0.5);
2497 damageLocalPlayer(damage, true);
2498 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2499 m_gamedef->event()->put(e);
2505 A quick draft of lava damage
2507 if(m_lava_hurt_interval.step(dtime, 1.0))
2509 v3f pf = lplayer->getPosition();
2511 // Feet, middle and head
2512 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2513 MapNode n1 = m_map->getNodeNoEx(p1);
2514 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2515 MapNode n2 = m_map->getNodeNoEx(p2);
2516 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2517 MapNode n3 = m_map->getNodeNoEx(p3);
2519 u32 damage_per_second = 0;
2520 damage_per_second = MYMAX(damage_per_second,
2521 m_gamedef->ndef()->get(n1).damage_per_second);
2522 damage_per_second = MYMAX(damage_per_second,
2523 m_gamedef->ndef()->get(n2).damage_per_second);
2524 damage_per_second = MYMAX(damage_per_second,
2525 m_gamedef->ndef()->get(n3).damage_per_second);
2527 if(damage_per_second != 0)
2529 damageLocalPlayer(damage_per_second, true);
2536 if(m_drowning_interval.step(dtime, 2.0))
2538 v3f pf = lplayer->getPosition();
2541 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2542 MapNode n = m_map->getNodeNoEx(p);
2543 ContentFeatures c = m_gamedef->ndef()->get(n);
2544 u8 drowning_damage = c.drowning;
2545 if(drowning_damage > 0 && lplayer->hp > 0){
2546 u16 breath = lplayer->getBreath();
2553 lplayer->setBreath(breath);
2554 updateLocalPlayerBreath(breath);
2557 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2558 damageLocalPlayer(drowning_damage, true);
2561 if(m_breathing_interval.step(dtime, 0.5))
2563 v3f pf = lplayer->getPosition();
2566 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2567 MapNode n = m_map->getNodeNoEx(p);
2568 ContentFeatures c = m_gamedef->ndef()->get(n);
2570 lplayer->setBreath(11);
2572 else if(c.drowning == 0){
2573 u16 breath = lplayer->getBreath();
2576 lplayer->setBreath(breath);
2577 updateLocalPlayerBreath(breath);
2583 Stuff that can be done in an arbitarily large dtime
2585 for(std::vector<Player*>::iterator i = m_players.begin();
2586 i != m_players.end(); ++i) {
2587 Player *player = *i;
2590 Handle non-local players
2592 if(player->isLocal() == false) {
2594 player->move(dtime, this, 100*BS);
2599 // Update lighting on local player (used for wield item)
2600 u32 day_night_ratio = getDayNightRatio();
2604 // On InvalidPositionException, use this as default
2605 // (day: LIGHT_SUN, night: 0)
2606 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2608 v3s16 p = lplayer->getLightPosition();
2609 node_at_lplayer = m_map->getNodeNoEx(p);
2611 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2612 u8 day = light & 0xff;
2613 u8 night = (light >> 8) & 0xff;
2614 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2618 Step active objects and update lighting of them
2621 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2622 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2623 for(std::map<u16, ClientActiveObject*>::iterator
2624 i = m_active_objects.begin();
2625 i != m_active_objects.end(); ++i)
2627 ClientActiveObject* obj = i->second;
2629 obj->step(dtime, this);
2638 v3s16 p = obj->getLightPosition();
2639 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2641 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2643 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2645 obj->updateLight(light);
2650 Step and handle simple objects
2652 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2653 for(std::vector<ClientSimpleObject*>::iterator
2654 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2655 std::vector<ClientSimpleObject*>::iterator cur = i;
2656 ClientSimpleObject *simple = *cur;
2658 simple->step(dtime);
2659 if(simple->m_to_be_removed) {
2661 i = m_simple_objects.erase(cur);
2669 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2671 m_simple_objects.push_back(simple);
2674 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2676 ClientActiveObject *obj = getActiveObject(id);
2677 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2678 return (GenericCAO*) obj;
2683 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2685 std::map<u16, ClientActiveObject*>::iterator n;
2686 n = m_active_objects.find(id);
2687 if(n == m_active_objects.end())
2692 bool isFreeClientActiveObjectId(u16 id,
2693 std::map<u16, ClientActiveObject*> &objects)
2698 return objects.find(id) == objects.end();
2701 u16 getFreeClientActiveObjectId(
2702 std::map<u16, ClientActiveObject*> &objects)
2704 //try to reuse id's as late as possible
2705 static u16 last_used_id = 0;
2706 u16 startid = last_used_id;
2710 if(isFreeClientActiveObjectId(last_used_id, objects))
2711 return last_used_id;
2713 if(last_used_id == startid)
2718 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2720 assert(object); // Pre-condition
2721 if(object->getId() == 0)
2723 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2726 infostream<<"ClientEnvironment::addActiveObject(): "
2727 <<"no free ids available"<<std::endl;
2731 object->setId(new_id);
2733 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2735 infostream<<"ClientEnvironment::addActiveObject(): "
2736 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2740 infostream<<"ClientEnvironment::addActiveObject(): "
2741 <<"added (id="<<object->getId()<<")"<<std::endl;
2742 m_active_objects[object->getId()] = object;
2743 object->addToScene(m_smgr, m_texturesource, m_irr);
2744 { // Update lighting immediately
2749 v3s16 p = object->getLightPosition();
2750 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2752 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2754 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2756 object->updateLight(light);
2758 return object->getId();
2761 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2762 const std::string &init_data)
2764 ClientActiveObject* obj =
2765 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2768 infostream<<"ClientEnvironment::addActiveObject(): "
2769 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2778 obj->initialize(init_data);
2780 catch(SerializationError &e)
2782 errorstream<<"ClientEnvironment::addActiveObject():"
2783 <<" id="<<id<<" type="<<type
2784 <<": SerializationError in initialize(): "
2786 <<": init_data="<<serializeJsonString(init_data)
2790 addActiveObject(obj);
2793 void ClientEnvironment::removeActiveObject(u16 id)
2795 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2796 <<"id="<<id<<std::endl;
2797 ClientActiveObject* obj = getActiveObject(id);
2800 infostream<<"ClientEnvironment::removeActiveObject(): "
2801 <<"id="<<id<<" not found"<<std::endl;
2804 obj->removeFromScene(true);
2806 m_active_objects.erase(id);
2809 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2811 ClientActiveObject *obj = getActiveObject(id);
2813 infostream << "ClientEnvironment::processActiveObjectMessage():"
2814 << " got message for id=" << id << ", which doesn't exist."
2820 obj->processMessage(data);
2821 } catch (SerializationError &e) {
2822 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2823 << " id=" << id << " type=" << obj->getType()
2824 << " SerializationError in processMessage(): " << e.what()
2830 Callbacks for activeobjects
2833 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2835 LocalPlayer *lplayer = getLocalPlayer();
2839 if (lplayer->hp > damage)
2840 lplayer->hp -= damage;
2845 ClientEnvEvent event;
2846 event.type = CEE_PLAYER_DAMAGE;
2847 event.player_damage.amount = damage;
2848 event.player_damage.send_to_server = handle_hp;
2849 m_client_event_queue.push(event);
2852 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2854 ClientEnvEvent event;
2855 event.type = CEE_PLAYER_BREATH;
2856 event.player_breath.amount = breath;
2857 m_client_event_queue.push(event);
2861 Client likes to call these
2864 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2865 std::vector<DistanceSortedActiveObject> &dest)
2867 for(std::map<u16, ClientActiveObject*>::iterator
2868 i = m_active_objects.begin();
2869 i != m_active_objects.end(); ++i)
2871 ClientActiveObject* obj = i->second;
2873 f32 d = (obj->getPosition() - origin).getLength();
2878 DistanceSortedActiveObject dso(obj, d);
2880 dest.push_back(dso);
2884 ClientEnvEvent ClientEnvironment::getClientEvent()
2886 ClientEnvEvent event;
2887 if(m_client_event_queue.empty())
2888 event.type = CEE_NONE;
2890 event = m_client_event_queue.front();
2891 m_client_event_queue.pop();
2896 #endif // #ifndef SERVER