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::map<v3s16, NodeTimer> elapsed_timers =
1034 block->m_node_timers.step((float)dtime_s);
1035 if(!elapsed_timers.empty()){
1037 for(std::map<v3s16, NodeTimer>::iterator
1038 i = elapsed_timers.begin();
1039 i != elapsed_timers.end(); ++i){
1040 n = block->getNodeNoEx(i->first);
1041 v3s16 p = i->first + block->getPosRelative();
1042 if(m_script->node_on_timer(p,n,i->second.elapsed))
1043 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
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::map<v3s16, NodeTimer> elapsed_timers =
1438 block->m_node_timers.step((float)dtime);
1439 if(!elapsed_timers.empty()){
1441 for(std::map<v3s16, NodeTimer>::iterator
1442 i = elapsed_timers.begin();
1443 i != elapsed_timers.end(); ++i){
1444 n = block->getNodeNoEx(i->first);
1445 p = i->first + block->getPosRelative();
1446 if(m_script->node_on_timer(p,n,i->second.elapsed))
1447 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1453 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1455 if(m_active_block_interval_overload_skip > 0){
1456 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1457 m_active_block_interval_overload_skip--;
1460 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1461 TimeTaker timer("modify in active blocks per interval");
1463 // Initialize handling of ActiveBlockModifiers
1464 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1466 for(std::set<v3s16>::iterator
1467 i = m_active_blocks.m_list.begin();
1468 i != m_active_blocks.m_list.end(); ++i)
1472 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1473 <<") being handled"<<std::endl;*/
1475 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1479 // Set current time as timestamp
1480 block->setTimestampNoChangedFlag(m_game_time);
1482 /* Handle ActiveBlockModifiers */
1483 abmhandler.apply(block);
1486 u32 time_ms = timer.stop(true);
1487 u32 max_time_ms = 200;
1488 if(time_ms > max_time_ms){
1489 warningstream<<"active block modifiers took "
1490 <<time_ms<<"ms (longer than "
1491 <<max_time_ms<<"ms)"<<std::endl;
1492 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1497 Step script environment (run global on_step())
1499 m_script->environment_Step(dtime);
1505 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1506 //TimeTaker timer("Step active objects");
1508 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1510 // This helps the objects to send data at the same time
1511 bool send_recommended = false;
1512 m_send_recommended_timer += dtime;
1513 if(m_send_recommended_timer > getSendRecommendedInterval())
1515 m_send_recommended_timer -= getSendRecommendedInterval();
1516 send_recommended = true;
1519 for(std::map<u16, ServerActiveObject*>::iterator
1520 i = m_active_objects.begin();
1521 i != m_active_objects.end(); ++i)
1523 ServerActiveObject* obj = i->second;
1524 // Don't step if is to be removed or stored statically
1525 if(obj->m_removed || obj->m_pending_deactivation)
1528 obj->step(dtime, send_recommended);
1529 // Read messages from object
1530 while(!obj->m_messages_out.empty())
1532 m_active_object_messages.push(
1533 obj->m_messages_out.front());
1534 obj->m_messages_out.pop();
1540 Manage active objects
1542 if(m_object_management_interval.step(dtime, 0.5))
1544 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1546 Remove objects that satisfy (m_removed && m_known_by_count==0)
1548 removeRemovedObjects();
1552 Manage particle spawner expiration
1554 if (m_particle_management_interval.step(dtime, 1.0)) {
1555 for (std::map<u32, float>::iterator i = m_particle_spawners.begin();
1556 i != m_particle_spawners.end(); ) {
1557 //non expiring spawners
1558 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1564 if (i->second <= 0.f)
1565 m_particle_spawners.erase(i++);
1572 u32 ServerEnvironment::addParticleSpawner(float exptime)
1574 // Timers with lifetime 0 do not expire
1575 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1578 for (;;) { // look for unused particlespawner id
1580 std::map<u32, float>::iterator f;
1581 f = m_particle_spawners.find(id);
1582 if (f == m_particle_spawners.end()) {
1583 m_particle_spawners[id] = time;
1590 void ServerEnvironment::deleteParticleSpawner(u32 id)
1592 m_particle_spawners.erase(id);
1595 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1597 std::map<u16, ServerActiveObject*>::iterator n;
1598 n = m_active_objects.find(id);
1599 if(n == m_active_objects.end())
1604 bool isFreeServerActiveObjectId(u16 id,
1605 std::map<u16, ServerActiveObject*> &objects)
1610 return objects.find(id) == objects.end();
1613 u16 getFreeServerActiveObjectId(
1614 std::map<u16, ServerActiveObject*> &objects)
1616 //try to reuse id's as late as possible
1617 static u16 last_used_id = 0;
1618 u16 startid = last_used_id;
1622 if(isFreeServerActiveObjectId(last_used_id, objects))
1623 return last_used_id;
1625 if(last_used_id == startid)
1630 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1632 assert(object); // Pre-condition
1634 u16 id = addActiveObjectRaw(object, true, 0);
1639 Finds out what new objects have been added to
1640 inside a radius around a position
1642 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1644 std::set<u16> ¤t_objects,
1645 std::queue<u16> &added_objects)
1647 f32 radius_f = radius * BS;
1648 f32 player_radius_f = player_radius * BS;
1650 if (player_radius_f < 0)
1651 player_radius_f = 0;
1654 Go through the object list,
1655 - discard m_removed objects,
1656 - discard objects that are too far away,
1657 - discard objects that are found in current_objects.
1658 - add remaining objects to added_objects
1660 for(std::map<u16, ServerActiveObject*>::iterator
1661 i = m_active_objects.begin();
1662 i != m_active_objects.end(); ++i) {
1666 ServerActiveObject *object = i->second;
1670 // Discard if removed or deactivating
1671 if(object->m_removed || object->m_pending_deactivation)
1674 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1675 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1676 // Discard if too far
1677 if (distance_f > player_radius_f && player_radius_f != 0)
1679 } else if (distance_f > radius_f)
1682 // Discard if already on current_objects
1683 std::set<u16>::iterator n;
1684 n = current_objects.find(id);
1685 if(n != current_objects.end())
1687 // Add to added_objects
1688 added_objects.push(id);
1693 Finds out what objects have been removed from
1694 inside a radius around a position
1696 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1698 std::set<u16> ¤t_objects,
1699 std::queue<u16> &removed_objects)
1701 f32 radius_f = radius * BS;
1702 f32 player_radius_f = player_radius * BS;
1704 if (player_radius_f < 0)
1705 player_radius_f = 0;
1708 Go through current_objects; object is removed if:
1709 - object is not found in m_active_objects (this is actually an
1710 error condition; objects should be set m_removed=true and removed
1711 only after all clients have been informed about removal), or
1712 - object has m_removed=true, or
1713 - object is too far away
1715 for(std::set<u16>::iterator
1716 i = current_objects.begin();
1717 i != current_objects.end(); ++i)
1720 ServerActiveObject *object = getActiveObject(id);
1722 if (object == NULL) {
1723 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1724 << " object in current_objects is NULL" << std::endl;
1725 removed_objects.push(id);
1729 if (object->m_removed || object->m_pending_deactivation) {
1730 removed_objects.push(id);
1734 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1735 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1736 if (distance_f <= player_radius_f || player_radius_f == 0)
1738 } else if (distance_f <= radius_f)
1741 // Object is no longer visible
1742 removed_objects.push(id);
1746 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1747 v3s16 blockpos, bool static_exists, v3s16 static_block)
1749 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1753 for (std::map<u16, StaticObject>::iterator
1754 so_it = block->m_static_objects.m_active.begin();
1755 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1756 // Get the ServerActiveObject counterpart to this StaticObject
1757 std::map<u16, ServerActiveObject *>::iterator ao_it;
1758 ao_it = m_active_objects.find(so_it->first);
1759 if (ao_it == m_active_objects.end()) {
1760 // If this ever happens, there must be some kind of nasty bug.
1761 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1762 "Object from MapBlock::m_static_objects::m_active not found "
1763 "in m_active_objects";
1767 ServerActiveObject *sao = ao_it->second;
1768 sao->m_static_exists = static_exists;
1769 sao->m_static_block = static_block;
1773 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1775 if(m_active_object_messages.empty())
1776 return ActiveObjectMessage(0);
1778 ActiveObjectMessage message = m_active_object_messages.front();
1779 m_active_object_messages.pop();
1784 ************ Private methods *************
1787 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1788 bool set_changed, u32 dtime_s)
1790 assert(object); // Pre-condition
1791 if(object->getId() == 0){
1792 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1795 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1796 <<"no free ids available"<<std::endl;
1797 if(object->environmentDeletes())
1801 object->setId(new_id);
1804 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1805 <<"supplied with id "<<object->getId()<<std::endl;
1807 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1809 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1810 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1811 if(object->environmentDeletes())
1816 if (objectpos_over_limit(object->getBasePosition())) {
1817 v3f p = object->getBasePosition();
1818 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1819 << "object position (" << p.X << "," << p.Y << "," << p.Z
1820 << ") outside maximum range" << std::endl;
1821 if (object->environmentDeletes())
1826 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1827 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1829 m_active_objects[object->getId()] = object;
1831 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1832 <<"Added id="<<object->getId()<<"; there are now "
1833 <<m_active_objects.size()<<" active objects."
1836 // Register reference in scripting api (must be done before post-init)
1837 m_script->addObjectReference(object);
1838 // Post-initialize object
1839 object->addedToEnvironment(dtime_s);
1841 // Add static data to block
1842 if(object->isStaticAllowed())
1844 // Add static object to active static list of the block
1845 v3f objectpos = object->getBasePosition();
1846 std::string staticdata = object->getStaticData();
1847 StaticObject s_obj(object->getType(), objectpos, staticdata);
1848 // Add to the block where the object is located in
1849 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1850 MapBlock *block = m_map->emergeBlock(blockpos);
1852 block->m_static_objects.m_active[object->getId()] = s_obj;
1853 object->m_static_exists = true;
1854 object->m_static_block = blockpos;
1857 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1858 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1860 v3s16 p = floatToInt(objectpos, BS);
1861 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1862 <<"could not emerge block for storing id="<<object->getId()
1863 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1867 return object->getId();
1871 Remove objects that satisfy (m_removed && m_known_by_count==0)
1873 void ServerEnvironment::removeRemovedObjects()
1875 std::vector<u16> objects_to_remove;
1876 for(std::map<u16, ServerActiveObject*>::iterator
1877 i = m_active_objects.begin();
1878 i != m_active_objects.end(); ++i) {
1880 ServerActiveObject* obj = i->second;
1881 // This shouldn't happen but check it
1884 infostream<<"NULL object found in ServerEnvironment"
1885 <<" while finding removed objects. id="<<id<<std::endl;
1886 // Id to be removed from m_active_objects
1887 objects_to_remove.push_back(id);
1892 We will delete objects that are marked as removed or thatare
1893 waiting for deletion after deactivation
1895 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1899 Delete static data from block if is marked as removed
1901 if(obj->m_static_exists && obj->m_removed)
1903 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1905 block->m_static_objects.remove(id);
1906 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1907 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1908 obj->m_static_exists = false;
1910 infostream<<"Failed to emerge block from which an object to "
1911 <<"be removed was loaded from. id="<<id<<std::endl;
1915 // If m_known_by_count > 0, don't actually remove. On some future
1916 // invocation this will be 0, which is when removal will continue.
1917 if(obj->m_known_by_count > 0)
1921 Move static data from active to stored if not marked as removed
1923 if(obj->m_static_exists && !obj->m_removed){
1924 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1926 std::map<u16, StaticObject>::iterator i =
1927 block->m_static_objects.m_active.find(id);
1928 if(i != block->m_static_objects.m_active.end()){
1929 block->m_static_objects.m_stored.push_back(i->second);
1930 block->m_static_objects.m_active.erase(id);
1931 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1932 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1935 infostream<<"Failed to emerge block from which an object to "
1936 <<"be deactivated was loaded from. id="<<id<<std::endl;
1940 // Tell the object about removal
1941 obj->removingFromEnvironment();
1942 // Deregister in scripting api
1943 m_script->removeObjectReference(obj);
1946 if(obj->environmentDeletes())
1949 // Id to be removed from m_active_objects
1950 objects_to_remove.push_back(id);
1952 // Remove references from m_active_objects
1953 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1954 i != objects_to_remove.end(); ++i) {
1955 m_active_objects.erase(*i);
1959 static void print_hexdump(std::ostream &o, const std::string &data)
1961 const int linelength = 16;
1962 for(int l=0; ; l++){
1963 int i0 = linelength * l;
1964 bool at_end = false;
1965 int thislinelength = linelength;
1966 if(i0 + thislinelength > (int)data.size()){
1967 thislinelength = data.size() - i0;
1970 for(int di=0; di<linelength; di++){
1973 if(di<thislinelength)
1974 snprintf(buf, 4, "%.2x ", data[i]);
1976 snprintf(buf, 4, " ");
1980 for(int di=0; di<thislinelength; di++){
1994 Convert stored objects from blocks near the players to active.
1996 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
2001 // Ignore if no stored objects (to not set changed flag)
2002 if(block->m_static_objects.m_stored.empty())
2005 verbosestream<<"ServerEnvironment::activateObjects(): "
2006 <<"activating objects of block "<<PP(block->getPos())
2007 <<" ("<<block->m_static_objects.m_stored.size()
2008 <<" objects)"<<std::endl;
2009 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
2011 errorstream<<"suspiciously large amount of objects detected: "
2012 <<block->m_static_objects.m_stored.size()<<" in "
2013 <<PP(block->getPos())
2014 <<"; removing all of them."<<std::endl;
2015 // Clear stored list
2016 block->m_static_objects.m_stored.clear();
2017 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2018 MOD_REASON_TOO_MANY_OBJECTS);
2022 // Activate stored objects
2023 std::vector<StaticObject> new_stored;
2024 for (std::vector<StaticObject>::iterator
2025 i = block->m_static_objects.m_stored.begin();
2026 i != block->m_static_objects.m_stored.end(); ++i) {
2027 StaticObject &s_obj = *i;
2029 // Create an active object from the data
2030 ServerActiveObject *obj = ServerActiveObject::create
2031 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
2032 // If couldn't create object, store static data back.
2034 errorstream<<"ServerEnvironment::activateObjects(): "
2035 <<"failed to create active object from static object "
2036 <<"in block "<<PP(s_obj.pos/BS)
2037 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
2038 print_hexdump(verbosestream, s_obj.data);
2040 new_stored.push_back(s_obj);
2043 verbosestream<<"ServerEnvironment::activateObjects(): "
2044 <<"activated static object pos="<<PP(s_obj.pos/BS)
2045 <<" type="<<(int)s_obj.type<<std::endl;
2046 // This will also add the object to the active static list
2047 addActiveObjectRaw(obj, false, dtime_s);
2049 // Clear stored list
2050 block->m_static_objects.m_stored.clear();
2051 // Add leftover failed stuff to stored list
2052 for(std::vector<StaticObject>::iterator
2053 i = new_stored.begin();
2054 i != new_stored.end(); ++i) {
2055 StaticObject &s_obj = *i;
2056 block->m_static_objects.m_stored.push_back(s_obj);
2059 // Turn the active counterparts of activated objects not pending for
2061 for(std::map<u16, StaticObject>::iterator
2062 i = block->m_static_objects.m_active.begin();
2063 i != block->m_static_objects.m_active.end(); ++i)
2066 ServerActiveObject *object = getActiveObject(id);
2068 object->m_pending_deactivation = false;
2072 Note: Block hasn't really been modified here.
2073 The objects have just been activated and moved from the stored
2074 static list to the active static list.
2075 As such, the block is essentially the same.
2076 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2077 Otherwise there would be a huge amount of unnecessary I/O.
2082 Convert objects that are not standing inside active blocks to static.
2084 If m_known_by_count != 0, active object is not deleted, but static
2085 data is still updated.
2087 If force_delete is set, active object is deleted nevertheless. It
2088 shall only be set so in the destructor of the environment.
2090 If block wasn't generated (not in memory or on disk),
2092 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2094 std::vector<u16> objects_to_remove;
2095 for(std::map<u16, ServerActiveObject*>::iterator
2096 i = m_active_objects.begin();
2097 i != m_active_objects.end(); ++i) {
2098 ServerActiveObject* obj = i->second;
2101 // Do not deactivate if static data creation not allowed
2102 if(!force_delete && !obj->isStaticAllowed())
2105 // If pending deactivation, let removeRemovedObjects() do it
2106 if(!force_delete && obj->m_pending_deactivation)
2110 v3f objectpos = obj->getBasePosition();
2112 // The block in which the object resides in
2113 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2115 // If object's static data is stored in a deactivated block and object
2116 // is actually located in an active block, re-save to the block in
2117 // which the object is actually located in.
2119 obj->m_static_exists &&
2120 !m_active_blocks.contains(obj->m_static_block) &&
2121 m_active_blocks.contains(blockpos_o))
2123 v3s16 old_static_block = obj->m_static_block;
2125 // Save to block where object is located
2126 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2128 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2129 <<"Could not save object id="<<id
2130 <<" to it's current block "<<PP(blockpos_o)
2134 std::string staticdata_new = obj->getStaticData();
2135 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2136 block->m_static_objects.insert(id, s_obj);
2137 obj->m_static_block = blockpos_o;
2138 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2139 MOD_REASON_STATIC_DATA_ADDED);
2141 // Delete from block where object was located
2142 block = m_map->emergeBlock(old_static_block, false);
2144 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2145 <<"Could not delete object id="<<id
2146 <<" from it's previous block "<<PP(old_static_block)
2150 block->m_static_objects.remove(id);
2151 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2152 MOD_REASON_STATIC_DATA_REMOVED);
2156 // If block is active, don't remove
2157 if(!force_delete && m_active_blocks.contains(blockpos_o))
2160 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2161 <<"deactivating object id="<<id<<" on inactive block "
2162 <<PP(blockpos_o)<<std::endl;
2164 // If known by some client, don't immediately delete.
2165 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2168 Update the static data
2171 if(obj->isStaticAllowed())
2173 // Create new static object
2174 std::string staticdata_new = obj->getStaticData();
2175 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2177 bool stays_in_same_block = false;
2178 bool data_changed = true;
2180 if (obj->m_static_exists) {
2181 if (obj->m_static_block == blockpos_o)
2182 stays_in_same_block = true;
2184 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2187 std::map<u16, StaticObject>::iterator n =
2188 block->m_static_objects.m_active.find(id);
2189 if (n != block->m_static_objects.m_active.end()) {
2190 StaticObject static_old = n->second;
2192 float save_movem = obj->getMinimumSavedMovement();
2194 if (static_old.data == staticdata_new &&
2195 (static_old.pos - objectpos).getLength() < save_movem)
2196 data_changed = false;
2198 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2199 <<"id="<<id<<" m_static_exists=true but "
2200 <<"static data doesn't actually exist in "
2201 <<PP(obj->m_static_block)<<std::endl;
2206 bool shall_be_written = (!stays_in_same_block || data_changed);
2208 // Delete old static object
2209 if(obj->m_static_exists)
2211 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2214 block->m_static_objects.remove(id);
2215 obj->m_static_exists = false;
2216 // Only mark block as modified if data changed considerably
2217 if(shall_be_written)
2218 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2219 MOD_REASON_STATIC_DATA_CHANGED);
2223 // Add to the block where the object is located in
2224 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2225 // Get or generate the block
2226 MapBlock *block = NULL;
2228 block = m_map->emergeBlock(blockpos);
2229 } catch(InvalidPositionException &e){
2230 // Handled via NULL pointer
2231 // NOTE: emergeBlock's failure is usually determined by it
2232 // actually returning NULL
2237 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2238 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2239 <<" statically but block "<<PP(blockpos)
2240 <<" already contains "
2241 <<block->m_static_objects.m_stored.size()
2243 <<" Forcing delete."<<std::endl;
2244 force_delete = true;
2246 // If static counterpart already exists in target block,
2248 // This shouldn't happen because the object is removed from
2249 // the previous block before this according to
2250 // obj->m_static_block, but happens rarely for some unknown
2251 // reason. Unsuccessful attempts have been made to find
2253 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2254 warningstream<<"ServerEnv: Performing hack #83274"
2256 block->m_static_objects.remove(id);
2258 // Store static data
2259 u16 store_id = pending_delete ? id : 0;
2260 block->m_static_objects.insert(store_id, s_obj);
2262 // Only mark block as modified if data changed considerably
2263 if(shall_be_written)
2264 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2265 MOD_REASON_STATIC_DATA_CHANGED);
2267 obj->m_static_exists = true;
2268 obj->m_static_block = block->getPos();
2273 v3s16 p = floatToInt(objectpos, BS);
2274 errorstream<<"ServerEnv: Could not find or generate "
2275 <<"a block for storing id="<<obj->getId()
2276 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2283 If known by some client, set pending deactivation.
2284 Otherwise delete it immediately.
2287 if(pending_delete && !force_delete)
2289 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2290 <<"object id="<<id<<" is known by clients"
2291 <<"; not deleting yet"<<std::endl;
2293 obj->m_pending_deactivation = true;
2297 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2298 <<"object id="<<id<<" is not known by clients"
2299 <<"; deleting"<<std::endl;
2301 // Tell the object about removal
2302 obj->removingFromEnvironment();
2303 // Deregister in scripting api
2304 m_script->removeObjectReference(obj);
2306 // Delete active object
2307 if(obj->environmentDeletes())
2309 // Id to be removed from m_active_objects
2310 objects_to_remove.push_back(id);
2313 // Remove references from m_active_objects
2314 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2315 i != objects_to_remove.end(); ++i) {
2316 m_active_objects.erase(*i);
2322 #include "clientsimpleobject.h"
2328 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2329 ITextureSource *texturesource, IGameDef *gamedef,
2330 IrrlichtDevice *irr):
2333 m_texturesource(texturesource),
2338 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2341 ClientEnvironment::~ClientEnvironment()
2343 // delete active objects
2344 for(std::map<u16, ClientActiveObject*>::iterator
2345 i = m_active_objects.begin();
2346 i != m_active_objects.end(); ++i)
2351 for(std::vector<ClientSimpleObject*>::iterator
2352 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2360 Map & ClientEnvironment::getMap()
2365 ClientMap & ClientEnvironment::getClientMap()
2370 void ClientEnvironment::addPlayer(Player *player)
2372 DSTACK(FUNCTION_NAME);
2374 It is a failure if player is local and there already is a local
2377 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2378 "Player is local but there is already a local player");
2380 Environment::addPlayer(player);
2383 LocalPlayer * ClientEnvironment::getLocalPlayer()
2385 for(std::vector<Player*>::iterator i = m_players.begin();
2386 i != m_players.end(); ++i) {
2387 Player *player = *i;
2388 if(player->isLocal())
2389 return (LocalPlayer*)player;
2394 void ClientEnvironment::step(float dtime)
2396 DSTACK(FUNCTION_NAME);
2398 /* Step time of day */
2399 stepTimeOfDay(dtime);
2401 // Get some settings
2402 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2403 bool free_move = fly_allowed && g_settings->getBool("free_move");
2406 LocalPlayer *lplayer = getLocalPlayer();
2408 // collision info queue
2409 std::vector<CollisionInfo> player_collisions;
2412 Get the speed the player is going
2414 bool is_climbing = lplayer->is_climbing;
2416 f32 player_speed = lplayer->getSpeed().getLength();
2419 Maximum position increment
2421 //f32 position_max_increment = 0.05*BS;
2422 f32 position_max_increment = 0.1*BS;
2424 // Maximum time increment (for collision detection etc)
2425 // time = distance / speed
2426 f32 dtime_max_increment = 1;
2427 if(player_speed > 0.001)
2428 dtime_max_increment = position_max_increment / player_speed;
2430 // Maximum time increment is 10ms or lower
2431 if(dtime_max_increment > 0.01)
2432 dtime_max_increment = 0.01;
2434 // Don't allow overly huge dtime
2438 f32 dtime_downcount = dtime;
2441 Stuff that has a maximum time increment
2450 if(dtime_downcount > dtime_max_increment)
2452 dtime_part = dtime_max_increment;
2453 dtime_downcount -= dtime_part;
2457 dtime_part = dtime_downcount;
2459 Setting this to 0 (no -=dtime_part) disables an infinite loop
2460 when dtime_part is so small that dtime_downcount -= dtime_part
2463 dtime_downcount = 0;
2472 if(free_move == false && is_climbing == false)
2475 v3f speed = lplayer->getSpeed();
2476 if(lplayer->in_liquid == false)
2477 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2479 // Liquid floating / sinking
2480 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2481 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2483 // Liquid resistance
2484 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2486 // How much the node's viscosity blocks movement, ranges between 0 and 1
2487 // Should match the scale at which viscosity increase affects other liquid attributes
2488 const f32 viscosity_factor = 0.3;
2490 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2491 f32 dl = d_wanted.getLength();
2492 if(dl > lplayer->movement_liquid_fluidity_smooth)
2493 dl = lplayer->movement_liquid_fluidity_smooth;
2494 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2496 v3f d = d_wanted.normalize() * dl;
2500 lplayer->setSpeed(speed);
2505 This also does collision detection.
2507 lplayer->move(dtime_part, this, position_max_increment,
2508 &player_collisions);
2511 while(dtime_downcount > 0.001);
2513 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2515 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2516 i != player_collisions.end(); ++i) {
2517 CollisionInfo &info = *i;
2518 v3f speed_diff = info.new_speed - info.old_speed;;
2519 // Handle only fall damage
2520 // (because otherwise walking against something in fast_move kills you)
2521 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2523 // Get rid of other components
2526 f32 pre_factor = 1; // 1 hp per node/s
2527 f32 tolerance = BS*14; // 5 without damage
2528 f32 post_factor = 1; // 1 hp per node/s
2529 if(info.type == COLLISION_NODE)
2531 const ContentFeatures &f = m_gamedef->ndef()->
2532 get(m_map->getNodeNoEx(info.node_p));
2533 // Determine fall damage multiplier
2534 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2535 pre_factor = 1.0 + (float)addp/100.0;
2537 float speed = pre_factor * speed_diff.getLength();
2538 if(speed > tolerance)
2540 f32 damage_f = (speed - tolerance)/BS * post_factor;
2541 u16 damage = (u16)(damage_f+0.5);
2543 damageLocalPlayer(damage, true);
2544 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2545 m_gamedef->event()->put(e);
2551 A quick draft of lava damage
2553 if(m_lava_hurt_interval.step(dtime, 1.0))
2555 v3f pf = lplayer->getPosition();
2557 // Feet, middle and head
2558 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2559 MapNode n1 = m_map->getNodeNoEx(p1);
2560 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2561 MapNode n2 = m_map->getNodeNoEx(p2);
2562 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2563 MapNode n3 = m_map->getNodeNoEx(p3);
2565 u32 damage_per_second = 0;
2566 damage_per_second = MYMAX(damage_per_second,
2567 m_gamedef->ndef()->get(n1).damage_per_second);
2568 damage_per_second = MYMAX(damage_per_second,
2569 m_gamedef->ndef()->get(n2).damage_per_second);
2570 damage_per_second = MYMAX(damage_per_second,
2571 m_gamedef->ndef()->get(n3).damage_per_second);
2573 if(damage_per_second != 0)
2575 damageLocalPlayer(damage_per_second, true);
2582 if(m_drowning_interval.step(dtime, 2.0))
2584 v3f pf = lplayer->getPosition();
2587 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2588 MapNode n = m_map->getNodeNoEx(p);
2589 ContentFeatures c = m_gamedef->ndef()->get(n);
2590 u8 drowning_damage = c.drowning;
2591 if(drowning_damage > 0 && lplayer->hp > 0){
2592 u16 breath = lplayer->getBreath();
2599 lplayer->setBreath(breath);
2600 updateLocalPlayerBreath(breath);
2603 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2604 damageLocalPlayer(drowning_damage, true);
2607 if(m_breathing_interval.step(dtime, 0.5))
2609 v3f pf = lplayer->getPosition();
2612 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2613 MapNode n = m_map->getNodeNoEx(p);
2614 ContentFeatures c = m_gamedef->ndef()->get(n);
2616 lplayer->setBreath(11);
2618 else if(c.drowning == 0){
2619 u16 breath = lplayer->getBreath();
2622 lplayer->setBreath(breath);
2623 updateLocalPlayerBreath(breath);
2629 Stuff that can be done in an arbitarily large dtime
2631 for(std::vector<Player*>::iterator i = m_players.begin();
2632 i != m_players.end(); ++i) {
2633 Player *player = *i;
2636 Handle non-local players
2638 if(player->isLocal() == false) {
2640 player->move(dtime, this, 100*BS);
2645 // Update lighting on local player (used for wield item)
2646 u32 day_night_ratio = getDayNightRatio();
2650 // On InvalidPositionException, use this as default
2651 // (day: LIGHT_SUN, night: 0)
2652 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2654 v3s16 p = lplayer->getLightPosition();
2655 node_at_lplayer = m_map->getNodeNoEx(p);
2657 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2658 u8 day = light & 0xff;
2659 u8 night = (light >> 8) & 0xff;
2660 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2664 Step active objects and update lighting of them
2667 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2668 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2669 for(std::map<u16, ClientActiveObject*>::iterator
2670 i = m_active_objects.begin();
2671 i != m_active_objects.end(); ++i)
2673 ClientActiveObject* obj = i->second;
2675 obj->step(dtime, this);
2684 v3s16 p = obj->getLightPosition();
2685 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2687 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2689 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2691 obj->updateLight(light);
2696 Step and handle simple objects
2698 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2699 for(std::vector<ClientSimpleObject*>::iterator
2700 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2701 std::vector<ClientSimpleObject*>::iterator cur = i;
2702 ClientSimpleObject *simple = *cur;
2704 simple->step(dtime);
2705 if(simple->m_to_be_removed) {
2707 i = m_simple_objects.erase(cur);
2715 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2717 m_simple_objects.push_back(simple);
2720 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2722 ClientActiveObject *obj = getActiveObject(id);
2723 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2724 return (GenericCAO*) obj;
2729 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2731 std::map<u16, ClientActiveObject*>::iterator n;
2732 n = m_active_objects.find(id);
2733 if(n == m_active_objects.end())
2738 bool isFreeClientActiveObjectId(u16 id,
2739 std::map<u16, ClientActiveObject*> &objects)
2744 return objects.find(id) == objects.end();
2747 u16 getFreeClientActiveObjectId(
2748 std::map<u16, ClientActiveObject*> &objects)
2750 //try to reuse id's as late as possible
2751 static u16 last_used_id = 0;
2752 u16 startid = last_used_id;
2756 if(isFreeClientActiveObjectId(last_used_id, objects))
2757 return last_used_id;
2759 if(last_used_id == startid)
2764 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2766 assert(object); // Pre-condition
2767 if(object->getId() == 0)
2769 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2772 infostream<<"ClientEnvironment::addActiveObject(): "
2773 <<"no free ids available"<<std::endl;
2777 object->setId(new_id);
2779 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2781 infostream<<"ClientEnvironment::addActiveObject(): "
2782 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2786 infostream<<"ClientEnvironment::addActiveObject(): "
2787 <<"added (id="<<object->getId()<<")"<<std::endl;
2788 m_active_objects[object->getId()] = object;
2789 object->addToScene(m_smgr, m_texturesource, m_irr);
2790 { // Update lighting immediately
2795 v3s16 p = object->getLightPosition();
2796 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2798 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2800 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2802 object->updateLight(light);
2804 return object->getId();
2807 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2808 const std::string &init_data)
2810 ClientActiveObject* obj =
2811 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2814 infostream<<"ClientEnvironment::addActiveObject(): "
2815 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2824 obj->initialize(init_data);
2826 catch(SerializationError &e)
2828 errorstream<<"ClientEnvironment::addActiveObject():"
2829 <<" id="<<id<<" type="<<type
2830 <<": SerializationError in initialize(): "
2832 <<": init_data="<<serializeJsonString(init_data)
2836 addActiveObject(obj);
2839 void ClientEnvironment::removeActiveObject(u16 id)
2841 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2842 <<"id="<<id<<std::endl;
2843 ClientActiveObject* obj = getActiveObject(id);
2846 infostream<<"ClientEnvironment::removeActiveObject(): "
2847 <<"id="<<id<<" not found"<<std::endl;
2850 obj->removeFromScene(true);
2852 m_active_objects.erase(id);
2855 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2857 ClientActiveObject *obj = getActiveObject(id);
2859 infostream << "ClientEnvironment::processActiveObjectMessage():"
2860 << " got message for id=" << id << ", which doesn't exist."
2866 obj->processMessage(data);
2867 } catch (SerializationError &e) {
2868 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2869 << " id=" << id << " type=" << obj->getType()
2870 << " SerializationError in processMessage(): " << e.what()
2876 Callbacks for activeobjects
2879 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2881 LocalPlayer *lplayer = getLocalPlayer();
2885 if (lplayer->hp > damage)
2886 lplayer->hp -= damage;
2891 ClientEnvEvent event;
2892 event.type = CEE_PLAYER_DAMAGE;
2893 event.player_damage.amount = damage;
2894 event.player_damage.send_to_server = handle_hp;
2895 m_client_event_queue.push(event);
2898 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2900 ClientEnvEvent event;
2901 event.type = CEE_PLAYER_BREATH;
2902 event.player_breath.amount = breath;
2903 m_client_event_queue.push(event);
2907 Client likes to call these
2910 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2911 std::vector<DistanceSortedActiveObject> &dest)
2913 for(std::map<u16, ClientActiveObject*>::iterator
2914 i = m_active_objects.begin();
2915 i != m_active_objects.end(); ++i)
2917 ClientActiveObject* obj = i->second;
2919 f32 d = (obj->getPosition() - origin).getLength();
2924 DistanceSortedActiveObject dso(obj, d);
2926 dest.push_back(dso);
2930 ClientEnvEvent ClientEnvironment::getClientEvent()
2932 ClientEnvEvent event;
2933 if(m_client_event_queue.empty())
2934 event.type = CEE_NONE;
2936 event = m_client_event_queue.front();
2937 m_client_event_queue.pop();
2942 #endif // #ifndef SERVER