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");
780 setTimeOfDay(args.getU64("time_of_day"));
781 } catch (SettingNotFoundException &e) {
782 // This is not as important
787 m_last_clear_objects_time = args.getU64("last_clear_objects_time");
788 } catch (SettingNotFoundException &e) {
789 // If missing, do as if clearObjects was never called
790 m_last_clear_objects_time = 0;
793 std::string lbm_introduction_times = "";
795 u64 ver = args.getU64("lbm_introduction_times_version");
797 lbm_introduction_times = args.get("lbm_introduction_times");
799 infostream << "ServerEnvironment::loadMeta(): Non-supported"
800 << " introduction time version " << ver << std::endl;
802 } catch (SettingNotFoundException &e) {
803 // No problem, this is expected. Just continue with an empty string
805 m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
808 m_day_count = args.getU64("day_count");
809 } catch (SettingNotFoundException &e) {
810 // If missing, start the day counter
815 void ServerEnvironment::loadDefaultMeta()
817 m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
822 ActiveBlockModifier *abm;
824 std::set<content_t> required_neighbors;
830 ServerEnvironment *m_env;
831 std::map<content_t, std::vector<ActiveABM> > m_aabms;
833 ABMHandler(std::vector<ABMWithState> &abms,
834 float dtime_s, ServerEnvironment *env,
840 INodeDefManager *ndef = env->getGameDef()->ndef();
841 for(std::vector<ABMWithState>::iterator
842 i = abms.begin(); i != abms.end(); ++i) {
843 ActiveBlockModifier *abm = i->abm;
844 float trigger_interval = abm->getTriggerInterval();
845 if(trigger_interval < 0.001)
846 trigger_interval = 0.001;
847 float actual_interval = dtime_s;
850 if(i->timer < trigger_interval)
852 i->timer -= trigger_interval;
853 actual_interval = trigger_interval;
855 float chance = abm->getTriggerChance();
860 if(abm->getSimpleCatchUp()) {
861 float intervals = actual_interval / trigger_interval;
864 aabm.chance = chance / intervals;
868 aabm.chance = chance;
871 std::set<std::string> required_neighbors_s
872 = abm->getRequiredNeighbors();
873 for(std::set<std::string>::iterator
874 i = required_neighbors_s.begin();
875 i != required_neighbors_s.end(); ++i)
877 ndef->getIds(*i, aabm.required_neighbors);
880 std::set<std::string> contents_s = abm->getTriggerContents();
881 for(std::set<std::string>::iterator
882 i = contents_s.begin(); i != contents_s.end(); ++i)
884 std::set<content_t> ids;
885 ndef->getIds(*i, ids);
886 for(std::set<content_t>::const_iterator k = ids.begin();
890 std::map<content_t, std::vector<ActiveABM> >::iterator j;
892 if(j == m_aabms.end()){
893 std::vector<ActiveABM> aabmlist;
894 m_aabms[c] = aabmlist;
897 j->second.push_back(aabm);
902 // Find out how many objects the given block and its neighbours contain.
903 // Returns the number of objects in the block, and also in 'wider' the
904 // number of objects in the block and all its neighbours. The latter
905 // may an estimate if any neighbours are unloaded.
906 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
909 u32 wider_unknown_count = 0;
910 for(s16 x=-1; x<=1; x++)
911 for(s16 y=-1; y<=1; y++)
912 for(s16 z=-1; z<=1; z++)
914 MapBlock *block2 = map->getBlockNoCreateNoEx(
915 block->getPos() + v3s16(x,y,z));
917 wider_unknown_count++;
920 wider += block2->m_static_objects.m_active.size()
921 + block2->m_static_objects.m_stored.size();
924 u32 active_object_count = block->m_static_objects.m_active.size();
925 u32 wider_known_count = 3*3*3 - wider_unknown_count;
926 wider += wider_unknown_count * wider / wider_known_count;
927 return active_object_count;
930 void apply(MapBlock *block)
935 ServerMap *map = &m_env->getServerMap();
937 u32 active_object_count_wider;
938 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
939 m_env->m_added_objects = 0;
942 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
943 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
944 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
946 MapNode n = block->getNodeNoEx(p0);
947 content_t c = n.getContent();
948 v3s16 p = p0 + block->getPosRelative();
950 std::map<content_t, std::vector<ActiveABM> >::iterator j;
952 if(j == m_aabms.end())
955 for(std::vector<ActiveABM>::iterator
956 i = j->second.begin(); i != j->second.end(); ++i) {
957 if(myrand() % i->chance != 0)
961 if(!i->required_neighbors.empty())
964 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
965 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
966 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
970 MapNode n = map->getNodeNoEx(p1);
971 content_t c = n.getContent();
972 std::set<content_t>::const_iterator k;
973 k = i->required_neighbors.find(c);
974 if(k != i->required_neighbors.end()){
978 // No required neighbor found
983 // Call all the trigger variations
984 i->abm->trigger(m_env, p, n);
985 i->abm->trigger(m_env, p, n,
986 active_object_count, active_object_count_wider);
988 // Count surrounding objects again if the abms added any
989 if(m_env->m_added_objects > 0) {
990 active_object_count = countObjects(block, map, active_object_count_wider);
991 m_env->m_added_objects = 0;
998 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
1000 // Reset usage timer immediately, otherwise a block that becomes active
1001 // again at around the same time as it would normally be unloaded will
1002 // get unloaded incorrectly. (I think this still leaves a small possibility
1003 // of a race condition between this and server::AsyncRunStep, which only
1004 // some kind of synchronisation will fix, but it at least reduces the window
1005 // of opportunity for it to break from seconds to nanoseconds)
1006 block->resetUsageTimer();
1008 // Get time difference
1010 u32 stamp = block->getTimestamp();
1011 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
1012 dtime_s = m_game_time - stamp;
1013 dtime_s += additional_dtime;
1015 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
1016 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
1018 // Remove stored static objects if clearObjects was called since block's timestamp
1019 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
1020 block->m_static_objects.m_stored.clear();
1021 // do not set changed flag to avoid unnecessary mapblock writes
1024 // Set current time as timestamp
1025 block->setTimestampNoChangedFlag(m_game_time);
1027 /*infostream<<"ServerEnvironment::activateBlock(): block is "
1028 <<dtime_s<<" seconds old."<<std::endl;*/
1030 // Activate stored objects
1031 activateObjects(block, dtime_s);
1033 /* Handle LoadingBlockModifiers */
1034 m_lbm_mgr.applyLBMs(this, block, stamp);
1037 std::map<v3s16, NodeTimer> elapsed_timers =
1038 block->m_node_timers.step((float)dtime_s);
1039 if(!elapsed_timers.empty()){
1041 for(std::map<v3s16, NodeTimer>::iterator
1042 i = elapsed_timers.begin();
1043 i != elapsed_timers.end(); ++i){
1044 n = block->getNodeNoEx(i->first);
1045 v3s16 p = i->first + block->getPosRelative();
1046 if(m_script->node_on_timer(p,n,i->second.elapsed))
1047 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1051 /* Handle ActiveBlockModifiers */
1052 ABMHandler abmhandler(m_abms, dtime_s, this, false);
1053 abmhandler.apply(block);
1056 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
1058 m_abms.push_back(ABMWithState(abm));
1061 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1063 m_lbm_mgr.addLBMDef(lbm);
1066 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1068 INodeDefManager *ndef = m_gamedef->ndef();
1069 MapNode n_old = m_map->getNodeNoEx(p);
1072 if (ndef->get(n_old).has_on_destruct)
1073 m_script->node_on_destruct(p, n_old);
1076 if (!m_map->addNodeWithEvent(p, n))
1079 // Update active VoxelManipulator if a mapgen thread
1080 m_map->updateVManip(p);
1082 // Call post-destructor
1083 if (ndef->get(n_old).has_after_destruct)
1084 m_script->node_after_destruct(p, n_old);
1087 if (ndef->get(n).has_on_construct)
1088 m_script->node_on_construct(p, n);
1093 bool ServerEnvironment::removeNode(v3s16 p)
1095 INodeDefManager *ndef = m_gamedef->ndef();
1096 MapNode n_old = m_map->getNodeNoEx(p);
1099 if (ndef->get(n_old).has_on_destruct)
1100 m_script->node_on_destruct(p, n_old);
1103 // This is slightly optimized compared to addNodeWithEvent(air)
1104 if (!m_map->removeNodeWithEvent(p))
1107 // Update active VoxelManipulator if a mapgen thread
1108 m_map->updateVManip(p);
1110 // Call post-destructor
1111 if (ndef->get(n_old).has_after_destruct)
1112 m_script->node_after_destruct(p, n_old);
1114 // Air doesn't require constructor
1118 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1120 if (!m_map->addNodeWithEvent(p, n, false))
1123 // Update active VoxelManipulator if a mapgen thread
1124 m_map->updateVManip(p);
1129 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1131 for(std::map<u16, ServerActiveObject*>::iterator
1132 i = m_active_objects.begin();
1133 i != m_active_objects.end(); ++i)
1135 ServerActiveObject* obj = i->second;
1137 v3f objectpos = obj->getBasePosition();
1138 if(objectpos.getDistanceFrom(pos) > radius)
1140 objects.push_back(id);
1144 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1146 infostream << "ServerEnvironment::clearObjects(): "
1147 << "Removing all active objects" << std::endl;
1148 std::vector<u16> objects_to_remove;
1149 for (std::map<u16, ServerActiveObject*>::iterator
1150 i = m_active_objects.begin();
1151 i != m_active_objects.end(); ++i) {
1152 ServerActiveObject* obj = i->second;
1153 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1156 // Delete static object if block is loaded
1157 if (obj->m_static_exists) {
1158 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1160 block->m_static_objects.remove(id);
1161 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1162 MOD_REASON_CLEAR_ALL_OBJECTS);
1163 obj->m_static_exists = false;
1166 // If known by some client, don't delete immediately
1167 if (obj->m_known_by_count > 0) {
1168 obj->m_pending_deactivation = true;
1169 obj->m_removed = true;
1173 // Tell the object about removal
1174 obj->removingFromEnvironment();
1175 // Deregister in scripting api
1176 m_script->removeObjectReference(obj);
1178 // Delete active object
1179 if (obj->environmentDeletes())
1181 // Id to be removed from m_active_objects
1182 objects_to_remove.push_back(id);
1185 // Remove references from m_active_objects
1186 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1187 i != objects_to_remove.end(); ++i) {
1188 m_active_objects.erase(*i);
1191 // Get list of loaded blocks
1192 std::vector<v3s16> loaded_blocks;
1193 infostream << "ServerEnvironment::clearObjects(): "
1194 << "Listing all loaded blocks" << std::endl;
1195 m_map->listAllLoadedBlocks(loaded_blocks);
1196 infostream << "ServerEnvironment::clearObjects(): "
1197 << "Done listing all loaded blocks: "
1198 << loaded_blocks.size()<<std::endl;
1200 // Get list of loadable blocks
1201 std::vector<v3s16> loadable_blocks;
1202 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1203 infostream << "ServerEnvironment::clearObjects(): "
1204 << "Listing all loadable blocks" << std::endl;
1205 m_map->listAllLoadableBlocks(loadable_blocks);
1206 infostream << "ServerEnvironment::clearObjects(): "
1207 << "Done listing all loadable blocks: "
1208 << loadable_blocks.size() << std::endl;
1210 loadable_blocks = loaded_blocks;
1213 infostream << "ServerEnvironment::clearObjects(): "
1214 << "Now clearing objects in " << loadable_blocks.size()
1215 << " blocks" << std::endl;
1217 // Grab a reference on each loaded block to avoid unloading it
1218 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1219 i != loaded_blocks.end(); ++i) {
1221 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1222 assert(block != NULL);
1226 // Remove objects in all loadable blocks
1227 u32 unload_interval = U32_MAX;
1228 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1229 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1230 unload_interval = MYMAX(unload_interval, 1);
1232 u32 report_interval = loadable_blocks.size() / 10;
1233 u32 num_blocks_checked = 0;
1234 u32 num_blocks_cleared = 0;
1235 u32 num_objs_cleared = 0;
1236 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1237 i != loadable_blocks.end(); ++i) {
1239 MapBlock *block = m_map->emergeBlock(p, false);
1241 errorstream << "ServerEnvironment::clearObjects(): "
1242 << "Failed to emerge block " << PP(p) << std::endl;
1245 u32 num_stored = block->m_static_objects.m_stored.size();
1246 u32 num_active = block->m_static_objects.m_active.size();
1247 if (num_stored != 0 || num_active != 0) {
1248 block->m_static_objects.m_stored.clear();
1249 block->m_static_objects.m_active.clear();
1250 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1251 MOD_REASON_CLEAR_ALL_OBJECTS);
1252 num_objs_cleared += num_stored + num_active;
1253 num_blocks_cleared++;
1255 num_blocks_checked++;
1257 if (report_interval != 0 &&
1258 num_blocks_checked % report_interval == 0) {
1259 float percent = 100.0 * (float)num_blocks_checked /
1260 loadable_blocks.size();
1261 infostream << "ServerEnvironment::clearObjects(): "
1262 << "Cleared " << num_objs_cleared << " objects"
1263 << " in " << num_blocks_cleared << " blocks ("
1264 << percent << "%)" << std::endl;
1266 if (num_blocks_checked % unload_interval == 0) {
1267 m_map->unloadUnreferencedBlocks();
1270 m_map->unloadUnreferencedBlocks();
1272 // Drop references that were added above
1273 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1274 i != loaded_blocks.end(); ++i) {
1276 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1281 m_last_clear_objects_time = m_game_time;
1283 infostream << "ServerEnvironment::clearObjects(): "
1284 << "Finished: Cleared " << num_objs_cleared << " objects"
1285 << " in " << num_blocks_cleared << " blocks" << std::endl;
1288 void ServerEnvironment::step(float dtime)
1290 DSTACK(FUNCTION_NAME);
1292 //TimeTaker timer("ServerEnv step");
1294 /* Step time of day */
1295 stepTimeOfDay(dtime);
1298 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1299 // really matter that much.
1300 static const float server_step = g_settings->getFloat("dedicated_server_step");
1301 m_recommended_send_interval = server_step;
1307 m_game_time_fraction_counter += dtime;
1308 u32 inc_i = (u32)m_game_time_fraction_counter;
1309 m_game_time += inc_i;
1310 m_game_time_fraction_counter -= (float)inc_i;
1317 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1318 for(std::vector<Player*>::iterator i = m_players.begin();
1319 i != m_players.end(); ++i)
1321 Player *player = *i;
1323 // Ignore disconnected players
1324 if(player->peer_id == 0)
1328 player->move(dtime, this, 100*BS);
1333 Manage active block list
1335 if(m_active_blocks_management_interval.step(dtime, 2.0))
1337 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1339 Get player block positions
1341 std::vector<v3s16> players_blockpos;
1342 for(std::vector<Player*>::iterator
1343 i = m_players.begin();
1344 i != m_players.end(); ++i) {
1345 Player *player = *i;
1346 // Ignore disconnected players
1347 if(player->peer_id == 0)
1350 v3s16 blockpos = getNodeBlockPos(
1351 floatToInt(player->getPosition(), BS));
1352 players_blockpos.push_back(blockpos);
1356 Update list of active blocks, collecting changes
1358 static const s16 active_block_range = g_settings->getS16("active_block_range");
1359 std::set<v3s16> blocks_removed;
1360 std::set<v3s16> blocks_added;
1361 m_active_blocks.update(players_blockpos, active_block_range,
1362 blocks_removed, blocks_added);
1365 Handle removed blocks
1368 // Convert active objects that are no more in active blocks to static
1369 deactivateFarObjects(false);
1371 for(std::set<v3s16>::iterator
1372 i = blocks_removed.begin();
1373 i != blocks_removed.end(); ++i) {
1376 /* infostream<<"Server: Block " << PP(p)
1377 << " became inactive"<<std::endl; */
1379 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1383 // Set current time as timestamp (and let it set ChangedFlag)
1384 block->setTimestamp(m_game_time);
1391 for(std::set<v3s16>::iterator
1392 i = blocks_added.begin();
1393 i != blocks_added.end(); ++i)
1397 MapBlock *block = m_map->getBlockOrEmerge(p);
1399 m_active_blocks.m_list.erase(p);
1403 activateBlock(block);
1404 /* infostream<<"Server: Block " << PP(p)
1405 << " became active"<<std::endl; */
1410 Mess around in active blocks
1412 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1414 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1418 for(std::set<v3s16>::iterator
1419 i = m_active_blocks.m_list.begin();
1420 i != m_active_blocks.m_list.end(); ++i)
1424 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1425 <<") being handled"<<std::endl;*/
1427 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1431 // Reset block usage timer
1432 block->resetUsageTimer();
1434 // Set current time as timestamp
1435 block->setTimestampNoChangedFlag(m_game_time);
1436 // If time has changed much from the one on disk,
1437 // set block to be saved when it is unloaded
1438 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1439 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1440 MOD_REASON_BLOCK_EXPIRED);
1443 std::map<v3s16, NodeTimer> elapsed_timers =
1444 block->m_node_timers.step((float)dtime);
1445 if(!elapsed_timers.empty()){
1447 for(std::map<v3s16, NodeTimer>::iterator
1448 i = elapsed_timers.begin();
1449 i != elapsed_timers.end(); ++i){
1450 n = block->getNodeNoEx(i->first);
1451 p = i->first + block->getPosRelative();
1452 if(m_script->node_on_timer(p,n,i->second.elapsed))
1453 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1459 const float abm_interval = 1.0;
1460 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1462 if(m_active_block_interval_overload_skip > 0){
1463 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1464 m_active_block_interval_overload_skip--;
1467 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1468 TimeTaker timer("modify in active blocks");
1470 // Initialize handling of ActiveBlockModifiers
1471 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1473 for(std::set<v3s16>::iterator
1474 i = m_active_blocks.m_list.begin();
1475 i != m_active_blocks.m_list.end(); ++i)
1479 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1480 <<") being handled"<<std::endl;*/
1482 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1486 // Set current time as timestamp
1487 block->setTimestampNoChangedFlag(m_game_time);
1489 /* Handle ActiveBlockModifiers */
1490 abmhandler.apply(block);
1493 u32 time_ms = timer.stop(true);
1494 u32 max_time_ms = 200;
1495 if(time_ms > max_time_ms){
1496 warningstream<<"active block modifiers took "
1497 <<time_ms<<"ms (longer than "
1498 <<max_time_ms<<"ms)"<<std::endl;
1499 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1504 Step script environment (run global on_step())
1506 m_script->environment_Step(dtime);
1512 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1513 //TimeTaker timer("Step active objects");
1515 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1517 // This helps the objects to send data at the same time
1518 bool send_recommended = false;
1519 m_send_recommended_timer += dtime;
1520 if(m_send_recommended_timer > getSendRecommendedInterval())
1522 m_send_recommended_timer -= getSendRecommendedInterval();
1523 send_recommended = true;
1526 for(std::map<u16, ServerActiveObject*>::iterator
1527 i = m_active_objects.begin();
1528 i != m_active_objects.end(); ++i)
1530 ServerActiveObject* obj = i->second;
1531 // Don't step if is to be removed or stored statically
1532 if(obj->m_removed || obj->m_pending_deactivation)
1535 obj->step(dtime, send_recommended);
1536 // Read messages from object
1537 while(!obj->m_messages_out.empty())
1539 m_active_object_messages.push(
1540 obj->m_messages_out.front());
1541 obj->m_messages_out.pop();
1547 Manage active objects
1549 if(m_object_management_interval.step(dtime, 0.5))
1551 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1553 Remove objects that satisfy (m_removed && m_known_by_count==0)
1555 removeRemovedObjects();
1559 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1561 std::map<u16, ServerActiveObject*>::iterator n;
1562 n = m_active_objects.find(id);
1563 if(n == m_active_objects.end())
1568 bool isFreeServerActiveObjectId(u16 id,
1569 std::map<u16, ServerActiveObject*> &objects)
1574 return objects.find(id) == objects.end();
1577 u16 getFreeServerActiveObjectId(
1578 std::map<u16, ServerActiveObject*> &objects)
1580 //try to reuse id's as late as possible
1581 static u16 last_used_id = 0;
1582 u16 startid = last_used_id;
1586 if(isFreeServerActiveObjectId(last_used_id, objects))
1587 return last_used_id;
1589 if(last_used_id == startid)
1594 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1596 assert(object); // Pre-condition
1598 u16 id = addActiveObjectRaw(object, true, 0);
1603 Finds out what new objects have been added to
1604 inside a radius around a position
1606 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1608 std::set<u16> ¤t_objects,
1609 std::queue<u16> &added_objects)
1611 f32 radius_f = radius * BS;
1612 f32 player_radius_f = player_radius * BS;
1614 if (player_radius_f < 0)
1615 player_radius_f = 0;
1618 Go through the object list,
1619 - discard m_removed objects,
1620 - discard objects that are too far away,
1621 - discard objects that are found in current_objects.
1622 - add remaining objects to added_objects
1624 for(std::map<u16, ServerActiveObject*>::iterator
1625 i = m_active_objects.begin();
1626 i != m_active_objects.end(); ++i) {
1630 ServerActiveObject *object = i->second;
1634 // Discard if removed or deactivating
1635 if(object->m_removed || object->m_pending_deactivation)
1638 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1639 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1640 // Discard if too far
1641 if (distance_f > player_radius_f && player_radius_f != 0)
1643 } else if (distance_f > radius_f)
1646 // Discard if already on current_objects
1647 std::set<u16>::iterator n;
1648 n = current_objects.find(id);
1649 if(n != current_objects.end())
1651 // Add to added_objects
1652 added_objects.push(id);
1657 Finds out what objects have been removed from
1658 inside a radius around a position
1660 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1662 std::set<u16> ¤t_objects,
1663 std::queue<u16> &removed_objects)
1665 f32 radius_f = radius * BS;
1666 f32 player_radius_f = player_radius * BS;
1668 if (player_radius_f < 0)
1669 player_radius_f = 0;
1672 Go through current_objects; object is removed if:
1673 - object is not found in m_active_objects (this is actually an
1674 error condition; objects should be set m_removed=true and removed
1675 only after all clients have been informed about removal), or
1676 - object has m_removed=true, or
1677 - object is too far away
1679 for(std::set<u16>::iterator
1680 i = current_objects.begin();
1681 i != current_objects.end(); ++i)
1684 ServerActiveObject *object = getActiveObject(id);
1686 if (object == NULL) {
1687 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1688 << " object in current_objects is NULL" << std::endl;
1689 removed_objects.push(id);
1693 if (object->m_removed || object->m_pending_deactivation) {
1694 removed_objects.push(id);
1698 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1699 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1700 if (distance_f <= player_radius_f || player_radius_f == 0)
1702 } else if (distance_f <= radius_f)
1705 // Object is no longer visible
1706 removed_objects.push(id);
1710 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1711 v3s16 blockpos, bool static_exists, v3s16 static_block)
1713 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1717 for (std::map<u16, StaticObject>::iterator
1718 so_it = block->m_static_objects.m_active.begin();
1719 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1720 // Get the ServerActiveObject counterpart to this StaticObject
1721 std::map<u16, ServerActiveObject *>::iterator ao_it;
1722 ao_it = m_active_objects.find(so_it->first);
1723 if (ao_it == m_active_objects.end()) {
1724 // If this ever happens, there must be some kind of nasty bug.
1725 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1726 "Object from MapBlock::m_static_objects::m_active not found "
1727 "in m_active_objects";
1731 ServerActiveObject *sao = ao_it->second;
1732 sao->m_static_exists = static_exists;
1733 sao->m_static_block = static_block;
1737 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1739 if(m_active_object_messages.empty())
1740 return ActiveObjectMessage(0);
1742 ActiveObjectMessage message = m_active_object_messages.front();
1743 m_active_object_messages.pop();
1748 ************ Private methods *************
1751 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1752 bool set_changed, u32 dtime_s)
1754 assert(object); // Pre-condition
1755 if(object->getId() == 0){
1756 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1759 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1760 <<"no free ids available"<<std::endl;
1761 if(object->environmentDeletes())
1765 object->setId(new_id);
1768 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1769 <<"supplied with id "<<object->getId()<<std::endl;
1771 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1773 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1774 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1775 if(object->environmentDeletes())
1780 if (objectpos_over_limit(object->getBasePosition())) {
1781 v3f p = object->getBasePosition();
1782 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1783 << "object position (" << p.X << "," << p.Y << "," << p.Z
1784 << ") outside maximum range" << std::endl;
1785 if (object->environmentDeletes())
1790 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1791 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1793 m_active_objects[object->getId()] = object;
1795 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1796 <<"Added id="<<object->getId()<<"; there are now "
1797 <<m_active_objects.size()<<" active objects."
1800 // Register reference in scripting api (must be done before post-init)
1801 m_script->addObjectReference(object);
1802 // Post-initialize object
1803 object->addedToEnvironment(dtime_s);
1805 // Add static data to block
1806 if(object->isStaticAllowed())
1808 // Add static object to active static list of the block
1809 v3f objectpos = object->getBasePosition();
1810 std::string staticdata = object->getStaticData();
1811 StaticObject s_obj(object->getType(), objectpos, staticdata);
1812 // Add to the block where the object is located in
1813 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1814 MapBlock *block = m_map->emergeBlock(blockpos);
1816 block->m_static_objects.m_active[object->getId()] = s_obj;
1817 object->m_static_exists = true;
1818 object->m_static_block = blockpos;
1821 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1822 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1824 v3s16 p = floatToInt(objectpos, BS);
1825 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1826 <<"could not emerge block for storing id="<<object->getId()
1827 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1831 return object->getId();
1835 Remove objects that satisfy (m_removed && m_known_by_count==0)
1837 void ServerEnvironment::removeRemovedObjects()
1839 std::vector<u16> objects_to_remove;
1840 for(std::map<u16, ServerActiveObject*>::iterator
1841 i = m_active_objects.begin();
1842 i != m_active_objects.end(); ++i) {
1844 ServerActiveObject* obj = i->second;
1845 // This shouldn't happen but check it
1848 infostream<<"NULL object found in ServerEnvironment"
1849 <<" while finding removed objects. id="<<id<<std::endl;
1850 // Id to be removed from m_active_objects
1851 objects_to_remove.push_back(id);
1856 We will delete objects that are marked as removed or thatare
1857 waiting for deletion after deactivation
1859 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1863 Delete static data from block if is marked as removed
1865 if(obj->m_static_exists && obj->m_removed)
1867 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1869 block->m_static_objects.remove(id);
1870 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1871 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1872 obj->m_static_exists = false;
1874 infostream<<"Failed to emerge block from which an object to "
1875 <<"be removed was loaded from. id="<<id<<std::endl;
1879 // If m_known_by_count > 0, don't actually remove. On some future
1880 // invocation this will be 0, which is when removal will continue.
1881 if(obj->m_known_by_count > 0)
1885 Move static data from active to stored if not marked as removed
1887 if(obj->m_static_exists && !obj->m_removed){
1888 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1890 std::map<u16, StaticObject>::iterator i =
1891 block->m_static_objects.m_active.find(id);
1892 if(i != block->m_static_objects.m_active.end()){
1893 block->m_static_objects.m_stored.push_back(i->second);
1894 block->m_static_objects.m_active.erase(id);
1895 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1896 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1899 infostream<<"Failed to emerge block from which an object to "
1900 <<"be deactivated was loaded from. id="<<id<<std::endl;
1904 // Tell the object about removal
1905 obj->removingFromEnvironment();
1906 // Deregister in scripting api
1907 m_script->removeObjectReference(obj);
1910 if(obj->environmentDeletes())
1913 // Id to be removed from m_active_objects
1914 objects_to_remove.push_back(id);
1916 // Remove references from m_active_objects
1917 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1918 i != objects_to_remove.end(); ++i) {
1919 m_active_objects.erase(*i);
1923 static void print_hexdump(std::ostream &o, const std::string &data)
1925 const int linelength = 16;
1926 for(int l=0; ; l++){
1927 int i0 = linelength * l;
1928 bool at_end = false;
1929 int thislinelength = linelength;
1930 if(i0 + thislinelength > (int)data.size()){
1931 thislinelength = data.size() - i0;
1934 for(int di=0; di<linelength; di++){
1937 if(di<thislinelength)
1938 snprintf(buf, 4, "%.2x ", data[i]);
1940 snprintf(buf, 4, " ");
1944 for(int di=0; di<thislinelength; di++){
1958 Convert stored objects from blocks near the players to active.
1960 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1965 // Ignore if no stored objects (to not set changed flag)
1966 if(block->m_static_objects.m_stored.empty())
1969 verbosestream<<"ServerEnvironment::activateObjects(): "
1970 <<"activating objects of block "<<PP(block->getPos())
1971 <<" ("<<block->m_static_objects.m_stored.size()
1972 <<" objects)"<<std::endl;
1973 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1975 errorstream<<"suspiciously large amount of objects detected: "
1976 <<block->m_static_objects.m_stored.size()<<" in "
1977 <<PP(block->getPos())
1978 <<"; removing all of them."<<std::endl;
1979 // Clear stored list
1980 block->m_static_objects.m_stored.clear();
1981 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1982 MOD_REASON_TOO_MANY_OBJECTS);
1986 // Activate stored objects
1987 std::vector<StaticObject> new_stored;
1988 for (std::vector<StaticObject>::iterator
1989 i = block->m_static_objects.m_stored.begin();
1990 i != block->m_static_objects.m_stored.end(); ++i) {
1991 StaticObject &s_obj = *i;
1993 // Create an active object from the data
1994 ServerActiveObject *obj = ServerActiveObject::create
1995 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
1996 // If couldn't create object, store static data back.
1998 errorstream<<"ServerEnvironment::activateObjects(): "
1999 <<"failed to create active object from static object "
2000 <<"in block "<<PP(s_obj.pos/BS)
2001 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
2002 print_hexdump(verbosestream, s_obj.data);
2004 new_stored.push_back(s_obj);
2007 verbosestream<<"ServerEnvironment::activateObjects(): "
2008 <<"activated static object pos="<<PP(s_obj.pos/BS)
2009 <<" type="<<(int)s_obj.type<<std::endl;
2010 // This will also add the object to the active static list
2011 addActiveObjectRaw(obj, false, dtime_s);
2013 // Clear stored list
2014 block->m_static_objects.m_stored.clear();
2015 // Add leftover failed stuff to stored list
2016 for(std::vector<StaticObject>::iterator
2017 i = new_stored.begin();
2018 i != new_stored.end(); ++i) {
2019 StaticObject &s_obj = *i;
2020 block->m_static_objects.m_stored.push_back(s_obj);
2023 // Turn the active counterparts of activated objects not pending for
2025 for(std::map<u16, StaticObject>::iterator
2026 i = block->m_static_objects.m_active.begin();
2027 i != block->m_static_objects.m_active.end(); ++i)
2030 ServerActiveObject *object = getActiveObject(id);
2032 object->m_pending_deactivation = false;
2036 Note: Block hasn't really been modified here.
2037 The objects have just been activated and moved from the stored
2038 static list to the active static list.
2039 As such, the block is essentially the same.
2040 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2041 Otherwise there would be a huge amount of unnecessary I/O.
2046 Convert objects that are not standing inside active blocks to static.
2048 If m_known_by_count != 0, active object is not deleted, but static
2049 data is still updated.
2051 If force_delete is set, active object is deleted nevertheless. It
2052 shall only be set so in the destructor of the environment.
2054 If block wasn't generated (not in memory or on disk),
2056 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2058 std::vector<u16> objects_to_remove;
2059 for(std::map<u16, ServerActiveObject*>::iterator
2060 i = m_active_objects.begin();
2061 i != m_active_objects.end(); ++i) {
2062 ServerActiveObject* obj = i->second;
2065 // Do not deactivate if static data creation not allowed
2066 if(!force_delete && !obj->isStaticAllowed())
2069 // If pending deactivation, let removeRemovedObjects() do it
2070 if(!force_delete && obj->m_pending_deactivation)
2074 v3f objectpos = obj->getBasePosition();
2076 // The block in which the object resides in
2077 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2079 // If object's static data is stored in a deactivated block and object
2080 // is actually located in an active block, re-save to the block in
2081 // which the object is actually located in.
2083 obj->m_static_exists &&
2084 !m_active_blocks.contains(obj->m_static_block) &&
2085 m_active_blocks.contains(blockpos_o))
2087 v3s16 old_static_block = obj->m_static_block;
2089 // Save to block where object is located
2090 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2092 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2093 <<"Could not save object id="<<id
2094 <<" to it's current block "<<PP(blockpos_o)
2098 std::string staticdata_new = obj->getStaticData();
2099 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2100 block->m_static_objects.insert(id, s_obj);
2101 obj->m_static_block = blockpos_o;
2102 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2103 MOD_REASON_STATIC_DATA_ADDED);
2105 // Delete from block where object was located
2106 block = m_map->emergeBlock(old_static_block, false);
2108 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2109 <<"Could not delete object id="<<id
2110 <<" from it's previous block "<<PP(old_static_block)
2114 block->m_static_objects.remove(id);
2115 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2116 MOD_REASON_STATIC_DATA_REMOVED);
2120 // If block is active, don't remove
2121 if(!force_delete && m_active_blocks.contains(blockpos_o))
2124 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2125 <<"deactivating object id="<<id<<" on inactive block "
2126 <<PP(blockpos_o)<<std::endl;
2128 // If known by some client, don't immediately delete.
2129 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2132 Update the static data
2135 if(obj->isStaticAllowed())
2137 // Create new static object
2138 std::string staticdata_new = obj->getStaticData();
2139 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2141 bool stays_in_same_block = false;
2142 bool data_changed = true;
2144 if (obj->m_static_exists) {
2145 if (obj->m_static_block == blockpos_o)
2146 stays_in_same_block = true;
2148 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2151 std::map<u16, StaticObject>::iterator n =
2152 block->m_static_objects.m_active.find(id);
2153 if (n != block->m_static_objects.m_active.end()) {
2154 StaticObject static_old = n->second;
2156 float save_movem = obj->getMinimumSavedMovement();
2158 if (static_old.data == staticdata_new &&
2159 (static_old.pos - objectpos).getLength() < save_movem)
2160 data_changed = false;
2162 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2163 <<"id="<<id<<" m_static_exists=true but "
2164 <<"static data doesn't actually exist in "
2165 <<PP(obj->m_static_block)<<std::endl;
2170 bool shall_be_written = (!stays_in_same_block || data_changed);
2172 // Delete old static object
2173 if(obj->m_static_exists)
2175 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2178 block->m_static_objects.remove(id);
2179 obj->m_static_exists = false;
2180 // Only mark block as modified if data changed considerably
2181 if(shall_be_written)
2182 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2183 MOD_REASON_STATIC_DATA_CHANGED);
2187 // Add to the block where the object is located in
2188 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2189 // Get or generate the block
2190 MapBlock *block = NULL;
2192 block = m_map->emergeBlock(blockpos);
2193 } catch(InvalidPositionException &e){
2194 // Handled via NULL pointer
2195 // NOTE: emergeBlock's failure is usually determined by it
2196 // actually returning NULL
2201 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2202 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2203 <<" statically but block "<<PP(blockpos)
2204 <<" already contains "
2205 <<block->m_static_objects.m_stored.size()
2207 <<" Forcing delete."<<std::endl;
2208 force_delete = true;
2210 // If static counterpart already exists in target block,
2212 // This shouldn't happen because the object is removed from
2213 // the previous block before this according to
2214 // obj->m_static_block, but happens rarely for some unknown
2215 // reason. Unsuccessful attempts have been made to find
2217 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2218 warningstream<<"ServerEnv: Performing hack #83274"
2220 block->m_static_objects.remove(id);
2222 // Store static data
2223 u16 store_id = pending_delete ? id : 0;
2224 block->m_static_objects.insert(store_id, s_obj);
2226 // Only mark block as modified if data changed considerably
2227 if(shall_be_written)
2228 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2229 MOD_REASON_STATIC_DATA_CHANGED);
2231 obj->m_static_exists = true;
2232 obj->m_static_block = block->getPos();
2237 v3s16 p = floatToInt(objectpos, BS);
2238 errorstream<<"ServerEnv: Could not find or generate "
2239 <<"a block for storing id="<<obj->getId()
2240 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2247 If known by some client, set pending deactivation.
2248 Otherwise delete it immediately.
2251 if(pending_delete && !force_delete)
2253 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2254 <<"object id="<<id<<" is known by clients"
2255 <<"; not deleting yet"<<std::endl;
2257 obj->m_pending_deactivation = true;
2261 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2262 <<"object id="<<id<<" is not known by clients"
2263 <<"; deleting"<<std::endl;
2265 // Tell the object about removal
2266 obj->removingFromEnvironment();
2267 // Deregister in scripting api
2268 m_script->removeObjectReference(obj);
2270 // Delete active object
2271 if(obj->environmentDeletes())
2273 // Id to be removed from m_active_objects
2274 objects_to_remove.push_back(id);
2277 // Remove references from m_active_objects
2278 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2279 i != objects_to_remove.end(); ++i) {
2280 m_active_objects.erase(*i);
2286 #include "clientsimpleobject.h"
2292 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2293 ITextureSource *texturesource, IGameDef *gamedef,
2294 IrrlichtDevice *irr):
2297 m_texturesource(texturesource),
2302 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2305 ClientEnvironment::~ClientEnvironment()
2307 // delete active objects
2308 for(std::map<u16, ClientActiveObject*>::iterator
2309 i = m_active_objects.begin();
2310 i != m_active_objects.end(); ++i)
2315 for(std::vector<ClientSimpleObject*>::iterator
2316 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2324 Map & ClientEnvironment::getMap()
2329 ClientMap & ClientEnvironment::getClientMap()
2334 void ClientEnvironment::addPlayer(Player *player)
2336 DSTACK(FUNCTION_NAME);
2338 It is a failure if player is local and there already is a local
2341 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2342 "Player is local but there is already a local player");
2344 Environment::addPlayer(player);
2347 LocalPlayer * ClientEnvironment::getLocalPlayer()
2349 for(std::vector<Player*>::iterator i = m_players.begin();
2350 i != m_players.end(); ++i) {
2351 Player *player = *i;
2352 if(player->isLocal())
2353 return (LocalPlayer*)player;
2358 void ClientEnvironment::step(float dtime)
2360 DSTACK(FUNCTION_NAME);
2362 /* Step time of day */
2363 stepTimeOfDay(dtime);
2365 // Get some settings
2366 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2367 bool free_move = fly_allowed && g_settings->getBool("free_move");
2370 LocalPlayer *lplayer = getLocalPlayer();
2372 // collision info queue
2373 std::vector<CollisionInfo> player_collisions;
2376 Get the speed the player is going
2378 bool is_climbing = lplayer->is_climbing;
2380 f32 player_speed = lplayer->getSpeed().getLength();
2383 Maximum position increment
2385 //f32 position_max_increment = 0.05*BS;
2386 f32 position_max_increment = 0.1*BS;
2388 // Maximum time increment (for collision detection etc)
2389 // time = distance / speed
2390 f32 dtime_max_increment = 1;
2391 if(player_speed > 0.001)
2392 dtime_max_increment = position_max_increment / player_speed;
2394 // Maximum time increment is 10ms or lower
2395 if(dtime_max_increment > 0.01)
2396 dtime_max_increment = 0.01;
2398 // Don't allow overly huge dtime
2402 f32 dtime_downcount = dtime;
2405 Stuff that has a maximum time increment
2414 if(dtime_downcount > dtime_max_increment)
2416 dtime_part = dtime_max_increment;
2417 dtime_downcount -= dtime_part;
2421 dtime_part = dtime_downcount;
2423 Setting this to 0 (no -=dtime_part) disables an infinite loop
2424 when dtime_part is so small that dtime_downcount -= dtime_part
2427 dtime_downcount = 0;
2436 if(free_move == false && is_climbing == false)
2439 v3f speed = lplayer->getSpeed();
2440 if(lplayer->in_liquid == false)
2441 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2443 // Liquid floating / sinking
2444 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2445 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2447 // Liquid resistance
2448 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2450 // How much the node's viscosity blocks movement, ranges between 0 and 1
2451 // Should match the scale at which viscosity increase affects other liquid attributes
2452 const f32 viscosity_factor = 0.3;
2454 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2455 f32 dl = d_wanted.getLength();
2456 if(dl > lplayer->movement_liquid_fluidity_smooth)
2457 dl = lplayer->movement_liquid_fluidity_smooth;
2458 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2460 v3f d = d_wanted.normalize() * dl;
2464 lplayer->setSpeed(speed);
2469 This also does collision detection.
2471 lplayer->move(dtime_part, this, position_max_increment,
2472 &player_collisions);
2475 while(dtime_downcount > 0.001);
2477 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2479 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2480 i != player_collisions.end(); ++i) {
2481 CollisionInfo &info = *i;
2482 v3f speed_diff = info.new_speed - info.old_speed;;
2483 // Handle only fall damage
2484 // (because otherwise walking against something in fast_move kills you)
2485 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2487 // Get rid of other components
2490 f32 pre_factor = 1; // 1 hp per node/s
2491 f32 tolerance = BS*14; // 5 without damage
2492 f32 post_factor = 1; // 1 hp per node/s
2493 if(info.type == COLLISION_NODE)
2495 const ContentFeatures &f = m_gamedef->ndef()->
2496 get(m_map->getNodeNoEx(info.node_p));
2497 // Determine fall damage multiplier
2498 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2499 pre_factor = 1.0 + (float)addp/100.0;
2501 float speed = pre_factor * speed_diff.getLength();
2502 if(speed > tolerance)
2504 f32 damage_f = (speed - tolerance)/BS * post_factor;
2505 u16 damage = (u16)(damage_f+0.5);
2507 damageLocalPlayer(damage, true);
2508 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2509 m_gamedef->event()->put(e);
2515 A quick draft of lava damage
2517 if(m_lava_hurt_interval.step(dtime, 1.0))
2519 v3f pf = lplayer->getPosition();
2521 // Feet, middle and head
2522 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2523 MapNode n1 = m_map->getNodeNoEx(p1);
2524 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2525 MapNode n2 = m_map->getNodeNoEx(p2);
2526 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2527 MapNode n3 = m_map->getNodeNoEx(p3);
2529 u32 damage_per_second = 0;
2530 damage_per_second = MYMAX(damage_per_second,
2531 m_gamedef->ndef()->get(n1).damage_per_second);
2532 damage_per_second = MYMAX(damage_per_second,
2533 m_gamedef->ndef()->get(n2).damage_per_second);
2534 damage_per_second = MYMAX(damage_per_second,
2535 m_gamedef->ndef()->get(n3).damage_per_second);
2537 if(damage_per_second != 0)
2539 damageLocalPlayer(damage_per_second, true);
2546 if(m_drowning_interval.step(dtime, 2.0))
2548 v3f pf = lplayer->getPosition();
2551 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2552 MapNode n = m_map->getNodeNoEx(p);
2553 ContentFeatures c = m_gamedef->ndef()->get(n);
2554 u8 drowning_damage = c.drowning;
2555 if(drowning_damage > 0 && lplayer->hp > 0){
2556 u16 breath = lplayer->getBreath();
2563 lplayer->setBreath(breath);
2564 updateLocalPlayerBreath(breath);
2567 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2568 damageLocalPlayer(drowning_damage, true);
2571 if(m_breathing_interval.step(dtime, 0.5))
2573 v3f pf = lplayer->getPosition();
2576 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2577 MapNode n = m_map->getNodeNoEx(p);
2578 ContentFeatures c = m_gamedef->ndef()->get(n);
2580 lplayer->setBreath(11);
2582 else if(c.drowning == 0){
2583 u16 breath = lplayer->getBreath();
2586 lplayer->setBreath(breath);
2587 updateLocalPlayerBreath(breath);
2593 Stuff that can be done in an arbitarily large dtime
2595 for(std::vector<Player*>::iterator i = m_players.begin();
2596 i != m_players.end(); ++i) {
2597 Player *player = *i;
2600 Handle non-local players
2602 if(player->isLocal() == false) {
2604 player->move(dtime, this, 100*BS);
2609 // Update lighting on local player (used for wield item)
2610 u32 day_night_ratio = getDayNightRatio();
2614 // On InvalidPositionException, use this as default
2615 // (day: LIGHT_SUN, night: 0)
2616 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2618 v3s16 p = lplayer->getLightPosition();
2619 node_at_lplayer = m_map->getNodeNoEx(p);
2621 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2622 u8 day = light & 0xff;
2623 u8 night = (light >> 8) & 0xff;
2624 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2628 Step active objects and update lighting of them
2631 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2632 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2633 for(std::map<u16, ClientActiveObject*>::iterator
2634 i = m_active_objects.begin();
2635 i != m_active_objects.end(); ++i)
2637 ClientActiveObject* obj = i->second;
2639 obj->step(dtime, this);
2648 v3s16 p = obj->getLightPosition();
2649 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2651 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2653 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2655 obj->updateLight(light);
2660 Step and handle simple objects
2662 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2663 for(std::vector<ClientSimpleObject*>::iterator
2664 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2665 std::vector<ClientSimpleObject*>::iterator cur = i;
2666 ClientSimpleObject *simple = *cur;
2668 simple->step(dtime);
2669 if(simple->m_to_be_removed) {
2671 i = m_simple_objects.erase(cur);
2679 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2681 m_simple_objects.push_back(simple);
2684 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2686 ClientActiveObject *obj = getActiveObject(id);
2687 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2688 return (GenericCAO*) obj;
2693 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2695 std::map<u16, ClientActiveObject*>::iterator n;
2696 n = m_active_objects.find(id);
2697 if(n == m_active_objects.end())
2702 bool isFreeClientActiveObjectId(u16 id,
2703 std::map<u16, ClientActiveObject*> &objects)
2708 return objects.find(id) == objects.end();
2711 u16 getFreeClientActiveObjectId(
2712 std::map<u16, ClientActiveObject*> &objects)
2714 //try to reuse id's as late as possible
2715 static u16 last_used_id = 0;
2716 u16 startid = last_used_id;
2720 if(isFreeClientActiveObjectId(last_used_id, objects))
2721 return last_used_id;
2723 if(last_used_id == startid)
2728 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2730 assert(object); // Pre-condition
2731 if(object->getId() == 0)
2733 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2736 infostream<<"ClientEnvironment::addActiveObject(): "
2737 <<"no free ids available"<<std::endl;
2741 object->setId(new_id);
2743 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2745 infostream<<"ClientEnvironment::addActiveObject(): "
2746 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2750 infostream<<"ClientEnvironment::addActiveObject(): "
2751 <<"added (id="<<object->getId()<<")"<<std::endl;
2752 m_active_objects[object->getId()] = object;
2753 object->addToScene(m_smgr, m_texturesource, m_irr);
2754 { // Update lighting immediately
2759 v3s16 p = object->getLightPosition();
2760 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2762 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2764 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2766 object->updateLight(light);
2768 return object->getId();
2771 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2772 const std::string &init_data)
2774 ClientActiveObject* obj =
2775 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2778 infostream<<"ClientEnvironment::addActiveObject(): "
2779 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2788 obj->initialize(init_data);
2790 catch(SerializationError &e)
2792 errorstream<<"ClientEnvironment::addActiveObject():"
2793 <<" id="<<id<<" type="<<type
2794 <<": SerializationError in initialize(): "
2796 <<": init_data="<<serializeJsonString(init_data)
2800 addActiveObject(obj);
2803 void ClientEnvironment::removeActiveObject(u16 id)
2805 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2806 <<"id="<<id<<std::endl;
2807 ClientActiveObject* obj = getActiveObject(id);
2810 infostream<<"ClientEnvironment::removeActiveObject(): "
2811 <<"id="<<id<<" not found"<<std::endl;
2814 obj->removeFromScene(true);
2816 m_active_objects.erase(id);
2819 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2821 ClientActiveObject *obj = getActiveObject(id);
2823 infostream << "ClientEnvironment::processActiveObjectMessage():"
2824 << " got message for id=" << id << ", which doesn't exist."
2830 obj->processMessage(data);
2831 } catch (SerializationError &e) {
2832 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2833 << " id=" << id << " type=" << obj->getType()
2834 << " SerializationError in processMessage(): " << e.what()
2840 Callbacks for activeobjects
2843 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2845 LocalPlayer *lplayer = getLocalPlayer();
2849 if (lplayer->hp > damage)
2850 lplayer->hp -= damage;
2855 ClientEnvEvent event;
2856 event.type = CEE_PLAYER_DAMAGE;
2857 event.player_damage.amount = damage;
2858 event.player_damage.send_to_server = handle_hp;
2859 m_client_event_queue.push(event);
2862 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2864 ClientEnvEvent event;
2865 event.type = CEE_PLAYER_BREATH;
2866 event.player_breath.amount = breath;
2867 m_client_event_queue.push(event);
2871 Client likes to call these
2874 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2875 std::vector<DistanceSortedActiveObject> &dest)
2877 for(std::map<u16, ClientActiveObject*>::iterator
2878 i = m_active_objects.begin();
2879 i != m_active_objects.end(); ++i)
2881 ClientActiveObject* obj = i->second;
2883 f32 d = (obj->getPosition() - origin).getLength();
2888 DistanceSortedActiveObject dso(obj, d);
2890 dest.push_back(dso);
2894 ClientEnvEvent ClientEnvironment::getClientEvent()
2896 ClientEnvEvent event;
2897 if(m_client_event_queue.empty())
2898 event.type = CEE_NONE;
2900 event = m_client_event_queue.front();
2901 m_client_event_queue.pop();
2906 #endif // #ifndef SERVER