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 (ActiveObjectMap::iterator i = m_active_objects.begin();
1128 i != m_active_objects.end(); ++i) {
1129 ServerActiveObject* obj = i->second;
1131 v3f objectpos = obj->getBasePosition();
1132 if (objectpos.getDistanceFrom(pos) > radius)
1134 objects.push_back(id);
1138 void ServerEnvironment::clearObjects(ClearObjectsMode mode)
1140 infostream << "ServerEnvironment::clearObjects(): "
1141 << "Removing all active objects" << std::endl;
1142 std::vector<u16> objects_to_remove;
1143 for (ActiveObjectMap::iterator i = m_active_objects.begin();
1144 i != m_active_objects.end(); ++i) {
1145 ServerActiveObject* obj = i->second;
1146 if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
1149 // Delete static object if block is loaded
1150 if (obj->m_static_exists) {
1151 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
1153 block->m_static_objects.remove(id);
1154 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1155 MOD_REASON_CLEAR_ALL_OBJECTS);
1156 obj->m_static_exists = false;
1159 // If known by some client, don't delete immediately
1160 if (obj->m_known_by_count > 0) {
1161 obj->m_pending_deactivation = true;
1162 obj->m_removed = true;
1166 // Tell the object about removal
1167 obj->removingFromEnvironment();
1168 // Deregister in scripting api
1169 m_script->removeObjectReference(obj);
1171 // Delete active object
1172 if (obj->environmentDeletes())
1174 // Id to be removed from m_active_objects
1175 objects_to_remove.push_back(id);
1178 // Remove references from m_active_objects
1179 for (std::vector<u16>::iterator i = objects_to_remove.begin();
1180 i != objects_to_remove.end(); ++i) {
1181 m_active_objects.erase(*i);
1184 // Get list of loaded blocks
1185 std::vector<v3s16> loaded_blocks;
1186 infostream << "ServerEnvironment::clearObjects(): "
1187 << "Listing all loaded blocks" << std::endl;
1188 m_map->listAllLoadedBlocks(loaded_blocks);
1189 infostream << "ServerEnvironment::clearObjects(): "
1190 << "Done listing all loaded blocks: "
1191 << loaded_blocks.size()<<std::endl;
1193 // Get list of loadable blocks
1194 std::vector<v3s16> loadable_blocks;
1195 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1196 infostream << "ServerEnvironment::clearObjects(): "
1197 << "Listing all loadable blocks" << std::endl;
1198 m_map->listAllLoadableBlocks(loadable_blocks);
1199 infostream << "ServerEnvironment::clearObjects(): "
1200 << "Done listing all loadable blocks: "
1201 << loadable_blocks.size() << std::endl;
1203 loadable_blocks = loaded_blocks;
1206 infostream << "ServerEnvironment::clearObjects(): "
1207 << "Now clearing objects in " << loadable_blocks.size()
1208 << " blocks" << std::endl;
1210 // Grab a reference on each loaded block to avoid unloading it
1211 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1212 i != loaded_blocks.end(); ++i) {
1214 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1215 assert(block != NULL);
1219 // Remove objects in all loadable blocks
1220 u32 unload_interval = U32_MAX;
1221 if (mode == CLEAR_OBJECTS_MODE_FULL) {
1222 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
1223 unload_interval = MYMAX(unload_interval, 1);
1225 u32 report_interval = loadable_blocks.size() / 10;
1226 u32 num_blocks_checked = 0;
1227 u32 num_blocks_cleared = 0;
1228 u32 num_objs_cleared = 0;
1229 for (std::vector<v3s16>::iterator i = loadable_blocks.begin();
1230 i != loadable_blocks.end(); ++i) {
1232 MapBlock *block = m_map->emergeBlock(p, false);
1234 errorstream << "ServerEnvironment::clearObjects(): "
1235 << "Failed to emerge block " << PP(p) << std::endl;
1238 u32 num_stored = block->m_static_objects.m_stored.size();
1239 u32 num_active = block->m_static_objects.m_active.size();
1240 if (num_stored != 0 || num_active != 0) {
1241 block->m_static_objects.m_stored.clear();
1242 block->m_static_objects.m_active.clear();
1243 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1244 MOD_REASON_CLEAR_ALL_OBJECTS);
1245 num_objs_cleared += num_stored + num_active;
1246 num_blocks_cleared++;
1248 num_blocks_checked++;
1250 if (report_interval != 0 &&
1251 num_blocks_checked % report_interval == 0) {
1252 float percent = 100.0 * (float)num_blocks_checked /
1253 loadable_blocks.size();
1254 infostream << "ServerEnvironment::clearObjects(): "
1255 << "Cleared " << num_objs_cleared << " objects"
1256 << " in " << num_blocks_cleared << " blocks ("
1257 << percent << "%)" << std::endl;
1259 if (num_blocks_checked % unload_interval == 0) {
1260 m_map->unloadUnreferencedBlocks();
1263 m_map->unloadUnreferencedBlocks();
1265 // Drop references that were added above
1266 for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
1267 i != loaded_blocks.end(); ++i) {
1269 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1274 m_last_clear_objects_time = m_game_time;
1276 infostream << "ServerEnvironment::clearObjects(): "
1277 << "Finished: Cleared " << num_objs_cleared << " objects"
1278 << " in " << num_blocks_cleared << " blocks" << std::endl;
1281 void ServerEnvironment::step(float dtime)
1283 DSTACK(FUNCTION_NAME);
1285 //TimeTaker timer("ServerEnv step");
1287 /* Step time of day */
1288 stepTimeOfDay(dtime);
1291 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1292 // really matter that much.
1293 static const float server_step = g_settings->getFloat("dedicated_server_step");
1294 m_recommended_send_interval = server_step;
1300 m_game_time_fraction_counter += dtime;
1301 u32 inc_i = (u32)m_game_time_fraction_counter;
1302 m_game_time += inc_i;
1303 m_game_time_fraction_counter -= (float)inc_i;
1310 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1311 for(std::vector<Player*>::iterator i = m_players.begin();
1312 i != m_players.end(); ++i)
1314 Player *player = *i;
1316 // Ignore disconnected players
1317 if(player->peer_id == 0)
1321 player->move(dtime, this, 100*BS);
1326 Manage active block list
1328 if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) {
1329 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG);
1331 Get player block positions
1333 std::vector<v3s16> players_blockpos;
1334 for(std::vector<Player*>::iterator
1335 i = m_players.begin();
1336 i != m_players.end(); ++i) {
1337 Player *player = *i;
1338 // Ignore disconnected players
1339 if(player->peer_id == 0)
1342 v3s16 blockpos = getNodeBlockPos(
1343 floatToInt(player->getPosition(), BS));
1344 players_blockpos.push_back(blockpos);
1348 Update list of active blocks, collecting changes
1350 static const s16 active_block_range = g_settings->getS16("active_block_range");
1351 std::set<v3s16> blocks_removed;
1352 std::set<v3s16> blocks_added;
1353 m_active_blocks.update(players_blockpos, active_block_range,
1354 blocks_removed, blocks_added);
1357 Handle removed blocks
1360 // Convert active objects that are no more in active blocks to static
1361 deactivateFarObjects(false);
1363 for(std::set<v3s16>::iterator
1364 i = blocks_removed.begin();
1365 i != blocks_removed.end(); ++i) {
1368 /* infostream<<"Server: Block " << PP(p)
1369 << " became inactive"<<std::endl; */
1371 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1375 // Set current time as timestamp (and let it set ChangedFlag)
1376 block->setTimestamp(m_game_time);
1383 for(std::set<v3s16>::iterator
1384 i = blocks_added.begin();
1385 i != blocks_added.end(); ++i)
1389 MapBlock *block = m_map->getBlockOrEmerge(p);
1391 m_active_blocks.m_list.erase(p);
1395 activateBlock(block);
1396 /* infostream<<"Server: Block " << PP(p)
1397 << " became active"<<std::endl; */
1402 Mess around in active blocks
1404 if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) {
1405 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG);
1407 float dtime = m_cache_nodetimer_interval;
1409 for(std::set<v3s16>::iterator
1410 i = m_active_blocks.m_list.begin();
1411 i != m_active_blocks.m_list.end(); ++i)
1415 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1416 <<") being handled"<<std::endl;*/
1418 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1422 // Reset block usage timer
1423 block->resetUsageTimer();
1425 // Set current time as timestamp
1426 block->setTimestampNoChangedFlag(m_game_time);
1427 // If time has changed much from the one on disk,
1428 // set block to be saved when it is unloaded
1429 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1430 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1431 MOD_REASON_BLOCK_EXPIRED);
1434 std::vector<NodeTimer> elapsed_timers =
1435 block->m_node_timers.step((float)dtime);
1436 if (!elapsed_timers.empty()) {
1438 for (std::vector<NodeTimer>::iterator
1439 i = elapsed_timers.begin();
1440 i != elapsed_timers.end(); ++i) {
1441 n = block->getNodeNoEx(i->position);
1442 p = i->position + block->getPosRelative();
1443 if (m_script->node_on_timer(p, n, i->elapsed)) {
1444 block->setNodeTimer(NodeTimer(
1445 i->timeout, 0, i->position));
1452 if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval))
1454 if(m_active_block_interval_overload_skip > 0){
1455 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1456 m_active_block_interval_overload_skip--;
1459 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG);
1460 TimeTaker timer("modify in active blocks per interval");
1462 // Initialize handling of ActiveBlockModifiers
1463 ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
1465 for(std::set<v3s16>::iterator
1466 i = m_active_blocks.m_list.begin();
1467 i != m_active_blocks.m_list.end(); ++i)
1471 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1472 <<") being handled"<<std::endl;*/
1474 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1478 // Set current time as timestamp
1479 block->setTimestampNoChangedFlag(m_game_time);
1481 /* Handle ActiveBlockModifiers */
1482 abmhandler.apply(block);
1485 u32 time_ms = timer.stop(true);
1486 u32 max_time_ms = 200;
1487 if(time_ms > max_time_ms){
1488 warningstream<<"active block modifiers took "
1489 <<time_ms<<"ms (longer than "
1490 <<max_time_ms<<"ms)"<<std::endl;
1491 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1496 Step script environment (run global on_step())
1498 m_script->environment_Step(dtime);
1504 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1505 //TimeTaker timer("Step active objects");
1507 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1509 // This helps the objects to send data at the same time
1510 bool send_recommended = false;
1511 m_send_recommended_timer += dtime;
1512 if(m_send_recommended_timer > getSendRecommendedInterval())
1514 m_send_recommended_timer -= getSendRecommendedInterval();
1515 send_recommended = true;
1518 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1519 i != m_active_objects.end(); ++i) {
1520 ServerActiveObject* obj = i->second;
1521 // Don't step if is to be removed or stored statically
1522 if(obj->m_removed || obj->m_pending_deactivation)
1525 obj->step(dtime, send_recommended);
1526 // Read messages from object
1527 while(!obj->m_messages_out.empty())
1529 m_active_object_messages.push(
1530 obj->m_messages_out.front());
1531 obj->m_messages_out.pop();
1537 Manage active objects
1539 if(m_object_management_interval.step(dtime, 0.5))
1541 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1543 Remove objects that satisfy (m_removed && m_known_by_count==0)
1545 removeRemovedObjects();
1549 Manage particle spawner expiration
1551 if (m_particle_management_interval.step(dtime, 1.0)) {
1552 for (UNORDERED_MAP<u32, float>::iterator i = m_particle_spawners.begin();
1553 i != m_particle_spawners.end(); ) {
1554 //non expiring spawners
1555 if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) {
1561 if (i->second <= 0.f)
1562 m_particle_spawners.erase(i++);
1569 u32 ServerEnvironment::addParticleSpawner(float exptime)
1571 // Timers with lifetime 0 do not expire
1572 float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY;
1575 for (;;) { // look for unused particlespawner id
1577 UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
1578 if (f == m_particle_spawners.end()) {
1579 m_particle_spawners[id] = time;
1586 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1588 ActiveObjectMap::iterator n = m_active_objects.find(id);
1589 return (n != m_active_objects.end() ? n->second : NULL);
1592 bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
1597 return objects.find(id) == objects.end();
1600 u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
1602 //try to reuse id's as late as possible
1603 static u16 last_used_id = 0;
1604 u16 startid = last_used_id;
1608 if(isFreeServerActiveObjectId(last_used_id, objects))
1609 return last_used_id;
1611 if(last_used_id == startid)
1616 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1618 assert(object); // Pre-condition
1620 u16 id = addActiveObjectRaw(object, true, 0);
1625 Finds out what new objects have been added to
1626 inside a radius around a position
1628 void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius,
1630 std::set<u16> ¤t_objects,
1631 std::queue<u16> &added_objects)
1633 f32 radius_f = radius * BS;
1634 f32 player_radius_f = player_radius * BS;
1636 if (player_radius_f < 0)
1637 player_radius_f = 0;
1640 Go through the object list,
1641 - discard m_removed objects,
1642 - discard objects that are too far away,
1643 - discard objects that are found in current_objects.
1644 - add remaining objects to added_objects
1646 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1647 i != m_active_objects.end(); ++i) {
1651 ServerActiveObject *object = i->second;
1655 // Discard if removed or deactivating
1656 if(object->m_removed || object->m_pending_deactivation)
1659 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1660 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1661 // Discard if too far
1662 if (distance_f > player_radius_f && player_radius_f != 0)
1664 } else if (distance_f > radius_f)
1667 // Discard if already on current_objects
1668 std::set<u16>::iterator n;
1669 n = current_objects.find(id);
1670 if(n != current_objects.end())
1672 // Add to added_objects
1673 added_objects.push(id);
1678 Finds out what objects have been removed from
1679 inside a radius around a position
1681 void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius,
1683 std::set<u16> ¤t_objects,
1684 std::queue<u16> &removed_objects)
1686 f32 radius_f = radius * BS;
1687 f32 player_radius_f = player_radius * BS;
1689 if (player_radius_f < 0)
1690 player_radius_f = 0;
1693 Go through current_objects; object is removed if:
1694 - object is not found in m_active_objects (this is actually an
1695 error condition; objects should be set m_removed=true and removed
1696 only after all clients have been informed about removal), or
1697 - object has m_removed=true, or
1698 - object is too far away
1700 for(std::set<u16>::iterator
1701 i = current_objects.begin();
1702 i != current_objects.end(); ++i)
1705 ServerActiveObject *object = getActiveObject(id);
1707 if (object == NULL) {
1708 infostream << "ServerEnvironment::getRemovedActiveObjects():"
1709 << " object in current_objects is NULL" << std::endl;
1710 removed_objects.push(id);
1714 if (object->m_removed || object->m_pending_deactivation) {
1715 removed_objects.push(id);
1719 f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition());
1720 if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
1721 if (distance_f <= player_radius_f || player_radius_f == 0)
1723 } else if (distance_f <= radius_f)
1726 // Object is no longer visible
1727 removed_objects.push(id);
1731 void ServerEnvironment::setStaticForActiveObjectsInBlock(
1732 v3s16 blockpos, bool static_exists, v3s16 static_block)
1734 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1738 for (std::map<u16, StaticObject>::iterator
1739 so_it = block->m_static_objects.m_active.begin();
1740 so_it != block->m_static_objects.m_active.end(); ++so_it) {
1741 // Get the ServerActiveObject counterpart to this StaticObject
1742 ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first);
1743 if (ao_it == m_active_objects.end()) {
1744 // If this ever happens, there must be some kind of nasty bug.
1745 errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
1746 "Object from MapBlock::m_static_objects::m_active not found "
1747 "in m_active_objects";
1751 ServerActiveObject *sao = ao_it->second;
1752 sao->m_static_exists = static_exists;
1753 sao->m_static_block = static_block;
1757 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1759 if(m_active_object_messages.empty())
1760 return ActiveObjectMessage(0);
1762 ActiveObjectMessage message = m_active_object_messages.front();
1763 m_active_object_messages.pop();
1768 ************ Private methods *************
1771 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1772 bool set_changed, u32 dtime_s)
1774 assert(object); // Pre-condition
1775 if(object->getId() == 0){
1776 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1779 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1780 <<"no free ids available"<<std::endl;
1781 if(object->environmentDeletes())
1785 object->setId(new_id);
1788 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1789 <<"supplied with id "<<object->getId()<<std::endl;
1792 if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
1793 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1794 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1795 if(object->environmentDeletes())
1800 if (objectpos_over_limit(object->getBasePosition())) {
1801 v3f p = object->getBasePosition();
1802 errorstream << "ServerEnvironment::addActiveObjectRaw(): "
1803 << "object position (" << p.X << "," << p.Y << "," << p.Z
1804 << ") outside maximum range" << std::endl;
1805 if (object->environmentDeletes())
1810 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1811 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1813 m_active_objects[object->getId()] = object;
1815 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1816 <<"Added id="<<object->getId()<<"; there are now "
1817 <<m_active_objects.size()<<" active objects."
1820 // Register reference in scripting api (must be done before post-init)
1821 m_script->addObjectReference(object);
1822 // Post-initialize object
1823 object->addedToEnvironment(dtime_s);
1825 // Add static data to block
1826 if(object->isStaticAllowed())
1828 // Add static object to active static list of the block
1829 v3f objectpos = object->getBasePosition();
1830 std::string staticdata = object->getStaticData();
1831 StaticObject s_obj(object->getType(), objectpos, staticdata);
1832 // Add to the block where the object is located in
1833 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1834 MapBlock *block = m_map->emergeBlock(blockpos);
1836 block->m_static_objects.m_active[object->getId()] = s_obj;
1837 object->m_static_exists = true;
1838 object->m_static_block = blockpos;
1841 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1842 MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
1844 v3s16 p = floatToInt(objectpos, BS);
1845 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1846 <<"could not emerge block for storing id="<<object->getId()
1847 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1851 return object->getId();
1855 Remove objects that satisfy (m_removed && m_known_by_count==0)
1857 void ServerEnvironment::removeRemovedObjects()
1859 std::vector<u16> objects_to_remove;
1860 for(ActiveObjectMap::iterator i = m_active_objects.begin();
1861 i != m_active_objects.end(); ++i) {
1863 ServerActiveObject* obj = i->second;
1864 // This shouldn't happen but check it
1867 infostream<<"NULL object found in ServerEnvironment"
1868 <<" while finding removed objects. id="<<id<<std::endl;
1869 // Id to be removed from m_active_objects
1870 objects_to_remove.push_back(id);
1875 We will delete objects that are marked as removed or thatare
1876 waiting for deletion after deactivation
1878 if (!obj->m_removed && !obj->m_pending_deactivation)
1882 Delete static data from block if is marked as removed
1884 if(obj->m_static_exists && obj->m_removed)
1886 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1888 block->m_static_objects.remove(id);
1889 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1890 MOD_REASON_REMOVE_OBJECTS_REMOVE);
1891 obj->m_static_exists = false;
1893 infostream<<"Failed to emerge block from which an object to "
1894 <<"be removed was loaded from. id="<<id<<std::endl;
1898 // If m_known_by_count > 0, don't actually remove. On some future
1899 // invocation this will be 0, which is when removal will continue.
1900 if(obj->m_known_by_count > 0)
1904 Move static data from active to stored if not marked as removed
1906 if(obj->m_static_exists && !obj->m_removed){
1907 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1909 std::map<u16, StaticObject>::iterator i =
1910 block->m_static_objects.m_active.find(id);
1911 if(i != block->m_static_objects.m_active.end()){
1912 block->m_static_objects.m_stored.push_back(i->second);
1913 block->m_static_objects.m_active.erase(id);
1914 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1915 MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
1918 infostream<<"Failed to emerge block from which an object to "
1919 <<"be deactivated was loaded from. id="<<id<<std::endl;
1923 // Tell the object about removal
1924 obj->removingFromEnvironment();
1925 // Deregister in scripting api
1926 m_script->removeObjectReference(obj);
1929 if(obj->environmentDeletes())
1932 // Id to be removed from m_active_objects
1933 objects_to_remove.push_back(id);
1935 // Remove references from m_active_objects
1936 for(std::vector<u16>::iterator i = objects_to_remove.begin();
1937 i != objects_to_remove.end(); ++i) {
1938 m_active_objects.erase(*i);
1942 static void print_hexdump(std::ostream &o, const std::string &data)
1944 const int linelength = 16;
1945 for(int l=0; ; l++){
1946 int i0 = linelength * l;
1947 bool at_end = false;
1948 int thislinelength = linelength;
1949 if(i0 + thislinelength > (int)data.size()){
1950 thislinelength = data.size() - i0;
1953 for(int di=0; di<linelength; di++){
1956 if(di<thislinelength)
1957 snprintf(buf, 4, "%.2x ", data[i]);
1959 snprintf(buf, 4, " ");
1963 for(int di=0; di<thislinelength; di++){
1977 Convert stored objects from blocks near the players to active.
1979 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1984 // Ignore if no stored objects (to not set changed flag)
1985 if(block->m_static_objects.m_stored.empty())
1988 verbosestream<<"ServerEnvironment::activateObjects(): "
1989 <<"activating objects of block "<<PP(block->getPos())
1990 <<" ("<<block->m_static_objects.m_stored.size()
1991 <<" objects)"<<std::endl;
1992 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1994 errorstream<<"suspiciously large amount of objects detected: "
1995 <<block->m_static_objects.m_stored.size()<<" in "
1996 <<PP(block->getPos())
1997 <<"; removing all of them."<<std::endl;
1998 // Clear stored list
1999 block->m_static_objects.m_stored.clear();
2000 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2001 MOD_REASON_TOO_MANY_OBJECTS);
2005 // Activate stored objects
2006 std::vector<StaticObject> new_stored;
2007 for (std::vector<StaticObject>::iterator
2008 i = block->m_static_objects.m_stored.begin();
2009 i != block->m_static_objects.m_stored.end(); ++i) {
2010 StaticObject &s_obj = *i;
2012 // Create an active object from the data
2013 ServerActiveObject *obj = ServerActiveObject::create
2014 ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
2015 // If couldn't create object, store static data back.
2017 errorstream<<"ServerEnvironment::activateObjects(): "
2018 <<"failed to create active object from static object "
2019 <<"in block "<<PP(s_obj.pos/BS)
2020 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
2021 print_hexdump(verbosestream, s_obj.data);
2023 new_stored.push_back(s_obj);
2026 verbosestream<<"ServerEnvironment::activateObjects(): "
2027 <<"activated static object pos="<<PP(s_obj.pos/BS)
2028 <<" type="<<(int)s_obj.type<<std::endl;
2029 // This will also add the object to the active static list
2030 addActiveObjectRaw(obj, false, dtime_s);
2032 // Clear stored list
2033 block->m_static_objects.m_stored.clear();
2034 // Add leftover failed stuff to stored list
2035 for(std::vector<StaticObject>::iterator
2036 i = new_stored.begin();
2037 i != new_stored.end(); ++i) {
2038 StaticObject &s_obj = *i;
2039 block->m_static_objects.m_stored.push_back(s_obj);
2042 // Turn the active counterparts of activated objects not pending for
2044 for(std::map<u16, StaticObject>::iterator
2045 i = block->m_static_objects.m_active.begin();
2046 i != block->m_static_objects.m_active.end(); ++i)
2049 ServerActiveObject *object = getActiveObject(id);
2051 object->m_pending_deactivation = false;
2055 Note: Block hasn't really been modified here.
2056 The objects have just been activated and moved from the stored
2057 static list to the active static list.
2058 As such, the block is essentially the same.
2059 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
2060 Otherwise there would be a huge amount of unnecessary I/O.
2065 Convert objects that are not standing inside active blocks to static.
2067 If m_known_by_count != 0, active object is not deleted, but static
2068 data is still updated.
2070 If force_delete is set, active object is deleted nevertheless. It
2071 shall only be set so in the destructor of the environment.
2073 If block wasn't generated (not in memory or on disk),
2075 void ServerEnvironment::deactivateFarObjects(bool force_delete)
2077 std::vector<u16> objects_to_remove;
2078 for(ActiveObjectMap::iterator i = m_active_objects.begin();
2079 i != m_active_objects.end(); ++i) {
2080 ServerActiveObject* obj = i->second;
2083 // Do not deactivate if static data creation not allowed
2084 if(!force_delete && !obj->isStaticAllowed())
2087 // If pending deactivation, let removeRemovedObjects() do it
2088 if(!force_delete && obj->m_pending_deactivation)
2092 v3f objectpos = obj->getBasePosition();
2094 // The block in which the object resides in
2095 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
2097 // If object's static data is stored in a deactivated block and object
2098 // is actually located in an active block, re-save to the block in
2099 // which the object is actually located in.
2101 obj->m_static_exists &&
2102 !m_active_blocks.contains(obj->m_static_block) &&
2103 m_active_blocks.contains(blockpos_o))
2105 v3s16 old_static_block = obj->m_static_block;
2107 // Save to block where object is located
2108 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
2110 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2111 <<"Could not save object id="<<id
2112 <<" to it's current block "<<PP(blockpos_o)
2116 std::string staticdata_new = obj->getStaticData();
2117 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2118 block->m_static_objects.insert(id, s_obj);
2119 obj->m_static_block = blockpos_o;
2120 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2121 MOD_REASON_STATIC_DATA_ADDED);
2123 // Delete from block where object was located
2124 block = m_map->emergeBlock(old_static_block, false);
2126 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2127 <<"Could not delete object id="<<id
2128 <<" from it's previous block "<<PP(old_static_block)
2132 block->m_static_objects.remove(id);
2133 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2134 MOD_REASON_STATIC_DATA_REMOVED);
2138 // If block is active, don't remove
2139 if(!force_delete && m_active_blocks.contains(blockpos_o))
2142 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2143 <<"deactivating object id="<<id<<" on inactive block "
2144 <<PP(blockpos_o)<<std::endl;
2146 // If known by some client, don't immediately delete.
2147 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
2150 Update the static data
2153 if(obj->isStaticAllowed())
2155 // Create new static object
2156 std::string staticdata_new = obj->getStaticData();
2157 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
2159 bool stays_in_same_block = false;
2160 bool data_changed = true;
2162 if (obj->m_static_exists) {
2163 if (obj->m_static_block == blockpos_o)
2164 stays_in_same_block = true;
2166 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2169 std::map<u16, StaticObject>::iterator n =
2170 block->m_static_objects.m_active.find(id);
2171 if (n != block->m_static_objects.m_active.end()) {
2172 StaticObject static_old = n->second;
2174 float save_movem = obj->getMinimumSavedMovement();
2176 if (static_old.data == staticdata_new &&
2177 (static_old.pos - objectpos).getLength() < save_movem)
2178 data_changed = false;
2180 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
2181 <<"id="<<id<<" m_static_exists=true but "
2182 <<"static data doesn't actually exist in "
2183 <<PP(obj->m_static_block)<<std::endl;
2188 bool shall_be_written = (!stays_in_same_block || data_changed);
2190 // Delete old static object
2191 if(obj->m_static_exists)
2193 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
2196 block->m_static_objects.remove(id);
2197 obj->m_static_exists = false;
2198 // Only mark block as modified if data changed considerably
2199 if(shall_be_written)
2200 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2201 MOD_REASON_STATIC_DATA_CHANGED);
2205 // Add to the block where the object is located in
2206 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
2207 // Get or generate the block
2208 MapBlock *block = NULL;
2210 block = m_map->emergeBlock(blockpos);
2211 } catch(InvalidPositionException &e){
2212 // Handled via NULL pointer
2213 // NOTE: emergeBlock's failure is usually determined by it
2214 // actually returning NULL
2219 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
2220 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
2221 <<" statically but block "<<PP(blockpos)
2222 <<" already contains "
2223 <<block->m_static_objects.m_stored.size()
2225 <<" Forcing delete."<<std::endl;
2226 force_delete = true;
2228 // If static counterpart already exists in target block,
2230 // This shouldn't happen because the object is removed from
2231 // the previous block before this according to
2232 // obj->m_static_block, but happens rarely for some unknown
2233 // reason. Unsuccessful attempts have been made to find
2235 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
2236 warningstream<<"ServerEnv: Performing hack #83274"
2238 block->m_static_objects.remove(id);
2240 // Store static data
2241 u16 store_id = pending_delete ? id : 0;
2242 block->m_static_objects.insert(store_id, s_obj);
2244 // Only mark block as modified if data changed considerably
2245 if(shall_be_written)
2246 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2247 MOD_REASON_STATIC_DATA_CHANGED);
2249 obj->m_static_exists = true;
2250 obj->m_static_block = block->getPos();
2255 v3s16 p = floatToInt(objectpos, BS);
2256 errorstream<<"ServerEnv: Could not find or generate "
2257 <<"a block for storing id="<<obj->getId()
2258 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2265 If known by some client, set pending deactivation.
2266 Otherwise delete it immediately.
2269 if(pending_delete && !force_delete)
2271 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2272 <<"object id="<<id<<" is known by clients"
2273 <<"; not deleting yet"<<std::endl;
2275 obj->m_pending_deactivation = true;
2279 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2280 <<"object id="<<id<<" is not known by clients"
2281 <<"; deleting"<<std::endl;
2283 // Tell the object about removal
2284 obj->removingFromEnvironment();
2285 // Deregister in scripting api
2286 m_script->removeObjectReference(obj);
2288 // Delete active object
2289 if(obj->environmentDeletes())
2291 // Id to be removed from m_active_objects
2292 objects_to_remove.push_back(id);
2295 // Remove references from m_active_objects
2296 for(std::vector<u16>::iterator i = objects_to_remove.begin();
2297 i != objects_to_remove.end(); ++i) {
2298 m_active_objects.erase(*i);
2304 #include "clientsimpleobject.h"
2310 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2311 ITextureSource *texturesource, IGameDef *gamedef,
2312 IrrlichtDevice *irr):
2315 m_texturesource(texturesource),
2320 memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
2323 ClientEnvironment::~ClientEnvironment()
2325 // delete active objects
2326 for(std::map<u16, ClientActiveObject*>::iterator
2327 i = m_active_objects.begin();
2328 i != m_active_objects.end(); ++i)
2333 for(std::vector<ClientSimpleObject*>::iterator
2334 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
2342 Map & ClientEnvironment::getMap()
2347 ClientMap & ClientEnvironment::getClientMap()
2352 void ClientEnvironment::addPlayer(Player *player)
2354 DSTACK(FUNCTION_NAME);
2356 It is a failure if player is local and there already is a local
2359 FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
2360 "Player is local but there is already a local player");
2362 Environment::addPlayer(player);
2365 LocalPlayer * ClientEnvironment::getLocalPlayer()
2367 for(std::vector<Player*>::iterator i = m_players.begin();
2368 i != m_players.end(); ++i) {
2369 Player *player = *i;
2370 if(player->isLocal())
2371 return (LocalPlayer*)player;
2376 void ClientEnvironment::step(float dtime)
2378 DSTACK(FUNCTION_NAME);
2380 /* Step time of day */
2381 stepTimeOfDay(dtime);
2383 // Get some settings
2384 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2385 bool free_move = fly_allowed && g_settings->getBool("free_move");
2388 LocalPlayer *lplayer = getLocalPlayer();
2390 // collision info queue
2391 std::vector<CollisionInfo> player_collisions;
2394 Get the speed the player is going
2396 bool is_climbing = lplayer->is_climbing;
2398 f32 player_speed = lplayer->getSpeed().getLength();
2401 Maximum position increment
2403 //f32 position_max_increment = 0.05*BS;
2404 f32 position_max_increment = 0.1*BS;
2406 // Maximum time increment (for collision detection etc)
2407 // time = distance / speed
2408 f32 dtime_max_increment = 1;
2409 if(player_speed > 0.001)
2410 dtime_max_increment = position_max_increment / player_speed;
2412 // Maximum time increment is 10ms or lower
2413 if(dtime_max_increment > 0.01)
2414 dtime_max_increment = 0.01;
2416 // Don't allow overly huge dtime
2420 f32 dtime_downcount = dtime;
2423 Stuff that has a maximum time increment
2432 if(dtime_downcount > dtime_max_increment)
2434 dtime_part = dtime_max_increment;
2435 dtime_downcount -= dtime_part;
2439 dtime_part = dtime_downcount;
2441 Setting this to 0 (no -=dtime_part) disables an infinite loop
2442 when dtime_part is so small that dtime_downcount -= dtime_part
2445 dtime_downcount = 0;
2454 if(free_move == false && is_climbing == false)
2457 v3f speed = lplayer->getSpeed();
2458 if(lplayer->in_liquid == false)
2459 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2461 // Liquid floating / sinking
2462 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2463 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2465 // Liquid resistance
2466 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2468 // How much the node's viscosity blocks movement, ranges between 0 and 1
2469 // Should match the scale at which viscosity increase affects other liquid attributes
2470 const f32 viscosity_factor = 0.3;
2472 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2473 f32 dl = d_wanted.getLength();
2474 if(dl > lplayer->movement_liquid_fluidity_smooth)
2475 dl = lplayer->movement_liquid_fluidity_smooth;
2476 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2478 v3f d = d_wanted.normalize() * dl;
2482 lplayer->setSpeed(speed);
2487 This also does collision detection.
2489 lplayer->move(dtime_part, this, position_max_increment,
2490 &player_collisions);
2493 while(dtime_downcount > 0.001);
2495 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2497 for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
2498 i != player_collisions.end(); ++i) {
2499 CollisionInfo &info = *i;
2500 v3f speed_diff = info.new_speed - info.old_speed;;
2501 // Handle only fall damage
2502 // (because otherwise walking against something in fast_move kills you)
2503 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2505 // Get rid of other components
2508 f32 pre_factor = 1; // 1 hp per node/s
2509 f32 tolerance = BS*14; // 5 without damage
2510 f32 post_factor = 1; // 1 hp per node/s
2511 if(info.type == COLLISION_NODE)
2513 const ContentFeatures &f = m_gamedef->ndef()->
2514 get(m_map->getNodeNoEx(info.node_p));
2515 // Determine fall damage multiplier
2516 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2517 pre_factor = 1.0 + (float)addp/100.0;
2519 float speed = pre_factor * speed_diff.getLength();
2520 if(speed > tolerance)
2522 f32 damage_f = (speed - tolerance)/BS * post_factor;
2523 u16 damage = (u16)(damage_f+0.5);
2525 damageLocalPlayer(damage, true);
2526 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2527 m_gamedef->event()->put(e);
2533 A quick draft of lava damage
2535 if(m_lava_hurt_interval.step(dtime, 1.0))
2537 v3f pf = lplayer->getPosition();
2539 // Feet, middle and head
2540 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2541 MapNode n1 = m_map->getNodeNoEx(p1);
2542 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2543 MapNode n2 = m_map->getNodeNoEx(p2);
2544 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2545 MapNode n3 = m_map->getNodeNoEx(p3);
2547 u32 damage_per_second = 0;
2548 damage_per_second = MYMAX(damage_per_second,
2549 m_gamedef->ndef()->get(n1).damage_per_second);
2550 damage_per_second = MYMAX(damage_per_second,
2551 m_gamedef->ndef()->get(n2).damage_per_second);
2552 damage_per_second = MYMAX(damage_per_second,
2553 m_gamedef->ndef()->get(n3).damage_per_second);
2555 if(damage_per_second != 0)
2557 damageLocalPlayer(damage_per_second, true);
2564 if(m_drowning_interval.step(dtime, 2.0))
2566 v3f pf = lplayer->getPosition();
2569 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2570 MapNode n = m_map->getNodeNoEx(p);
2571 ContentFeatures c = m_gamedef->ndef()->get(n);
2572 u8 drowning_damage = c.drowning;
2573 if(drowning_damage > 0 && lplayer->hp > 0){
2574 u16 breath = lplayer->getBreath();
2581 lplayer->setBreath(breath);
2582 updateLocalPlayerBreath(breath);
2585 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2586 damageLocalPlayer(drowning_damage, true);
2589 if(m_breathing_interval.step(dtime, 0.5))
2591 v3f pf = lplayer->getPosition();
2594 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2595 MapNode n = m_map->getNodeNoEx(p);
2596 ContentFeatures c = m_gamedef->ndef()->get(n);
2598 lplayer->setBreath(11);
2600 else if(c.drowning == 0){
2601 u16 breath = lplayer->getBreath();
2604 lplayer->setBreath(breath);
2605 updateLocalPlayerBreath(breath);
2611 Stuff that can be done in an arbitarily large dtime
2613 for(std::vector<Player*>::iterator i = m_players.begin();
2614 i != m_players.end(); ++i) {
2615 Player *player = *i;
2618 Handle non-local players
2620 if(player->isLocal() == false) {
2622 player->move(dtime, this, 100*BS);
2627 // Update lighting on local player (used for wield item)
2628 u32 day_night_ratio = getDayNightRatio();
2632 // On InvalidPositionException, use this as default
2633 // (day: LIGHT_SUN, night: 0)
2634 MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
2636 v3s16 p = lplayer->getLightPosition();
2637 node_at_lplayer = m_map->getNodeNoEx(p);
2639 u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
2640 u8 day = light & 0xff;
2641 u8 night = (light >> 8) & 0xff;
2642 finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
2646 Step active objects and update lighting of them
2649 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2650 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2651 for(std::map<u16, ClientActiveObject*>::iterator
2652 i = m_active_objects.begin();
2653 i != m_active_objects.end(); ++i)
2655 ClientActiveObject* obj = i->second;
2657 obj->step(dtime, this);
2666 v3s16 p = obj->getLightPosition();
2667 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2669 light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
2671 light = blend_light(day_night_ratio, LIGHT_SUN, 0);
2673 obj->updateLight(light);
2678 Step and handle simple objects
2680 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2681 for(std::vector<ClientSimpleObject*>::iterator
2682 i = m_simple_objects.begin(); i != m_simple_objects.end();) {
2683 std::vector<ClientSimpleObject*>::iterator cur = i;
2684 ClientSimpleObject *simple = *cur;
2686 simple->step(dtime);
2687 if(simple->m_to_be_removed) {
2689 i = m_simple_objects.erase(cur);
2697 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2699 m_simple_objects.push_back(simple);
2702 GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
2704 ClientActiveObject *obj = getActiveObject(id);
2705 if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
2706 return (GenericCAO*) obj;
2711 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2713 std::map<u16, ClientActiveObject*>::iterator n;
2714 n = m_active_objects.find(id);
2715 if(n == m_active_objects.end())
2720 bool isFreeClientActiveObjectId(u16 id,
2721 std::map<u16, ClientActiveObject*> &objects)
2726 return objects.find(id) == objects.end();
2729 u16 getFreeClientActiveObjectId(
2730 std::map<u16, ClientActiveObject*> &objects)
2732 //try to reuse id's as late as possible
2733 static u16 last_used_id = 0;
2734 u16 startid = last_used_id;
2738 if(isFreeClientActiveObjectId(last_used_id, objects))
2739 return last_used_id;
2741 if(last_used_id == startid)
2746 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2748 assert(object); // Pre-condition
2749 if(object->getId() == 0)
2751 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2754 infostream<<"ClientEnvironment::addActiveObject(): "
2755 <<"no free ids available"<<std::endl;
2759 object->setId(new_id);
2761 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2763 infostream<<"ClientEnvironment::addActiveObject(): "
2764 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2768 infostream<<"ClientEnvironment::addActiveObject(): "
2769 <<"added (id="<<object->getId()<<")"<<std::endl;
2770 m_active_objects[object->getId()] = object;
2771 object->addToScene(m_smgr, m_texturesource, m_irr);
2772 { // Update lighting immediately
2777 v3s16 p = object->getLightPosition();
2778 MapNode n = m_map->getNodeNoEx(p, &pos_ok);
2780 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2782 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2784 object->updateLight(light);
2786 return object->getId();
2789 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2790 const std::string &init_data)
2792 ClientActiveObject* obj =
2793 ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
2796 infostream<<"ClientEnvironment::addActiveObject(): "
2797 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2806 obj->initialize(init_data);
2808 catch(SerializationError &e)
2810 errorstream<<"ClientEnvironment::addActiveObject():"
2811 <<" id="<<id<<" type="<<type
2812 <<": SerializationError in initialize(): "
2814 <<": init_data="<<serializeJsonString(init_data)
2818 addActiveObject(obj);
2821 void ClientEnvironment::removeActiveObject(u16 id)
2823 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2824 <<"id="<<id<<std::endl;
2825 ClientActiveObject* obj = getActiveObject(id);
2828 infostream<<"ClientEnvironment::removeActiveObject(): "
2829 <<"id="<<id<<" not found"<<std::endl;
2832 obj->removeFromScene(true);
2834 m_active_objects.erase(id);
2837 void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
2839 ClientActiveObject *obj = getActiveObject(id);
2841 infostream << "ClientEnvironment::processActiveObjectMessage():"
2842 << " got message for id=" << id << ", which doesn't exist."
2848 obj->processMessage(data);
2849 } catch (SerializationError &e) {
2850 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2851 << " id=" << id << " type=" << obj->getType()
2852 << " SerializationError in processMessage(): " << e.what()
2858 Callbacks for activeobjects
2861 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2863 LocalPlayer *lplayer = getLocalPlayer();
2867 if (lplayer->hp > damage)
2868 lplayer->hp -= damage;
2873 ClientEnvEvent event;
2874 event.type = CEE_PLAYER_DAMAGE;
2875 event.player_damage.amount = damage;
2876 event.player_damage.send_to_server = handle_hp;
2877 m_client_event_queue.push(event);
2880 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2882 ClientEnvEvent event;
2883 event.type = CEE_PLAYER_BREATH;
2884 event.player_breath.amount = breath;
2885 m_client_event_queue.push(event);
2889 Client likes to call these
2892 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2893 std::vector<DistanceSortedActiveObject> &dest)
2895 for(std::map<u16, ClientActiveObject*>::iterator
2896 i = m_active_objects.begin();
2897 i != m_active_objects.end(); ++i)
2899 ClientActiveObject* obj = i->second;
2901 f32 d = (obj->getPosition() - origin).getLength();
2906 DistanceSortedActiveObject dso(obj, d);
2908 dest.push_back(dso);
2912 ClientEnvEvent ClientEnvironment::getClientEvent()
2914 ClientEnvEvent event;
2915 if(m_client_event_queue.empty())
2916 event.type = CEE_NONE;
2918 event = m_client_event_queue.front();
2919 m_client_event_queue.pop();
2924 #endif // #ifndef SERVER