X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fenvironment.cpp;h=eea2646997c466989bfed37a05ae7ad43fb51e7a;hb=d1df09841d0eac7a88f638676b80ec848522cca5;hp=cd87839847236f28b310f8c10007e7a3b7a87a52;hpb=88d43af276a4ed85500863b427bf370d8eca2095;p=dragonfireclient.git diff --git a/src/environment.cpp b/src/environment.cpp index cd8783984..eea264699 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -17,9 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include -#include -#include +#include #include "environment.h" #include "filesys.h" #include "porting.h" @@ -28,47 +26,59 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "serverobject.h" #include "content_sao.h" -#include "mapgen.h" #include "settings.h" #include "log.h" #include "profiler.h" -#include "cpp_api/scriptapi.h" +#include "scripting_game.h" #include "nodedef.h" #include "nodemetadata.h" -#include "main.h" // For g_settings, g_profiler #include "gamedef.h" #ifndef SERVER #include "clientmap.h" #include "localplayer.h" +#include "mapblock_mesh.h" #include "event.h" #endif +#include "server.h" #include "daynightratio.h" #include "map.h" +#include "emerge.h" #include "util/serialize.h" +#include "threading/mutex_auto_lock.h" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" +#define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" + +// A number that is much smaller than the timeout for particle spawners should/could ever be +#define PARTICLE_SPAWNER_NO_EXPIRY -1024.f + Environment::Environment(): + m_time_of_day_speed(0), m_time_of_day(9000), m_time_of_day_f(9000./24000), - m_time_of_day_speed(0), - m_time_counter(0) -{ + m_time_conversion_skew(0.0f), + m_enable_day_night_ratio_override(false), + m_day_night_ratio_override(0.0f) +{ + m_cache_enable_shaders = g_settings->getBool("enable_shaders"); + m_cache_active_block_mgmt_interval = g_settings->getFloat("active_block_mgmt_interval"); + m_cache_abm_interval = g_settings->getFloat("abm_interval"); + m_cache_nodetimer_interval = g_settings->getFloat("nodetimer_interval"); } Environment::~Environment() { // Deallocate players - for(std::list::iterator i = m_players.begin(); - i != m_players.end(); ++i) - { + for(std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { delete (*i); } } void Environment::addPlayer(Player *player) { - DSTACK(__FUNCTION_NAME); + DSTACK(FUNCTION_NAME); /* Check that peer_ids are unique. Also check that names are unique. @@ -76,37 +86,29 @@ void Environment::addPlayer(Player *player) */ // If peer id is non-zero, it has to be unique. if(player->peer_id != 0) - assert(getPlayer(player->peer_id) == NULL); + FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique"); // Name has to be unique. - assert(getPlayer(player->getName()) == NULL); + FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique"); // Add. m_players.push_back(player); } -void Environment::removePlayer(u16 peer_id) +void Environment::removePlayer(Player* player) { - DSTACK(__FUNCTION_NAME); -re_search: - for(std::list::iterator i = m_players.begin(); - i != m_players.end(); ++i) - { - Player *player = *i; - if(player->peer_id != peer_id) - continue; - - delete player; - m_players.erase(i); - // See if there is an another one - // (shouldn't be, but just to be sure) - goto re_search; + for (std::vector::iterator it = m_players.begin(); + it != m_players.end(); ++it) { + if ((*it) == player) { + delete *it; + m_players.erase(it); + return; + } } } Player * Environment::getPlayer(u16 peer_id) { - for(std::list::iterator i = m_players.begin(); - i != m_players.end(); ++i) - { + for(std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { Player *player = *i; if(player->peer_id == peer_id) return player; @@ -116,9 +118,8 @@ Player * Environment::getPlayer(u16 peer_id) Player * Environment::getPlayer(const char *name) { - for(std::list::iterator i = m_players.begin(); - i != m_players.end(); ++i) - { + for(std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { Player *player = *i; if(strcmp(player->getName(), name) == 0) return player; @@ -128,15 +129,13 @@ Player * Environment::getPlayer(const char *name) Player * Environment::getRandomConnectedPlayer() { - std::list connected_players = getPlayers(true); + std::vector connected_players = getPlayers(true); u32 chosen_one = myrand() % connected_players.size(); u32 j = 0; - for(std::list::iterator + for(std::vector::iterator i = connected_players.begin(); - i != connected_players.end(); ++i) - { - if(j == chosen_one) - { + i != connected_players.end(); ++i) { + if(j == chosen_one) { Player *player = *i; return player; } @@ -147,17 +146,15 @@ Player * Environment::getRandomConnectedPlayer() Player * Environment::getNearestConnectedPlayer(v3f pos) { - std::list connected_players = getPlayers(true); + std::vector connected_players = getPlayers(true); f32 nearest_d = 0; Player *nearest_player = NULL; - for(std::list::iterator + for(std::vector::iterator i = connected_players.begin(); - i != connected_players.end(); ++i) - { + i != connected_players.end(); ++i) { Player *player = *i; f32 d = player->getPosition().getDistanceFrom(pos); - if(d < nearest_d || nearest_player == NULL) - { + if(d < nearest_d || nearest_player == NULL) { nearest_d = d; nearest_player = player; } @@ -165,22 +162,20 @@ Player * Environment::getNearestConnectedPlayer(v3f pos) return nearest_player; } -std::list Environment::getPlayers() +std::vector Environment::getPlayers() { return m_players; } -std::list Environment::getPlayers(bool ignore_disconnected) +std::vector Environment::getPlayers(bool ignore_disconnected) { - std::list newlist; - for(std::list::iterator + std::vector newlist; + for(std::vector::iterator i = m_players.begin(); - i != m_players.end(); ++i) - { + i != m_players.end(); ++i) { Player *player = *i; - - if(ignore_disconnected) - { + + if(ignore_disconnected) { // Ignore disconnected players if(player->peer_id == 0) continue; @@ -191,47 +186,93 @@ std::list Environment::getPlayers(bool ignore_disconnected) return newlist; } -void Environment::printPlayers(std::ostream &o) +u32 Environment::getDayNightRatio() { - o<<"Players in environment:"<::iterator i = m_players.begin(); - i != m_players.end(); i++) - { - Player *player = *i; - o<<"Player peer_id="<peer_id<m_time_lock); + if (m_enable_day_night_ratio_override) + return m_day_night_ratio_override; + return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders); } -u32 Environment::getDayNightRatio() +void Environment::setTimeOfDaySpeed(float speed) +{ + m_time_of_day_speed = speed; +} + +float Environment::getTimeOfDaySpeed() +{ + return m_time_of_day_speed; +} + +void Environment::setDayNightRatioOverride(bool enable, u32 value) +{ + MutexAutoLock lock(this->m_time_lock); + m_enable_day_night_ratio_override = enable; + m_day_night_ratio_override = value; +} + +void Environment::setTimeOfDay(u32 time) +{ + MutexAutoLock lock(this->m_time_lock); + if (m_time_of_day > time) + m_day_count++; + m_time_of_day = time; + m_time_of_day_f = (float)time / 24000.0; +} + +u32 Environment::getTimeOfDay() +{ + MutexAutoLock lock(this->m_time_lock); + return m_time_of_day; +} + +float Environment::getTimeOfDayF() { - bool smooth = g_settings->getBool("enable_shaders"); - return time_to_daynight_ratio(m_time_of_day_f*24000, smooth); + MutexAutoLock lock(this->m_time_lock); + return m_time_of_day_f; } void Environment::stepTimeOfDay(float dtime) { - m_time_counter += dtime; - f32 speed = m_time_of_day_speed * 24000./(24.*3600); - u32 units = (u32)(m_time_counter*speed); - m_time_counter -= (f32)units / speed; + MutexAutoLock lock(this->m_time_lock); + + // Cached in order to prevent the two reads we do to give + // different results (can be written by code not under the lock) + f32 cached_time_of_day_speed = m_time_of_day_speed; + + f32 speed = cached_time_of_day_speed * 24000. / (24. * 3600); + m_time_conversion_skew += dtime; + u32 units = (u32)(m_time_conversion_skew * speed); bool sync_f = false; - if(units > 0){ + if (units > 0) { // Sync at overflow - if(m_time_of_day + units >= 24000) + if (m_time_of_day + units >= 24000) { sync_f = true; + m_day_count++; + } m_time_of_day = (m_time_of_day + units) % 24000; - if(sync_f) + if (sync_f) m_time_of_day_f = (float)m_time_of_day / 24000.0; } - if(!sync_f){ - m_time_of_day_f += m_time_of_day_speed/24/3600*dtime; - if(m_time_of_day_f > 1.0) + if (speed > 0) { + m_time_conversion_skew -= (f32)units / speed; + } + if (!sync_f) { + m_time_of_day_f += cached_time_of_day_speed / 24 / 3600 * dtime; + if (m_time_of_day_f > 1.0) m_time_of_day_f -= 1.0; - if(m_time_of_day_f < 0.0) + if (m_time_of_day_f < 0.0) m_time_of_day_f += 1.0; } } +u32 Environment::getDayCount() +{ + // Atomic counter + return m_day_count; +} + + /* ABMWithState */ @@ -248,6 +289,223 @@ ABMWithState::ABMWithState(ActiveBlockModifier *abm_): timer = myrand_range(minval, maxval); } +/* + LBMManager +*/ + +void LBMContentMapping::deleteContents() +{ + for (std::vector::iterator it = lbm_list.begin(); + it != lbm_list.end(); ++it) { + delete *it; + } +} + +void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef) +{ + // Add the lbm_def to the LBMContentMapping. + // Unknown names get added to the global NameIdMapping. + INodeDefManager *nodedef = gamedef->ndef(); + + lbm_list.push_back(lbm_def); + + for (std::set::const_iterator it = lbm_def->trigger_contents.begin(); + it != lbm_def->trigger_contents.end(); ++it) { + std::set c_ids; + bool found = nodedef->getIds(*it, c_ids); + if (!found) { + content_t c_id = gamedef->allocateUnknownNodeId(*it); + if (c_id == CONTENT_IGNORE) { + // Seems it can't be allocated. + warningstream << "Could not internalize node name \"" << *it + << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl; + continue; + } + c_ids.insert(c_id); + } + + for (std::set::const_iterator iit = + c_ids.begin(); iit != c_ids.end(); ++iit) { + content_t c_id = *iit; + map[c_id].push_back(lbm_def); + } + } +} + +const std::vector * + LBMContentMapping::lookup(content_t c) const +{ + container_map::const_iterator it = map.find(c); + if (it == map.end()) + return NULL; + // This first dereferences the iterator, returning + // a std::vector + // reference, then we convert it to a pointer. + return &(it->second); +} + +LBMManager::~LBMManager() +{ + for (std::map::iterator it = + m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) { + delete it->second; + } + for (lbm_lookup_map::iterator it = m_lbm_lookup.begin(); + it != m_lbm_lookup.end(); ++it) { + (it->second).deleteContents(); + } +} + +void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def) +{ + // Precondition, in query mode the map isn't used anymore + FATAL_ERROR_IF(m_query_mode == true, + "attempted to modify LBMManager in query mode"); + + if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) { + throw ModError("Error adding LBM \"" + lbm_def->name + + "\": Does not follow naming conventions: " + "Only chararacters [a-z0-9_:] are allowed."); + } + + m_lbm_defs[lbm_def->name] = lbm_def; +} + +void LBMManager::loadIntroductionTimes(const std::string ×, + IGameDef *gamedef, u32 now) +{ + m_query_mode = true; + + // name -> time map. + // Storing it in a map first instead of + // handling the stuff directly in the loop + // removes all duplicate entries. + // TODO make this std::unordered_map + std::map introduction_times; + + /* + The introduction times string consists of name~time entries, + with each entry terminated by a semicolon. The time is decimal. + */ + + size_t idx = 0; + size_t idx_new; + while ((idx_new = times.find(";", idx)) != std::string::npos) { + std::string entry = times.substr(idx, idx_new - idx); + std::vector components = str_split(entry, '~'); + if (components.size() != 2) + throw SerializationError("Introduction times entry \"" + + entry + "\" requires exactly one '~'!"); + const std::string &name = components[0]; + u32 time = from_string(components[1]); + introduction_times[name] = time; + idx = idx_new + 1; + } + + // Put stuff from introduction_times into m_lbm_lookup + for (std::map::const_iterator it = introduction_times.begin(); + it != introduction_times.end(); ++it) { + const std::string &name = it->first; + u32 time = it->second; + + std::map::iterator def_it = + m_lbm_defs.find(name); + if (def_it == m_lbm_defs.end()) { + // This seems to be an LBM entry for + // an LBM we haven't loaded. Discard it. + continue; + } + LoadingBlockModifierDef *lbm_def = def_it->second; + if (lbm_def->run_at_every_load) { + // This seems to be an LBM entry for + // an LBM that runs at every load. + // Don't add it just yet. + continue; + } + + m_lbm_lookup[time].addLBM(lbm_def, gamedef); + + // Erase the entry so that we know later + // what elements didn't get put into m_lbm_lookup + m_lbm_defs.erase(name); + } + + // Now also add the elements from m_lbm_defs to m_lbm_lookup + // that weren't added in the previous step. + // They are introduced first time to this world, + // or are run at every load (introducement time hardcoded to U32_MAX). + + LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now]; + LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX]; + + for (std::map::iterator it = + m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) { + if (it->second->run_at_every_load) { + lbms_running_always.addLBM(it->second, gamedef); + } else { + lbms_we_introduce_now.addLBM(it->second, gamedef); + } + } + + // Clear the list, so that we don't delete remaining elements + // twice in the destructor + m_lbm_defs.clear(); +} + +std::string LBMManager::createIntroductionTimesString() +{ + // Precondition, we must be in query mode + FATAL_ERROR_IF(m_query_mode == false, + "attempted to query on non fully set up LBMManager"); + + std::ostringstream oss; + for (lbm_lookup_map::iterator it = m_lbm_lookup.begin(); + it != m_lbm_lookup.end(); ++it) { + u32 time = it->first; + std::vector &lbm_list = it->second.lbm_list; + for (std::vector::iterator iit = lbm_list.begin(); + iit != lbm_list.end(); ++iit) { + // Don't add if the LBM runs at every load, + // then introducement time is hardcoded + // and doesn't need to be stored + if ((*iit)->run_at_every_load) + continue; + oss << (*iit)->name << "~" << time << ";"; + } + } + return oss.str(); +} + +void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp) +{ + // Precondition, we need m_lbm_lookup to be initialized + FATAL_ERROR_IF(m_query_mode == false, + "attempted to query on non fully set up LBMManager"); + v3s16 pos_of_block = block->getPosRelative(); + v3s16 pos; + MapNode n; + content_t c; + lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp); + for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) + for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++) + for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) + { + n = block->getNodeNoEx(pos); + c = n.getContent(); + for (LBMManager::lbm_lookup_map::const_iterator iit = it; + iit != m_lbm_lookup.end(); ++iit) { + const std::vector *lbm_list = + iit->second.lookup(c); + if (!lbm_list) + continue; + for (std::vector::const_iterator iit = + lbm_list->begin(); iit != lbm_list->end(); ++iit) { + (*iit)->trigger(env, pos + pos_of_block, n); + } + } + } +} + /* ActiveBlockList */ @@ -264,7 +522,7 @@ void fillRadiusBlock(v3s16 p0, s16 r, std::set &list) } } -void ActiveBlockList::update(std::list &active_positions, +void ActiveBlockList::update(std::vector &active_positions, s16 radius, std::set &blocks_removed, std::set &blocks_added) @@ -272,8 +530,8 @@ void ActiveBlockList::update(std::list &active_positions, /* Create the new list */ - std::set newlist; - for(std::list::iterator i = active_positions.begin(); + std::set newlist = m_forceloaded_list; + for(std::vector::iterator i = active_positions.begin(); i != active_positions.end(); ++i) { fillRadiusBlock(*i, radius, newlist); @@ -321,18 +579,20 @@ void ActiveBlockList::update(std::list &active_positions, ServerEnvironment */ -ServerEnvironment::ServerEnvironment(ServerMap *map, ScriptApi *scriptIface, - IGameDef *gamedef, IBackgroundBlockEmerger *emerger): +ServerEnvironment::ServerEnvironment(ServerMap *map, + GameScripting *scriptIface, IGameDef *gamedef, + const std::string &path_world) : m_map(map), m_script(scriptIface), m_gamedef(gamedef), - m_emerger(emerger), - m_random_spawn_timer(3), + m_path_world(path_world), m_send_recommended_timer(0), m_active_block_interval_overload_skip(0), m_game_time(0), m_game_time_fraction_counter(0), - m_recommended_send_interval(0.1) + m_last_clear_objects_time(0), + m_recommended_send_interval(0.1), + m_max_lag_estimate(0.1) { } @@ -349,7 +609,7 @@ ServerEnvironment::~ServerEnvironment() m_map->drop(); // Delete ActiveBlockModifiers - for(std::list::iterator + for(std::vector::iterator i = m_abms.begin(); i != m_abms.end(); ++i){ delete i->abm; } @@ -365,14 +625,14 @@ ServerMap & ServerEnvironment::getServerMap() return *m_map; } -bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize) +bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p) { float distance = pos1.getDistanceFrom(pos2); //calculate normalized direction vector v3f normalized_vector = v3f((pos2.X - pos1.X)/distance, - (pos2.Y - pos1.Y)/distance, - (pos2.Z - pos1.Z)/distance); + (pos2.Y - pos1.Y)/distance, + (pos2.Z - pos1.Z)/distance); //find out if there's a node on path between pos1 and pos2 for (float i = 1; i < distance; i += stepsize) { @@ -383,260 +643,174 @@ bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize) MapNode n = getMap().getNodeNoEx(pos); if(n.param0 != CONTENT_AIR) { + if (p) { + *p = pos; + } return false; } } return true; } -void ServerEnvironment::serializePlayers(const std::string &savedir) +void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason, + const std::string &str_reason, bool reconnect) { - std::string players_path = savedir + "/players"; - fs::CreateDir(players_path); - - std::set saved_players; - - std::vector player_files = fs::GetDirListing(players_path); - for(u32 i=0; icheckModified()) - { - // Open file and serialize - std::ofstream os(path.c_str(), std::ios_base::binary); - if(os.good() == false) - { - infostream<<"Failed to overwrite "<serialize(os); - saved_players.insert(player); - } else { - saved_players.insert(player); - } + for (std::vector::iterator it = m_players.begin(); + it != m_players.end(); + ++it) { + ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id, + (*it)->protocol_version, (AccessDeniedCode)reason, + str_reason, reconnect); } +} - for(std::list::iterator i = m_players.begin(); - i != m_players.end(); ++i) - { - Player *player = *i; - if(saved_players.find(player) != saved_players.end()) - { - /*infostream<<"Player "<getName() - <<" was already saved."<getName(); - // Don't save unnamed player - if(playername == "") - { - //infostream<<"Not saving unnamed player."<getName()<<" to " - <serialize(os); - saved_players.insert(player); + for (std::vector::iterator it = m_players.begin(); + it != m_players.end(); + ++it) { + RemotePlayer *player = static_cast(*it); + if (player->checkModified()) { + player->save(players_path); } } - - //infostream<<"Saved "< player_files = fs::GetDirListing(players_path); - for(u32 i=0; isave(players_path); +} - //infostream<<"Checking player file "<(getPlayer(playername.c_str())); + if (!player) { + player = new RemotePlayer(m_gamedef, ""); + newplayer = true; + } - if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS)) - { - infostream<<"Not loading player with invalid name: " - <deSerialize(is, path); + is.close(); - // Load player - { - verbosestream<<"Reading player "<deSerialize(is, player_files[i].name); + if (player->getName() == playername) { + found = true; + break; } - if(newplayer) - { - addPlayer(player); - } + path = players_path + playername + itos(i); } + + if (!found) { + infostream << "Player file for player " << playername + << " not found" << std::endl; + if (newplayer) + delete player; + return NULL; + } + + if (newplayer) + addPlayer(player); + player->setModified(false); + return player; } -void ServerEnvironment::saveMeta(const std::string &savedir) +void ServerEnvironment::saveMeta() { - std::string path = savedir + "/env_meta.txt"; + std::string path = m_path_world + DIR_DELIM "env_meta.txt"; // Open file and serialize - std::ofstream os(path.c_str(), std::ios_base::binary); - if(os.good() == false) - { - infostream<<"ServerEnvironment::saveMeta(): Failed to open " - < > m_aabms; + std::map > m_aabms; public: - ABMHandler(std::list &abms, + ABMHandler(std::vector &abms, float dtime_s, ServerEnvironment *env, bool use_timers): m_env(env) @@ -660,8 +834,8 @@ class ABMHandler if(dtime_s < 0.001) return; INodeDefManager *ndef = env->getGameDef()->ndef(); - for(std::list::iterator - i = abms.begin(); i != abms.end(); ++i){ + for(std::vector::iterator + i = abms.begin(); i != abms.end(); ++i) { ActiveBlockModifier *abm = i->abm; float trigger_interval = abm->getTriggerInterval(); if(trigger_interval < 0.001) @@ -674,41 +848,45 @@ class ABMHandler i->timer -= trigger_interval; actual_interval = trigger_interval; } - float intervals = actual_interval / trigger_interval; - if(intervals == 0) - continue; float chance = abm->getTriggerChance(); if(chance == 0) chance = 1; ActiveABM aabm; aabm.abm = abm; - aabm.chance = chance / intervals; - if(aabm.chance == 0) - aabm.chance = 1; + if(abm->getSimpleCatchUp()) { + float intervals = actual_interval / trigger_interval; + if(intervals == 0) + continue; + aabm.chance = chance / intervals; + if(aabm.chance == 0) + aabm.chance = 1; + } else { + aabm.chance = chance; + } // Trigger neighbors std::set required_neighbors_s = abm->getRequiredNeighbors(); for(std::set::iterator i = required_neighbors_s.begin(); - i != required_neighbors_s.end(); i++) + i != required_neighbors_s.end(); ++i) { ndef->getIds(*i, aabm.required_neighbors); } // Trigger contents std::set contents_s = abm->getTriggerContents(); for(std::set::iterator - i = contents_s.begin(); i != contents_s.end(); i++) + i = contents_s.begin(); i != contents_s.end(); ++i) { std::set ids; ndef->getIds(*i, ids); for(std::set::const_iterator k = ids.begin(); - k != ids.end(); k++) + k != ids.end(); ++k) { content_t c = *k; - std::map >::iterator j; + std::map >::iterator j; j = m_aabms.find(c); if(j == m_aabms.end()){ - std::list aabmlist; + std::vector aabmlist; m_aabms[c] = aabmlist; j = m_aabms.find(c); } @@ -717,6 +895,34 @@ class ABMHandler } } } + // Find out how many objects the given block and its neighbours contain. + // Returns the number of objects in the block, and also in 'wider' the + // number of objects in the block and all its neighbours. The latter + // may an estimate if any neighbours are unloaded. + u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider) + { + wider = 0; + u32 wider_unknown_count = 0; + for(s16 x=-1; x<=1; x++) + for(s16 y=-1; y<=1; y++) + for(s16 z=-1; z<=1; z++) + { + MapBlock *block2 = map->getBlockNoCreateNoEx( + block->getPos() + v3s16(x,y,z)); + if(block2==NULL){ + wider_unknown_count++; + continue; + } + wider += block2->m_static_objects.m_active.size() + + block2->m_static_objects.m_stored.size(); + } + // Extrapolate + u32 active_object_count = block->m_static_objects.m_active.size(); + u32 wider_known_count = 3*3*3 - wider_unknown_count; + wider += wider_unknown_count * wider / wider_known_count; + return active_object_count; + + } void apply(MapBlock *block) { if(m_aabms.empty()) @@ -724,6 +930,10 @@ class ABMHandler ServerMap *map = &m_env->getServerMap(); + u32 active_object_count_wider; + u32 active_object_count = this->countObjects(block, map, active_object_count_wider); + m_env->m_added_objects = 0; + v3s16 p0; for(p0.X=0; p0.XgetPosRelative(); - std::map >::iterator j; + std::map >::iterator j; j = m_aabms.find(c); if(j == m_aabms.end()) continue; - for(std::list::iterator - i = j->second.begin(); i != j->second.end(); i++) - { + for(std::vector::iterator + i = j->second.begin(); i != j->second.end(); ++i) { if(myrand() % i->chance != 0) continue; @@ -767,33 +976,16 @@ class ABMHandler } neighbor_found: - // Find out how many objects the block contains - u32 active_object_count = block->m_static_objects.m_active.size(); - // Find out how many objects this and all the neighbors contain - u32 active_object_count_wider = 0; - u32 wider_unknown_count = 0; - for(s16 x=-1; x<=1; x++) - for(s16 y=-1; y<=1; y++) - for(s16 z=-1; z<=1; z++) - { - MapBlock *block2 = map->getBlockNoCreateNoEx( - block->getPos() + v3s16(x,y,z)); - if(block2==NULL){ - wider_unknown_count = 0; - continue; - } - active_object_count_wider += - block2->m_static_objects.m_active.size() - + block2->m_static_objects.m_stored.size(); - } - // Extrapolate - u32 wider_known_count = 3*3*3 - wider_unknown_count; - active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count; - // Call all the trigger variations i->abm->trigger(m_env, p, n); i->abm->trigger(m_env, p, n, active_object_count, active_object_count_wider); + + // Count surrounding objects again if the abms added any + if(m_env->m_added_objects > 0) { + active_object_count = countObjects(block, map, active_object_count_wider); + m_env->m_added_objects = 0; + } } } } @@ -801,37 +993,54 @@ class ABMHandler void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) { + // Reset usage timer immediately, otherwise a block that becomes active + // again at around the same time as it would normally be unloaded will + // get unloaded incorrectly. (I think this still leaves a small possibility + // of a race condition between this and server::AsyncRunStep, which only + // some kind of synchronisation will fix, but it at least reduces the window + // of opportunity for it to break from seconds to nanoseconds) + block->resetUsageTimer(); + // Get time difference u32 dtime_s = 0; u32 stamp = block->getTimestamp(); - if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED) - dtime_s = m_game_time - block->getTimestamp(); + if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED) + dtime_s = m_game_time - stamp; dtime_s += additional_dtime; /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: " <m_static_objects.m_stored.clear(); + // do not set changed flag to avoid unnecessary mapblock writes + } + // Set current time as timestamp block->setTimestampNoChangedFlag(m_game_time); /*infostream<<"ServerEnvironment::activateBlock(): block is " < elapsed_timers = + std::vector elapsed_timers = block->m_node_timers.step((float)dtime_s); - if(!elapsed_timers.empty()){ + if (!elapsed_timers.empty()) { MapNode n; - for(std::map::iterator + for (std::vector::iterator i = elapsed_timers.begin(); - i != elapsed_timers.end(); i++){ - n = block->getNodeNoEx(i->first); - v3s16 p = i->first + block->getPosRelative(); - if(m_script->node_on_timer(p,n,i->second.elapsed)) - block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0)); + i != elapsed_timers.end(); ++i){ + n = block->getNodeNoEx(i->position); + v3s16 p = i->position + block->getPosRelative(); + if (m_script->node_on_timer(p, n, i->elapsed)) + block->setNodeTimer(NodeTimer(i->timeout, 0, i->position)); } } @@ -845,23 +1054,35 @@ void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm) m_abms.push_back(ABMWithState(abm)); } +void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm) +{ + m_lbm_mgr.addLBMDef(lbm); +} + bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) { INodeDefManager *ndef = m_gamedef->ndef(); MapNode n_old = m_map->getNodeNoEx(p); + // Call destructor - if(ndef->get(n_old).has_on_destruct) + if (ndef->get(n_old).has_on_destruct) m_script->node_on_destruct(p, n_old); + // Replace node - bool succeeded = m_map->addNodeWithEvent(p, n); - if(!succeeded) + if (!m_map->addNodeWithEvent(p, n)) return false; + + // Update active VoxelManipulator if a mapgen thread + m_map->updateVManip(p); + // Call post-destructor - if(ndef->get(n_old).has_after_destruct) + if (ndef->get(n_old).has_after_destruct) m_script->node_after_destruct(p, n_old); + // Call constructor - if(ndef->get(n).has_on_construct) + if (ndef->get(n).has_on_construct) m_script->node_on_construct(p, n); + return true; } @@ -869,24 +1090,40 @@ bool ServerEnvironment::removeNode(v3s16 p) { INodeDefManager *ndef = m_gamedef->ndef(); MapNode n_old = m_map->getNodeNoEx(p); + // Call destructor - if(ndef->get(n_old).has_on_destruct) + if (ndef->get(n_old).has_on_destruct) m_script->node_on_destruct(p, n_old); + // Replace with air // This is slightly optimized compared to addNodeWithEvent(air) - bool succeeded = m_map->removeNodeWithEvent(p); - if(!succeeded) + if (!m_map->removeNodeWithEvent(p)) return false; + + // Update active VoxelManipulator if a mapgen thread + m_map->updateVManip(p); + // Call post-destructor - if(ndef->get(n_old).has_after_destruct) + if (ndef->get(n_old).has_after_destruct) m_script->node_after_destruct(p, n_old); + // Air doesn't require constructor return true; } -std::set ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius) +bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n) +{ + if (!m_map->addNodeWithEvent(p, n, false)) + return false; + + // Update active VoxelManipulator if a mapgen thread + m_map->updateVManip(p); + + return true; +} + +void ServerEnvironment::getObjectsInsideRadius(std::vector &objects, v3f pos, float radius) { - std::set objects; for(std::map::iterator i = m_active_objects.begin(); i != m_active_objects.end(); ++i) @@ -896,36 +1133,34 @@ std::set ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius) v3f objectpos = obj->getBasePosition(); if(objectpos.getDistanceFrom(pos) > radius) continue; - objects.insert(id); + objects.push_back(id); } - return objects; } -void ServerEnvironment::clearAllObjects() +void ServerEnvironment::clearObjects(ClearObjectsMode mode) { - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Removing all active objects"< objects_to_remove; - for(std::map::iterator + infostream << "ServerEnvironment::clearObjects(): " + << "Removing all active objects" << std::endl; + std::vector objects_to_remove; + for (std::map::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) - { + i != m_active_objects.end(); ++i) { ServerActiveObject* obj = i->second; - if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) + if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) continue; u16 id = i->first; // Delete static object if block is loaded - if(obj->m_static_exists){ + if (obj->m_static_exists) { MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block); - if(block){ + if (block) { block->m_static_objects.remove(id); block->raiseModified(MOD_STATE_WRITE_NEEDED, - "clearAllObjects"); + MOD_REASON_CLEAR_ALL_OBJECTS); obj->m_static_exists = false; } } // If known by some client, don't delete immediately - if(obj->m_known_by_count > 0){ + if (obj->m_known_by_count > 0) { obj->m_pending_deactivation = true; obj->m_removed = true; continue; @@ -937,109 +1172,119 @@ void ServerEnvironment::clearAllObjects() m_script->removeObjectReference(obj); // Delete active object - if(obj->environmentDeletes()) + if (obj->environmentDeletes()) delete obj; // Id to be removed from m_active_objects objects_to_remove.push_back(id); } + // Remove references from m_active_objects - for(std::list::iterator i = objects_to_remove.begin(); - i != objects_to_remove.end(); ++i) - { + for (std::vector::iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); ++i) { m_active_objects.erase(*i); } // Get list of loaded blocks - std::list loaded_blocks; - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Listing all loaded blocks"< loaded_blocks; + infostream << "ServerEnvironment::clearObjects(): " + << "Listing all loaded blocks" << std::endl; m_map->listAllLoadedBlocks(loaded_blocks); - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Done listing all loaded blocks: " - < loadable_blocks; - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Listing all loadable blocks"<listAllLoadableBlocks(loadable_blocks); - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Done listing all loadable blocks: " - < loadable_blocks; + if (mode == CLEAR_OBJECTS_MODE_FULL) { + infostream << "ServerEnvironment::clearObjects(): " + << "Listing all loadable blocks" << std::endl; + m_map->listAllLoadableBlocks(loadable_blocks); + infostream << "ServerEnvironment::clearObjects(): " + << "Done listing all loadable blocks: " + << loadable_blocks.size() << std::endl; + } else { + loadable_blocks = loaded_blocks; + } + + infostream << "ServerEnvironment::clearObjects(): " + << "Now clearing objects in " << loadable_blocks.size() + << " blocks" << std::endl; // Grab a reference on each loaded block to avoid unloading it - for(std::list::iterator i = loaded_blocks.begin(); - i != loaded_blocks.end(); ++i) - { + for (std::vector::iterator i = loaded_blocks.begin(); + i != loaded_blocks.end(); ++i) { v3s16 p = *i; MapBlock *block = m_map->getBlockNoCreateNoEx(p); - assert(block); + assert(block != NULL); block->refGrab(); } // Remove objects in all loadable blocks - u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks"); - unload_interval = MYMAX(unload_interval, 1); + u32 unload_interval = U32_MAX; + if (mode == CLEAR_OBJECTS_MODE_FULL) { + unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks"); + unload_interval = MYMAX(unload_interval, 1); + } u32 report_interval = loadable_blocks.size() / 10; u32 num_blocks_checked = 0; u32 num_blocks_cleared = 0; u32 num_objs_cleared = 0; - for(std::list::iterator i = loadable_blocks.begin(); - i != loadable_blocks.end(); ++i) - { + for (std::vector::iterator i = loadable_blocks.begin(); + i != loadable_blocks.end(); ++i) { v3s16 p = *i; MapBlock *block = m_map->emergeBlock(p, false); - if(!block){ - errorstream<<"ServerEnvironment::clearAllObjects(): " - <<"Failed to emerge block "<m_static_objects.m_stored.size(); u32 num_active = block->m_static_objects.m_active.size(); - if(num_stored != 0 || num_active != 0){ + if (num_stored != 0 || num_active != 0) { block->m_static_objects.m_stored.clear(); block->m_static_objects.m_active.clear(); block->raiseModified(MOD_STATE_WRITE_NEEDED, - "clearAllObjects"); + MOD_REASON_CLEAR_ALL_OBJECTS); num_objs_cleared += num_stored + num_active; num_blocks_cleared++; } num_blocks_checked++; - if(num_blocks_checked % report_interval == 0){ + if (report_interval != 0 && + num_blocks_checked % report_interval == 0) { float percent = 100.0 * (float)num_blocks_checked / - loadable_blocks.size(); - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Cleared "<unloadUnreferencedBlocks(); } } m_map->unloadUnreferencedBlocks(); // Drop references that were added above - for(std::list::iterator i = loaded_blocks.begin(); - i != loaded_blocks.end(); ++i) - { + for (std::vector::iterator i = loaded_blocks.begin(); + i != loaded_blocks.end(); ++i) { v3s16 p = *i; MapBlock *block = m_map->getBlockNoCreateNoEx(p); assert(block); block->refDrop(); } - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Finished: Cleared "<getFloat("dedicated_server_step"); + static const float server_step = g_settings->getFloat("dedicated_server_step"); + m_recommended_send_interval = server_step; /* Increment game time @@ -1059,53 +1305,52 @@ void ServerEnvironment::step(float dtime) m_game_time += inc_i; m_game_time_fraction_counter -= (float)inc_i; } - + /* Handle players */ { ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG); - for(std::list::iterator i = m_players.begin(); + for(std::vector::iterator i = m_players.begin(); i != m_players.end(); ++i) { Player *player = *i; - + // Ignore disconnected players if(player->peer_id == 0) continue; - + // Move - player->move(dtime, *m_map, 100*BS); + player->move(dtime, this, 100*BS); } } /* Manage active block list */ - if(m_active_blocks_management_interval.step(dtime, 2.0)) - { - ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG); + if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) { + ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG); /* Get player block positions */ - std::list players_blockpos; - for(std::list::iterator + std::vector players_blockpos; + for(std::vector::iterator i = m_players.begin(); - i != m_players.end(); ++i) - { + i != m_players.end(); ++i) { Player *player = *i; // Ignore disconnected players if(player->peer_id == 0) continue; + v3s16 blockpos = getNodeBlockPos( floatToInt(player->getPosition(), BS)); players_blockpos.push_back(blockpos); } - + /* Update list of active blocks, collecting changes */ - const s16 active_block_range = g_settings->getS16("active_block_range"); + static const s16 active_block_range = g_settings->getS16("active_block_range"); std::set blocks_removed; std::set blocks_added; m_active_blocks.update(players_blockpos, active_block_range, @@ -1117,20 +1362,19 @@ void ServerEnvironment::step(float dtime) // Convert active objects that are no more in active blocks to static deactivateFarObjects(false); - + for(std::set::iterator i = blocks_removed.begin(); - i != blocks_removed.end(); ++i) - { + i != blocks_removed.end(); ++i) { v3s16 p = *i; /* infostream<<"Server: Block " << PP(p) << " became inactive"<getBlockNoCreateNoEx(p); if(block==NULL) continue; - + // Set current time as timestamp (and let it set ChangedFlag) block->setTimestamp(m_game_time); } @@ -1145,10 +1389,8 @@ void ServerEnvironment::step(float dtime) { v3s16 p = *i; - MapBlock *block = m_map->getBlockNoCreateNoEx(p); + MapBlock *block = m_map->getBlockOrEmerge(p); if(block==NULL){ - // Block needs to be fetched first - m_emerger->queueBlockEmerge(p, false); m_active_blocks.m_list.erase(p); continue; } @@ -1162,18 +1404,17 @@ void ServerEnvironment::step(float dtime) /* Mess around in active blocks */ - if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0)) - { - ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG); - - float dtime = 1.0; + if (m_active_blocks_nodemetadata_interval.step(dtime, m_cache_nodetimer_interval)) { + ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg per interval", SPT_AVG); + + float dtime = m_cache_nodetimer_interval; for(std::set::iterator i = m_active_blocks.m_list.begin(); i != m_active_blocks.m_list.end(); ++i) { v3s16 p = *i; - + /*infostream<<"Server: Block ("<resetUsageTimer(); - + // Set current time as timestamp block->setTimestampNoChangedFlag(m_game_time); // If time has changed much from the one on disk, // set block to be saved when it is unloaded if(block->getTimestamp() > block->getDiskTimestamp() + 60) block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD, - "Timestamp older than 60s (step)"); + MOD_REASON_BLOCK_EXPIRED); // Run node timers - std::map elapsed_timers = + std::vector elapsed_timers = block->m_node_timers.step((float)dtime); - if(!elapsed_timers.empty()){ + if (!elapsed_timers.empty()) { MapNode n; - for(std::map::iterator + for (std::vector::iterator i = elapsed_timers.begin(); - i != elapsed_timers.end(); i++){ - n = block->getNodeNoEx(i->first); - p = i->first + block->getPosRelative(); - if(m_script->node_on_timer(p,n,i->second.elapsed)) - block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0)); + i != elapsed_timers.end(); ++i) { + n = block->getNodeNoEx(i->position); + p = i->position + block->getPosRelative(); + if (m_script->node_on_timer(p, n, i->elapsed)) { + block->setNodeTimer(NodeTimer( + i->timeout, 0, i->position)); + } } } } } - - const float abm_interval = 1.0; - if(m_active_block_modifier_interval.step(dtime, abm_interval)) + + if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) do{ // breakable if(m_active_block_interval_overload_skip > 0){ ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips"); m_active_block_interval_overload_skip--; break; } - ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG); - TimeTaker timer("modify in active blocks"); - + ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG); + TimeTaker timer("modify in active blocks per interval"); + // Initialize handling of ActiveBlockModifiers - ABMHandler abmhandler(m_abms, abm_interval, this, true); + ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true); for(std::set::iterator i = m_active_blocks.m_list.begin(); i != m_active_blocks.m_list.end(); ++i) { v3s16 p = *i; - + /*infostream<<"Server: Block ("<getBlockNoCreateNoEx(p); - if(block==NULL) + if(block == NULL) continue; - + // Set current time as timestamp block->setTimestampNoChangedFlag(m_game_time); @@ -1246,13 +1488,13 @@ void ServerEnvironment::step(float dtime) u32 time_ms = timer.stop(true); u32 max_time_ms = 200; if(time_ms > max_time_ms){ - infostream<<"WARNING: active block modifiers took " + warningstream<<"active block modifiers took " <avg("SEnv: num of objects", m_active_objects.size()); - + // This helps the objects to send data at the same time bool send_recommended = false; m_send_recommended_timer += dtime; @@ -1281,11 +1523,6 @@ void ServerEnvironment::step(float dtime) i != m_active_objects.end(); ++i) { ServerActiveObject* obj = i->second; - // Remove non-peaceful mobs on peaceful mode - if(g_settings->getBool("only_peaceful_mobs")){ - if(!obj->isPeaceful()) - obj->m_removed = true; - } // Don't step if is to be removed or stored statically if(obj->m_removed || obj->m_pending_deactivation) continue; @@ -1294,12 +1531,13 @@ void ServerEnvironment::step(float dtime) // Read messages from object while(!obj->m_messages_out.empty()) { - m_active_object_messages.push_back( - obj->m_messages_out.pop_front()); + m_active_object_messages.push( + obj->m_messages_out.front()); + obj->m_messages_out.pop(); } } } - + /* Manage active objects */ @@ -1311,6 +1549,49 @@ void ServerEnvironment::step(float dtime) */ removeRemovedObjects(); } + + /* + Manage particle spawner expiration + */ + if (m_particle_management_interval.step(dtime, 1.0)) { + for (std::map::iterator i = m_particle_spawners.begin(); + i != m_particle_spawners.end(); ) { + //non expiring spawners + if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) { + ++i; + continue; + } + + i->second -= 1.0f; + if (i->second <= 0.f) + m_particle_spawners.erase(i++); + else + ++i; + } + } +} + +u32 ServerEnvironment::addParticleSpawner(float exptime) +{ + // Timers with lifetime 0 do not expire + float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY; + + u32 id = 0; + for (;;) { // look for unused particlespawner id + id++; + std::map::iterator f; + f = m_particle_spawners.find(id); + if (f == m_particle_spawners.end()) { + m_particle_spawners[id] = time; + break; + } + } + return id; +} + +void ServerEnvironment::deleteParticleSpawner(u32 id) +{ + m_particle_spawners.erase(id); } ServerActiveObject* ServerEnvironment::getActiveObject(u16 id) @@ -1342,7 +1623,7 @@ u16 getFreeServerActiveObjectId( last_used_id ++; if(isFreeServerActiveObjectId(last_used_id, objects)) return last_used_id; - + if(last_used_id == startid) return 0; } @@ -1350,66 +1631,27 @@ u16 getFreeServerActiveObjectId( u16 ServerEnvironment::addActiveObject(ServerActiveObject *object) { - assert(object); + assert(object); // Pre-condition + m_added_objects++; u16 id = addActiveObjectRaw(object, true, 0); return id; } -#if 0 -bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj) -{ - assert(obj); - - v3f objectpos = obj->getBasePosition(); - - // The block in which the object resides in - v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS)); - - /* - Update the static data - */ - - // Create new static object - std::string staticdata = obj->getStaticData(); - StaticObject s_obj(obj->getType(), objectpos, staticdata); - // Add to the block where the object is located in - v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); - // Get or generate the block - MapBlock *block = m_map->emergeBlock(blockpos); - - bool succeeded = false; - - if(block) - { - block->m_static_objects.insert(0, s_obj); - block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD, - "addActiveObjectAsStatic"); - succeeded = true; - } - else{ - infostream<<"ServerEnvironment::addActiveObjectAsStatic: " - <<"Could not find or generate " - <<"a block for storing static object"<environmentDeletes()) - delete obj; - - return succeeded; -} -#endif - /* Finds out what new objects have been added to inside a radius around a position */ -void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius, +void ServerEnvironment::getAddedActiveObjects(Player *player, s16 radius, + s16 player_radius, std::set ¤t_objects, - std::set &added_objects) + std::queue &added_objects) { - v3f pos_f = intToFloat(pos, BS); f32 radius_f = radius * BS; + f32 player_radius_f = player_radius * BS; + + if (player_radius_f < 0) + player_radius_f = 0; + /* Go through the object list, - discard m_removed objects, @@ -1419,29 +1661,33 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius, */ for(std::map::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) - { + i != m_active_objects.end(); ++i) { u16 id = i->first; + // Get object ServerActiveObject *object = i->second; if(object == NULL) continue; - // Discard if removed - if(object->m_removed) + + // Discard if removed or deactivating + if(object->m_removed || object->m_pending_deactivation) continue; - if(object->unlimitedTransferDistance() == false){ + + f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition()); + if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) { // Discard if too far - f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f); - if(distance_f > radius_f) + if (distance_f > player_radius_f && player_radius_f != 0) continue; - } + } else if (distance_f > radius_f) + continue; + // Discard if already on current_objects std::set::iterator n; n = current_objects.find(id); if(n != current_objects.end()) continue; // Add to added_objects - added_objects.insert(id); + added_objects.push(id); } } @@ -1449,12 +1695,17 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius, Finds out what objects have been removed from inside a radius around a position */ -void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius, +void ServerEnvironment::getRemovedActiveObjects(Player *player, s16 radius, + s16 player_radius, std::set ¤t_objects, - std::set &removed_objects) + std::queue &removed_objects) { - v3f pos_f = intToFloat(pos, BS); f32 radius_f = radius * BS; + f32 player_radius_f = player_radius * BS; + + if (player_radius_f < 0) + player_radius_f = 0; + /* Go through current_objects; object is removed if: - object is not found in m_active_objects (this is actually an @@ -1470,32 +1721,54 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius, u16 id = *i; ServerActiveObject *object = getActiveObject(id); - if(object == NULL){ - infostream<<"ServerEnvironment::getRemovedActiveObjects():" - <<" object in current_objects is NULL"<m_removed) - { - removed_objects.insert(id); + if (object->m_removed || object->m_pending_deactivation) { + removed_objects.push(id); continue; } - - // If transfer distance is unlimited, don't remove - if(object->unlimitedTransferDistance()) + + f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition()); + if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + if (distance_f <= player_radius_f || player_radius_f == 0) + continue; + } else if (distance_f <= radius_f) continue; - f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f); + // Object is no longer visible + removed_objects.push(id); + } +} - if(distance_f >= radius_f) - { - removed_objects.insert(id); +void ServerEnvironment::setStaticForActiveObjectsInBlock( + v3s16 blockpos, bool static_exists, v3s16 static_block) +{ + MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); + if (!block) + return; + + for (std::map::iterator + so_it = block->m_static_objects.m_active.begin(); + so_it != block->m_static_objects.m_active.end(); ++so_it) { + // Get the ServerActiveObject counterpart to this StaticObject + std::map::iterator ao_it; + ao_it = m_active_objects.find(so_it->first); + if (ao_it == m_active_objects.end()) { + // If this ever happens, there must be some kind of nasty bug. + errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): " + "Object from MapBlock::m_static_objects::m_active not found " + "in m_active_objects"; continue; } - - // Not removed + + ServerActiveObject *sao = ao_it->second; + sao->m_static_exists = static_exists; + sao->m_static_block = static_block; } } @@ -1503,8 +1776,10 @@ ActiveObjectMessage ServerEnvironment::getActiveObjectMessage() { if(m_active_object_messages.empty()) return ActiveObjectMessage(0); - - return m_active_object_messages.pop_front(); + + ActiveObjectMessage message = m_active_object_messages.front(); + m_active_object_messages.pop(); + return message; } /* @@ -1514,7 +1789,7 @@ ActiveObjectMessage ServerEnvironment::getActiveObjectMessage() u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, bool set_changed, u32 dtime_s) { - assert(object); + assert(object); // Pre-condition if(object->getId() == 0){ u16 new_id = getFreeServerActiveObjectId(m_active_objects); if(new_id == 0) @@ -1539,21 +1814,32 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, delete object; return 0; } + + if (objectpos_over_limit(object->getBasePosition())) { + v3f p = object->getBasePosition(); + errorstream << "ServerEnvironment::addActiveObjectRaw(): " + << "object position (" << p.X << "," << p.Y << "," << p.Z + << ") outside maximum range" << std::endl; + if (object->environmentDeletes()) + delete object; + return 0; + } + /*infostream<<"ServerEnvironment::addActiveObjectRaw(): " <<"added (id="<getId()<<")"<getId()] = object; - + verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " <<"Added id="<getId()<<"; there are now " <addObjectReference(object); // Post-initialize object object->addedToEnvironment(dtime_s); - + // Add static data to block if(object->isStaticAllowed()) { @@ -1563,25 +1849,23 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, StaticObject s_obj(object->getType(), objectpos, staticdata); // Add to the block where the object is located in v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); - MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); - if(block) - { + MapBlock *block = m_map->emergeBlock(blockpos); + if(block){ block->m_static_objects.m_active[object->getId()] = s_obj; object->m_static_exists = true; object->m_static_block = blockpos; if(set_changed) - block->raiseModified(MOD_STATE_WRITE_NEEDED, - "addActiveObjectRaw"); - } - else{ + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_ADD_ACTIVE_OBJECT_RAW); + } else { v3s16 p = floatToInt(objectpos, BS); errorstream<<"ServerEnvironment::addActiveObjectRaw(): " - <<"could not find block for storing id="<getId() + <<"could not emerge block for storing id="<getId() <<" statically (pos="<getId(); } @@ -1590,11 +1874,10 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, */ void ServerEnvironment::removeRemovedObjects() { - std::list objects_to_remove; + std::vector objects_to_remove; for(std::map::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) - { + i != m_active_objects.end(); ++i) { u16 id = i->first; ServerActiveObject* obj = i->second; // This shouldn't happen but check it @@ -1623,18 +1906,39 @@ void ServerEnvironment::removeRemovedObjects() if (block) { block->m_static_objects.remove(id); block->raiseModified(MOD_STATE_WRITE_NEEDED, - "removeRemovedObjects"); + MOD_REASON_REMOVE_OBJECTS_REMOVE); obj->m_static_exists = false; } else { - infostream << "failed to emerge block from which " - "an object to be removed was loaded from. id="<getPos()) <<" ("<m_static_objects.m_stored.size() <<" objects)"<m_static_objects.m_stored.size() > 49); - if(large_amount){ + bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block")); + if (large_amount) { errorstream<<"suspiciously large amount of objects detected: " <m_static_objects.m_stored.size()<<" in " <getPos()) @@ -1711,33 +2017,28 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s) // Clear stored list block->m_static_objects.m_stored.clear(); block->raiseModified(MOD_STATE_WRITE_NEEDED, - "stored list cleared in activateObjects due to " - "large amount of objects"); + MOD_REASON_TOO_MANY_OBJECTS); return; } - // A list for objects that couldn't be converted to active for some - // reason. They will be stored back. - std::list new_stored; - // Loop through stored static objects - for(std::list::iterator + + // Activate stored objects + std::vector new_stored; + for (std::vector::iterator i = block->m_static_objects.m_stored.begin(); - i != block->m_static_objects.m_stored.end(); ++i) - { - /*infostream<<"Server: Creating an active object from " - <<"static data"<m_static_objects.m_stored.end(); ++i) { StaticObject &s_obj = *i; + // Create an active object from the data ServerActiveObject *obj = ServerActiveObject::create - (s_obj.type, this, 0, s_obj.pos, s_obj.data); + ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data); // If couldn't create object, store static data back. - if(obj==NULL) - { + if(obj == NULL) { errorstream<<"ServerEnvironment::activateObjects(): " <<"failed to create active object from static object " <<"in block "<m_static_objects.m_stored.clear(); // Add leftover failed stuff to stored list - for(std::list::iterator + for(std::vector::iterator i = new_stored.begin(); - i != new_stored.end(); ++i) - { + i != new_stored.end(); ++i) { StaticObject &s_obj = *i; block->m_static_objects.m_stored.push_back(s_obj); } + + // Turn the active counterparts of activated objects not pending for + // deactivation + for(std::map::iterator + i = block->m_static_objects.m_active.begin(); + i != block->m_static_objects.m_active.end(); ++i) + { + u16 id = i->first; + ServerActiveObject *object = getActiveObject(id); + assert(object); + object->m_pending_deactivation = false; + } + /* Note: Block hasn't really been modified here. The objects have just been activated and moved from the stored @@ -1776,18 +2089,17 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s) If force_delete is set, active object is deleted nevertheless. It shall only be set so in the destructor of the environment. - If block wasn't generated (not in memory or on disk), + If block wasn't generated (not in memory or on disk), */ void ServerEnvironment::deactivateFarObjects(bool force_delete) { - std::list objects_to_remove; + std::vector objects_to_remove; for(std::map::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) - { + i != m_active_objects.end(); ++i) { ServerActiveObject* obj = i->second; assert(obj); - + // Do not deactivate if static data creation not allowed if(!force_delete && !obj->isStaticAllowed()) continue; @@ -1797,11 +2109,52 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) continue; u16 id = i->first; - v3f objectpos = obj->getBasePosition(); + v3f objectpos = obj->getBasePosition(); // The block in which the object resides in v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS)); + // If object's static data is stored in a deactivated block and object + // is actually located in an active block, re-save to the block in + // which the object is actually located in. + if(!force_delete && + obj->m_static_exists && + !m_active_blocks.contains(obj->m_static_block) && + m_active_blocks.contains(blockpos_o)) + { + v3s16 old_static_block = obj->m_static_block; + + // Save to block where object is located + MapBlock *block = m_map->emergeBlock(blockpos_o, false); + if(!block){ + errorstream<<"ServerEnvironment::deactivateFarObjects(): " + <<"Could not save object id="<getStaticData(); + StaticObject s_obj(obj->getType(), objectpos, staticdata_new); + block->m_static_objects.insert(id, s_obj); + obj->m_static_block = blockpos_o; + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_STATIC_DATA_ADDED); + + // Delete from block where object was located + block = m_map->emergeBlock(old_static_block, false); + if(!block){ + errorstream<<"ServerEnvironment::deactivateFarObjects(): " + <<"Could not delete object id="<m_static_objects.remove(id); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_STATIC_DATA_REMOVED); + continue; + } + // If block is active, don't remove if(!force_delete && m_active_blocks.contains(blockpos_o)) continue; @@ -1822,36 +2175,38 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) // Create new static object std::string staticdata_new = obj->getStaticData(); StaticObject s_obj(obj->getType(), objectpos, staticdata_new); - + bool stays_in_same_block = false; bool data_changed = true; - if(obj->m_static_exists){ - if(obj->m_static_block == blockpos_o) + if (obj->m_static_exists) { + if (obj->m_static_block == blockpos_o) stays_in_same_block = true; MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); - - std::map::iterator n = + + if (block) { + std::map::iterator n = block->m_static_objects.m_active.find(id); - if(n != block->m_static_objects.m_active.end()){ - StaticObject static_old = n->second; + if (n != block->m_static_objects.m_active.end()) { + StaticObject static_old = n->second; - float save_movem = obj->getMinimumSavedMovement(); + float save_movem = obj->getMinimumSavedMovement(); - if(static_old.data == staticdata_new && - (static_old.pos - objectpos).getLength() < save_movem) - data_changed = false; - } else { - errorstream<<"ServerEnvironment::deactivateFarObjects(): " + if (static_old.data == staticdata_new && + (static_old.pos - objectpos).getLength() < save_movem) + data_changed = false; + } else { + errorstream<<"ServerEnvironment::deactivateFarObjects(): " <<"id="<m_static_block)<m_static_exists) { @@ -1863,8 +2218,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) // Only mark block as modified if data changed considerably if(shall_be_written) block->raiseModified(MOD_STATE_WRITE_NEEDED, - "deactivateFarObjects: Static data " - "changed considerably"); + MOD_REASON_STATIC_DATA_CHANGED); } } @@ -1876,37 +2230,42 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) block = m_map->emergeBlock(blockpos); } catch(InvalidPositionException &e){ // Handled via NULL pointer + // NOTE: emergeBlock's failure is usually determined by it + // actually returning NULL } if(block) { - if(block->m_static_objects.m_stored.size() >= 49){ + if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){ errorstream<<"ServerEnv: Trying to store id="<getId() <<" statically but block "<m_static_objects.m_stored.size() - <<" (over 49) objects." + <<" objects." <<" Forcing delete."<m_static_block, but happens rarely for some unknown + // reason. Unsuccessful attempts have been made to find + // said reason. if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){ - infostream<<"ServerEnv: WARNING: Performing hack #83274" + warningstream<<"ServerEnv: Performing hack #83274" <m_static_objects.remove(id); } - //store static data - block->m_static_objects.insert(0, s_obj); - + // Store static data + u16 store_id = pending_delete ? id : 0; + block->m_static_objects.insert(store_id, s_obj); + // Only mark block as modified if data changed considerably if(shall_be_written) block->raiseModified(MOD_STATE_WRITE_NEEDED, - "deactivateFarObjects: Static data " - "changed considerably"); - + MOD_REASON_STATIC_DATA_CHANGED); + obj->m_static_exists = true; obj->m_static_block = block->getPos(); } @@ -1936,7 +2295,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) obj->m_pending_deactivation = true; continue; } - + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " <<"object id="<::iterator i = objects_to_remove.begin(); - i != objects_to_remove.end(); ++i) - { + for(std::vector::iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); ++i) { m_active_objects.erase(*i); } } - #ifndef SERVER #include "clientsimpleobject.h" @@ -1979,6 +2336,8 @@ ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, m_gamedef(gamedef), m_irr(irr) { + char zero = 0; + memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids)); } ClientEnvironment::~ClientEnvironment() @@ -1991,9 +2350,8 @@ ClientEnvironment::~ClientEnvironment() delete i->second; } - for(std::list::iterator - i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) - { + for(std::vector::iterator + i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) { delete *i; } @@ -2013,21 +2371,21 @@ ClientMap & ClientEnvironment::getClientMap() void ClientEnvironment::addPlayer(Player *player) { - DSTACK(__FUNCTION_NAME); + DSTACK(FUNCTION_NAME); /* It is a failure if player is local and there already is a local player */ - assert(!(player->isLocal() == true && getLocalPlayer() != NULL)); + FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL, + "Player is local but there is already a local player"); Environment::addPlayer(player); } LocalPlayer * ClientEnvironment::getLocalPlayer() { - for(std::list::iterator i = m_players.begin(); - i != m_players.end(); ++i) - { + for(std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { Player *player = *i; if(player->isLocal()) return (LocalPlayer*)player; @@ -2037,7 +2395,7 @@ LocalPlayer * ClientEnvironment::getLocalPlayer() void ClientEnvironment::step(float dtime) { - DSTACK(__FUNCTION_NAME); + DSTACK(FUNCTION_NAME); /* Step time of day */ stepTimeOfDay(dtime); @@ -2050,15 +2408,15 @@ void ClientEnvironment::step(float dtime) LocalPlayer *lplayer = getLocalPlayer(); assert(lplayer); // collision info queue - std::list player_collisions; - + std::vector player_collisions; + /* Get the speed the player is going */ bool is_climbing = lplayer->is_climbing; - + f32 player_speed = lplayer->getSpeed().getLength(); - + /* Maximum position increment */ @@ -2070,15 +2428,15 @@ void ClientEnvironment::step(float dtime) f32 dtime_max_increment = 1; if(player_speed > 0.001) dtime_max_increment = position_max_increment / player_speed; - + // Maximum time increment is 10ms or lower if(dtime_max_increment > 0.01) dtime_max_increment = 0.01; - + // Don't allow overly huge dtime if(dtime > 0.5) dtime = 0.5; - + f32 dtime_downcount = dtime; /* @@ -2106,11 +2464,11 @@ void ClientEnvironment::step(float dtime) */ dtime_downcount = 0; } - + /* Handle local player */ - + { // Apply physics if(free_move == false && is_climbing == false) @@ -2136,18 +2494,9 @@ void ClientEnvironment::step(float dtime) if(dl > lplayer->movement_liquid_fluidity_smooth) dl = lplayer->movement_liquid_fluidity_smooth; dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor); - + v3f d = d_wanted.normalize() * dl; speed += d; - -#if 0 // old code - if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth; - if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth; - if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth; - if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth; - if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth; - if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth; -#endif } lplayer->setSpeed(speed); @@ -2162,13 +2511,11 @@ void ClientEnvironment::step(float dtime) } } while(dtime_downcount > 0.001); - + //std::cout<<"Looped "<::iterator - i = player_collisions.begin(); - i != player_collisions.end(); ++i) - { + + for(std::vector::iterator i = player_collisions.begin(); + i != player_collisions.end(); ++i) { CollisionInfo &info = *i; v3f speed_diff = info.new_speed - info.old_speed;; // Handle only fall damage @@ -2201,14 +2548,14 @@ void ClientEnvironment::step(float dtime) } } } - + /* A quick draft of lava damage */ if(m_lava_hurt_interval.step(dtime, 1.0)) { v3f pf = lplayer->getPosition(); - + // Feet, middle and head v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS); MapNode n1 = m_map->getNodeNoEx(p1); @@ -2224,7 +2571,7 @@ void ClientEnvironment::step(float dtime) m_gamedef->ndef()->get(n2).damage_per_second); damage_per_second = MYMAX(damage_per_second, m_gamedef->ndef()->get(n3).damage_per_second); - + if(damage_per_second != 0) { damageLocalPlayer(damage_per_second, true); @@ -2242,16 +2589,21 @@ void ClientEnvironment::step(float dtime) v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); ContentFeatures c = m_gamedef->ndef()->get(n); - - if(c.isLiquid() && c.drowning){ - if(lplayer->breath > 10) - lplayer->breath = 11; - if(lplayer->breath > 0) - lplayer->breath -= 1; + u8 drowning_damage = c.drowning; + if(drowning_damage > 0 && lplayer->hp > 0){ + u16 breath = lplayer->getBreath(); + if(breath > 10){ + breath = 11; + } + if(breath > 0){ + breath -= 1; + } + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); } - if(lplayer->breath == 0){ - damageLocalPlayer(1, true); + if(lplayer->getBreath() == 0 && drowning_damage > 0){ + damageLocalPlayer(drowning_damage, true); } } if(m_breathing_interval.step(dtime, 0.5)) @@ -2262,49 +2614,58 @@ void ClientEnvironment::step(float dtime) v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); ContentFeatures c = m_gamedef->ndef()->get(n); - - if(!c.isLiquid() || !c.drowning){ - if(lplayer->breath <= 10) - lplayer->breath += 1; + if (!lplayer->hp){ + lplayer->setBreath(11); + } + else if(c.drowning == 0){ + u16 breath = lplayer->getBreath(); + if(breath <= 10){ + breath += 1; + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); + } } } /* Stuff that can be done in an arbitarily large dtime */ - for(std::list::iterator i = m_players.begin(); - i != m_players.end(); ++i) - { + for(std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { Player *player = *i; - + /* Handle non-local players */ - if(player->isLocal() == false) - { + if(player->isLocal() == false) { // Move - player->move(dtime, *m_map, 100*BS); + player->move(dtime, this, 100*BS); } - - // Update lighting on all players on client - float light = 1.0; - try{ - // Get node at head - v3s16 p = player->getLightPosition(); - MapNode n = m_map->getNode(p); - light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef()); - } - catch(InvalidPositionException &e){ - light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0); - } - player->light = light; } - + + // Update lighting on local player (used for wield item) + u32 day_night_ratio = getDayNightRatio(); + { + // Get node at head + + // On InvalidPositionException, use this as default + // (day: LIGHT_SUN, night: 0) + MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0); + + v3s16 p = lplayer->getLightPosition(); + node_at_lplayer = m_map->getNodeNoEx(p); + + u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef()); + u8 day = light & 0xff; + u8 night = (light >> 8) & 0xff; + finalColorBlend(lplayer->light_color, day, night, day_night_ratio); + } + /* Step active objects and update lighting of them */ - + g_profiler->avg("CEnv: num of objects", m_active_objects.size()); bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21); for(std::map::iterator @@ -2319,15 +2680,16 @@ void ClientEnvironment::step(float dtime) { // Update lighting u8 light = 0; - try{ - // Get node at head - v3s16 p = obj->getLightPosition(); - MapNode n = m_map->getNode(p); - light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); - } - catch(InvalidPositionException &e){ - light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); - } + bool pos_ok; + + // Get node at head + v3s16 p = obj->getLightPosition(); + MapNode n = m_map->getNodeNoEx(p, &pos_ok); + if (pos_ok) + light = n.getLightBlend(day_night_ratio, m_gamedef->ndef()); + else + light = blend_light(day_night_ratio, LIGHT_SUN, 0); + obj->updateLight(light); } } @@ -2336,25 +2698,36 @@ void ClientEnvironment::step(float dtime) Step and handle simple objects */ g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size()); - for(std::list::iterator - i = m_simple_objects.begin(); i != m_simple_objects.end();) - { - ClientSimpleObject *simple = *i; - std::list::iterator cur = i; - ++i; + for(std::vector::iterator + i = m_simple_objects.begin(); i != m_simple_objects.end();) { + std::vector::iterator cur = i; + ClientSimpleObject *simple = *cur; + simple->step(dtime); - if(simple->m_to_be_removed){ + if(simple->m_to_be_removed) { delete simple; - m_simple_objects.erase(cur); + i = m_simple_objects.erase(cur); + } + else { + ++i; } } } - + void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple) { m_simple_objects.push_back(simple); } +GenericCAO* ClientEnvironment::getGenericCAO(u16 id) +{ + ClientActiveObject *obj = getActiveObject(id); + if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC) + return (GenericCAO*) obj; + else + return NULL; +} + ClientActiveObject* ClientEnvironment::getActiveObject(u16 id) { std::map::iterator n; @@ -2384,7 +2757,7 @@ u16 getFreeClientActiveObjectId( last_used_id ++; if(isFreeClientActiveObjectId(last_used_id, objects)) return last_used_id; - + if(last_used_id == startid) return 0; } @@ -2392,7 +2765,7 @@ u16 getFreeClientActiveObjectId( u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) { - assert(object); + assert(object); // Pre-condition if(object->getId() == 0) { u16 new_id = getFreeClientActiveObjectId(m_active_objects); @@ -2418,15 +2791,16 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) object->addToScene(m_smgr, m_texturesource, m_irr); { // Update lighting immediately u8 light = 0; - try{ - // Get node at head - v3s16 p = object->getLightPosition(); - MapNode n = m_map->getNode(p); + bool pos_ok; + + // Get node at head + v3s16 p = object->getLightPosition(); + MapNode n = m_map->getNodeNoEx(p, &pos_ok); + if (pos_ok) light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); - } - catch(InvalidPositionException &e){ + else light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); - } + object->updateLight(light); } return object->getId(); @@ -2436,7 +2810,7 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type, const std::string &init_data) { ClientActiveObject* obj = - ClientActiveObject::create(type, m_gamedef, this); + ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this); if(obj == NULL) { infostream<<"ClientEnvironment::addActiveObject(): " @@ -2444,7 +2818,7 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type, <setId(id); try @@ -2480,28 +2854,23 @@ void ClientEnvironment::removeActiveObject(u16 id) m_active_objects.erase(id); } -void ClientEnvironment::processActiveObjectMessage(u16 id, - const std::string &data) +void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data) { - ClientActiveObject* obj = getActiveObject(id); - if(obj == NULL) - { - infostream<<"ClientEnvironment::processActiveObjectMessage():" - <<" got message for id="<processMessage(data); - } - catch(SerializationError &e) - { + } catch (SerializationError &e) { errorstream<<"ClientEnvironment::processActiveObjectMessage():" - <<" id="<hp > damage) + + if (handle_hp) { + if (lplayer->hp > damage) lplayer->hp -= damage; else lplayer->hp = 0; @@ -2525,13 +2894,21 @@ void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp) event.type = CEE_PLAYER_DAMAGE; event.player_damage.amount = damage; event.player_damage.send_to_server = handle_hp; - m_client_event_queue.push_back(event); + m_client_event_queue.push(event); +} + +void ClientEnvironment::updateLocalPlayerBreath(u16 breath) +{ + ClientEnvEvent event; + event.type = CEE_PLAYER_BREATH; + event.player_breath.amount = breath; + m_client_event_queue.push(event); } /* Client likes to call these */ - + void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, std::vector &dest) { @@ -2554,15 +2931,14 @@ void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, ClientEnvEvent ClientEnvironment::getClientEvent() { + ClientEnvEvent event; if(m_client_event_queue.empty()) - { - ClientEnvEvent event; event.type = CEE_NONE; - return event; + else { + event = m_client_event_queue.front(); + m_client_event_queue.pop(); } - return m_client_event_queue.pop_front(); + return event; } #endif // #ifndef SERVER - -