3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "environment.h"
24 #include "collision.h"
25 #include "content_mapnode.h"
27 #include "serverobject.h"
28 #include "content_sao.h"
32 #include "scripting_game.h"
34 #include "nodemetadata.h"
37 #include "clientmap.h"
38 #include "localplayer.h"
39 #include "mapblock_mesh.h"
43 #include "daynightratio.h"
46 #include "util/serialize.h"
47 #include "threading/mutex_auto_lock.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:"
53 // A number that is much smaller than the timeout for particle spawners should/could ever be
54 #define PARTICLE_SPAWNER_NO_EXPIRY -1024.f
56 Environment::Environment():
57 m_time_of_day_speed(0),
59 m_time_of_day_f(9000./24000),
60 m_time_conversion_skew(0.0f),
61 m_enable_day_night_ratio_override(false),
62 m_day_night_ratio_override(0.0f)
64 m_cache_enable_shaders = g_settings->getBool("enable_shaders");
65 m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval");
66 m_cache_abm_interval = g_settings->getFloat("abm_interval");
67 m_cache_nodetimer_interval = g_settings->getFloat("nodetimer_interval");
70 Environment::~Environment()
73 for(std::vector<Player*>::iterator i = m_players.begin();
74 i != m_players.end(); ++i) {
79 void Environment::addPlayer(Player *player)
81 DSTACK(FUNCTION_NAME);
83 Check that peer_ids are unique.
84 Also check that names are unique.
85 Exception: there can be multiple players with peer_id=0
87 // If peer id is non-zero, it has to be unique.
88 if(player->peer_id != 0)
89 FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
90 // Name has to be unique.
91 FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
93 m_players.push_back(player);
96 void Environment::removePlayer(Player* player)
98 for (std::vector<Player*>::iterator it = m_players.begin();
99 it != m_players.end(); ++it) {
100 if ((*it) == player) {
108 Player * Environment::getPlayer(u16 peer_id)
110 for(std::vector<Player*>::iterator i = m_players.begin();
111 i != m_players.end(); ++i) {
113 if(player->peer_id == peer_id)
119 Player * Environment::getPlayer(const char *name)
121 for(std::vector<Player*>::iterator i = m_players.begin();
122 i != m_players.end(); ++i) {
124 if(strcmp(player->getName(), name) == 0)
130 Player * Environment::getRandomConnectedPlayer()
132 std::vector<Player*> connected_players = getPlayers(true);
133 u32 chosen_one = myrand() % connected_players.size();
135 for(std::vector<Player*>::iterator
136 i = connected_players.begin();
137 i != connected_players.end(); ++i) {
138 if(j == chosen_one) {
147 Player * Environment::getNearestConnectedPlayer(v3f pos)
149 std::vector<Player*> connected_players = getPlayers(true);
151 Player *nearest_player = NULL;
152 for(std::vector<Player*>::iterator
153 i = connected_players.begin();
154 i != connected_players.end(); ++i) {
156 f32 d = player->getPosition().getDistanceFrom(pos);
157 if(d < nearest_d || nearest_player == NULL) {
159 nearest_player = player;
162 return nearest_player;
165 std::vector<Player*> Environment::getPlayers()
170 std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
172 std::vector<Player*> newlist;
173 for(std::vector<Player*>::iterator
174 i = m_players.begin();
175 i != m_players.end(); ++i) {
178 if(ignore_disconnected) {
179 // Ignore disconnected players
180 if(player->peer_id == 0)
184 newlist.push_back(player);
189 u32 Environment::getDayNightRatio()
191 MutexAutoLock lock(this->m_time_lock);
192 if (m_enable_day_night_ratio_override)
193 return m_day_night_ratio_override;
194 return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders);
197 void Environment::setTimeOfDaySpeed(float speed)
199 m_time_of_day_speed = speed;
202 float Environment::getTimeOfDaySpeed()
204 return m_time_of_day_speed;
207 void Environment::setDayNightRatioOverride(bool enable, u32 value)
209 MutexAutoLock lock(this->m_time_lock);
210 m_enable_day_night_ratio_override = enable;
211 m_day_night_ratio_override = value;
214 void Environment::setTimeOfDay(u32 time)
216 MutexAutoLock lock(this->m_time_lock);
217 if (m_time_of_day > time)
219 m_time_of_day = time;
220 m_time_of_day_f = (float)time / 24000.0;
223 u32 Environment::getTimeOfDay()
225 MutexAutoLock lock(this->m_time_lock);
226 return m_time_of_day;
229 float Environment::getTimeOfDayF()
231 MutexAutoLock lock(this->m_time_lock);
232 return m_time_of_day_f;
235 void Environment::stepTimeOfDay(float dtime)
237 MutexAutoLock lock(this->m_time_lock);
239 // Cached in order to prevent the two reads we do to give
240 // different results (can be written by code not under the lock)
241 f32 cached_time_of_day_speed = m_time_of_day_speed;
243 f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600);
244 m_time_conversion_skew += dtime;
245 u32 units = (u32)(m_time_conversion_skew * speed);
249 if (m_time_of_day + units >= 24000) {
253 m_time_of_day = (m_time_of_day + units) % 24000;
255 m_time_of_day_f = (float)m_time_of_day / 24000.0;
258 m_time_conversion_skew -= (f32)units / speed;
261 m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime;
262 if (m_time_of_day_f > 1.0)
263 m_time_of_day_f -= 1.0;
264 if (m_time_of_day_f < 0.0)
265 m_time_of_day_f += 1.0;
269 u32 Environment::getDayCount()
271 // Atomic<u32> counter
280 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
284 // Initialize timer to random value to spread processing
285 float itv = abm->getTriggerInterval();
286 itv = MYMAX(0.001, itv); // No less than 1ms
287 int minval = MYMAX(-0.51*itv, -60); // Clamp to
288 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
289 timer = myrand_range(minval, maxval);
296 void LBMContentMapping::deleteContents()
298 for (std::vector<LoadingBlockModifierDef *>::iterator it = lbm_list.begin();
299 it != lbm_list.end(); ++it) {
304 void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef)
306 // Add the lbm_def to the LBMContentMapping.
307 // Unknown names get added to the global NameIdMapping.
308 INodeDefManager *nodedef = gamedef->ndef();
310 lbm_list.push_back(lbm_def);
312 for (std::set<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
313 it != lbm_def->trigger_contents.end(); ++it) {
314 std::set<content_t> c_ids;
315 bool found = nodedef->getIds(*it, c_ids);
317 content_t c_id = gamedef->allocateUnknownNodeId(*it);
318 if (c_id == CONTENT_IGNORE) {
319 // Seems it can't be allocated.
320 warningstream << "Could not internalize node name \"" << *it
321 << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl;
327 for (std::set<content_t>::const_iterator iit =
328 c_ids.begin(); iit != c_ids.end(); ++iit) {
329 content_t c_id = *iit;
330 map[c_id].push_back(lbm_def);
335 const std::vector<LoadingBlockModifierDef *> *
336 LBMContentMapping::lookup(content_t c) const
338 container_map::const_iterator it = map.find(c);
341 // This first dereferences the iterator, returning
342 // a std::vector<LoadingBlockModifierDef *>
343 // reference, then we convert it to a pointer.
344 return &(it->second);
347 LBMManager::~LBMManager()
349 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
350 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
353 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
354 it != m_lbm_lookup.end(); ++it) {
355 (it->second).deleteContents();
359 void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def)
361 // Precondition, in query mode the map isn't used anymore
362 FATAL_ERROR_IF(m_query_mode == true,
363 "attempted to modify LBMManager in query mode");
365 if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) {
366 throw ModError("Error adding LBM \"" + lbm_def->name +
367 "\": Does not follow naming conventions: "
368 "Only chararacters [a-z0-9_:] are allowed.");
371 m_lbm_defs[lbm_def->name] = lbm_def;
374 void LBMManager::loadIntroductionTimes(const std::string ×,
375 IGameDef *gamedef, u32 now)
380 // Storing it in a map first instead of
381 // handling the stuff directly in the loop
382 // removes all duplicate entries.
383 // TODO make this std::unordered_map
384 std::map<std::string, u32> introduction_times;
387 The introduction times string consists of name~time entries,
388 with each entry terminated by a semicolon. The time is decimal.
393 while ((idx_new = times.find(";", idx)) != std::string::npos) {
394 std::string entry = times.substr(idx, idx_new - idx);
395 std::vector<std::string> components = str_split(entry, '~');
396 if (components.size() != 2)
397 throw SerializationError("Introduction times entry \""
398 + entry + "\" requires exactly one '~'!");
399 const std::string &name = components[0];
400 u32 time = from_string<u32>(components[1]);
401 introduction_times[name] = time;
405 // Put stuff from introduction_times into m_lbm_lookup
406 for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
407 it != introduction_times.end(); ++it) {
408 const std::string &name = it->first;
409 u32 time = it->second;
411 std::map<std::string, LoadingBlockModifierDef *>::iterator def_it =
412 m_lbm_defs.find(name);
413 if (def_it == m_lbm_defs.end()) {
414 // This seems to be an LBM entry for
415 // an LBM we haven't loaded. Discard it.
418 LoadingBlockModifierDef *lbm_def = def_it->second;
419 if (lbm_def->run_at_every_load) {
420 // This seems to be an LBM entry for
421 // an LBM that runs at every load.
422 // Don't add it just yet.
426 m_lbm_lookup[time].addLBM(lbm_def, gamedef);
428 // Erase the entry so that we know later
429 // what elements didn't get put into m_lbm_lookup
430 m_lbm_defs.erase(name);
433 // Now also add the elements from m_lbm_defs to m_lbm_lookup
434 // that weren't added in the previous step.
435 // They are introduced first time to this world,
436 // or are run at every load (introducement time hardcoded to U32_MAX).
438 LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now];
439 LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX];
441 for (std::map<std::string, LoadingBlockModifierDef *>::iterator it =
442 m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) {
443 if (it->second->run_at_every_load) {
444 lbms_running_always.addLBM(it->second, gamedef);
446 lbms_we_introduce_now.addLBM(it->second, gamedef);
450 // Clear the list, so that we don't delete remaining elements
451 // twice in the destructor
455 std::string LBMManager::createIntroductionTimesString()
457 // Precondition, we must be in query mode
458 FATAL_ERROR_IF(m_query_mode == false,
459 "attempted to query on non fully set up LBMManager");
461 std::ostringstream oss;
462 for (lbm_lookup_map::iterator it = m_lbm_lookup.begin();
463 it != m_lbm_lookup.end(); ++it) {
464 u32 time = it->first;
465 std::vector<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
466 for (std::vector<LoadingBlockModifierDef *>::iterator iit = lbm_list.begin();
467 iit != lbm_list.end(); ++iit) {
468 // Don't add if the LBM runs at every load,
469 // then introducement time is hardcoded
470 // and doesn't need to be stored
471 if ((*iit)->run_at_every_load)
473 oss << (*iit)->name << "~" << time << ";";
479 void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp)
481 // Precondition, we need m_lbm_lookup to be initialized
482 FATAL_ERROR_IF(m_query_mode == false,
483 "attempted to query on non fully set up LBMManager");
484 v3s16 pos_of_block = block->getPosRelative();
488 lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
489 for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
490 for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
491 for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++)
493 n = block->getNodeNoEx(pos);
495 for (LBMManager::lbm_lookup_map::const_iterator iit = it;
496 iit != m_lbm_lookup.end(); ++iit) {
497 const std::vector<LoadingBlockModifierDef *> *lbm_list =
498 iit->second.lookup(c);
501 for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
502 lbm_list->begin(); iit != lbm_list->end(); ++iit) {
503 (*iit)->trigger(env, pos + pos_of_block, n);
513 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
516 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
517 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
518 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
525 void ActiveBlockList::update(std::vector<v3s16> &active_positions,
527 std::set<v3s16> &blocks_removed,
528 std::set<v3s16> &blocks_added)
533 std::set<v3s16> newlist = m_forceloaded_list;
534 for(std::vector<v3s16>::iterator i = active_positions.begin();
535 i != active_positions.end(); ++i)
537 fillRadiusBlock(*i, radius, newlist);
541 Find out which blocks on the old list are not on the new list
543 // Go through old list
544 for(std::set<v3s16>::iterator i = m_list.begin();
545 i != m_list.end(); ++i)
548 // If not on new list, it's been removed
549 if(newlist.find(p) == newlist.end())
550 blocks_removed.insert(p);
554 Find out which blocks on the new list are not on the old list
556 // Go through new list
557 for(std::set<v3s16>::iterator i = newlist.begin();
558 i != newlist.end(); ++i)
561 // If not on old list, it's been added
562 if(m_list.find(p) == m_list.end())
563 blocks_added.insert(p);
570 for(std::set<v3s16>::iterator i = newlist.begin();
571 i != newlist.end(); ++i)
582 ServerEnvironment::ServerEnvironment(ServerMap *map,
583 GameScripting *scriptIface, IGameDef *gamedef,
584 const std::string &path_world) :
586 m_script(scriptIface),
588 m_path_world(path_world),
589 m_send_recommended_timer(0),
590 m_active_block_interval_overload_skip(0),
592 m_game_time_fraction_counter(0),
593 m_last_clear_objects_time(0),
594 m_recommended_send_interval(0.1),
595 m_max_lag_estimate(0.1)
599 ServerEnvironment::~ServerEnvironment()
601 // Clear active block list.
602 // This makes the next one delete all active objects.
603 m_active_blocks.clear();
605 // Convert all objects to static and delete the active objects
606 deactivateFarObjects(true);
611 // Delete ActiveBlockModifiers
612 for(std::vector<ABMWithState>::iterator
613 i = m_abms.begin(); i != m_abms.end(); ++i){
618 Map & ServerEnvironment::getMap()
623 ServerMap & ServerEnvironment::getServerMap()
628 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
630 float distance = pos1.getDistanceFrom(pos2);
632 //calculate normalized direction vector
633 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
634 (pos2.Y - pos1.Y)/distance,
635 (pos2.Z - pos1.Z)/distance);
637 //find out if there's a node on path between pos1 and pos2
638 for (float i = 1; i < distance; i += stepsize) {
639 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
640 normalized_vector.Y * i,
641 normalized_vector.Z * i) +pos1,BS);
643 MapNode n = getMap().getNodeNoEx(pos);
645 if(n.param0 != CONTENT_AIR) {
655 void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
656 const std::string &str_reason, bool reconnect)
658 for (std::vector<Player*>::iterator it = m_players.begin();
659 it != m_players.end();
661 ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
662 (*it)->protocol_version, (AccessDeniedCode)reason,
663 str_reason, reconnect);
667 void ServerEnvironment::saveLoadedPlayers()
669 std::string players_path = m_path_world + DIR_DELIM "players";
670 fs::CreateDir(players_path);
672 for (std::vector<Player*>::iterator it = m_players.begin();
673 it != m_players.end();
675 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
676 if (player->checkModified()) {
677 player->save(players_path);
682 void ServerEnvironment::savePlayer(RemotePlayer *player)
684 std::string players_path = m_path_world + DIR_DELIM "players";
685 fs::CreateDir(players_path);
687 player->save(players_path);
690 Player *ServerEnvironment::loadPlayer(const std::string &playername)
692 bool newplayer = false;
694 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
695 std::string path = players_path + playername;
697 RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
699 player = new RemotePlayer(m_gamedef, "");
703 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
704 //// Open file and deserialize
705 std::ifstream is(path.c_str(), std::ios_base::binary);
708 player->deSerialize(is, path);
711 if (player->getName() == playername) {
716 path = players_path + playername + itos(i);
720 infostream << "Player file for player " << playername
721 << " not found" << std::endl;
729 player->setModified(false);
733 void ServerEnvironment::saveMeta()
735 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
737 // Open file and serialize
738 std::ostringstream ss(std::ios_base::binary);
741 args.setU64("game_time", m_game_time);
742 args.setU64("time_of_day", getTimeOfDay());
743 args.setU64("last_clear_objects_time", m_last_clear_objects_time);
744 args.setU64("lbm_introduction_times_version", 1);
745 args.set("lbm_introduction_times",
746 m_lbm_mgr.createIntroductionTimesString());
747 args.setU64("day_count", m_day_count);
751 if(!fs::safeWriteToFile(path, ss.str()))
753 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
755 throw SerializationError("Couldn't save env meta");
759 void ServerEnvironment::loadMeta()
761 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
763 // Open file and deserialize
764 std::ifstream is(path.c_str(), std::ios_base::binary);
766 infostream << "ServerEnvironment::loadMeta(): Failed to open "
767 << path << std::endl;
768 throw SerializationError("Couldn't load env meta");
773 if (!args.parseConfigLines(is, "EnvArgsEnd")) {
774 throw SerializationError("ServerEnvironment::loadMeta(): "
775 "EnvArgsEnd not found!");
779 m_game_time = args.getU64("game_time");
780 } catch (SettingNotFoundException &e) {
781 // Getting this is crucial, otherwise timestamps are useless
782 throw SerializationError("Couldn't load env meta game_time");
785 setTimeOfDay(args.exists("time_of_day") ?
786 // set day to morning by default
787 args.getU64("time_of_day") : 9000);
789 m_last_clear_objects_time = args.exists("last_clear_objects_time") ?
790 // If missing, do as if clearObjects was never called
791 args.getU64("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);
807 m_day_count = args.exists("day_count") ?
808 args.getU64("day_count") : 0;
811 void ServerEnvironment::loadDefaultMeta()
813 m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time);
818 ActiveBlockModifier *abm;
820 std::set<content_t> required_neighbors;
826 ServerEnvironment *m_env;
827 std::map<content_t, std::vector<ActiveABM> > m_aabms;
829 ABMHandler(std::vector<ABMWithState> &abms,
830 float dtime_s, ServerEnvironment *env,
836 INodeDefManager *ndef = env->getGameDef()->ndef();
837 for(std::vector<ABMWithState>::iterator
838 i = abms.begin(); i != abms.end(); ++i) {
839 ActiveBlockModifier *abm = i->abm;
840 float trigger_interval = abm->getTriggerInterval();
841 if(trigger_interval < 0.001)
842 trigger_interval = 0.001;
843 float actual_interval = dtime_s;
846 if(i->timer < trigger_interval)
848 i->timer -= trigger_interval;
849 actual_interval = trigger_interval;
851 float chance = abm->getTriggerChance();
856 if(abm->getSimpleCatchUp()) {
857 float intervals = actual_interval / trigger_interval;
860 aabm.chance = chance / intervals;
864 aabm.chance = chance;
867 std::set<std::string> required_neighbors_s
868 = abm->getRequiredNeighbors();
869 for(std::set<std::string>::iterator
870 i = required_neighbors_s.begin();
871 i != required_neighbors_s.end(); ++i)
873 ndef->getIds(*i, aabm.required_neighbors);
876 std::set<std::string> contents_s = abm->getTriggerContents();
877 for(std::set<std::string>::iterator
878 i = contents_s.begin(); i != contents_s.end(); ++i)
880 std::set<content_t> ids;
881 ndef->getIds(*i, ids);
882 for(std::set<content_t>::const_iterator k = ids.begin();
886 std::map<content_t, std::vector<ActiveABM> >::iterator j;
888 if(j == m_aabms.end()){
889 std::vector<ActiveABM> aabmlist;
890 m_aabms[c] = aabmlist;
893 j->second.push_back(aabm);
898 // Find out how many objects the given block and its neighbours contain.
899 // Returns the number of objects in the block, and also in 'wider' the
900 // number of objects in the block and all its neighbours. The latter
901 // may an estimate if any neighbours are unloaded.
902 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
905 u32 wider_unknown_count = 0;
906 for(s16 x=-1; x<=1; x++)
907 for(s16 y=-1; y<=1; y++)
908 for(s16 z=-1; z<=1; z++)
910 MapBlock *block2 = map->getBlockNoCreateNoEx(
911 block->getPos() + v3s16(x,y,z));
913 wider_unknown_count++;
916 wider += block2->m_static_objects.m_active.size()
917 + block2->m_static_objects.m_stored.size();
920 u32 active_object_count = block->m_static_objects.m_active.size();
921 u32 wider_known_count = 3*3*3 - wider_unknown_count;
922 wider += wider_unknown_count * wider / wider_known_count;
923 return active_object_count;
926 void apply(MapBlock *block)
931 ServerMap *map = &m_env->getServerMap();
933 u32 active_object_count_wider;
934 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
935 m_env->m_added_objects = 0;
938 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
939 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
940 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
942 MapNode n = block->getNodeNoEx(p0);
943 content_t c = n.getContent();
944 v3s16 p = p0 + block->getPosRelative();
946 std::map<content_t, std::vector<ActiveABM> >::iterator j;
948 if(j == m_aabms.end())
951 for(std::vector<ActiveABM>::iterator
952 i = j->second.begin(); i != j->second.end(); ++i) {
953 if(myrand() % i->chance != 0)
957 if(!i->required_neighbors.empty())
960 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
961 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
962 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
966 MapNode n = map->getNodeNoEx(p1);
967 content_t c = n.getContent();
968 std::set<content_t>::const_iterator k;
969 k = i->required_neighbors.find(c);
970 if(k != i->required_neighbors.end()){
974 // No required neighbor found
979 // Call all the trigger variations
980 i->abm->trigger(m_env, p, n);
981 i->abm->trigger(m_env, p, n,
982 active_object_count, active_object_count_wider);
984 // Count surrounding objects again if the abms added any
985 if(m_env->m_added_objects > 0) {
986 active_object_count = countObjects(block, map, active_object_count_wider);
987 m_env->m_added_objects = 0;
994 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
996 // Reset usage timer immediately, otherwise a block that becomes active
997 // again at around the same time as it would normally be unloaded will
998 // get unloaded incorrectly. (I think this still leaves a small possibility
999 // of a race condition between this and server::AsyncRunStep, which only
1000 // some kind of synchronisation will fix, but it at least reduces the window
1001 // of opportunity for it to break from seconds to nanoseconds)
1002 block->resetUsageTimer();
1004 // Get time difference
1006 u32 stamp = block->getTimestamp();
1007 if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
1008 dtime_s = m_game_time - stamp;
1009 dtime_s += additional_dtime;
1011 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
1012 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
1014 // Remove stored static objects if clearObjects was called since block's timestamp
1015 if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
1016 block->m_static_objects.m_stored.clear();
1017 // do not set changed flag to avoid unnecessary mapblock writes
1020 // Set current time as timestamp
1021 block->setTimestampNoChangedFlag(m_game_time);
1023 /*infostream<<"ServerEnvironment::activateBlock(): block is "
1024 <<dtime_s<<" seconds old."<<std::endl;*/
1026 // Activate stored objects
1027 activateObjects(block, dtime_s);
1029 /* Handle LoadingBlockModifiers */
1030 m_lbm_mgr.applyLBMs(this, block, stamp);
1033 std::vector<NodeTimer> elapsed_timers =
1034 block->m_node_timers.step((float)dtime_s);
1035 if (!elapsed_timers.empty()) {
1037 for (std::vector<NodeTimer>::iterator
1038 i = elapsed_timers.begin();
1039 i != elapsed_timers.end(); ++i){
1040 n = block->getNodeNoEx(i->position);
1041 v3s16 p = i->position + block->getPosRelative();
1042 if (m_script->node_on_timer(p, n, i->elapsed))
1043 block->setNodeTimer(NodeTimer(i->timeout, 0, i->position));
1047 /* Handle ActiveBlockModifiers */
1048 ABMHandler abmhandler(m_abms, dtime_s, this, false);
1049 abmhandler.apply(block);
1052 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
1054 m_abms.push_back(ABMWithState(abm));
1057 void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm)
1059 m_lbm_mgr.addLBMDef(lbm);
1062 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
1064 INodeDefManager *ndef = m_gamedef->ndef();
1065 MapNode n_old = m_map->getNodeNoEx(p);
1068 if (ndef->get(n_old).has_on_destruct)
1069 m_script->node_on_destruct(p, n_old);
1072 if (!m_map->addNodeWithEvent(p, n))
1075 // Update active VoxelManipulator if a mapgen thread
1076 m_map->updateVManip(p);
1078 // Call post-destructor
1079 if (ndef->get(n_old).has_after_destruct)
1080 m_script->node_after_destruct(p, n_old);
1083 if (ndef->get(n).has_on_construct)
1084 m_script->node_on_construct(p, n);
1089 bool ServerEnvironment::removeNode(v3s16 p)
1091 INodeDefManager *ndef = m_gamedef->ndef();
1092 MapNode n_old = m_map->getNodeNoEx(p);
1095 if (ndef->get(n_old).has_on_destruct)
1096 m_script->node_on_destruct(p, n_old);
1099 // This is slightly optimized compared to addNodeWithEvent(air)
1100 if (!m_map->removeNodeWithEvent(p))
1103 // Update active VoxelManipulator if a mapgen thread
1104 m_map->updateVManip(p);
1106 // Call post-destructor
1107 if (ndef->get(n_old).has_after_destruct)
1108 m_script->node_after_destruct(p, n_old);
1110 // Air doesn't require constructor
1114 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
1116 if (!m_map->addNodeWithEvent(p, n, false))
1119 // Update active VoxelManipulator if a mapgen thread
1120 m_map->updateVManip(p);
1125 void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
1127 for(std::map<u16, ServerActiveObject*>::iterator
1128 i = m_active_objects.begin();
1129 i != m_active_objects.end(); ++i)
1131 ServerActiveObject* obj = i->second;
1133 v3f objectpos = obj->getBasePosition();
1134 if(objectpos.getDistanceFrom(pos) > radius)
1136 objects.push_back(id);
1140 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1142 infostream << "ServerEnvironment::clearObjects(): "
1143 << "Removing all active objects" << std::endl;
1144 std::vector<u16> objects_to_remove;
1145 for (std::map<u16, ServerActiveObject*>::iterator
1146 i = m_active_objects.begin();
1147 i != m_active_objects.end(); ++i) {
1148 ServerActiveObject* obj = i->second;
1149 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1152 // Delete static object if block is loaded
1153 if (obj->m_static_exists) {
1154 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1156 block->m_static_objects.remove(id);
1157 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1158 MOD_REASON_CLEAR_ALL_OBJECTS);
1159 obj->m_static_exists = false;
1162 // If known by some client, don't delete immediately
1163 if (obj->m_known_by_count > 0) {
1164 obj->m_pending_deactivation = true;
1165 obj->m_removed = true;
1169 // Tell the object about removal
1170 obj->removingFromEnvironment();
1171 // Deregister in scripting api
1172 m_script->removeObjectReference(obj);
1174 // Delete active object
1175 if (obj->environmentDeletes())
1177 // Id to be removed from m_active_objects
1178 objects_to_remove.push_back(id);
1181 // Remove references from m_active_objects
1182 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1183 i != objects_to_remove.end(); ++i) {
1184 m_active_objects.erase(*i);
1187 // Get list of loaded blocks
1188 std::vector<v3s16> loaded_blocks;
1189 infostream << "ServerEnvironment::clearObjects(): "
1190 << "Listing all loaded blocks" << std::endl;
1191 m_map->listAllLoadedBlocks(loaded_blocks);
1192 infostream << "ServerEnvironment::clearObjects(): "
1193 << "Done listing all loaded blocks: "
1194 << loaded_blocks.size()<<std::endl;
1196 // Get list of loadable blocks
1197 std::vector<v3s16> loadable_blocks;
1198 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1199 infostream << "ServerEnvironment::clearObjects(): "
1200 << "Listing all loadable blocks" << std::endl;
1201 m_map->listAllLoadableBlocks(loadable_blocks);
1202 infostream << "ServerEnvironment::clearObjects(): "
1203 << "Done listing all loadable blocks: "
1204 << loadable_blocks.size() << std::endl;
1206 loadable_blocks = loaded_blocks;
1209 infostream << "ServerEnvironment::clearObjects(): "
1210 << "Now clearing objects in " << loadable_blocks.size()
1211 << " blocks" << std::endl;
1213 // Grab a reference on each loaded block to avoid unloading it
1214 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1215 i != loaded_blocks.end(); ++i) {
1217 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1218 assert(block != NULL);
1222 // Remove objects in all loadable blocks
1223 u32 unload_interval = U32_MAX;
1224 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1225 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1226 unload_interval = MYMAX(unload_interval, 1);
1228 u32 report_interval = loadable_blocks.size() / 10;
1229 u32 num_blocks_checked = 0;
1230 u32 num_blocks_cleared = 0;
1231 u32 num_objs_cleared = 0;
1232 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1233 i != loadable_blocks.end(); ++i) {
1235 MapBlock *block = m_map->emergeBlock(p, false);
1237 errorstream << "ServerEnvironment::clearObjects(): "
1238 << "Failed to emerge block " << PP(p) << std::endl;
1241 u32 num_stored = block->m_static_objects.m_stored.size();
1242 u32 num_active = block->m_static_objects.m_active.size();
1243 if (num_stored != 0 || num_active != 0) {
1244 block->m_static_objects.m_stored.clear();
1245 block->m_static_objects.m_active.clear();
1246 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1247 MOD_REASON_CLEAR_ALL_OBJECTS);
1248 num_objs_cleared += num_stored + num_active;
1249 num_blocks_cleared++;
1251 num_blocks_checked++;
1253 if (report_interval != 0 &&
1254 num_blocks_checked % report_interval == 0) {
1255 float percent = 100.0 * (float)num_blocks_checked /
1256 loadable_blocks.size();
1257 infostream << "ServerEnvironment::clearObjects(): "
1258 << "Cleared " << num_objs_cleared << " objects"
1259 << " in " << num_blocks_cleared << " blocks ("
1260 << percent << "%)" << std::endl;
1262 if (num_blocks_checked % unload_interval == 0) {
1263 m_map->unloadUnreferencedBlocks();
1266 m_map->unloadUnreferencedBlocks();
1268 // Drop references that were added above
1269 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1270 i != loaded_blocks.end(); ++i) {
1272 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1277 m_last_clear_objects_time = m_game_time;
1279 infostream << "ServerEnvironment::clearObjects(): "
1280 << "Finished: Cleared " << num_objs_cleared << " objects"
1281 << " in " << num_blocks_cleared << " blocks" << std::endl;
1284 void ServerEnvironment::step(float dtime)
1286 DSTACK(FUNCTION_NAME);
1288 //TimeTaker timer("ServerEnv step");
1290 /* Step time of day */
1291 stepTimeOfDay(dtime);
1294 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1295 // really matter that much.
1296 static const float server_step = g_settings->getFloat("dedicated_server_step");
1297 m_recommended_send_interval = server_step;
1303 m_game_time_fraction_counter += dtime;
1304 u32 inc_i = (u32)m_game_time_fraction_counter;
1305 m_game_time += inc_i;
1306 m_game_time_fraction_counter -= (float)inc_i;
1313 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1314 for(std::vector<Player*>::iterator i = m_players.begin();
1315 i != m_players.end(); ++i)
1317 Player *player = *i;
1319 // Ignore disconnected players
1320 if(player->peer_id == 0)
1324 player->move(dtime, this, 100*BS);
1329 Manage active block list
1331 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1332 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1334 Get player block positions
1336 std::vector<v3s16> players_blockpos;
1337 for(std::vector<Player*>::iterator
1338 i = m_players.begin();
1339 i != m_players.end(); ++i) {
1340 Player *player = *i;
1341 // Ignore disconnected players
1342 if(player->peer_id == 0)
1345 v3s16 blockpos = getNodeBlockPos(
1346 floatToInt(player->getPosition(), BS));
1347 players_blockpos.push_back(blockpos);
1351 Update list of active blocks, collecting changes
1353 static const s16 active_block_range = g_settings->getS16("active_block_range");
1354 std::set<v3s16> blocks_removed;
1355 std::set<v3s16> blocks_added;
1356 m_active_blocks.update(players_blockpos, active_block_range,
1357 blocks_removed, blocks_added);
1360 Handle removed blocks
1363 // Convert active objects that are no more in active blocks to static
1364 deactivateFarObjects(false);
1366 for(std::set<v3s16>::iterator
1367 i = blocks_removed.begin();
1368 i != blocks_removed.end(); ++i) {
1371 /* infostream<<"Server: Block " << PP(p)
1372 << " became inactive"<<std::endl; */
1374 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1378 // Set current time as timestamp (and let it set ChangedFlag)
1379 block->setTimestamp(m_game_time);
1386 for(std::set<v3s16>::iterator
1387 i = blocks_added.begin();
1388 i != blocks_added.end(); ++i)
1392 MapBlock *block = m_map->getBlockOrEmerge(p);
1394 m_active_blocks.m_list.erase(p);
1398 activateBlock(block);
1399 /* infostream<<"Server: Block " << PP(p)
1400 << " became active"<<std::endl; */
1405 Mess around in active blocks
1407 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1408 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1410 float dtime = m_cache_nodetimer_interval;
1412 for(std::set<v3s16>::iterator
1413 i = m_active_blocks.m_list.begin();
1414 i != m_active_blocks.m_list.end(); ++i)
1418 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1419 <<") being handled"<<std::endl;*/
1421 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1425 // Reset block usage timer
1426 block->resetUsageTimer();
1428 // Set current time as timestamp
1429 block->setTimestampNoChangedFlag(m_game_time);
1430 // If time has changed much from the one on disk,
1431 // set block to be saved when it is unloaded
1432 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1433 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1434 MOD_REASON_BLOCK_EXPIRED);
1437 std::vector<NodeTimer> elapsed_timers =
1438 block->m_node_timers.step((float)dtime);
1439 if (!elapsed_timers.empty()) {
1441 for (std::vector<NodeTimer>::iterator
1442 i = elapsed_timers.begin();
1443 i != elapsed_timers.end(); ++i) {
1444 n = block->getNodeNoEx(i->position);
1445 p = i->position + block->getPosRelative();
1446 if (m_script->node_on_timer(p, n, i->elapsed)) {
1447 block->setNodeTimer(NodeTimer(
1448 i->timeout, 0, i->position));
1455 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1457 if(m_active_block_interval_overload_skip > 0){
1458 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1459 m_active_block_interval_overload_skip--;
1462 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1463 TimeTaker timer("modify in active blocks per interval");
1465 // Initialize handling of ActiveBlockModifiers
1466 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1468 for(std::set<v3s16>::iterator
1469 i = m_active_blocks.m_list.begin();
1470 i != m_active_blocks.m_list.end(); ++i)
1474 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1475 <<") being handled"<<std::endl;*/
1477 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1481 // Set current time as timestamp
1482 block->setTimestampNoChangedFlag(m_game_time);
1484 /* Handle ActiveBlockModifiers */
1485 abmhandler.apply(block);
1488 u32 time_ms = timer.stop(true);
1489 u32 max_time_ms = 200;
1490 if(time_ms > max_time_ms){
1491 warningstream<<"active block modifiers took "
1492 <<time_ms<<"ms (longer than "
1493 <<max_time_ms<<"ms)"<<std::endl;
1494 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1499 Step script environment (run global on_step())
1501 m_script->environment_Step(dtime);
1507 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1508 //TimeTaker timer("Step active objects");
1510 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1512 // This helps the objects to send data at the same time
1513 bool send_recommended = false;
1514 m_send_recommended_timer += dtime;
1515 if(m_send_recommended_timer > getSendRecommendedInterval())
1517 m_send_recommended_timer -= getSendRecommendedInterval();
1518 send_recommended = true;
1521 for(std::map<u16, ServerActiveObject*>::iterator
1522 i = m_active_objects.begin();
1523 i != m_active_objects.end(); ++i)
1525 ServerActiveObject* obj = i->second;
1526 // Don't step if is to be removed or stored statically
1527 if(obj->m_removed || obj->m_pending_deactivation)
1530 obj->step(dtime, send_recommended);
1531 // Read messages from object
1532 while(!obj->m_messages_out.empty())
1534 m_active_object_messages.push(
1535 obj->m_messages_out.front());
1536 obj->m_messages_out.pop();
1542 Manage active objects
1544 if(m_object_management_interval.step(dtime, 0.5))
1546 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1548 Remove objects that satisfy (m_removed && m_known_by_count==0)
1550 removeRemovedObjects();
1554 Manage particle spawner expiration
1556 if (m_particle_management_interval.step(dtime, 1.0)) {
1557 for (std::map<u32, float>::iterator i = m_particle_spawners.begin();
1558 i != m_particle_spawners.end(); ) {
1559 //non expiring spawners
1560 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1566 if (i->second <= 0.f)
1567 m_particle_spawners.erase(i++);
1574 u32 ServerEnvironment::addParticleSpawner(float exptime)
1576 // Timers with lifetime 0 do not expire
1577 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1580 for (;;) { // look for unused particlespawner id
1582 std::map<u32, float>::iterator f;
1583 f = m_particle_spawners.find(id);
1584 if (f == m_particle_spawners.end()) {
1585 m_particle_spawners[id] = time;
1592 void ServerEnvironment::deleteParticleSpawner(u32 id)
1594 m_particle_spawners.erase(id);
1597 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1599 std::map<u16, ServerActiveObject*>::iterator n;
1600 n = m_active_objects.find(id);
1601 if(n == m_active_objects.end())
1606 bool isFreeServerActiveObjectId(u16 id,
1607 std::map<u16, ServerActiveObject*> &objects)
1612 return objects.find(id) == objects.end();
1615 u16 getFreeServerActiveObjectId(
1616 std::map<u16, ServerActiveObject*> &objects)
1618 //try to reuse id's as late as possible
1619 static u16 last_used_id = 0;
1620 u16 startid = last_used_id;
1624 if(isFreeServerActiveObjectId(last_used_id, objects))
1625 return last_used_id;
1627 if(last_used_id == startid)
1632 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1634 assert(object); // Pre-condition
1636 u16 id = addActiveObjectRaw(object, true, 0);
1641 Finds out what new objects have been added to
1642 inside a radius around a position
1644 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1646 std::set<u16> ¤t_objects,
1647 std::queue<u16> &added_objects)
1649 f32 radius_f = radius * BS;
1650 f32 player_radius_f = player_radius * BS;
1652 if (player_radius_f < 0)
1653 player_radius_f = 0;
1656 Go through the object list,
1657 - discard m_removed objects,
1658 - discard objects that are too far away,
1659 - discard objects that are found in current_objects.
1660 - add remaining objects to added_objects
1662 for(std::map<u16, ServerActiveObject*>::iterator
1663 i = m_active_objects.begin();
1664 i != m_active_objects.end(); ++i) {
1668 ServerActiveObject *object = i->second;
1672 // Discard if removed or deactivating
1673 if(object->m_removed || object->m_pending_deactivation)
1676 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1677 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1678 // Discard if too far
1679 if (distance_f > player_radius_f && player_radius_f != 0)
1681 } else if (distance_f > radius_f)
1684 // Discard if already on current_objects
1685 std::set<u16>::iterator n;
1686 n = current_objects.find(id);
1687 if(n != current_objects.end())
1689 // Add to added_objects
1690 added_objects.push(id);
1695 Finds out what objects have been removed from
1696 inside a radius around a position
1698 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1700 std::set<u16> ¤t_objects,
1701 std::queue<u16> &removed_objects)
1703 f32 radius_f = radius * BS;
1704 f32 player_radius_f = player_radius * BS;
1706 if (player_radius_f < 0)
1707 player_radius_f = 0;
1710 Go through current_objects; object is removed if:
1711 - object is not found in m_active_objects (this is actually an
1712 error condition; objects should be set m_removed=true and removed
1713 only after all clients have been informed about removal), or
1714 - object has m_removed=true, or
1715 - object is too far away
1717 for(std::set<u16>::iterator
1718 i = current_objects.begin();
1719 i != current_objects.end(); ++i)
1722 ServerActiveObject *object = getActiveObject(id);
1724 if (object == NULL) {
1725 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1726 << " object in current_objects is NULL" << std::endl;
1727 removed_objects.push(id);
1731 if (object->m_removed || object->m_pending_deactivation) {
1732 removed_objects.push(id);
1736 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1737 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1738 if (distance_f <= player_radius_f || player_radius_f == 0)
1740 } else if (distance_f <= radius_f)
1743 // Object is no longer visible
1744 removed_objects.push(id);
1748 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1749 v3s16 blockpos, bool static_exists, v3s16 static_block)
1751 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1755 for (std::map<u16, StaticObject>::iterator
1756 so_it = block->m_static_objects.m_active.begin();
1757 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1758 // Get the ServerActiveObject counterpart to this StaticObject
1759 std::map<u16, ServerActiveObject *>::iterator ao_it;
1760 ao_it = m_active_objects.find(so_it->first);
1761 if (ao_it == m_active_objects.end()) {
1762 // If this ever happens, there must be some kind of nasty bug.
1763 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1764 "Object from MapBlock::m_static_objects::m_active not found "
1765 "in m_active_objects";
1769 ServerActiveObject *sao = ao_it->second;
1770 sao->m_static_exists = static_exists;
1771 sao->m_static_block = static_block;
1775 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1777 if(m_active_object_messages.empty())
1778 return ActiveObjectMessage(0);
1780 ActiveObjectMessage message = m_active_object_messages.front();
1781 m_active_object_messages.pop();
1786 ************ Private methods *************
1789 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1790 bool set_changed, u32 dtime_s)
1792 assert(object); // Pre-condition
1793 if(object->getId() == 0){
1794 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1797 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1798 <<"no free ids available"<<std::endl;
1799 if(object->environmentDeletes())
1803 object->setId(new_id);
1806 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1807 <<"supplied with id "<<object->getId()<<std::endl;
1809 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1811 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1812 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1813 if(object->environmentDeletes())
1818 if (objectpos_over_limit(object->getBasePosition())) {
1819 v3f p = object->getBasePosition();
1820 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1821 << "object position (" << p.X << "," << p.Y << "," << p.Z
1822 << ") outside maximum range" << std::endl;
1823 if (object->environmentDeletes())
1828 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1829 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1831 m_active_objects[object->getId()] = object;
1833 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1834 <<"Added id="<<object->getId()<<"; there are now "
1835 <<m_active_objects.size()<<" active objects."
1838 // Register reference in scripting api (must be done before post-init)
1839 m_script->addObjectReference(object);
1840 // Post-initialize object
1841 object->addedToEnvironment(dtime_s);
1843 // Add static data to block
1844 if(object->isStaticAllowed())
1846 // Add static object to active static list of the block
1847 v3f objectpos = object->getBasePosition();
1848 std::string staticdata = object->getStaticData();
1849 StaticObject s_obj(object->getType(), objectpos, staticdata);
1850 // Add to the block where the object is located in
1851 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1852 MapBlock *block = m_map->emergeBlock(blockpos);
1854 block->m_static_objects.m_active[object->getId()] = s_obj;
1855 object->m_static_exists = true;
1856 object->m_static_block = blockpos;
1859 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1860 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1862 v3s16 p = floatToInt(objectpos, BS);
1863 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1864 <<"could not emerge block for storing id="<<object->getId()
1865 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1869 return object->getId();
1873 Remove objects that satisfy (m_removed && m_known_by_count==0)
1875 void ServerEnvironment::removeRemovedObjects()
1877 std::vector<u16> objects_to_remove;
1878 for(std::map<u16, ServerActiveObject*>::iterator
1879 i = m_active_objects.begin();
1880 i != m_active_objects.end(); ++i) {
1882 ServerActiveObject* obj = i->second;
1883 // This shouldn't happen but check it
1886 infostream<<"NULL object found in ServerEnvironment"
1887 <<" while finding removed objects. id="<<id<<std::endl;
1888 // Id to be removed from m_active_objects
1889 objects_to_remove.push_back(id);
1894 We will delete objects that are marked as removed or thatare
1895 waiting for deletion after deactivation
1897 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1901 Delete static data from block if is marked as removed
1903 if(obj->m_static_exists && obj->m_removed)
1905 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1907 block->m_static_objects.remove(id);
1908 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1909 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1910 obj->m_static_exists = false;
1912 infostream<<"Failed to emerge block from which an object to "
1913 <<"be removed was loaded from. id="<<id<<std::endl;
1917 // If m_known_by_count > 0, don't actually remove. On some future
1918 // invocation this will be 0, which is when removal will continue.
1919 if(obj->m_known_by_count > 0)
1923 Move static data from active to stored if not marked as removed
1925 if(obj->m_static_exists && !obj->m_removed){
1926 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1928 std::map<u16, StaticObject>::iterator i =
1929 block->m_static_objects.m_active.find(id);
1930 if(i != block->m_static_objects.m_active.end()){
1931 block->m_static_objects.m_stored.push_back(i->second);
1932 block->m_static_objects.m_active.erase(id);
1933 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1934 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1937 infostream<<"Failed to emerge block from which an object to "
1938 <<"be deactivated was loaded from. id="<<id<<std::endl;
1942 // Tell the object about removal
1943 obj->removingFromEnvironment();
1944 // Deregister in scripting api
1945 m_script->removeObjectReference(obj);
1948 if(obj->environmentDeletes())
1951 // Id to be removed from m_active_objects
1952 objects_to_remove.push_back(id);
1954 // Remove references from m_active_objects
1955 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1956 i != objects_to_remove.end(); ++i) {
1957 m_active_objects.erase(*i);
1961 static void print_hexdump(std::ostream &o, const std::string &data)
1963 const int linelength = 16;
1964 for(int l=0; ; l++){
1965 int i0 = linelength * l;
1966 bool at_end = false;
1967 int thislinelength = linelength;
1968 if(i0 + thislinelength > (int)data.size()){
1969 thislinelength = data.size() - i0;
1972 for(int di=0; di<linelength; di++){
1975 if(di<thislinelength)
1976 snprintf(buf, 4, "%.2x ", data[i]);
1978 snprintf(buf, 4, " ");
1982 for(int di=0; di<thislinelength; di++){
1996 Convert stored objects from blocks near the players to active.
1998 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
2003 // Ignore if no stored objects (to not set changed flag)
2004 if(block->m_static_objects.m_stored.empty())
2007 verbosestream<<"ServerEnvironment::activateObjects(): "
2008 <<"activating objects of block "<<PP(block->getPos())
2009 <<" ("<<block->m_static_objects.m_stored.size()
2010 <<" objects)"<<std::endl;
2011 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
2013 errorstream<<"suspiciously large amount of objects detected: "
2014 <<block->m_static_objects.m_stored.size()<<" in "
2015 <<PP(block->getPos())
2016 <<"; removing all of them."<<std::endl;
2017 // Clear stored list
2018 block->m_static_objects.m_stored.clear();
2019 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2020 MOD_REASON_TOO_MANY_OBJECTS);
2024 // Activate stored objects
2025 std::vector<StaticObject> new_stored;
2026 for (std::vector<StaticObject>::iterator
2027 i = block->m_static_objects.m_stored.begin();
2028 i != block->m_static_objects.m_stored.end(); ++i) {
2029 StaticObject &s_obj = *i;
2031 // Create an active object from the data
2032 ServerActiveObject *obj = ServerActiveObject::create
2033 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
2034 // If couldn't create object, store static data back.
2036 errorstream<<"ServerEnvironment::activateObjects(): "
2037 <<"failed to create active object from static object "
2038 <<"in block "<<PP(s_obj.pos/BS)
2039 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
2040 print_hexdump(verbosestream, s_obj.data);
2042 new_stored.push_back(s_obj);
2045 verbosestream<<"ServerEnvironment::activateObjects(): "
2046 <<"activated static object pos="<<PP(s_obj.pos/BS)
2047 <<" type="<<(int)s_obj.type<<std::endl;
2048 // This will also add the object to the active static list
2049 addActiveObjectRaw(obj, false, dtime_s);
2051 // Clear stored list
2052 block->m_static_objects.m_stored.clear();
2053 // Add leftover failed stuff to stored list
2054 for(std::vector<StaticObject>::iterator
2055 i = new_stored.begin();
2056 i != new_stored.end(); ++i) {
2057 StaticObject &s_obj = *i;
2058 block->m_static_objects.m_stored.push_back(s_obj);
2061 // Turn the active counterparts of activated objects not pending for
2063 for(std::map<u16, StaticObject>::iterator
2064 i = block->m_static_objects.m_active.begin();
2065 i != block->m_static_objects.m_active.end(); ++i)
2068 ServerActiveObject *object = getActiveObject(id);
2070 object->m_pending_deactivation = false;
2074 Note: Block hasn't really been modified here.
2075 The objects have just been activated and moved from the stored
2076 static list to the active static list.
2077 As such, the block is essentially the same.
2078 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2079 Otherwise there would be a huge amount of unnecessary I/O.
2084 Convert objects that are not standing inside active blocks to static.
2086 If m_known_by_count != 0, active object is not deleted, but static
2087 data is still updated.
2089 If force_delete is set, active object is deleted nevertheless. It
2090 shall only be set so in the destructor of the environment.
2092 If block wasn't generated (not in memory or on disk),
2094 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2096 std::vector<u16> objects_to_remove;
2097 for(std::map<u16, ServerActiveObject*>::iterator
2098 i = m_active_objects.begin();
2099 i != m_active_objects.end(); ++i) {
2100 ServerActiveObject* obj = i->second;
2103 // Do not deactivate if static data creation not allowed
2104 if(!force_delete && !obj->isStaticAllowed())
2107 // If pending deactivation, let removeRemovedObjects() do it
2108 if(!force_delete && obj->m_pending_deactivation)
2112 v3f objectpos = obj->getBasePosition();
2114 // The block in which the object resides in
2115 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2117 // If object's static data is stored in a deactivated block and object
2118 // is actually located in an active block, re-save to the block in
2119 // which the object is actually located in.
2121 obj->m_static_exists &&
2122 !m_active_blocks.contains(obj->m_static_block) &&
2123 m_active_blocks.contains(blockpos_o))
2125 v3s16 old_static_block = obj->m_static_block;
2127 // Save to block where object is located
2128 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2130 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2131 <<"Could not save object id="<<id
2132 <<" to it's current block "<<PP(blockpos_o)
2136 std::string staticdata_new = obj->getStaticData();
2137 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2138 block->m_static_objects.insert(id, s_obj);
2139 obj->m_static_block = blockpos_o;
2140 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2141 MOD_REASON_STATIC_DATA_ADDED);
2143 // Delete from block where object was located
2144 block = m_map->emergeBlock(old_static_block, false);
2146 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2147 <<"Could not delete object id="<<id
2148 <<" from it's previous block "<<PP(old_static_block)
2152 block->m_static_objects.remove(id);
2153 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2154 MOD_REASON_STATIC_DATA_REMOVED);
2158 // If block is active, don't remove
2159 if(!force_delete && m_active_blocks.contains(blockpos_o))
2162 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2163 <<"deactivating object id="<<id<<" on inactive block "
2164 <<PP(blockpos_o)<<std::endl;
2166 // If known by some client, don't immediately delete.
2167 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2170 Update the static data
2173 if(obj->isStaticAllowed())
2175 // Create new static object
2176 std::string staticdata_new = obj->getStaticData();
2177 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2179 bool stays_in_same_block = false;
2180 bool data_changed = true;
2182 if (obj->m_static_exists) {
2183 if (obj->m_static_block == blockpos_o)
2184 stays_in_same_block = true;
2186 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2189 std::map<u16, StaticObject>::iterator n =
2190 block->m_static_objects.m_active.find(id);
2191 if (n != block->m_static_objects.m_active.end()) {
2192 StaticObject static_old = n->second;
2194 float save_movem = obj->getMinimumSavedMovement();
2196 if (static_old.data == staticdata_new &&
2197 (static_old.pos - objectpos).getLength() < save_movem)
2198 data_changed = false;
2200 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2201 <<"id="<<id<<" m_static_exists=true but "
2202 <<"static data doesn't actually exist in "
2203 <<PP(obj->m_static_block)<<std::endl;
2208 bool shall_be_written = (!stays_in_same_block || data_changed);
2210 // Delete old static object
2211 if(obj->m_static_exists)
2213 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2216 block->m_static_objects.remove(id);
2217 obj->m_static_exists = false;
2218 // Only mark block as modified if data changed considerably
2219 if(shall_be_written)
2220 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2221 MOD_REASON_STATIC_DATA_CHANGED);
2225 // Add to the block where the object is located in
2226 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2227 // Get or generate the block
2228 MapBlock *block = NULL;
2230 block = m_map->emergeBlock(blockpos);
2231 } catch(InvalidPositionException &e){
2232 // Handled via NULL pointer
2233 // NOTE: emergeBlock's failure is usually determined by it
2234 // actually returning NULL
2239 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2240 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2241 <<" statically but block "<<PP(blockpos)
2242 <<" already contains "
2243 <<block->m_static_objects.m_stored.size()
2245 <<" Forcing delete."<<std::endl;
2246 force_delete = true;
2248 // If static counterpart already exists in target block,
2250 // This shouldn't happen because the object is removed from
2251 // the previous block before this according to
2252 // obj->m_static_block, but happens rarely for some unknown
2253 // reason. Unsuccessful attempts have been made to find
2255 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2256 warningstream<<"ServerEnv: Performing hack #83274"
2258 block->m_static_objects.remove(id);
2260 // Store static data
2261 u16 store_id = pending_delete ? id : 0;
2262 block->m_static_objects.insert(store_id, s_obj);
2264 // Only mark block as modified if data changed considerably
2265 if(shall_be_written)
2266 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2267 MOD_REASON_STATIC_DATA_CHANGED);
2269 obj->m_static_exists = true;
2270 obj->m_static_block = block->getPos();
2275 v3s16 p = floatToInt(objectpos, BS);
2276 errorstream<<"ServerEnv: Could not find or generate "
2277 <<"a block for storing id="<<obj->getId()
2278 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2285 If known by some client, set pending deactivation.
2286 Otherwise delete it immediately.
2289 if(pending_delete && !force_delete)
2291 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2292 <<"object id="<<id<<" is known by clients"
2293 <<"; not deleting yet"<<std::endl;
2295 obj->m_pending_deactivation = true;
2299 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2300 <<"object id="<<id<<" is not known by clients"
2301 <<"; deleting"<<std::endl;
2303 // Tell the object about removal
2304 obj->removingFromEnvironment();
2305 // Deregister in scripting api
2306 m_script->removeObjectReference(obj);
2308 // Delete active object
2309 if(obj->environmentDeletes())
2311 // Id to be removed from m_active_objects
2312 objects_to_remove.push_back(id);
2315 // Remove references from m_active_objects
2316 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2317 i != objects_to_remove.end(); ++i) {
2318 m_active_objects.erase(*i);
2324 #include "clientsimpleobject.h"
2330 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2331 ITextureSource *texturesource, IGameDef *gamedef,
2332 IrrlichtDevice *irr):
2335 m_texturesource(texturesource),
2340 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2343 ClientEnvironment::~ClientEnvironment()
2345 // delete active objects
2346 for(std::map<u16, ClientActiveObject*>::iterator
2347 i = m_active_objects.begin();
2348 i != m_active_objects.end(); ++i)
2353 for(std::vector<ClientSimpleObject*>::iterator
2354 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2362 Map & ClientEnvironment::getMap()
2367 ClientMap & ClientEnvironment::getClientMap()
2372 void ClientEnvironment::addPlayer(Player *player)
2374 DSTACK(FUNCTION_NAME);
2376 It is a failure if player is local and there already is a local
2379 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2380 "Player is local but there is already a local player");
2382 Environment::addPlayer(player);
2385 LocalPlayer * ClientEnvironment::getLocalPlayer()
2387 for(std::vector<Player*>::iterator i = m_players.begin();
2388 i != m_players.end(); ++i) {
2389 Player *player = *i;
2390 if(player->isLocal())
2391 return (LocalPlayer*)player;
2396 void ClientEnvironment::step(float dtime)
2398 DSTACK(FUNCTION_NAME);
2400 /* Step time of day */
2401 stepTimeOfDay(dtime);
2403 // Get some settings
2404 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2405 bool free_move = fly_allowed && g_settings->getBool("free_move");
2408 LocalPlayer *lplayer = getLocalPlayer();
2410 // collision info queue
2411 std::vector<CollisionInfo> player_collisions;
2414 Get the speed the player is going
2416 bool is_climbing = lplayer->is_climbing;
2418 f32 player_speed = lplayer->getSpeed().getLength();
2421 Maximum position increment
2423 //f32 position_max_increment = 0.05*BS;
2424 f32 position_max_increment = 0.1*BS;
2426 // Maximum time increment (for collision detection etc)
2427 // time = distance / speed
2428 f32 dtime_max_increment = 1;
2429 if(player_speed > 0.001)
2430 dtime_max_increment = position_max_increment / player_speed;
2432 // Maximum time increment is 10ms or lower
2433 if(dtime_max_increment > 0.01)
2434 dtime_max_increment = 0.01;
2436 // Don't allow overly huge dtime
2440 f32 dtime_downcount = dtime;
2443 Stuff that has a maximum time increment
2452 if(dtime_downcount > dtime_max_increment)
2454 dtime_part = dtime_max_increment;
2455 dtime_downcount -= dtime_part;
2459 dtime_part = dtime_downcount;
2461 Setting this to 0 (no -=dtime_part) disables an infinite loop
2462 when dtime_part is so small that dtime_downcount -= dtime_part
2465 dtime_downcount = 0;
2474 if(free_move == false && is_climbing == false)
2477 v3f speed = lplayer->getSpeed();
2478 if(lplayer->in_liquid == false)
2479 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2481 // Liquid floating / sinking
2482 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2483 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2485 // Liquid resistance
2486 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2488 // How much the node's viscosity blocks movement, ranges between 0 and 1
2489 // Should match the scale at which viscosity increase affects other liquid attributes
2490 const f32 viscosity_factor = 0.3;
2492 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2493 f32 dl = d_wanted.getLength();
2494 if(dl > lplayer->movement_liquid_fluidity_smooth)
2495 dl = lplayer->movement_liquid_fluidity_smooth;
2496 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2498 v3f d = d_wanted.normalize() * dl;
2502 lplayer->setSpeed(speed);
2507 This also does collision detection.
2509 lplayer->move(dtime_part, this, position_max_increment,
2510 &player_collisions);
2513 while(dtime_downcount > 0.001);
2515 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2517 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2518 i != player_collisions.end(); ++i) {
2519 CollisionInfo &info = *i;
2520 v3f speed_diff = info.new_speed - info.old_speed;;
2521 // Handle only fall damage
2522 // (because otherwise walking against something in fast_move kills you)
2523 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2525 // Get rid of other components
2528 f32 pre_factor = 1; // 1 hp per node/s
2529 f32 tolerance = BS*14; // 5 without damage
2530 f32 post_factor = 1; // 1 hp per node/s
2531 if(info.type == COLLISION_NODE)
2533 const ContentFeatures &f = m_gamedef->ndef()->
2534 get(m_map->getNodeNoEx(info.node_p));
2535 // Determine fall damage multiplier
2536 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2537 pre_factor = 1.0 + (float)addp/100.0;
2539 float speed = pre_factor * speed_diff.getLength();
2540 if(speed > tolerance)
2542 f32 damage_f = (speed - tolerance)/BS * post_factor;
2543 u16 damage = (u16)(damage_f+0.5);
2545 damageLocalPlayer(damage, true);
2546 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2547 m_gamedef->event()->put(e);
2553 A quick draft of lava damage
2555 if(m_lava_hurt_interval.step(dtime, 1.0))
2557 v3f pf = lplayer->getPosition();
2559 // Feet, middle and head
2560 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2561 MapNode n1 = m_map->getNodeNoEx(p1);
2562 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2563 MapNode n2 = m_map->getNodeNoEx(p2);
2564 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2565 MapNode n3 = m_map->getNodeNoEx(p3);
2567 u32 damage_per_second = 0;
2568 damage_per_second = MYMAX(damage_per_second,
2569 m_gamedef->ndef()->get(n1).damage_per_second);
2570 damage_per_second = MYMAX(damage_per_second,
2571 m_gamedef->ndef()->get(n2).damage_per_second);
2572 damage_per_second = MYMAX(damage_per_second,
2573 m_gamedef->ndef()->get(n3).damage_per_second);
2575 if(damage_per_second != 0)
2577 damageLocalPlayer(damage_per_second, true);
2584 if(m_drowning_interval.step(dtime, 2.0))
2586 v3f pf = lplayer->getPosition();
2589 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2590 MapNode n = m_map->getNodeNoEx(p);
2591 ContentFeatures c = m_gamedef->ndef()->get(n);
2592 u8 drowning_damage = c.drowning;
2593 if(drowning_damage > 0 && lplayer->hp > 0){
2594 u16 breath = lplayer->getBreath();
2601 lplayer->setBreath(breath);
2602 updateLocalPlayerBreath(breath);
2605 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2606 damageLocalPlayer(drowning_damage, true);
2609 if(m_breathing_interval.step(dtime, 0.5))
2611 v3f pf = lplayer->getPosition();
2614 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2615 MapNode n = m_map->getNodeNoEx(p);
2616 ContentFeatures c = m_gamedef->ndef()->get(n);
2618 lplayer->setBreath(11);
2620 else if(c.drowning == 0){
2621 u16 breath = lplayer->getBreath();
2624 lplayer->setBreath(breath);
2625 updateLocalPlayerBreath(breath);
2631 Stuff that can be done in an arbitarily large dtime
2633 for(std::vector<Player*>::iterator i = m_players.begin();
2634 i != m_players.end(); ++i) {
2635 Player *player = *i;
2638 Handle non-local players
2640 if(player->isLocal() == false) {
2642 player->move(dtime, this, 100*BS);
2647 // Update lighting on local player (used for wield item)
2648 u32 day_night_ratio = getDayNightRatio();
2652 // On InvalidPositionException, use this as default
2653 // (day: LIGHT_SUN, night: 0)
2654 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2656 v3s16 p = lplayer->getLightPosition();
2657 node_at_lplayer = m_map->getNodeNoEx(p);
2659 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2660 u8 day = light & 0xff;
2661 u8 night = (light >> 8) & 0xff;
2662 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2666 Step active objects and update lighting of them
2669 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2670 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2671 for(std::map<u16, ClientActiveObject*>::iterator
2672 i = m_active_objects.begin();
2673 i != m_active_objects.end(); ++i)
2675 ClientActiveObject* obj = i->second;
2677 obj->step(dtime, this);
2686 v3s16 p = obj->getLightPosition();
2687 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2689 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2691 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2693 obj->updateLight(light);
2698 Step and handle simple objects
2700 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2701 for(std::vector<ClientSimpleObject*>::iterator
2702 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2703 std::vector<ClientSimpleObject*>::iterator cur = i;
2704 ClientSimpleObject *simple = *cur;
2706 simple->step(dtime);
2707 if(simple->m_to_be_removed) {
2709 i = m_simple_objects.erase(cur);
2717 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2719 m_simple_objects.push_back(simple);
2722 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2724 ClientActiveObject *obj = getActiveObject(id);
2725 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2726 return (GenericCAO*) obj;
2731 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2733 std::map<u16, ClientActiveObject*>::iterator n;
2734 n = m_active_objects.find(id);
2735 if(n == m_active_objects.end())
2740 bool isFreeClientActiveObjectId(u16 id,
2741 std::map<u16, ClientActiveObject*> &objects)
2746 return objects.find(id) == objects.end();
2749 u16 getFreeClientActiveObjectId(
2750 std::map<u16, ClientActiveObject*> &objects)
2752 //try to reuse id's as late as possible
2753 static u16 last_used_id = 0;
2754 u16 startid = last_used_id;
2758 if(isFreeClientActiveObjectId(last_used_id, objects))
2759 return last_used_id;
2761 if(last_used_id == startid)
2766 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2768 assert(object); // Pre-condition
2769 if(object->getId() == 0)
2771 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2774 infostream<<"ClientEnvironment::addActiveObject(): "
2775 <<"no free ids available"<<std::endl;
2779 object->setId(new_id);
2781 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2783 infostream<<"ClientEnvironment::addActiveObject(): "
2784 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2788 infostream<<"ClientEnvironment::addActiveObject(): "
2789 <<"added (id="<<object->getId()<<")"<<std::endl;
2790 m_active_objects[object->getId()] = object;
2791 object->addToScene(m_smgr, m_texturesource, m_irr);
2792 { // Update lighting immediately
2797 v3s16 p = object->getLightPosition();
2798 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2800 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2802 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2804 object->updateLight(light);
2806 return object->getId();
2809 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2810 const std::string &init_data)
2812 ClientActiveObject* obj =
2813 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2816 infostream<<"ClientEnvironment::addActiveObject(): "
2817 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2826 obj->initialize(init_data);
2828 catch(SerializationError &e)
2830 errorstream<<"ClientEnvironment::addActiveObject():"
2831 <<" id="<<id<<" type="<<type
2832 <<": SerializationError in initialize(): "
2834 <<": init_data="<<serializeJsonString(init_data)
2838 addActiveObject(obj);
2841 void ClientEnvironment::removeActiveObject(u16 id)
2843 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2844 <<"id="<<id<<std::endl;
2845 ClientActiveObject* obj = getActiveObject(id);
2848 infostream<<"ClientEnvironment::removeActiveObject(): "
2849 <<"id="<<id<<" not found"<<std::endl;
2852 obj->removeFromScene(true);
2854 m_active_objects.erase(id);
2857 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2859 ClientActiveObject *obj = getActiveObject(id);
2861 infostream << "ClientEnvironment::processActiveObjectMessage():"
2862 << " got message for id=" << id << ", which doesn't exist."
2868 obj->processMessage(data);
2869 } catch (SerializationError &e) {
2870 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2871 << " id=" << id << " type=" << obj->getType()
2872 << " SerializationError in processMessage(): " << e.what()
2878 Callbacks for activeobjects
2881 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2883 LocalPlayer *lplayer = getLocalPlayer();
2887 if (lplayer->hp > damage)
2888 lplayer->hp -= damage;
2893 ClientEnvEvent event;
2894 event.type = CEE_PLAYER_DAMAGE;
2895 event.player_damage.amount = damage;
2896 event.player_damage.send_to_server = handle_hp;
2897 m_client_event_queue.push(event);
2900 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2902 ClientEnvEvent event;
2903 event.type = CEE_PLAYER_BREATH;
2904 event.player_breath.amount = breath;
2905 m_client_event_queue.push(event);
2909 Client likes to call these
2912 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2913 std::vector<DistanceSortedActiveObject> &dest)
2915 for(std::map<u16, ClientActiveObject*>::iterator
2916 i = m_active_objects.begin();
2917 i != m_active_objects.end(); ++i)
2919 ClientActiveObject* obj = i->second;
2921 f32 d = (obj->getPosition() - origin).getLength();
2926 DistanceSortedActiveObject dso(obj, d);
2928 dest.push_back(dso);
2932 ClientEnvEvent ClientEnvironment::getClientEvent()
2934 ClientEnvEvent event;
2935 if(m_client_event_queue.empty())
2936 event.type = CEE_NONE;
2938 event = m_client_event_queue.front();
2939 m_client_event_queue.pop();
2944 #endif // #ifndef SERVER