X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fenvironment.cpp;h=eea2646997c466989bfed37a05ae7ad43fb51e7a;hb=1475c1b1c8bb1a2a2812d485d3590e1f817f7c7b;hp=8ade7fe05a3f40d4388697ed4646e1b887592560;hpb=e0eec201a18a0741114094b600f765313a838bfb;p=minetest.git diff --git a/src/environment.cpp b/src/environment.cpp index 8ade7fe05..eea264699 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -39,23 +39,32 @@ with this program; if not, write to the Free Software Foundation, Inc., #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 "jthread/jmutexautolock.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() @@ -69,7 +78,7 @@ Environment::~Environment() void Environment::addPlayer(Player *player) { - DSTACK(__FUNCTION_NAME); + DSTACK(FUNCTION_NAME); /* Check that peer_ids are unique. Also check that names are unique. @@ -84,28 +93,11 @@ void Environment::addPlayer(Player *player) m_players.push_back(player); } -void Environment::removePlayer(u16 peer_id) -{ - DSTACK(__FUNCTION_NAME); - - for(std::vector::iterator i = m_players.begin(); - i != m_players.end();) - { - Player *player = *i; - if(player->peer_id == peer_id) { - delete player; - i = m_players.erase(i); - } else { - ++i; - } - } -} - -void Environment::removePlayer(const char *name) +void Environment::removePlayer(Player* player) { for (std::vector::iterator it = m_players.begin(); it != m_players.end(); ++it) { - if (strcmp((*it)->getName(), name) == 0) { + if ((*it) == player) { delete *it; m_players.erase(it); return; @@ -196,74 +188,91 @@ std::vector Environment::getPlayers(bool ignore_disconnected) u32 Environment::getDayNightRatio() { - if(m_enable_day_night_ratio_override) + MutexAutoLock lock(this->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); + return time_to_daynight_ratio(m_time_of_day_f * 24000, m_cache_enable_shaders); } void Environment::setTimeOfDaySpeed(float speed) { - JMutexAutoLock(this->m_timeofday_lock); m_time_of_day_speed = speed; } float Environment::getTimeOfDaySpeed() { - JMutexAutoLock(this->m_timeofday_lock); - float retval = m_time_of_day_speed; - return retval; + 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) { - JMutexAutoLock(this->m_time_lock); + 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() { - JMutexAutoLock(this->m_time_lock); - u32 retval = m_time_of_day; - return retval; + MutexAutoLock lock(this->m_time_lock); + return m_time_of_day; } float Environment::getTimeOfDayF() { - JMutexAutoLock(this->m_time_lock); - float retval = m_time_of_day_f; - return retval; + MutexAutoLock lock(this->m_time_lock); + return m_time_of_day_f; } void Environment::stepTimeOfDay(float dtime) { - // getTimeOfDaySpeed lock the value we need to prevent MT problems - float day_speed = getTimeOfDaySpeed(); + 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; - m_time_counter += dtime; - f32 speed = day_speed * 24000./(24.*3600); - u32 units = (u32)(m_time_counter*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 (speed > 0) { - m_time_counter -= (f32)units / speed; + m_time_conversion_skew -= (f32)units / speed; } - if(!sync_f){ - m_time_of_day_f += day_speed/24/3600*dtime; - if(m_time_of_day_f > 1.0) + 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 */ @@ -280,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 */ @@ -364,6 +590,7 @@ ServerEnvironment::ServerEnvironment(ServerMap *map, m_active_block_interval_overload_skip(0), m_game_time(0), m_game_time_fraction_counter(0), + m_last_clear_objects_time(0), m_recommended_send_interval(0.1), m_max_lag_estimate(0.1) { @@ -425,6 +652,18 @@ bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 return true; } +void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason, + const std::string &str_reason, bool reconnect) +{ + 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); + } +} + void ServerEnvironment::saveLoadedPlayers() { std::string players_path = m_path_world + DIR_DELIM "players"; @@ -440,15 +679,12 @@ void ServerEnvironment::saveLoadedPlayers() } } -void ServerEnvironment::savePlayer(const std::string &playername) +void ServerEnvironment::savePlayer(RemotePlayer *player) { std::string players_path = m_path_world + DIR_DELIM "players"; fs::CreateDir(players_path); - RemotePlayer *player = static_cast(getPlayer(playername.c_str())); - if (player) { - player->save(players_path); - } + player->save(players_path); } Player *ServerEnvironment::loadPlayer(const std::string &playername) @@ -504,6 +740,11 @@ void ServerEnvironment::saveMeta() Settings args; args.setU64("game_time", m_game_time); args.setU64("time_of_day", getTimeOfDay()); + args.setU64("last_clear_objects_time", m_last_clear_objects_time); + args.setU64("lbm_introduction_times_version", 1); + args.set("lbm_introduction_times", + m_lbm_mgr.createIntroductionTimesString()); + args.setU64("day_count", m_day_count); args.writeLines(ss); ss<<"EnvArgsEnd\n"; @@ -541,12 +782,35 @@ void ServerEnvironment::loadMeta() throw SerializationError("Couldn't load env meta game_time"); } + setTimeOfDay(args.exists("time_of_day") ? + // set day to morning by default + args.getU64("time_of_day") : 9000); + + m_last_clear_objects_time = args.exists("last_clear_objects_time") ? + // If missing, do as if clearObjects was never called + args.getU64("last_clear_objects_time") : 0; + + std::string lbm_introduction_times = ""; try { - m_time_of_day = args.getU64("time_of_day"); + u64 ver = args.getU64("lbm_introduction_times_version"); + if (ver == 1) { + lbm_introduction_times = args.get("lbm_introduction_times"); + } else { + infostream << "ServerEnvironment::loadMeta(): Non-supported" + << " introduction time version " << ver << std::endl; + } } catch (SettingNotFoundException &e) { - // This is not as important - m_time_of_day = 9000; + // No problem, this is expected. Just continue with an empty string } + m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time); + + m_day_count = args.exists("day_count") ? + args.getU64("day_count") : 0; +} + +void ServerEnvironment::loadDefaultMeta() +{ + m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time); } struct ActiveABM @@ -584,35 +848,39 @@ 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; @@ -681,7 +949,7 @@ class ABMHandler continue; for(std::vector::iterator - i = j->second.begin(); i != j->second.end(); i++) { + i = j->second.begin(); i != j->second.end(); ++i) { if(myrand() % i->chance != 0) continue; @@ -736,13 +1004,19 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) // 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); @@ -752,18 +1026,21 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) // Activate stored objects activateObjects(block, dtime_s); + /* Handle LoadingBlockModifiers */ + m_lbm_mgr.applyLBMs(this, block, stamp); + // Run node timers - std::map 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)); } } @@ -777,6 +1054,11 @@ 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(); @@ -855,30 +1137,30 @@ void ServerEnvironment::getObjectsInsideRadius(std::vector &objects, v3f po } } -void ServerEnvironment::clearAllObjects() +void ServerEnvironment::clearObjects(ClearObjectsMode mode) { - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Removing all active objects"< objects_to_remove; - for(std::map::iterator + for (std::map::iterator i = m_active_objects.begin(); 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; @@ -890,39 +1172,46 @@ 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::vector::iterator i = objects_to_remove.begin(); + 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::vector loaded_blocks; - infostream<<"ServerEnvironment::clearAllObjects(): " - <<"Listing all loaded blocks"<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: " - <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::vector::iterator i = loaded_blocks.begin(); + for (std::vector::iterator i = loaded_blocks.begin(); i != loaded_blocks.end(); ++i) { v3s16 p = *i; MapBlock *block = m_map->getBlockNoCreateNoEx(p); @@ -931,50 +1220,53 @@ void ServerEnvironment::clearAllObjects() } // 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::vector::iterator i = loadable_blocks.begin(); + 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(report_interval != 0 && - 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::vector::iterator i = loaded_blocks.begin(); + for (std::vector::iterator i = loaded_blocks.begin(); i != loaded_blocks.end(); ++i) { v3s16 p = *i; MapBlock *block = m_map->getBlockNoCreateNoEx(p); @@ -982,14 +1274,16 @@ void ServerEnvironment::clearAllObjects() 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 @@ -1033,9 +1328,8 @@ void ServerEnvironment::step(float dtime) /* 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 */ @@ -1056,7 +1350,7 @@ void ServerEnvironment::step(float dtime) /* 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, @@ -1071,8 +1365,7 @@ void ServerEnvironment::step(float dtime) 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) @@ -1111,11 +1404,10 @@ 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); + 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 = 1.0; + float dtime = m_cache_nodetimer_interval; for(std::set::iterator i = m_active_blocks.m_list.begin(); @@ -1139,38 +1431,39 @@ void ServerEnvironment::step(float dtime) // 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(); @@ -1195,7 +1488,7 @@ 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 " <m_messages_out.empty()) { - m_active_object_messages.push_back( + m_active_object_messages.push( obj->m_messages_out.front()); obj->m_messages_out.pop(); } @@ -1256,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) @@ -1301,61 +1637,15 @@ u16 ServerEnvironment::addActiveObject(ServerActiveObject *object) 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; @@ -1371,18 +1661,19 @@ 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 or deactivating if(object->m_removed || object->m_pending_deactivation) continue; - f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f); + f32 distance_f = object->getBasePosition().getDistanceFrom(player->getPosition()); if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) { // Discard if too far if (distance_f > player_radius_f && player_radius_f != 0) @@ -1396,7 +1687,7 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius, if(n != current_objects.end()) continue; // Add to added_objects - added_objects.insert(id); + added_objects.push(id); } } @@ -1404,12 +1695,11 @@ 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; @@ -1431,20 +1721,19 @@ 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 || object->m_pending_deactivation) - { - removed_objects.insert(id); + if (object->m_removed || object->m_pending_deactivation) { + removed_objects.push(id); continue; } - f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f); + 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; @@ -1452,7 +1741,34 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius, continue; // Object is no longer visible - removed_objects.insert(id); + removed_objects.push(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; + } + + ServerActiveObject *sao = ao_it->second; + sao->m_static_exists = static_exists; + sao->m_static_block = static_block; } } @@ -1462,7 +1778,7 @@ ActiveObjectMessage ServerEnvironment::getActiveObjectMessage() return ActiveObjectMessage(0); ActiveObjectMessage message = m_active_object_messages.front(); - m_active_object_messages.pop_front(); + m_active_object_messages.pop(); return message; } @@ -1498,6 +1814,17 @@ 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()<<")"<raiseModified(MOD_STATE_WRITE_NEEDED, - "addActiveObjectRaw"); + MOD_REASON_ADD_ACTIVE_OBJECT_RAW); } else { v3s16 p = floatToInt(objectpos, BS); errorstream<<"ServerEnvironment::addActiveObjectRaw(): " @@ -1579,7 +1906,7 @@ void ServerEnvironment::removeRemovedObjects() if (block) { block->m_static_objects.remove(id); block->raiseModified(MOD_STATE_WRITE_NEEDED, - "removeRemovedObjects/remove"); + MOD_REASON_REMOVE_OBJECTS_REMOVE); obj->m_static_exists = false; } else { infostream<<"Failed to emerge block from which an object to " @@ -1604,7 +1931,7 @@ void ServerEnvironment::removeRemovedObjects() block->m_static_objects.m_stored.push_back(i->second); block->m_static_objects.m_active.erase(id); block->raiseModified(MOD_STATE_WRITE_NEEDED, - "removeRemovedObjects/deactivate"); + MOD_REASON_REMOVE_OBJECTS_DEACTIVATE); } } else { infostream<<"Failed to emerge block from which an object to " @@ -1690,8 +2017,7 @@ 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; } @@ -1812,7 +2138,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) block->m_static_objects.insert(id, s_obj); obj->m_static_block = blockpos_o; block->raiseModified(MOD_STATE_WRITE_NEEDED, - "deactivateFarObjects: Static data moved in"); + MOD_REASON_STATIC_DATA_ADDED); // Delete from block where object was located block = m_map->emergeBlock(old_static_block, false); @@ -1825,7 +2151,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) } block->m_static_objects.remove(id); block->raiseModified(MOD_STATE_WRITE_NEEDED, - "deactivateFarObjects: Static data moved out"); + MOD_REASON_STATIC_DATA_REMOVED); continue; } @@ -1853,27 +2179,29 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) 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)<raiseModified(MOD_STATE_WRITE_NEEDED, - "deactivateFarObjects: Static data " - "changed considerably"); + MOD_REASON_STATIC_DATA_CHANGED); } } @@ -1926,7 +2253,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) // 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); } @@ -1937,8 +2264,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); obj->m_static_exists = true; obj->m_static_block = block->getPos(); @@ -1993,7 +2319,6 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) } } - #ifndef SERVER #include "clientsimpleobject.h" @@ -2012,7 +2337,7 @@ ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, m_irr(irr) { char zero = 0; - memset(m_attachements, zero, sizeof(m_attachements)); + memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids)); } ClientEnvironment::~ClientEnvironment() @@ -2046,7 +2371,7 @@ 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 @@ -2070,7 +2395,7 @@ LocalPlayer * ClientEnvironment::getLocalPlayer() void ClientEnvironment::step(float dtime) { - DSTACK(__FUNCTION_NAME); + DSTACK(FUNCTION_NAME); /* Step time of day */ stepTimeOfDay(dtime); @@ -2172,15 +2497,6 @@ void ClientEnvironment::step(float dtime) 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); @@ -2403,6 +2719,15 @@ 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; @@ -2529,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="<