Move ServerEnvironment to dedicated cpp/header files
authorLoic Blot <loic.blot@unix-experience.fr>
Sun, 8 Jan 2017 10:01:35 +0000 (11:01 +0100)
committerNer'zhul <nerzhul@users.noreply.github.com>
Sun, 8 Jan 2017 11:16:06 +0000 (12:16 +0100)
* also cleanup some unneeded inclusions

14 files changed:
src/CMakeLists.txt
src/clientiface.cpp
src/collision.cpp
src/environment.cpp
src/environment.h
src/inventorymanager.cpp
src/pathfinder.cpp
src/script/lua_api/l_env.h
src/script/lua_api/l_nodemeta.cpp
src/script/lua_api/l_nodetimer.cpp
src/server.h
src/serverenvironment.cpp [new file with mode: 0644]
src/serverenvironment.h [new file with mode: 0644]
src/treegen.cpp

index 5228eb9acc0830854af8274ba3f8a9a207acc6fe..f90542be9e79a80f5eab5b2357f14d679ead7186 100644 (file)
@@ -452,6 +452,7 @@ set(common_SRCS
        rollback_interface.cpp
        serialization.cpp
        server.cpp
+       serverenvironment.cpp
        serverlist.cpp
        serverobject.cpp
        settings.cpp
index 0390cf0ffc55e821a9dbf45f5dfe79f8f45ff4a7..1610c21fd9d58f9a5cca6b21192e25e6178d2fbd 100644 (file)
@@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "mapblock.h"
 #include "network/connection.h"
-#include "environment.h"
+#include "serverenvironment.h"
 #include "map.h"
 #include "emerge.h"
 #include "content_sao.h"              // TODO this is used for cleanup of only
index 21f14bec15565a7d9d51e9158f23ad6d1e67c4dc..595fa80598445ebbb08aa31379f948e7895afc0a 100644 (file)
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nodedef.h"
 #include "gamedef.h"
 #include "clientenvironment.h"
+#include "serverenvironment.h"
 #include "serverobject.h"
 #include "profiler.h"
 
index 6f6e20238e17d16da66c2f15e1ba496541779980..8c1aad9d3274796c075bc223f40af08035b91165 100644 (file)
@@ -19,35 +19,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include <fstream>
 #include "environment.h"
-#include "filesys.h"
-#include "porting.h"
 #include "collision.h"
-#include "content_mapnode.h"
-#include "mapblock.h"
 #include "serverobject.h"
-#include "content_sao.h"
-#include "settings.h"
-#include "log.h"
-#include "profiler.h"
 #include "scripting_game.h"
-#include "nodedef.h"
-#include "nodemetadata.h"
-#include "gamedef.h"
 #include "server.h"
 #include "daynightratio.h"
-#include "map.h"
 #include "emerge.h"
-#include "raycast.h"
-#include "voxelalgorithms.h"
-#include "util/serialize.h"
-#include "util/basic_macros.h"
-#include "util/pointedthing.h"
-#include "threading/mutex_auto_lock.h"
-
-#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),
@@ -147,2129 +124,3 @@ u32 Environment::getDayCount()
        // Atomic<u32> counter
        return m_day_count;
 }
-
-
-/*
-       ABMWithState
-*/
-
-ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
-       abm(abm_),
-       timer(0)
-{
-       // Initialize timer to random value to spread processing
-       float itv = abm->getTriggerInterval();
-       itv = MYMAX(0.001, itv); // No less than 1ms
-       int minval = MYMAX(-0.51*itv, -60); // Clamp to
-       int maxval = MYMIN(0.51*itv, 60);   // +-60 seconds
-       timer = myrand_range(minval, maxval);
-}
-
-/*
-       LBMManager
-*/
-
-void LBMContentMapping::deleteContents()
-{
-       for (std::vector<LoadingBlockModifierDef *>::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<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
-                       it != lbm_def->trigger_contents.end(); ++it) {
-               std::set<content_t> 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<content_t>::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<LoadingBlockModifierDef *> *
-               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<LoadingBlockModifierDef *>
-       // reference, then we convert it to a pointer.
-       return &(it->second);
-}
-
-LBMManager::~LBMManager()
-{
-       for (std::map<std::string, LoadingBlockModifierDef *>::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 &times,
-               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<std::string, u32> 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<std::string> 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<u32>(components[1]);
-               introduction_times[name] = time;
-               idx = idx_new + 1;
-       }
-
-       // Put stuff from introduction_times into m_lbm_lookup
-       for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
-                       it != introduction_times.end(); ++it) {
-               const std::string &name = it->first;
-               u32 time = it->second;
-
-               std::map<std::string, LoadingBlockModifierDef *>::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<std::string, LoadingBlockModifierDef *>::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<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
-               for (std::vector<LoadingBlockModifierDef *>::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<LoadingBlockModifierDef *> *lbm_list =
-                               iit->second.lookup(c);
-                       if (!lbm_list)
-                               continue;
-                       for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
-                                       lbm_list->begin(); iit != lbm_list->end(); ++iit) {
-                               (*iit)->trigger(env, pos + pos_of_block, n);
-                       }
-               }
-       }
-}
-
-/*
-       ActiveBlockList
-*/
-
-void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
-{
-       v3s16 p;
-       for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
-       for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
-       for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
-       {
-               // limit to a sphere
-               if (p.getDistanceFrom(p0) <= r) {
-                       // Set in list
-                       list.insert(p);
-               }
-       }
-}
-
-void ActiveBlockList::update(std::vector<v3s16> &active_positions,
-               s16 radius,
-               std::set<v3s16> &blocks_removed,
-               std::set<v3s16> &blocks_added)
-{
-       /*
-               Create the new list
-       */
-       std::set<v3s16> newlist = m_forceloaded_list;
-       for(std::vector<v3s16>::iterator i = active_positions.begin();
-                       i != active_positions.end(); ++i)
-       {
-               fillRadiusBlock(*i, radius, newlist);
-       }
-
-       /*
-               Find out which blocks on the old list are not on the new list
-       */
-       // Go through old list
-       for(std::set<v3s16>::iterator i = m_list.begin();
-                       i != m_list.end(); ++i)
-       {
-               v3s16 p = *i;
-               // If not on new list, it's been removed
-               if(newlist.find(p) == newlist.end())
-                       blocks_removed.insert(p);
-       }
-
-       /*
-               Find out which blocks on the new list are not on the old list
-       */
-       // Go through new list
-       for(std::set<v3s16>::iterator i = newlist.begin();
-                       i != newlist.end(); ++i)
-       {
-               v3s16 p = *i;
-               // If not on old list, it's been added
-               if(m_list.find(p) == m_list.end())
-                       blocks_added.insert(p);
-       }
-
-       /*
-               Update m_list
-       */
-       m_list.clear();
-       for(std::set<v3s16>::iterator i = newlist.begin();
-                       i != newlist.end(); ++i)
-       {
-               v3s16 p = *i;
-               m_list.insert(p);
-       }
-}
-
-/*
-       ServerEnvironment
-*/
-
-ServerEnvironment::ServerEnvironment(ServerMap *map,
-               GameScripting *scriptIface, IGameDef *gamedef,
-               const std::string &path_world) :
-       m_map(map),
-       m_script(scriptIface),
-       m_gamedef(gamedef),
-       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_last_clear_objects_time(0),
-       m_recommended_send_interval(0.1),
-       m_max_lag_estimate(0.1)
-{
-}
-
-ServerEnvironment::~ServerEnvironment()
-{
-       // Clear active block list.
-       // This makes the next one delete all active objects.
-       m_active_blocks.clear();
-
-       // Convert all objects to static and delete the active objects
-       deactivateFarObjects(true);
-
-       // Drop/delete map
-       m_map->drop();
-
-       // Delete ActiveBlockModifiers
-       for (std::vector<ABMWithState>::iterator
-                       i = m_abms.begin(); i != m_abms.end(); ++i){
-               delete i->abm;
-       }
-
-       // Deallocate players
-       for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
-                       i != m_players.end(); ++i) {
-               delete (*i);
-       }
-}
-
-Map & ServerEnvironment::getMap()
-{
-       return *m_map;
-}
-
-ServerMap & ServerEnvironment::getServerMap()
-{
-       return *m_map;
-}
-
-RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
-{
-       for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
-                       i != m_players.end(); ++i) {
-               RemotePlayer *player = *i;
-               if (player->peer_id == peer_id)
-                       return player;
-       }
-       return NULL;
-}
-
-RemotePlayer *ServerEnvironment::getPlayer(const char* name)
-{
-       for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
-                       i != m_players.end(); ++i) {
-               RemotePlayer *player = *i;
-               if (strcmp(player->getName(), name) == 0)
-                       return player;
-       }
-       return NULL;
-}
-
-void ServerEnvironment::addPlayer(RemotePlayer *player)
-{
-       DSTACK(FUNCTION_NAME);
-       /*
-               Check that peer_ids are unique.
-               Also check that names are unique.
-               Exception: there can be multiple players with peer_id=0
-       */
-       // If peer id is non-zero, it has to be unique.
-       if (player->peer_id != 0)
-               FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
-       // Name has to be unique.
-       FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
-       // Add.
-       m_players.push_back(player);
-}
-
-void ServerEnvironment::removePlayer(RemotePlayer *player)
-{
-       for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
-                       it != m_players.end(); ++it) {
-               if ((*it) == player) {
-                       delete *it;
-                       m_players.erase(it);
-                       return;
-               }
-       }
-}
-
-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);
-
-       //find out if there's a node on path between pos1 and pos2
-       for (float i = 1; i < distance; i += stepsize) {
-               v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
-                               normalized_vector.Y * i,
-                               normalized_vector.Z * i) +pos1,BS);
-
-               MapNode n = getMap().getNodeNoEx(pos);
-
-               if(n.param0 != CONTENT_AIR) {
-                       if (p) {
-                               *p = pos;
-                       }
-                       return false;
-               }
-       }
-       return true;
-}
-
-void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
-               const std::string &str_reason, bool reconnect)
-{
-       for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
-                       it != m_players.end(); ++it) {
-               RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
-               ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id,
-                               player->protocol_version, reason, str_reason, reconnect);
-       }
-}
-
-void ServerEnvironment::saveLoadedPlayers()
-{
-       std::string players_path = m_path_world + DIR_DELIM "players";
-       fs::CreateDir(players_path);
-
-       for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
-                       it != m_players.end();
-                       ++it) {
-               if ((*it)->checkModified()) {
-                       (*it)->save(players_path, m_gamedef);
-               }
-       }
-}
-
-void ServerEnvironment::savePlayer(RemotePlayer *player)
-{
-       std::string players_path = m_path_world + DIR_DELIM "players";
-       fs::CreateDir(players_path);
-
-       player->save(players_path, m_gamedef);
-}
-
-RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao)
-{
-       bool newplayer = false;
-       bool found = false;
-       std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
-       std::string path = players_path + playername;
-
-       RemotePlayer *player = getPlayer(playername.c_str());
-       if (!player) {
-               player = new RemotePlayer("", m_gamedef->idef());
-               newplayer = true;
-       }
-
-       for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
-               //// Open file and deserialize
-               std::ifstream is(path.c_str(), std::ios_base::binary);
-               if (!is.good())
-                       continue;
-
-               player->deSerialize(is, path, sao);
-               is.close();
-
-               if (player->getName() == playername) {
-                       found = true;
-                       break;
-               }
-
-               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()
-{
-       std::string path = m_path_world + DIR_DELIM "env_meta.txt";
-
-       // Open file and serialize
-       std::ostringstream ss(std::ios_base::binary);
-
-       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";
-
-       if(!fs::safeWriteToFile(path, ss.str()))
-       {
-               infostream<<"ServerEnvironment::saveMeta(): Failed to write "
-                               <<path<<std::endl;
-               throw SerializationError("Couldn't save env meta");
-       }
-}
-
-void ServerEnvironment::loadMeta()
-{
-       std::string path = m_path_world + DIR_DELIM "env_meta.txt";
-
-       // Open file and deserialize
-       std::ifstream is(path.c_str(), std::ios_base::binary);
-       if (!is.good()) {
-               infostream << "ServerEnvironment::loadMeta(): Failed to open "
-                               << path << std::endl;
-               throw SerializationError("Couldn't load env meta");
-       }
-
-       Settings args;
-
-       if (!args.parseConfigLines(is, "EnvArgsEnd")) {
-               throw SerializationError("ServerEnvironment::loadMeta(): "
-                               "EnvArgsEnd not found!");
-       }
-
-       try {
-               m_game_time = args.getU64("game_time");
-       } catch (SettingNotFoundException &e) {
-               // Getting this is crucial, otherwise timestamps are useless
-               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 {
-               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) {
-               // 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
-{
-       ActiveBlockModifier *abm;
-       int chance;
-       std::set<content_t> required_neighbors;
-};
-
-class ABMHandler
-{
-private:
-       ServerEnvironment *m_env;
-       std::vector<std::vector<ActiveABM> *> m_aabms;
-public:
-       ABMHandler(std::vector<ABMWithState> &abms,
-                       float dtime_s, ServerEnvironment *env,
-                       bool use_timers):
-               m_env(env)
-       {
-               if(dtime_s < 0.001)
-                       return;
-               INodeDefManager *ndef = env->getGameDef()->ndef();
-               for(std::vector<ABMWithState>::iterator
-                               i = abms.begin(); i != abms.end(); ++i) {
-                       ActiveBlockModifier *abm = i->abm;
-                       float trigger_interval = abm->getTriggerInterval();
-                       if(trigger_interval < 0.001)
-                               trigger_interval = 0.001;
-                       float actual_interval = dtime_s;
-                       if(use_timers){
-                               i->timer += dtime_s;
-                               if(i->timer < trigger_interval)
-                                       continue;
-                               i->timer -= trigger_interval;
-                               actual_interval = trigger_interval;
-                       }
-                       float chance = abm->getTriggerChance();
-                       if(chance == 0)
-                               chance = 1;
-                       ActiveABM aabm;
-                       aabm.abm = abm;
-                       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<std::string> required_neighbors_s
-                                       = abm->getRequiredNeighbors();
-                       for(std::set<std::string>::iterator
-                                       i = required_neighbors_s.begin();
-                                       i != required_neighbors_s.end(); ++i)
-                       {
-                               ndef->getIds(*i, aabm.required_neighbors);
-                       }
-                       // Trigger contents
-                       std::set<std::string> contents_s = abm->getTriggerContents();
-                       for(std::set<std::string>::iterator
-                                       i = contents_s.begin(); i != contents_s.end(); ++i)
-                       {
-                               std::set<content_t> ids;
-                               ndef->getIds(*i, ids);
-                               for(std::set<content_t>::const_iterator k = ids.begin();
-                                               k != ids.end(); ++k)
-                               {
-                                       content_t c = *k;
-                                       if (c >= m_aabms.size())
-                                               m_aabms.resize(c + 256, NULL);
-                                       if (!m_aabms[c])
-                                               m_aabms[c] = new std::vector<ActiveABM>;
-                                       m_aabms[c]->push_back(aabm);
-                               }
-                       }
-               }
-       }
-
-       ~ABMHandler()
-       {
-               for (size_t i = 0; i < m_aabms.size(); i++)
-                       delete m_aabms[i];
-       }
-
-       // 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() || block->isDummy())
-                       return;
-
-               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.X<MAP_BLOCKSIZE; p0.X++)
-               for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
-               for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
-               {
-                       const MapNode &n = block->getNodeUnsafe(p0);
-                       content_t c = n.getContent();
-
-                       if (c >= m_aabms.size() || !m_aabms[c])
-                               continue;
-
-                       v3s16 p = p0 + block->getPosRelative();
-                       for(std::vector<ActiveABM>::iterator
-                                       i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
-                               if(myrand() % i->chance != 0)
-                                       continue;
-
-                               // Check neighbors
-                               if(!i->required_neighbors.empty())
-                               {
-                                       v3s16 p1;
-                                       for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
-                                       for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
-                                       for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
-                                       {
-                                               if(p1 == p0)
-                                                       continue;
-                                               content_t c;
-                                               if (block->isValidPosition(p1)) {
-                                                       // if the neighbor is found on the same map block
-                                                       // get it straight from there
-                                                       const MapNode &n = block->getNodeUnsafe(p1);
-                                                       c = n.getContent();
-                                               } else {
-                                                       // otherwise consult the map
-                                                       MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
-                                                       c = n.getContent();
-                                               }
-                                               std::set<content_t>::const_iterator k;
-                                               k = i->required_neighbors.find(c);
-                                               if(k != i->required_neighbors.end()){
-                                                       goto neighbor_found;
-                                               }
-                                       }
-                                       // No required neighbor found
-                                       continue;
-                               }
-neighbor_found:
-
-                               // 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;
-                               }
-                       }
-               }
-       }
-};
-
-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 - stamp;
-       dtime_s += additional_dtime;
-
-       /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
-                       <<stamp<<", game time: "<<m_game_time<<std::endl;*/
-
-       // Remove stored static objects if clearObjects was called since block's timestamp
-       if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
-               block->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 "
-                       <<dtime_s<<" seconds old."<<std::endl;*/
-
-       // Activate stored objects
-       activateObjects(block, dtime_s);
-
-       /* Handle LoadingBlockModifiers */
-       m_lbm_mgr.applyLBMs(this, block, stamp);
-
-       // Run node timers
-       std::vector<NodeTimer> elapsed_timers =
-               block->m_node_timers.step((float)dtime_s);
-       if (!elapsed_timers.empty()) {
-               MapNode n;
-               for (std::vector<NodeTimer>::iterator
-                               i = elapsed_timers.begin();
-                               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));
-               }
-       }
-
-       /* Handle ActiveBlockModifiers */
-       ABMHandler abmhandler(m_abms, dtime_s, this, false);
-       abmhandler.apply(block);
-}
-
-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)
-               m_script->node_on_destruct(p, n_old);
-
-       // Replace node
-       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)
-               m_script->node_after_destruct(p, n_old);
-
-       // Call constructor
-       if (ndef->get(n).has_on_construct)
-               m_script->node_on_construct(p, n);
-
-       return true;
-}
-
-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)
-               m_script->node_on_destruct(p, n_old);
-
-       // Replace with air
-       // This is slightly optimized compared to addNodeWithEvent(air)
-       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)
-               m_script->node_after_destruct(p, n_old);
-
-       // Air doesn't require constructor
-       return true;
-}
-
-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<u16> &objects, v3f pos, float radius)
-{
-       for (ActiveObjectMap::iterator i = m_active_objects.begin();
-                       i != m_active_objects.end(); ++i) {
-               ServerActiveObject* obj = i->second;
-               u16 id = i->first;
-               v3f objectpos = obj->getBasePosition();
-               if (objectpos.getDistanceFrom(pos) > radius)
-                       continue;
-               objects.push_back(id);
-       }
-}
-
-void ServerEnvironment::clearObjects(ClearObjectsMode mode)
-{
-       infostream << "ServerEnvironment::clearObjects(): "
-               << "Removing all active objects" << std::endl;
-       std::vector<u16> objects_to_remove;
-       for (ActiveObjectMap::iterator i = m_active_objects.begin();
-                       i != m_active_objects.end(); ++i) {
-               ServerActiveObject* obj = i->second;
-               if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
-                       continue;
-               u16 id = i->first;
-               // Delete static object if block is loaded
-               if (obj->m_static_exists) {
-                       MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
-                       if (block) {
-                               block->m_static_objects.remove(id);
-                               block->raiseModified(MOD_STATE_WRITE_NEEDED,
-                                               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) {
-                       obj->m_pending_deactivation = true;
-                       obj->m_removed = true;
-                       continue;
-               }
-
-               // Tell the object about removal
-               obj->removingFromEnvironment();
-               // Deregister in scripting api
-               m_script->removeObjectReference(obj);
-
-               // Delete active object
-               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<u16>::iterator i = objects_to_remove.begin();
-                       i != objects_to_remove.end(); ++i) {
-               m_active_objects.erase(*i);
-       }
-
-       // Get list of loaded blocks
-       std::vector<v3s16> loaded_blocks;
-       infostream << "ServerEnvironment::clearObjects(): "
-               << "Listing all loaded blocks" << std::endl;
-       m_map->listAllLoadedBlocks(loaded_blocks);
-       infostream << "ServerEnvironment::clearObjects(): "
-               << "Done listing all loaded blocks: "
-               << loaded_blocks.size()<<std::endl;
-
-       // Get list of loadable blocks
-       std::vector<v3s16> 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::vector<v3s16>::iterator i = loaded_blocks.begin();
-                       i != loaded_blocks.end(); ++i) {
-               v3s16 p = *i;
-               MapBlock *block = m_map->getBlockNoCreateNoEx(p);
-               assert(block != NULL);
-               block->refGrab();
-       }
-
-       // Remove objects in all loadable blocks
-       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<v3s16>::iterator i = loadable_blocks.begin();
-                       i != loadable_blocks.end(); ++i) {
-               v3s16 p = *i;
-               MapBlock *block = m_map->emergeBlock(p, false);
-               if (!block) {
-                       errorstream << "ServerEnvironment::clearObjects(): "
-                               << "Failed to emerge block " << PP(p) << std::endl;
-                       continue;
-               }
-               u32 num_stored = block->m_static_objects.m_stored.size();
-               u32 num_active = block->m_static_objects.m_active.size();
-               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,
-                               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) {
-                       float percent = 100.0 * (float)num_blocks_checked /
-                               loadable_blocks.size();
-                       infostream << "ServerEnvironment::clearObjects(): "
-                               << "Cleared " << num_objs_cleared << " objects"
-                               << " in " << num_blocks_cleared << " blocks ("
-                               << percent << "%)" << std::endl;
-               }
-               if (num_blocks_checked % unload_interval == 0) {
-                       m_map->unloadUnreferencedBlocks();
-               }
-       }
-       m_map->unloadUnreferencedBlocks();
-
-       // Drop references that were added above
-       for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
-                       i != loaded_blocks.end(); ++i) {
-               v3s16 p = *i;
-               MapBlock *block = m_map->getBlockNoCreateNoEx(p);
-               assert(block);
-               block->refDrop();
-       }
-
-       m_last_clear_objects_time = m_game_time;
-
-       infostream << "ServerEnvironment::clearObjects(): "
-               << "Finished: Cleared " << num_objs_cleared << " objects"
-               << " in " << num_blocks_cleared << " blocks" << std::endl;
-}
-
-void ServerEnvironment::step(float dtime)
-{
-       DSTACK(FUNCTION_NAME);
-
-       //TimeTaker timer("ServerEnv step");
-
-       /* Step time of day */
-       stepTimeOfDay(dtime);
-
-       // Update this one
-       // NOTE: This is kind of funny on a singleplayer game, but doesn't
-       // really matter that much.
-       static const float server_step = g_settings->getFloat("dedicated_server_step");
-       m_recommended_send_interval = server_step;
-
-       /*
-               Increment game time
-       */
-       {
-               m_game_time_fraction_counter += dtime;
-               u32 inc_i = (u32)m_game_time_fraction_counter;
-               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::vector<RemotePlayer *>::iterator i = m_players.begin();
-                               i != m_players.end(); ++i) {
-                       RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
-                       assert(player);
-
-                       // Ignore disconnected players
-                       if(player->peer_id == 0)
-                               continue;
-
-                       // Move
-                       player->move(dtime, this, 100*BS);
-               }
-       }
-
-       /*
-               Manage active block list
-       */
-       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::vector<v3s16> players_blockpos;
-               for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
-                               i != m_players.end(); ++i) {
-                       RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
-                       assert(player);
-
-                       // Ignore disconnected players
-                       if (player->peer_id == 0)
-                               continue;
-
-                       PlayerSAO *playersao = player->getPlayerSAO();
-                       assert(playersao);
-
-                       v3s16 blockpos = getNodeBlockPos(
-                                       floatToInt(playersao->getBasePosition(), BS));
-                       players_blockpos.push_back(blockpos);
-               }
-
-               /*
-                       Update list of active blocks, collecting changes
-               */
-               static const s16 active_block_range = g_settings->getS16("active_block_range");
-               std::set<v3s16> blocks_removed;
-               std::set<v3s16> blocks_added;
-               m_active_blocks.update(players_blockpos, active_block_range,
-                               blocks_removed, blocks_added);
-
-               /*
-                       Handle removed blocks
-               */
-
-               // Convert active objects that are no more in active blocks to static
-               deactivateFarObjects(false);
-
-               for(std::set<v3s16>::iterator
-                               i = blocks_removed.begin();
-                               i != blocks_removed.end(); ++i) {
-                       v3s16 p = *i;
-
-                       /* infostream<<"Server: Block " << PP(p)
-                               << " became inactive"<<std::endl; */
-
-                       MapBlock *block = m_map->getBlockNoCreateNoEx(p);
-                       if(block==NULL)
-                               continue;
-
-                       // Set current time as timestamp (and let it set ChangedFlag)
-                       block->setTimestamp(m_game_time);
-               }
-
-               /*
-                       Handle added blocks
-               */
-
-               for(std::set<v3s16>::iterator
-                               i = blocks_added.begin();
-                               i != blocks_added.end(); ++i)
-               {
-                       v3s16 p = *i;
-
-                       MapBlock *block = m_map->getBlockOrEmerge(p);
-                       if(block==NULL){
-                               m_active_blocks.m_list.erase(p);
-                               continue;
-                       }
-
-                       activateBlock(block);
-                       /* infostream<<"Server: Block " << PP(p)
-                               << " became active"<<std::endl; */
-               }
-       }
-
-       /*
-               Mess around in active blocks
-       */
-       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<v3s16>::iterator
-                               i = m_active_blocks.m_list.begin();
-                               i != m_active_blocks.m_list.end(); ++i)
-               {
-                       v3s16 p = *i;
-
-                       /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
-                                       <<") being handled"<<std::endl;*/
-
-                       MapBlock *block = m_map->getBlockNoCreateNoEx(p);
-                       if(block==NULL)
-                               continue;
-
-                       // Reset block usage timer
-                       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,
-                                       MOD_REASON_BLOCK_EXPIRED);
-
-                       // Run node timers
-                       std::vector<NodeTimer> elapsed_timers =
-                               block->m_node_timers.step((float)dtime);
-                       if (!elapsed_timers.empty()) {
-                               MapNode n;
-                               for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
-                                               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));
-                                       }
-                               }
-                       }
-               }
-       }
-
-       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 per interval", SPT_AVG);
-               TimeTaker timer("modify in active blocks per interval");
-
-               // Initialize handling of ActiveBlockModifiers
-               ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
-
-               for(std::set<v3s16>::iterator
-                               i = m_active_blocks.m_list.begin();
-                               i != m_active_blocks.m_list.end(); ++i)
-               {
-                       v3s16 p = *i;
-
-                       /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
-                                       <<") being handled"<<std::endl;*/
-
-                       MapBlock *block = m_map->getBlockNoCreateNoEx(p);
-                       if(block == NULL)
-                               continue;
-
-                       // Set current time as timestamp
-                       block->setTimestampNoChangedFlag(m_game_time);
-
-                       /* Handle ActiveBlockModifiers */
-                       abmhandler.apply(block);
-               }
-
-               u32 time_ms = timer.stop(true);
-               u32 max_time_ms = 200;
-               if(time_ms > max_time_ms){
-                       warningstream<<"active block modifiers took "
-                                       <<time_ms<<"ms (longer than "
-                                       <<max_time_ms<<"ms)"<<std::endl;
-                       m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
-               }
-       }while(0);
-
-       /*
-               Step script environment (run global on_step())
-       */
-       m_script->environment_Step(dtime);
-
-       /*
-               Step active objects
-       */
-       {
-               ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
-               //TimeTaker timer("Step active objects");
-
-               g_profiler->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;
-               if(m_send_recommended_timer > getSendRecommendedInterval())
-               {
-                       m_send_recommended_timer -= getSendRecommendedInterval();
-                       send_recommended = true;
-               }
-
-               for(ActiveObjectMap::iterator i = m_active_objects.begin();
-                               i != m_active_objects.end(); ++i) {
-                       ServerActiveObject* obj = i->second;
-                       // Don't step if is to be removed or stored statically
-                       if(obj->m_removed || obj->m_pending_deactivation)
-                               continue;
-                       // Step object
-                       obj->step(dtime, send_recommended);
-                       // Read messages from object
-                       while(!obj->m_messages_out.empty())
-                       {
-                               m_active_object_messages.push(
-                                               obj->m_messages_out.front());
-                               obj->m_messages_out.pop();
-                       }
-               }
-       }
-
-       /*
-               Manage active objects
-       */
-       if(m_object_management_interval.step(dtime, 0.5))
-       {
-               ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
-               /*
-                       Remove objects that satisfy (m_removed && m_known_by_count==0)
-               */
-               removeRemovedObjects();
-       }
-
-       /*
-               Manage particle spawner expiration
-       */
-       if (m_particle_management_interval.step(dtime, 1.0)) {
-               for (UNORDERED_MAP<u32, float>::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++;
-               UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
-               if (f == m_particle_spawners.end()) {
-                       m_particle_spawners[id] = time;
-                       break;
-               }
-       }
-       return id;
-}
-
-u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
-{
-       u32 id = addParticleSpawner(exptime);
-       m_particle_spawner_attachments[id] = attached_id;
-       if (ServerActiveObject *obj = getActiveObject(attached_id)) {
-               obj->attachParticleSpawner(id);
-       }
-       return id;
-}
-
-void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
-{
-       m_particle_spawners.erase(id);
-       UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
-       if (it != m_particle_spawner_attachments.end()) {
-               u16 obj_id = (*it).second;
-               ServerActiveObject *sao = getActiveObject(obj_id);
-               if (sao != NULL && remove_from_object) {
-                       sao->detachParticleSpawner(id);
-               }
-               m_particle_spawner_attachments.erase(id);
-       }
-}
-
-ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
-{
-       ActiveObjectMap::iterator n = m_active_objects.find(id);
-       return (n != m_active_objects.end() ? n->second : NULL);
-}
-
-bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
-{
-       if (id == 0)
-               return false;
-
-       return objects.find(id) == objects.end();
-}
-
-u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
-{
-       //try to reuse id's as late as possible
-       static u16 last_used_id = 0;
-       u16 startid = last_used_id;
-       for(;;)
-       {
-               last_used_id ++;
-               if(isFreeServerActiveObjectId(last_used_id, objects))
-                       return last_used_id;
-
-               if(last_used_id == startid)
-                       return 0;
-       }
-}
-
-u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
-{
-       assert(object); // Pre-condition
-       m_added_objects++;
-       u16 id = addActiveObjectRaw(object, true, 0);
-       return id;
-}
-
-/*
-       Finds out what new objects have been added to
-       inside a radius around a position
-*/
-void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
-               s16 player_radius,
-               std::set<u16> &current_objects,
-               std::queue<u16> &added_objects)
-{
-       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,
-               - discard objects that are too far away,
-               - discard objects that are found in current_objects.
-               - add remaining objects to added_objects
-       */
-       for (ActiveObjectMap::iterator i = m_active_objects.begin();
-                       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(playersao->getBasePosition());
-               if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
-                       // Discard if too far
-                       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<u16>::iterator n;
-               n = current_objects.find(id);
-               if(n != current_objects.end())
-                       continue;
-               // Add to added_objects
-               added_objects.push(id);
-       }
-}
-
-/*
-       Finds out what objects have been removed from
-       inside a radius around a position
-*/
-void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
-               s16 player_radius,
-               std::set<u16> &current_objects,
-               std::queue<u16> &removed_objects)
-{
-       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
-                 error condition; objects should be set m_removed=true and removed
-                 only after all clients have been informed about removal), or
-               - object has m_removed=true, or
-               - object is too far away
-       */
-       for(std::set<u16>::iterator
-                       i = current_objects.begin();
-                       i != current_objects.end(); ++i)
-       {
-               u16 id = *i;
-               ServerActiveObject *object = getActiveObject(id);
-
-               if (object == NULL) {
-                       infostream << "ServerEnvironment::getRemovedActiveObjects():"
-                               << " object in current_objects is NULL" << std::endl;
-                       removed_objects.push(id);
-                       continue;
-               }
-
-               if (object->m_removed || object->m_pending_deactivation) {
-                       removed_objects.push(id);
-                       continue;
-               }
-
-               f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
-               if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
-                       if (distance_f <= player_radius_f || player_radius_f == 0)
-                               continue;
-               } else if (distance_f <= radius_f)
-                       continue;
-
-               // Object is no longer visible
-               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<u16, StaticObject>::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
-               ActiveObjectMap::iterator 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;
-       }
-}
-
-ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
-{
-       if(m_active_object_messages.empty())
-               return ActiveObjectMessage(0);
-
-       ActiveObjectMessage message = m_active_object_messages.front();
-       m_active_object_messages.pop();
-       return message;
-}
-
-/*
-       ************ Private methods *************
-*/
-
-u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
-               bool set_changed, u32 dtime_s)
-{
-       assert(object); // Pre-condition
-       if(object->getId() == 0){
-               u16 new_id = getFreeServerActiveObjectId(m_active_objects);
-               if(new_id == 0)
-               {
-                       errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
-                                       <<"no free ids available"<<std::endl;
-                       if(object->environmentDeletes())
-                               delete object;
-                       return 0;
-               }
-               object->setId(new_id);
-       }
-       else{
-               verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
-                               <<"supplied with id "<<object->getId()<<std::endl;
-       }
-
-       if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
-               errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
-                               <<"id is not free ("<<object->getId()<<")"<<std::endl;
-               if(object->environmentDeletes())
-                       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="<<object->getId()<<")"<<std::endl;*/
-
-       m_active_objects[object->getId()] = object;
-
-       verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
-                       <<"Added id="<<object->getId()<<"; there are now "
-                       <<m_active_objects.size()<<" active objects."
-                       <<std::endl;
-
-       // Register reference in scripting api (must be done before post-init)
-       m_script->addObjectReference(object);
-       // Post-initialize object
-       object->addedToEnvironment(dtime_s);
-
-       // Add static data to block
-       if(object->isStaticAllowed())
-       {
-               // Add static object to active static list of the block
-               v3f objectpos = object->getBasePosition();
-               std::string staticdata = object->getStaticData();
-               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->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,
-                                       MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
-               } else {
-                       v3s16 p = floatToInt(objectpos, BS);
-                       errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
-                                       <<"could not emerge block for storing id="<<object->getId()
-                                       <<" statically (pos="<<PP(p)<<")"<<std::endl;
-               }
-       }
-
-       return object->getId();
-}
-
-/*
-       Remove objects that satisfy (m_removed && m_known_by_count==0)
-*/
-void ServerEnvironment::removeRemovedObjects()
-{
-       std::vector<u16> objects_to_remove;
-       for(ActiveObjectMap::iterator i = m_active_objects.begin();
-                       i != m_active_objects.end(); ++i) {
-               u16 id = i->first;
-               ServerActiveObject* obj = i->second;
-               // This shouldn't happen but check it
-               if(obj == NULL)
-               {
-                       infostream<<"NULL object found in ServerEnvironment"
-                                       <<" while finding removed objects. id="<<id<<std::endl;
-                       // Id to be removed from m_active_objects
-                       objects_to_remove.push_back(id);
-                       continue;
-               }
-
-               /*
-                       We will delete objects that are marked as removed or thatare
-                       waiting for deletion after deactivation
-               */
-               if (!obj->m_removed && !obj->m_pending_deactivation)
-                       continue;
-
-               /*
-                       Delete static data from block if is marked as removed
-               */
-               if(obj->m_static_exists && obj->m_removed)
-               {
-                       MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
-                       if (block) {
-                               block->m_static_objects.remove(id);
-                               block->raiseModified(MOD_STATE_WRITE_NEEDED,
-                                       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="<<id<<std::endl;
-                       }
-               }
-
-               // If m_known_by_count > 0, don't actually remove. On some future
-               // invocation this will be 0, which is when removal will continue.
-               if(obj->m_known_by_count > 0)
-                       continue;
-
-               /*
-                       Move static data from active to stored if not marked as removed
-               */
-               if(obj->m_static_exists && !obj->m_removed){
-                       MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
-                       if (block) {
-                               std::map<u16, StaticObject>::iterator i =
-                                               block->m_static_objects.m_active.find(id);
-                               if(i != block->m_static_objects.m_active.end()){
-                                       block->m_static_objects.m_stored.push_back(i->second);
-                                       block->m_static_objects.m_active.erase(id);
-                                       block->raiseModified(MOD_STATE_WRITE_NEEDED,
-                                               MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
-                               }
-                       } else {
-                               infostream<<"Failed to emerge block from which an object to "
-                                               <<"be deactivated was loaded from. id="<<id<<std::endl;
-                       }
-               }
-
-               // Tell the object about removal
-               obj->removingFromEnvironment();
-               // Deregister in scripting api
-               m_script->removeObjectReference(obj);
-
-               // Delete
-               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<u16>::iterator i = objects_to_remove.begin();
-                       i != objects_to_remove.end(); ++i) {
-               m_active_objects.erase(*i);
-       }
-}
-
-static void print_hexdump(std::ostream &o, const std::string &data)
-{
-       const int linelength = 16;
-       for(int l=0; ; l++){
-               int i0 = linelength * l;
-               bool at_end = false;
-               int thislinelength = linelength;
-               if(i0 + thislinelength > (int)data.size()){
-                       thislinelength = data.size() - i0;
-                       at_end = true;
-               }
-               for(int di=0; di<linelength; di++){
-                       int i = i0 + di;
-                       char buf[4];
-                       if(di<thislinelength)
-                               snprintf(buf, 4, "%.2x ", data[i]);
-                       else
-                               snprintf(buf, 4, "   ");
-                       o<<buf;
-               }
-               o<<" ";
-               for(int di=0; di<thislinelength; di++){
-                       int i = i0 + di;
-                       if(data[i] >= 32)
-                               o<<data[i];
-                       else
-                               o<<".";
-               }
-               o<<std::endl;
-               if(at_end)
-                       break;
-       }
-}
-
-/*
-       Convert stored objects from blocks near the players to active.
-*/
-void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
-{
-       if(block == NULL)
-               return;
-
-       // Ignore if no stored objects (to not set changed flag)
-       if(block->m_static_objects.m_stored.empty())
-               return;
-
-       verbosestream<<"ServerEnvironment::activateObjects(): "
-                       <<"activating objects of block "<<PP(block->getPos())
-                       <<" ("<<block->m_static_objects.m_stored.size()
-                       <<" objects)"<<std::endl;
-       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: "
-                               <<block->m_static_objects.m_stored.size()<<" in "
-                               <<PP(block->getPos())
-                               <<"; removing all of them."<<std::endl;
-               // Clear stored list
-               block->m_static_objects.m_stored.clear();
-               block->raiseModified(MOD_STATE_WRITE_NEEDED,
-                       MOD_REASON_TOO_MANY_OBJECTS);
-               return;
-       }
-
-       // Activate stored objects
-       std::vector<StaticObject> new_stored;
-       for (std::vector<StaticObject>::iterator
-                       i = block->m_static_objects.m_stored.begin();
-                       i != block->m_static_objects.m_stored.end(); ++i) {
-               StaticObject &s_obj = *i;
-
-               // Create an active object from the data
-               ServerActiveObject *obj = ServerActiveObject::create
-                               ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
-               // If couldn't create object, store static data back.
-               if(obj == NULL) {
-                       errorstream<<"ServerEnvironment::activateObjects(): "
-                                       <<"failed to create active object from static object "
-                                       <<"in block "<<PP(s_obj.pos/BS)
-                                       <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
-                       print_hexdump(verbosestream, s_obj.data);
-
-                       new_stored.push_back(s_obj);
-                       continue;
-               }
-               verbosestream<<"ServerEnvironment::activateObjects(): "
-                               <<"activated static object pos="<<PP(s_obj.pos/BS)
-                               <<" type="<<(int)s_obj.type<<std::endl;
-               // This will also add the object to the active static list
-               addActiveObjectRaw(obj, false, dtime_s);
-       }
-       // Clear stored list
-       block->m_static_objects.m_stored.clear();
-       // Add leftover failed stuff to stored list
-       for(std::vector<StaticObject>::iterator
-                       i = new_stored.begin();
-                       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<u16, StaticObject>::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
-               static list to the active static list.
-               As such, the block is essentially the same.
-               Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
-               Otherwise there would be a huge amount of unnecessary I/O.
-       */
-}
-
-/*
-       Convert objects that are not standing inside active blocks to static.
-
-       If m_known_by_count != 0, active object is not deleted, but static
-       data is still updated.
-
-       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),
-*/
-void ServerEnvironment::deactivateFarObjects(bool force_delete)
-{
-       std::vector<u16> objects_to_remove;
-       for(ActiveObjectMap::iterator i = m_active_objects.begin();
-                       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;
-
-               // If pending deactivation, let removeRemovedObjects() do it
-               if(!force_delete && obj->m_pending_deactivation)
-                       continue;
-
-               u16 id = i->first;
-               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="<<id
-                                               <<" to it's current block "<<PP(blockpos_o)
-                                               <<std::endl;
-                               continue;
-                       }
-                       std::string staticdata_new = obj->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="<<id
-                                               <<" from it's previous block "<<PP(old_static_block)
-                                               <<std::endl;
-                               continue;
-                       }
-                       block->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;
-
-               verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
-                               <<"deactivating object id="<<id<<" on inactive block "
-                               <<PP(blockpos_o)<<std::endl;
-
-               // If known by some client, don't immediately delete.
-               bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
-
-               /*
-                       Update the static data
-               */
-
-               if(obj->isStaticAllowed())
-               {
-                       // 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)
-                                       stays_in_same_block = true;
-
-                               MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
-
-                               if (block) {
-                                       std::map<u16, StaticObject>::iterator n =
-                                               block->m_static_objects.m_active.find(id);
-                                       if (n != block->m_static_objects.m_active.end()) {
-                                               StaticObject static_old = n->second;
-
-                                               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(): "
-                                                       <<"id="<<id<<" m_static_exists=true but "
-                                                       <<"static data doesn't actually exist in "
-                                                       <<PP(obj->m_static_block)<<std::endl;
-                                       }
-                               }
-                       }
-
-                       bool shall_be_written = (!stays_in_same_block || data_changed);
-
-                       // Delete old static object
-                       if(obj->m_static_exists)
-                       {
-                               MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
-                               if(block)
-                               {
-                                       block->m_static_objects.remove(id);
-                                       obj->m_static_exists = false;
-                                       // Only mark block as modified if data changed considerably
-                                       if(shall_be_written)
-                                               block->raiseModified(MOD_STATE_WRITE_NEEDED,
-                                                       MOD_REASON_STATIC_DATA_CHANGED);
-                               }
-                       }
-
-                       // Add to the block where the object is located in
-                       v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
-                       // Get or generate the block
-                       MapBlock *block = NULL;
-                       try{
-                               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() >= g_settings->getU16("max_objects_per_block")) {
-                                       warningstream << "ServerEnv: Trying to store id = " << obj->getId()
-                                                       << " statically but block " << PP(blockpos)
-                                                       << " already contains "
-                                                       << block->m_static_objects.m_stored.size()
-                                                       << " objects."
-                                                       << " Forcing delete." << std::endl;
-                                       force_delete = true;
-                               } else {
-                                       // If static counterpart already exists in target block,
-                                       // remove it first.
-                                       // This shouldn't happen because the object is removed from
-                                       // the previous block before this according to
-                                       // obj->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()){
-                                               warningstream<<"ServerEnv: Performing hack #83274"
-                                                               <<std::endl;
-                                               block->m_static_objects.remove(id);
-                                       }
-                                       // 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,
-                                                       MOD_REASON_STATIC_DATA_CHANGED);
-
-                                       obj->m_static_exists = true;
-                                       obj->m_static_block = block->getPos();
-                               }
-                       }
-                       else{
-                               if(!force_delete){
-                                       v3s16 p = floatToInt(objectpos, BS);
-                                       errorstream<<"ServerEnv: Could not find or generate "
-                                                       <<"a block for storing id="<<obj->getId()
-                                                       <<" statically (pos="<<PP(p)<<")"<<std::endl;
-                                       continue;
-                               }
-                       }
-               }
-
-               /*
-                       If known by some client, set pending deactivation.
-                       Otherwise delete it immediately.
-               */
-
-               if(pending_delete && !force_delete)
-               {
-                       verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
-                                       <<"object id="<<id<<" is known by clients"
-                                       <<"; not deleting yet"<<std::endl;
-
-                       obj->m_pending_deactivation = true;
-                       continue;
-               }
-
-               verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
-                               <<"object id="<<id<<" is not known by clients"
-                               <<"; deleting"<<std::endl;
-
-               // Tell the object about removal
-               obj->removingFromEnvironment();
-               // Deregister in scripting api
-               m_script->removeObjectReference(obj);
-
-               // Delete active object
-               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<u16>::iterator i = objects_to_remove.begin();
-                       i != objects_to_remove.end(); ++i) {
-               m_active_objects.erase(*i);
-       }
-}
-
index 209d795d870bf35a5158ebaeebd2b1c7afcb0875..14a18421bc0b4f11a493bdb8bc6132ad1aa23c04 100644 (file)
@@ -30,7 +30,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        - etc.
 */
 
-#include <set>
 #include <list>
 #include <queue>
 #include <map>
@@ -43,17 +42,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "threading/atomic.h"
 #include "network/networkprotocol.h" // for AccessDeniedCode
 
-class ServerEnvironment;
-class ActiveBlockModifier;
-class ServerActiveObject;
 class ITextureSource;
 class IGameDef;
 class Map;
-class ServerMap;
 class GameScripting;
 class Player;
-class RemotePlayer;
-class PlayerSAO;
 class PointedThing;
 
 class Environment
@@ -134,395 +127,5 @@ private:
        DISABLE_CLASS_COPY(Environment);
 };
 
-/*
-       {Active, Loading} block modifier interface.
-
-       These are fed into ServerEnvironment at initialization time;
-       ServerEnvironment handles deleting them.
-*/
-
-class ActiveBlockModifier
-{
-public:
-       ActiveBlockModifier(){};
-       virtual ~ActiveBlockModifier(){};
-
-       // Set of contents to trigger on
-       virtual std::set<std::string> getTriggerContents()=0;
-       // Set of required neighbors (trigger doesn't happen if none are found)
-       // Empty = do not check neighbors
-       virtual std::set<std::string> getRequiredNeighbors()
-       { return std::set<std::string>(); }
-       // Trigger interval in seconds
-       virtual float getTriggerInterval() = 0;
-       // Random chance of (1 / return value), 0 is disallowed
-       virtual u32 getTriggerChance() = 0;
-       // Whether to modify chance to simulate time lost by an unnattended block
-       virtual bool getSimpleCatchUp() = 0;
-       // This is called usually at interval for 1/chance of the nodes
-       virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){};
-       virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
-                       u32 active_object_count, u32 active_object_count_wider){};
-};
-
-struct ABMWithState
-{
-       ActiveBlockModifier *abm;
-       float timer;
-
-       ABMWithState(ActiveBlockModifier *abm_);
-};
-
-struct LoadingBlockModifierDef
-{
-       // Set of contents to trigger on
-       std::set<std::string> trigger_contents;
-       std::string name;
-       bool run_at_every_load;
-
-       virtual ~LoadingBlockModifierDef() {}
-       virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){};
-};
-
-struct LBMContentMapping
-{
-       typedef std::map<content_t, std::vector<LoadingBlockModifierDef *> > container_map;
-       container_map map;
-
-       std::vector<LoadingBlockModifierDef *> lbm_list;
-
-       // Needs to be separate method (not inside destructor),
-       // because the LBMContentMapping may be copied and destructed
-       // many times during operation in the lbm_lookup_map.
-       void deleteContents();
-       void addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef);
-       const std::vector<LoadingBlockModifierDef *> *lookup(content_t c) const;
-};
-
-class LBMManager
-{
-public:
-       LBMManager():
-               m_query_mode(false)
-       {}
-
-       ~LBMManager();
-
-       // Don't call this after loadIntroductionTimes() ran.
-       void addLBMDef(LoadingBlockModifierDef *lbm_def);
-
-       void loadIntroductionTimes(const std::string &times,
-               IGameDef *gamedef, u32 now);
-
-       // Don't call this before loadIntroductionTimes() ran.
-       std::string createIntroductionTimesString();
-
-       // Don't call this before loadIntroductionTimes() ran.
-       void applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp);
-
-       // Warning: do not make this std::unordered_map, order is relevant here
-       typedef std::map<u32, LBMContentMapping> lbm_lookup_map;
-
-private:
-       // Once we set this to true, we can only query,
-       // not modify
-       bool m_query_mode;
-
-       // For m_query_mode == false:
-       // The key of the map is the LBM def's name.
-       // TODO make this std::unordered_map
-       std::map<std::string, LoadingBlockModifierDef *> m_lbm_defs;
-
-       // For m_query_mode == true:
-       // The key of the map is the LBM def's first introduction time.
-       lbm_lookup_map m_lbm_lookup;
-
-       // Returns an iterator to the LBMs that were introduced
-       // after the given time. This is guaranteed to return
-       // valid values for everything
-       lbm_lookup_map::const_iterator getLBMsIntroducedAfter(u32 time)
-       { return m_lbm_lookup.lower_bound(time); }
-};
-
-/*
-       List of active blocks, used by ServerEnvironment
-*/
-
-class ActiveBlockList
-{
-public:
-       void update(std::vector<v3s16> &active_positions,
-                       s16 radius,
-                       std::set<v3s16> &blocks_removed,
-                       std::set<v3s16> &blocks_added);
-
-       bool contains(v3s16 p){
-               return (m_list.find(p) != m_list.end());
-       }
-
-       void clear(){
-               m_list.clear();
-       }
-
-       std::set<v3s16> m_list;
-       std::set<v3s16> m_forceloaded_list;
-
-private:
-};
-
-/*
-       Operation mode for ServerEnvironment::clearObjects()
-*/
-enum ClearObjectsMode {
-       // Load and go through every mapblock, clearing objects
-       CLEAR_OBJECTS_MODE_FULL,
-
-       // Clear objects immediately in loaded mapblocks;
-       // clear objects in unloaded mapblocks only when the mapblocks are next activated.
-       CLEAR_OBJECTS_MODE_QUICK,
-};
-
-/*
-       The server-side environment.
-
-       This is not thread-safe. Server uses an environment mutex.
-*/
-
-typedef UNORDERED_MAP<u16, ServerActiveObject *> ActiveObjectMap;
-
-class ServerEnvironment : public Environment
-{
-public:
-       ServerEnvironment(ServerMap *map, GameScripting *scriptIface,
-                       IGameDef *gamedef, const std::string &path_world);
-       ~ServerEnvironment();
-
-       Map & getMap();
-
-       ServerMap & getServerMap();
-
-       //TODO find way to remove this fct!
-       GameScripting* getScriptIface()
-               { return m_script; }
-
-       IGameDef *getGameDef()
-               { return m_gamedef; }
-
-       float getSendRecommendedInterval()
-               { return m_recommended_send_interval; }
-
-       void kickAllPlayers(AccessDeniedCode reason,
-               const std::string &str_reason, bool reconnect);
-       // Save players
-       void saveLoadedPlayers();
-       void savePlayer(RemotePlayer *player);
-       RemotePlayer *loadPlayer(const std::string &playername, PlayerSAO *sao);
-       void addPlayer(RemotePlayer *player);
-       void removePlayer(RemotePlayer *player);
-
-       /*
-               Save and load time of day and game timer
-       */
-       void saveMeta();
-       void loadMeta();
-       // to be called instead of loadMeta if
-       // env_meta.txt doesn't exist (e.g. new world)
-       void loadDefaultMeta();
-
-       u32 addParticleSpawner(float exptime);
-       u32 addParticleSpawner(float exptime, u16 attached_id);
-       void deleteParticleSpawner(u32 id, bool remove_from_object = true);
-
-       /*
-               External ActiveObject interface
-               -------------------------------------------
-       */
-
-       ServerActiveObject* getActiveObject(u16 id);
-
-       /*
-               Add an active object to the environment.
-               Environment handles deletion of object.
-               Object may be deleted by environment immediately.
-               If id of object is 0, assigns a free id to it.
-               Returns the id of the object.
-               Returns 0 if not added and thus deleted.
-       */
-       u16 addActiveObject(ServerActiveObject *object);
-
-       /*
-               Add an active object as a static object to the corresponding
-               MapBlock.
-               Caller allocates memory, ServerEnvironment frees memory.
-               Return value: true if succeeded, false if failed.
-               (note:  not used, pending removal from engine)
-       */
-       //bool addActiveObjectAsStatic(ServerActiveObject *object);
-
-       /*
-               Find out what new objects have been added to
-               inside a radius around a position
-       */
-       void getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
-                       s16 player_radius,
-                       std::set<u16> &current_objects,
-                       std::queue<u16> &added_objects);
-
-       /*
-               Find out what new objects have been removed from
-               inside a radius around a position
-       */
-       void getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
-                       s16 player_radius,
-                       std::set<u16> &current_objects,
-                       std::queue<u16> &removed_objects);
-
-       /*
-               Get the next message emitted by some active object.
-               Returns a message with id=0 if no messages are available.
-       */
-       ActiveObjectMessage getActiveObjectMessage();
-
-       /*
-               Activate objects and dynamically modify for the dtime determined
-               from timestamp and additional_dtime
-       */
-       void activateBlock(MapBlock *block, u32 additional_dtime=0);
-
-       /*
-               {Active,Loading}BlockModifiers
-               -------------------------------------------
-       */
-
-       void addActiveBlockModifier(ActiveBlockModifier *abm);
-       void addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm);
-
-       /*
-               Other stuff
-               -------------------------------------------
-       */
-
-       // Script-aware node setters
-       bool setNode(v3s16 p, const MapNode &n);
-       bool removeNode(v3s16 p);
-       bool swapNode(v3s16 p, const MapNode &n);
-
-       // Find all active objects inside a radius around a point
-       void getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius);
-
-       // Clear objects, loading and going through every MapBlock
-       void clearObjects(ClearObjectsMode mode);
-
-       // This makes stuff happen
-       void step(f32 dtime);
-
-       //check if there's a line of sight between two positions
-       bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0, v3s16 *p=NULL);
-
-       u32 getGameTime() { return m_game_time; }
-
-       void reportMaxLagEstimate(float f) { m_max_lag_estimate = f; }
-       float getMaxLagEstimate() { return m_max_lag_estimate; }
-
-       std::set<v3s16>* getForceloadedBlocks() { return &m_active_blocks.m_forceloaded_list; };
-
-       // Sets the static object status all the active objects in the specified block
-       // This is only really needed for deleting blocks from the map
-       void setStaticForActiveObjectsInBlock(v3s16 blockpos,
-               bool static_exists, v3s16 static_block=v3s16(0,0,0));
-
-       RemotePlayer *getPlayer(const u16 peer_id);
-       RemotePlayer *getPlayer(const char* name);
-private:
-
-       /*
-               Internal ActiveObject interface
-               -------------------------------------------
-       */
-
-       /*
-               Add an active object to the environment.
-
-               Called by addActiveObject.
-
-               Object may be deleted by environment immediately.
-               If id of object is 0, assigns a free id to it.
-               Returns the id of the object.
-               Returns 0 if not added and thus deleted.
-       */
-       u16 addActiveObjectRaw(ServerActiveObject *object, bool set_changed, u32 dtime_s);
-
-       /*
-               Remove all objects that satisfy (m_removed && m_known_by_count==0)
-       */
-       void removeRemovedObjects();
-
-       /*
-               Convert stored objects from block to active
-       */
-       void activateObjects(MapBlock *block, u32 dtime_s);
-
-       /*
-               Convert objects that are not in active blocks to static.
-
-               If m_known_by_count != 0, active object is not deleted, but static
-               data is still updated.
-
-               If force_delete is set, active object is deleted nevertheless. It
-               shall only be set so in the destructor of the environment.
-       */
-       void deactivateFarObjects(bool force_delete);
-
-       /*
-               Member variables
-       */
-
-       // The map
-       ServerMap *m_map;
-       // Lua state
-       GameScripting* m_script;
-       // Game definition
-       IGameDef *m_gamedef;
-       // World path
-       const std::string m_path_world;
-       // Active object list
-       ActiveObjectMap m_active_objects;
-       // Outgoing network message buffer for active objects
-       std::queue<ActiveObjectMessage> m_active_object_messages;
-       // Some timers
-       float m_send_recommended_timer;
-       IntervalLimiter m_object_management_interval;
-       // List of active blocks
-       ActiveBlockList m_active_blocks;
-       IntervalLimiter m_active_blocks_management_interval;
-       IntervalLimiter m_active_block_modifier_interval;
-       IntervalLimiter m_active_blocks_nodemetadata_interval;
-       int m_active_block_interval_overload_skip;
-       // Time from the beginning of the game in seconds.
-       // Incremented in step().
-       u32 m_game_time;
-       // A helper variable for incrementing the latter
-       float m_game_time_fraction_counter;
-       // Time of last clearObjects call (game time).
-       // When a mapblock older than this is loaded, its objects are cleared.
-       u32 m_last_clear_objects_time;
-       // Active block modifiers
-       std::vector<ABMWithState> m_abms;
-       LBMManager m_lbm_mgr;
-       // An interval for generally sending object positions and stuff
-       float m_recommended_send_interval;
-       // Estimate for general maximum lag as determined by server.
-       // Can raise to high values like 15s with eg. map generation mods.
-       float m_max_lag_estimate;
-
-       // peer_ids in here should be unique, except that there may be many 0s
-       std::vector<RemotePlayer*> m_players;
-
-       // Particles
-       IntervalLimiter m_particle_management_interval;
-       UNORDERED_MAP<u32, float> m_particle_spawners;
-       UNORDERED_MAP<u32, u16> m_particle_spawner_attachments;
-};
-
 #endif
 
index f36a2fa4ee46f82ea9f2f475296038f72c27dac8..469e7396bf049a7c53cd5e310c0f4b49709a11e0 100644 (file)
@@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "inventorymanager.h"
 #include "log.h"
-#include "environment.h"
+#include "serverenvironment.h"
 #include "scripting_game.h"
 #include "serverobject.h"
 #include "settings.h"
index 073670c6d9bc33a17913a305fdc32a7cf4b1eba4..84aa9252c10503ba0c9ff2160e72de7ef970eb21 100644 (file)
@@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 /******************************************************************************/
 
 #include "pathfinder.h"
-#include "environment.h"
+#include "serverenvironment.h"
 #include "gamedef.h"
 #include "nodedef.h"
 #include "map.h"
index 89dd7978f910f914ceef3b04bf093b2d099f3f81..21b235f841beacccdae54de959bdf814ce787268 100644 (file)
@@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define L_ENV_H_
 
 #include "lua_api/l_base.h"
-#include "environment.h"
+#include "serverenvironment.h"
 
 class ModApiEnvMod : public ModApiBase {
 private:
index c8bc7d558ccadaf5f1bca106833ea3ac852af019..3cdd3cbfefbab0d2d4b409badd188299bc2fee90 100644 (file)
@@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "lua_api/l_inventory.h"
 #include "common/c_converter.h"
 #include "common/c_content.h"
-#include "environment.h"
+#include "serverenvironment.h"
 #include "map.h"
 #include "gamedef.h"
 #include "nodemetadata.h"
index 3242d6ea58b54aed8319eff50a4037a58cb3dfe2..ed11cc58e7c5b3b47d02e99bc3c059a6c325ce18 100644 (file)
@@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "lua_api/l_nodetimer.h"
 #include "lua_api/l_internal.h"
-#include "environment.h"
+#include "serverenvironment.h"
 #include "map.h"
 
 
index f0df0f9ec6d8fdd7639d25918f04ec6a86f247e7..fe7b50b7756fc0773343addba2b32e05293e0e62 100644 (file)
@@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/numeric.h"
 #include "util/thread.h"
 #include "util/basic_macros.h"
-#include "environment.h"
+#include "serverenvironment.h"
 #include "chat_interface.h"
 #include "clientiface.h"
 #include "remoteplayer.h"
diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp
new file mode 100644 (file)
index 0000000..6229e4c
--- /dev/null
@@ -0,0 +1,2167 @@
+/*
+Minetest
+Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "serverenvironment.h"
+#include "content_sao.h"
+#include "settings.h"
+#include "log.h"
+#include "nodedef.h"
+#include "nodemetadata.h"
+#include "gamedef.h"
+#include "map.h"
+#include "profiler.h"
+#include "raycast.h"
+#include "remoteplayer.h"
+#include "scripting_game.h"
+#include "server.h"
+#include "voxelalgorithms.h"
+#include "util/serialize.h"
+#include "util/basic_macros.h"
+#include "util/pointedthing.h"
+#include "threading/mutex_auto_lock.h"
+#include "filesys.h"
+
+#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
+
+/*
+       ABMWithState
+*/
+
+ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
+       abm(abm_),
+       timer(0)
+{
+       // Initialize timer to random value to spread processing
+       float itv = abm->getTriggerInterval();
+       itv = MYMAX(0.001, itv); // No less than 1ms
+       int minval = MYMAX(-0.51*itv, -60); // Clamp to
+       int maxval = MYMIN(0.51*itv, 60);   // +-60 seconds
+       timer = myrand_range(minval, maxval);
+}
+
+/*
+       LBMManager
+*/
+
+void LBMContentMapping::deleteContents()
+{
+       for (std::vector<LoadingBlockModifierDef *>::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<std::string>::const_iterator it = lbm_def->trigger_contents.begin();
+               it != lbm_def->trigger_contents.end(); ++it) {
+               std::set<content_t> 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<content_t>::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<LoadingBlockModifierDef *> *
+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<LoadingBlockModifierDef *>
+       // reference, then we convert it to a pointer.
+       return &(it->second);
+}
+
+LBMManager::~LBMManager()
+{
+       for (std::map<std::string, LoadingBlockModifierDef *>::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 &times,
+       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<std::string, u32> 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<std::string> 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<u32>(components[1]);
+               introduction_times[name] = time;
+               idx = idx_new + 1;
+       }
+
+       // Put stuff from introduction_times into m_lbm_lookup
+       for (std::map<std::string, u32>::const_iterator it = introduction_times.begin();
+               it != introduction_times.end(); ++it) {
+               const std::string &name = it->first;
+               u32 time = it->second;
+
+               std::map<std::string, LoadingBlockModifierDef *>::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<std::string, LoadingBlockModifierDef *>::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<LoadingBlockModifierDef *> &lbm_list = it->second.lbm_list;
+               for (std::vector<LoadingBlockModifierDef *>::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<LoadingBlockModifierDef *> *lbm_list =
+                                               iit->second.lookup(c);
+                                       if (!lbm_list)
+                                               continue;
+                                       for (std::vector<LoadingBlockModifierDef *>::const_iterator iit =
+                                               lbm_list->begin(); iit != lbm_list->end(); ++iit) {
+                                               (*iit)->trigger(env, pos + pos_of_block, n);
+                                       }
+                               }
+                       }
+}
+
+/*
+       ActiveBlockList
+*/
+
+void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
+{
+       v3s16 p;
+       for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
+               for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
+                       for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
+                       {
+                               // limit to a sphere
+                               if (p.getDistanceFrom(p0) <= r) {
+                                       // Set in list
+                                       list.insert(p);
+                               }
+                       }
+}
+
+void ActiveBlockList::update(std::vector<v3s16> &active_positions,
+       s16 radius,
+       std::set<v3s16> &blocks_removed,
+       std::set<v3s16> &blocks_added)
+{
+       /*
+               Create the new list
+       */
+       std::set<v3s16> newlist = m_forceloaded_list;
+       for(std::vector<v3s16>::iterator i = active_positions.begin();
+               i != active_positions.end(); ++i)
+       {
+               fillRadiusBlock(*i, radius, newlist);
+       }
+
+       /*
+               Find out which blocks on the old list are not on the new list
+       */
+       // Go through old list
+       for(std::set<v3s16>::iterator i = m_list.begin();
+               i != m_list.end(); ++i)
+       {
+               v3s16 p = *i;
+               // If not on new list, it's been removed
+               if(newlist.find(p) == newlist.end())
+                       blocks_removed.insert(p);
+       }
+
+       /*
+               Find out which blocks on the new list are not on the old list
+       */
+       // Go through new list
+       for(std::set<v3s16>::iterator i = newlist.begin();
+               i != newlist.end(); ++i)
+       {
+               v3s16 p = *i;
+               // If not on old list, it's been added
+               if(m_list.find(p) == m_list.end())
+                       blocks_added.insert(p);
+       }
+
+       /*
+               Update m_list
+       */
+       m_list.clear();
+       for(std::set<v3s16>::iterator i = newlist.begin();
+               i != newlist.end(); ++i)
+       {
+               v3s16 p = *i;
+               m_list.insert(p);
+       }
+}
+
+/*
+       ServerEnvironment
+*/
+
+ServerEnvironment::ServerEnvironment(ServerMap *map,
+       GameScripting *scriptIface, IGameDef *gamedef,
+       const std::string &path_world) :
+       m_map(map),
+       m_script(scriptIface),
+       m_gamedef(gamedef),
+       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_last_clear_objects_time(0),
+       m_recommended_send_interval(0.1),
+       m_max_lag_estimate(0.1)
+{
+}
+
+ServerEnvironment::~ServerEnvironment()
+{
+       // Clear active block list.
+       // This makes the next one delete all active objects.
+       m_active_blocks.clear();
+
+       // Convert all objects to static and delete the active objects
+       deactivateFarObjects(true);
+
+       // Drop/delete map
+       m_map->drop();
+
+       // Delete ActiveBlockModifiers
+       for (std::vector<ABMWithState>::iterator
+               i = m_abms.begin(); i != m_abms.end(); ++i){
+               delete i->abm;
+       }
+
+       // Deallocate players
+       for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
+               i != m_players.end(); ++i) {
+               delete (*i);
+       }
+}
+
+Map & ServerEnvironment::getMap()
+{
+       return *m_map;
+}
+
+ServerMap & ServerEnvironment::getServerMap()
+{
+       return *m_map;
+}
+
+RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id)
+{
+       for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
+               i != m_players.end(); ++i) {
+               RemotePlayer *player = *i;
+               if (player->peer_id == peer_id)
+                       return player;
+       }
+       return NULL;
+}
+
+RemotePlayer *ServerEnvironment::getPlayer(const char* name)
+{
+       for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
+               i != m_players.end(); ++i) {
+               RemotePlayer *player = *i;
+               if (strcmp(player->getName(), name) == 0)
+                       return player;
+       }
+       return NULL;
+}
+
+void ServerEnvironment::addPlayer(RemotePlayer *player)
+{
+       DSTACK(FUNCTION_NAME);
+       /*
+               Check that peer_ids are unique.
+               Also check that names are unique.
+               Exception: there can be multiple players with peer_id=0
+       */
+       // If peer id is non-zero, it has to be unique.
+       if (player->peer_id != 0)
+               FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
+       // Name has to be unique.
+       FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
+       // Add.
+       m_players.push_back(player);
+}
+
+void ServerEnvironment::removePlayer(RemotePlayer *player)
+{
+       for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
+               it != m_players.end(); ++it) {
+               if ((*it) == player) {
+                       delete *it;
+                       m_players.erase(it);
+                       return;
+               }
+       }
+}
+
+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);
+
+       //find out if there's a node on path between pos1 and pos2
+       for (float i = 1; i < distance; i += stepsize) {
+               v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
+                       normalized_vector.Y * i,
+                       normalized_vector.Z * i) +pos1,BS);
+
+               MapNode n = getMap().getNodeNoEx(pos);
+
+               if(n.param0 != CONTENT_AIR) {
+                       if (p) {
+                               *p = pos;
+                       }
+                       return false;
+               }
+       }
+       return true;
+}
+
+void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
+       const std::string &str_reason, bool reconnect)
+{
+       for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
+               it != m_players.end(); ++it) {
+               RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
+               ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id,
+                       player->protocol_version, reason, str_reason, reconnect);
+       }
+}
+
+void ServerEnvironment::saveLoadedPlayers()
+{
+       std::string players_path = m_path_world + DIR_DELIM "players";
+       fs::CreateDir(players_path);
+
+       for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
+               it != m_players.end();
+               ++it) {
+               if ((*it)->checkModified()) {
+                       (*it)->save(players_path, m_gamedef);
+               }
+       }
+}
+
+void ServerEnvironment::savePlayer(RemotePlayer *player)
+{
+       std::string players_path = m_path_world + DIR_DELIM "players";
+       fs::CreateDir(players_path);
+
+       player->save(players_path, m_gamedef);
+}
+
+RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao)
+{
+       bool newplayer = false;
+       bool found = false;
+       std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
+       std::string path = players_path + playername;
+
+       RemotePlayer *player = getPlayer(playername.c_str());
+       if (!player) {
+               player = new RemotePlayer("", m_gamedef->idef());
+               newplayer = true;
+       }
+
+       for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
+               //// Open file and deserialize
+               std::ifstream is(path.c_str(), std::ios_base::binary);
+               if (!is.good())
+                       continue;
+
+               player->deSerialize(is, path, sao);
+               is.close();
+
+               if (player->getName() == playername) {
+                       found = true;
+                       break;
+               }
+
+               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()
+{
+       std::string path = m_path_world + DIR_DELIM "env_meta.txt";
+
+       // Open file and serialize
+       std::ostringstream ss(std::ios_base::binary);
+
+       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";
+
+       if(!fs::safeWriteToFile(path, ss.str()))
+       {
+               infostream<<"ServerEnvironment::saveMeta(): Failed to write "
+                       <<path<<std::endl;
+               throw SerializationError("Couldn't save env meta");
+       }
+}
+
+void ServerEnvironment::loadMeta()
+{
+       std::string path = m_path_world + DIR_DELIM "env_meta.txt";
+
+       // Open file and deserialize
+       std::ifstream is(path.c_str(), std::ios_base::binary);
+       if (!is.good()) {
+               infostream << "ServerEnvironment::loadMeta(): Failed to open "
+                       << path << std::endl;
+               throw SerializationError("Couldn't load env meta");
+       }
+
+       Settings args;
+
+       if (!args.parseConfigLines(is, "EnvArgsEnd")) {
+               throw SerializationError("ServerEnvironment::loadMeta(): "
+                       "EnvArgsEnd not found!");
+       }
+
+       try {
+               m_game_time = args.getU64("game_time");
+       } catch (SettingNotFoundException &e) {
+               // Getting this is crucial, otherwise timestamps are useless
+               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 {
+               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) {
+               // 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
+{
+       ActiveBlockModifier *abm;
+       int chance;
+       std::set<content_t> required_neighbors;
+};
+
+class ABMHandler
+{
+private:
+       ServerEnvironment *m_env;
+       std::vector<std::vector<ActiveABM> *> m_aabms;
+public:
+       ABMHandler(std::vector<ABMWithState> &abms,
+               float dtime_s, ServerEnvironment *env,
+               bool use_timers):
+               m_env(env)
+       {
+               if(dtime_s < 0.001)
+                       return;
+               INodeDefManager *ndef = env->getGameDef()->ndef();
+               for(std::vector<ABMWithState>::iterator
+                       i = abms.begin(); i != abms.end(); ++i) {
+                       ActiveBlockModifier *abm = i->abm;
+                       float trigger_interval = abm->getTriggerInterval();
+                       if(trigger_interval < 0.001)
+                               trigger_interval = 0.001;
+                       float actual_interval = dtime_s;
+                       if(use_timers){
+                               i->timer += dtime_s;
+                               if(i->timer < trigger_interval)
+                                       continue;
+                               i->timer -= trigger_interval;
+                               actual_interval = trigger_interval;
+                       }
+                       float chance = abm->getTriggerChance();
+                       if(chance == 0)
+                               chance = 1;
+                       ActiveABM aabm;
+                       aabm.abm = abm;
+                       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<std::string> required_neighbors_s
+                               = abm->getRequiredNeighbors();
+                       for(std::set<std::string>::iterator
+                               i = required_neighbors_s.begin();
+                               i != required_neighbors_s.end(); ++i)
+                       {
+                               ndef->getIds(*i, aabm.required_neighbors);
+                       }
+                       // Trigger contents
+                       std::set<std::string> contents_s = abm->getTriggerContents();
+                       for(std::set<std::string>::iterator
+                               i = contents_s.begin(); i != contents_s.end(); ++i)
+                       {
+                               std::set<content_t> ids;
+                               ndef->getIds(*i, ids);
+                               for(std::set<content_t>::const_iterator k = ids.begin();
+                                       k != ids.end(); ++k)
+                               {
+                                       content_t c = *k;
+                                       if (c >= m_aabms.size())
+                                               m_aabms.resize(c + 256, NULL);
+                                       if (!m_aabms[c])
+                                               m_aabms[c] = new std::vector<ActiveABM>;
+                                       m_aabms[c]->push_back(aabm);
+                               }
+                       }
+               }
+       }
+
+       ~ABMHandler()
+       {
+               for (size_t i = 0; i < m_aabms.size(); i++)
+                       delete m_aabms[i];
+       }
+
+       // 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() || block->isDummy())
+                       return;
+
+               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.X<MAP_BLOCKSIZE; p0.X++)
+                       for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
+                               for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
+                               {
+                                       const MapNode &n = block->getNodeUnsafe(p0);
+                                       content_t c = n.getContent();
+
+                                       if (c >= m_aabms.size() || !m_aabms[c])
+                                               continue;
+
+                                       v3s16 p = p0 + block->getPosRelative();
+                                       for(std::vector<ActiveABM>::iterator
+                                               i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) {
+                                               if(myrand() % i->chance != 0)
+                                                       continue;
+
+                                               // Check neighbors
+                                               if(!i->required_neighbors.empty())
+                                               {
+                                                       v3s16 p1;
+                                                       for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++)
+                                                               for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++)
+                                                                       for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++)
+                                                                       {
+                                                                               if(p1 == p0)
+                                                                                       continue;
+                                                                               content_t c;
+                                                                               if (block->isValidPosition(p1)) {
+                                                                                       // if the neighbor is found on the same map block
+                                                                                       // get it straight from there
+                                                                                       const MapNode &n = block->getNodeUnsafe(p1);
+                                                                                       c = n.getContent();
+                                                                               } else {
+                                                                                       // otherwise consult the map
+                                                                                       MapNode n = map->getNodeNoEx(p1 + block->getPosRelative());
+                                                                                       c = n.getContent();
+                                                                               }
+                                                                               std::set<content_t>::const_iterator k;
+                                                                               k = i->required_neighbors.find(c);
+                                                                               if(k != i->required_neighbors.end()){
+                                                                                       goto neighbor_found;
+                                                                               }
+                                                                       }
+                                                       // No required neighbor found
+                                                       continue;
+                                               }
+                                               neighbor_found:
+
+                                               // 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;
+                                               }
+                                       }
+                               }
+       }
+};
+
+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 - stamp;
+       dtime_s += additional_dtime;
+
+       /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
+                       <<stamp<<", game time: "<<m_game_time<<std::endl;*/
+
+       // Remove stored static objects if clearObjects was called since block's timestamp
+       if (stamp == BLOCK_TIMESTAMP_UNDEFINED || stamp < m_last_clear_objects_time) {
+               block->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 "
+                       <<dtime_s<<" seconds old."<<std::endl;*/
+
+       // Activate stored objects
+       activateObjects(block, dtime_s);
+
+       /* Handle LoadingBlockModifiers */
+       m_lbm_mgr.applyLBMs(this, block, stamp);
+
+       // Run node timers
+       std::vector<NodeTimer> elapsed_timers =
+               block->m_node_timers.step((float)dtime_s);
+       if (!elapsed_timers.empty()) {
+               MapNode n;
+               for (std::vector<NodeTimer>::iterator
+                       i = elapsed_timers.begin();
+                       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));
+               }
+       }
+
+       /* Handle ActiveBlockModifiers */
+       ABMHandler abmhandler(m_abms, dtime_s, this, false);
+       abmhandler.apply(block);
+}
+
+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)
+               m_script->node_on_destruct(p, n_old);
+
+       // Replace node
+       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)
+               m_script->node_after_destruct(p, n_old);
+
+       // Call constructor
+       if (ndef->get(n).has_on_construct)
+               m_script->node_on_construct(p, n);
+
+       return true;
+}
+
+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)
+               m_script->node_on_destruct(p, n_old);
+
+       // Replace with air
+       // This is slightly optimized compared to addNodeWithEvent(air)
+       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)
+               m_script->node_after_destruct(p, n_old);
+
+       // Air doesn't require constructor
+       return true;
+}
+
+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<u16> &objects, v3f pos, float radius)
+{
+       for (ActiveObjectMap::iterator i = m_active_objects.begin();
+               i != m_active_objects.end(); ++i) {
+               ServerActiveObject* obj = i->second;
+               u16 id = i->first;
+               v3f objectpos = obj->getBasePosition();
+               if (objectpos.getDistanceFrom(pos) > radius)
+                       continue;
+               objects.push_back(id);
+       }
+}
+
+void ServerEnvironment::clearObjects(ClearObjectsMode mode)
+{
+       infostream << "ServerEnvironment::clearObjects(): "
+               << "Removing all active objects" << std::endl;
+       std::vector<u16> objects_to_remove;
+       for (ActiveObjectMap::iterator i = m_active_objects.begin();
+               i != m_active_objects.end(); ++i) {
+               ServerActiveObject* obj = i->second;
+               if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
+                       continue;
+               u16 id = i->first;
+               // Delete static object if block is loaded
+               if (obj->m_static_exists) {
+                       MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
+                       if (block) {
+                               block->m_static_objects.remove(id);
+                               block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                       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) {
+                       obj->m_pending_deactivation = true;
+                       obj->m_removed = true;
+                       continue;
+               }
+
+               // Tell the object about removal
+               obj->removingFromEnvironment();
+               // Deregister in scripting api
+               m_script->removeObjectReference(obj);
+
+               // Delete active object
+               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<u16>::iterator i = objects_to_remove.begin();
+               i != objects_to_remove.end(); ++i) {
+               m_active_objects.erase(*i);
+       }
+
+       // Get list of loaded blocks
+       std::vector<v3s16> loaded_blocks;
+       infostream << "ServerEnvironment::clearObjects(): "
+               << "Listing all loaded blocks" << std::endl;
+       m_map->listAllLoadedBlocks(loaded_blocks);
+       infostream << "ServerEnvironment::clearObjects(): "
+               << "Done listing all loaded blocks: "
+               << loaded_blocks.size()<<std::endl;
+
+       // Get list of loadable blocks
+       std::vector<v3s16> 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::vector<v3s16>::iterator i = loaded_blocks.begin();
+               i != loaded_blocks.end(); ++i) {
+               v3s16 p = *i;
+               MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+               assert(block != NULL);
+               block->refGrab();
+       }
+
+       // Remove objects in all loadable blocks
+       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<v3s16>::iterator i = loadable_blocks.begin();
+               i != loadable_blocks.end(); ++i) {
+               v3s16 p = *i;
+               MapBlock *block = m_map->emergeBlock(p, false);
+               if (!block) {
+                       errorstream << "ServerEnvironment::clearObjects(): "
+                               << "Failed to emerge block " << PP(p) << std::endl;
+                       continue;
+               }
+               u32 num_stored = block->m_static_objects.m_stored.size();
+               u32 num_active = block->m_static_objects.m_active.size();
+               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,
+                               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) {
+                       float percent = 100.0 * (float)num_blocks_checked /
+                               loadable_blocks.size();
+                       infostream << "ServerEnvironment::clearObjects(): "
+                               << "Cleared " << num_objs_cleared << " objects"
+                               << " in " << num_blocks_cleared << " blocks ("
+                               << percent << "%)" << std::endl;
+               }
+               if (num_blocks_checked % unload_interval == 0) {
+                       m_map->unloadUnreferencedBlocks();
+               }
+       }
+       m_map->unloadUnreferencedBlocks();
+
+       // Drop references that were added above
+       for (std::vector<v3s16>::iterator i = loaded_blocks.begin();
+               i != loaded_blocks.end(); ++i) {
+               v3s16 p = *i;
+               MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+               assert(block);
+               block->refDrop();
+       }
+
+       m_last_clear_objects_time = m_game_time;
+
+       infostream << "ServerEnvironment::clearObjects(): "
+               << "Finished: Cleared " << num_objs_cleared << " objects"
+               << " in " << num_blocks_cleared << " blocks" << std::endl;
+}
+
+void ServerEnvironment::step(float dtime)
+{
+       DSTACK(FUNCTION_NAME);
+
+       //TimeTaker timer("ServerEnv step");
+
+       /* Step time of day */
+       stepTimeOfDay(dtime);
+
+       // Update this one
+       // NOTE: This is kind of funny on a singleplayer game, but doesn't
+       // really matter that much.
+       static const float server_step = g_settings->getFloat("dedicated_server_step");
+       m_recommended_send_interval = server_step;
+
+       /*
+               Increment game time
+       */
+       {
+               m_game_time_fraction_counter += dtime;
+               u32 inc_i = (u32)m_game_time_fraction_counter;
+               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::vector<RemotePlayer *>::iterator i = m_players.begin();
+                       i != m_players.end(); ++i) {
+                       RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
+                       assert(player);
+
+                       // Ignore disconnected players
+                       if(player->peer_id == 0)
+                               continue;
+
+                       // Move
+                       player->move(dtime, this, 100*BS);
+               }
+       }
+
+       /*
+               Manage active block list
+       */
+       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::vector<v3s16> players_blockpos;
+               for (std::vector<RemotePlayer *>::iterator i = m_players.begin();
+                       i != m_players.end(); ++i) {
+                       RemotePlayer *player = dynamic_cast<RemotePlayer *>(*i);
+                       assert(player);
+
+                       // Ignore disconnected players
+                       if (player->peer_id == 0)
+                               continue;
+
+                       PlayerSAO *playersao = player->getPlayerSAO();
+                       assert(playersao);
+
+                       v3s16 blockpos = getNodeBlockPos(
+                               floatToInt(playersao->getBasePosition(), BS));
+                       players_blockpos.push_back(blockpos);
+               }
+
+               /*
+                       Update list of active blocks, collecting changes
+               */
+               static const s16 active_block_range = g_settings->getS16("active_block_range");
+               std::set<v3s16> blocks_removed;
+               std::set<v3s16> blocks_added;
+               m_active_blocks.update(players_blockpos, active_block_range,
+                       blocks_removed, blocks_added);
+
+               /*
+                       Handle removed blocks
+               */
+
+               // Convert active objects that are no more in active blocks to static
+               deactivateFarObjects(false);
+
+               for(std::set<v3s16>::iterator
+                       i = blocks_removed.begin();
+                       i != blocks_removed.end(); ++i) {
+                       v3s16 p = *i;
+
+                       /* infostream<<"Server: Block " << PP(p)
+                               << " became inactive"<<std::endl; */
+
+                       MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+                       if(block==NULL)
+                               continue;
+
+                       // Set current time as timestamp (and let it set ChangedFlag)
+                       block->setTimestamp(m_game_time);
+               }
+
+               /*
+                       Handle added blocks
+               */
+
+               for(std::set<v3s16>::iterator
+                       i = blocks_added.begin();
+                       i != blocks_added.end(); ++i)
+               {
+                       v3s16 p = *i;
+
+                       MapBlock *block = m_map->getBlockOrEmerge(p);
+                       if(block==NULL){
+                               m_active_blocks.m_list.erase(p);
+                               continue;
+                       }
+
+                       activateBlock(block);
+                       /* infostream<<"Server: Block " << PP(p)
+                               << " became active"<<std::endl; */
+               }
+       }
+
+       /*
+               Mess around in active blocks
+       */
+       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<v3s16>::iterator
+                       i = m_active_blocks.m_list.begin();
+                       i != m_active_blocks.m_list.end(); ++i)
+               {
+                       v3s16 p = *i;
+
+                       /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
+                                       <<") being handled"<<std::endl;*/
+
+                       MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+                       if(block==NULL)
+                               continue;
+
+                       // Reset block usage timer
+                       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,
+                                       MOD_REASON_BLOCK_EXPIRED);
+
+                       // Run node timers
+                       std::vector<NodeTimer> elapsed_timers =
+                               block->m_node_timers.step((float)dtime);
+                       if (!elapsed_timers.empty()) {
+                               MapNode n;
+                               for (std::vector<NodeTimer>::iterator i = elapsed_timers.begin();
+                                       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));
+                                       }
+                               }
+                       }
+               }
+       }
+
+       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 per interval", SPT_AVG);
+                       TimeTaker timer("modify in active blocks per interval");
+
+                       // Initialize handling of ActiveBlockModifiers
+                       ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true);
+
+                       for(std::set<v3s16>::iterator
+                               i = m_active_blocks.m_list.begin();
+                               i != m_active_blocks.m_list.end(); ++i)
+                       {
+                               v3s16 p = *i;
+
+                               /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
+                                               <<") being handled"<<std::endl;*/
+
+                               MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+                               if(block == NULL)
+                                       continue;
+
+                               // Set current time as timestamp
+                               block->setTimestampNoChangedFlag(m_game_time);
+
+                               /* Handle ActiveBlockModifiers */
+                               abmhandler.apply(block);
+                       }
+
+                       u32 time_ms = timer.stop(true);
+                       u32 max_time_ms = 200;
+                       if(time_ms > max_time_ms){
+                               warningstream<<"active block modifiers took "
+                                       <<time_ms<<"ms (longer than "
+                                       <<max_time_ms<<"ms)"<<std::endl;
+                               m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
+                       }
+               }while(0);
+
+       /*
+               Step script environment (run global on_step())
+       */
+       m_script->environment_Step(dtime);
+
+       /*
+               Step active objects
+       */
+       {
+               ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
+               //TimeTaker timer("Step active objects");
+
+               g_profiler->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;
+               if(m_send_recommended_timer > getSendRecommendedInterval())
+               {
+                       m_send_recommended_timer -= getSendRecommendedInterval();
+                       send_recommended = true;
+               }
+
+               for(ActiveObjectMap::iterator i = m_active_objects.begin();
+                       i != m_active_objects.end(); ++i) {
+                       ServerActiveObject* obj = i->second;
+                       // Don't step if is to be removed or stored statically
+                       if(obj->m_removed || obj->m_pending_deactivation)
+                               continue;
+                       // Step object
+                       obj->step(dtime, send_recommended);
+                       // Read messages from object
+                       while(!obj->m_messages_out.empty())
+                       {
+                               m_active_object_messages.push(
+                                       obj->m_messages_out.front());
+                               obj->m_messages_out.pop();
+                       }
+               }
+       }
+
+       /*
+               Manage active objects
+       */
+       if(m_object_management_interval.step(dtime, 0.5))
+       {
+               ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
+               /*
+                       Remove objects that satisfy (m_removed && m_known_by_count==0)
+               */
+               removeRemovedObjects();
+       }
+
+       /*
+               Manage particle spawner expiration
+       */
+       if (m_particle_management_interval.step(dtime, 1.0)) {
+               for (UNORDERED_MAP<u32, float>::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++;
+               UNORDERED_MAP<u32, float>::iterator f = m_particle_spawners.find(id);
+               if (f == m_particle_spawners.end()) {
+                       m_particle_spawners[id] = time;
+                       break;
+               }
+       }
+       return id;
+}
+
+u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id)
+{
+       u32 id = addParticleSpawner(exptime);
+       m_particle_spawner_attachments[id] = attached_id;
+       if (ServerActiveObject *obj = getActiveObject(attached_id)) {
+               obj->attachParticleSpawner(id);
+       }
+       return id;
+}
+
+void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object)
+{
+       m_particle_spawners.erase(id);
+       UNORDERED_MAP<u32, u16>::iterator it = m_particle_spawner_attachments.find(id);
+       if (it != m_particle_spawner_attachments.end()) {
+               u16 obj_id = (*it).second;
+               ServerActiveObject *sao = getActiveObject(obj_id);
+               if (sao != NULL && remove_from_object) {
+                       sao->detachParticleSpawner(id);
+               }
+               m_particle_spawner_attachments.erase(id);
+       }
+}
+
+ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
+{
+       ActiveObjectMap::iterator n = m_active_objects.find(id);
+       return (n != m_active_objects.end() ? n->second : NULL);
+}
+
+bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects)
+{
+       if (id == 0)
+               return false;
+
+       return objects.find(id) == objects.end();
+}
+
+u16 getFreeServerActiveObjectId(ActiveObjectMap &objects)
+{
+       //try to reuse id's as late as possible
+       static u16 last_used_id = 0;
+       u16 startid = last_used_id;
+       for(;;)
+       {
+               last_used_id ++;
+               if(isFreeServerActiveObjectId(last_used_id, objects))
+                       return last_used_id;
+
+               if(last_used_id == startid)
+                       return 0;
+       }
+}
+
+u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
+{
+       assert(object); // Pre-condition
+       m_added_objects++;
+       u16 id = addActiveObjectRaw(object, true, 0);
+       return id;
+}
+
+/*
+       Finds out what new objects have been added to
+       inside a radius around a position
+*/
+void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
+       s16 player_radius,
+       std::set<u16> &current_objects,
+       std::queue<u16> &added_objects)
+{
+       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,
+               - discard objects that are too far away,
+               - discard objects that are found in current_objects.
+               - add remaining objects to added_objects
+       */
+       for (ActiveObjectMap::iterator i = m_active_objects.begin();
+               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(playersao->getBasePosition());
+               if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+                       // Discard if too far
+                       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<u16>::iterator n;
+               n = current_objects.find(id);
+               if(n != current_objects.end())
+                       continue;
+               // Add to added_objects
+               added_objects.push(id);
+       }
+}
+
+/*
+       Finds out what objects have been removed from
+       inside a radius around a position
+*/
+void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
+       s16 player_radius,
+       std::set<u16> &current_objects,
+       std::queue<u16> &removed_objects)
+{
+       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
+                 error condition; objects should be set m_removed=true and removed
+                 only after all clients have been informed about removal), or
+               - object has m_removed=true, or
+               - object is too far away
+       */
+       for(std::set<u16>::iterator
+               i = current_objects.begin();
+               i != current_objects.end(); ++i)
+       {
+               u16 id = *i;
+               ServerActiveObject *object = getActiveObject(id);
+
+               if (object == NULL) {
+                       infostream << "ServerEnvironment::getRemovedActiveObjects():"
+                               << " object in current_objects is NULL" << std::endl;
+                       removed_objects.push(id);
+                       continue;
+               }
+
+               if (object->m_removed || object->m_pending_deactivation) {
+                       removed_objects.push(id);
+                       continue;
+               }
+
+               f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition());
+               if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+                       if (distance_f <= player_radius_f || player_radius_f == 0)
+                               continue;
+               } else if (distance_f <= radius_f)
+                       continue;
+
+               // Object is no longer visible
+               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<u16, StaticObject>::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
+               ActiveObjectMap::iterator 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;
+       }
+}
+
+ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
+{
+       if(m_active_object_messages.empty())
+               return ActiveObjectMessage(0);
+
+       ActiveObjectMessage message = m_active_object_messages.front();
+       m_active_object_messages.pop();
+       return message;
+}
+
+/*
+       ************ Private methods *************
+*/
+
+u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
+       bool set_changed, u32 dtime_s)
+{
+       assert(object); // Pre-condition
+       if(object->getId() == 0){
+               u16 new_id = getFreeServerActiveObjectId(m_active_objects);
+               if(new_id == 0)
+               {
+                       errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
+                               <<"no free ids available"<<std::endl;
+                       if(object->environmentDeletes())
+                               delete object;
+                       return 0;
+               }
+               object->setId(new_id);
+       }
+       else{
+               verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
+                       <<"supplied with id "<<object->getId()<<std::endl;
+       }
+
+       if(!isFreeServerActiveObjectId(object->getId(), m_active_objects)) {
+               errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
+                       <<"id is not free ("<<object->getId()<<")"<<std::endl;
+               if(object->environmentDeletes())
+                       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="<<object->getId()<<")"<<std::endl;*/
+
+       m_active_objects[object->getId()] = object;
+
+       verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
+               <<"Added id="<<object->getId()<<"; there are now "
+               <<m_active_objects.size()<<" active objects."
+               <<std::endl;
+
+       // Register reference in scripting api (must be done before post-init)
+       m_script->addObjectReference(object);
+       // Post-initialize object
+       object->addedToEnvironment(dtime_s);
+
+       // Add static data to block
+       if(object->isStaticAllowed())
+       {
+               // Add static object to active static list of the block
+               v3f objectpos = object->getBasePosition();
+               std::string staticdata = object->getStaticData();
+               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->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,
+                                       MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
+               } else {
+                       v3s16 p = floatToInt(objectpos, BS);
+                       errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
+                               <<"could not emerge block for storing id="<<object->getId()
+                               <<" statically (pos="<<PP(p)<<")"<<std::endl;
+               }
+       }
+
+       return object->getId();
+}
+
+/*
+       Remove objects that satisfy (m_removed && m_known_by_count==0)
+*/
+void ServerEnvironment::removeRemovedObjects()
+{
+       std::vector<u16> objects_to_remove;
+       for(ActiveObjectMap::iterator i = m_active_objects.begin();
+               i != m_active_objects.end(); ++i) {
+               u16 id = i->first;
+               ServerActiveObject* obj = i->second;
+               // This shouldn't happen but check it
+               if(obj == NULL)
+               {
+                       infostream<<"NULL object found in ServerEnvironment"
+                               <<" while finding removed objects. id="<<id<<std::endl;
+                       // Id to be removed from m_active_objects
+                       objects_to_remove.push_back(id);
+                       continue;
+               }
+
+               /*
+                       We will delete objects that are marked as removed or thatare
+                       waiting for deletion after deactivation
+               */
+               if (!obj->m_removed && !obj->m_pending_deactivation)
+                       continue;
+
+               /*
+                       Delete static data from block if is marked as removed
+               */
+               if(obj->m_static_exists && obj->m_removed)
+               {
+                       MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
+                       if (block) {
+                               block->m_static_objects.remove(id);
+                               block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                       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="<<id<<std::endl;
+                       }
+               }
+
+               // If m_known_by_count > 0, don't actually remove. On some future
+               // invocation this will be 0, which is when removal will continue.
+               if(obj->m_known_by_count > 0)
+                       continue;
+
+               /*
+                       Move static data from active to stored if not marked as removed
+               */
+               if(obj->m_static_exists && !obj->m_removed){
+                       MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
+                       if (block) {
+                               std::map<u16, StaticObject>::iterator i =
+                                       block->m_static_objects.m_active.find(id);
+                               if(i != block->m_static_objects.m_active.end()){
+                                       block->m_static_objects.m_stored.push_back(i->second);
+                                       block->m_static_objects.m_active.erase(id);
+                                       block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                               MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
+                               }
+                       } else {
+                               infostream<<"Failed to emerge block from which an object to "
+                                       <<"be deactivated was loaded from. id="<<id<<std::endl;
+                       }
+               }
+
+               // Tell the object about removal
+               obj->removingFromEnvironment();
+               // Deregister in scripting api
+               m_script->removeObjectReference(obj);
+
+               // Delete
+               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<u16>::iterator i = objects_to_remove.begin();
+               i != objects_to_remove.end(); ++i) {
+               m_active_objects.erase(*i);
+       }
+}
+
+static void print_hexdump(std::ostream &o, const std::string &data)
+{
+       const int linelength = 16;
+       for(int l=0; ; l++){
+               int i0 = linelength * l;
+               bool at_end = false;
+               int thislinelength = linelength;
+               if(i0 + thislinelength > (int)data.size()){
+                       thislinelength = data.size() - i0;
+                       at_end = true;
+               }
+               for(int di=0; di<linelength; di++){
+                       int i = i0 + di;
+                       char buf[4];
+                       if(di<thislinelength)
+                               snprintf(buf, 4, "%.2x ", data[i]);
+                       else
+                               snprintf(buf, 4, "   ");
+                       o<<buf;
+               }
+               o<<" ";
+               for(int di=0; di<thislinelength; di++){
+                       int i = i0 + di;
+                       if(data[i] >= 32)
+                               o<<data[i];
+                       else
+                               o<<".";
+               }
+               o<<std::endl;
+               if(at_end)
+                       break;
+       }
+}
+
+/*
+       Convert stored objects from blocks near the players to active.
+*/
+void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
+{
+       if(block == NULL)
+               return;
+
+       // Ignore if no stored objects (to not set changed flag)
+       if(block->m_static_objects.m_stored.empty())
+               return;
+
+       verbosestream<<"ServerEnvironment::activateObjects(): "
+               <<"activating objects of block "<<PP(block->getPos())
+               <<" ("<<block->m_static_objects.m_stored.size()
+               <<" objects)"<<std::endl;
+       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: "
+                       <<block->m_static_objects.m_stored.size()<<" in "
+                       <<PP(block->getPos())
+                       <<"; removing all of them."<<std::endl;
+               // Clear stored list
+               block->m_static_objects.m_stored.clear();
+               block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                       MOD_REASON_TOO_MANY_OBJECTS);
+               return;
+       }
+
+       // Activate stored objects
+       std::vector<StaticObject> new_stored;
+       for (std::vector<StaticObject>::iterator
+               i = block->m_static_objects.m_stored.begin();
+               i != block->m_static_objects.m_stored.end(); ++i) {
+               StaticObject &s_obj = *i;
+
+               // Create an active object from the data
+               ServerActiveObject *obj = ServerActiveObject::create
+                       ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
+               // If couldn't create object, store static data back.
+               if(obj == NULL) {
+                       errorstream<<"ServerEnvironment::activateObjects(): "
+                               <<"failed to create active object from static object "
+                               <<"in block "<<PP(s_obj.pos/BS)
+                               <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
+                       print_hexdump(verbosestream, s_obj.data);
+
+                       new_stored.push_back(s_obj);
+                       continue;
+               }
+               verbosestream<<"ServerEnvironment::activateObjects(): "
+                       <<"activated static object pos="<<PP(s_obj.pos/BS)
+                       <<" type="<<(int)s_obj.type<<std::endl;
+               // This will also add the object to the active static list
+               addActiveObjectRaw(obj, false, dtime_s);
+       }
+       // Clear stored list
+       block->m_static_objects.m_stored.clear();
+       // Add leftover failed stuff to stored list
+       for(std::vector<StaticObject>::iterator
+               i = new_stored.begin();
+               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<u16, StaticObject>::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
+               static list to the active static list.
+               As such, the block is essentially the same.
+               Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
+               Otherwise there would be a huge amount of unnecessary I/O.
+       */
+}
+
+/*
+       Convert objects that are not standing inside active blocks to static.
+
+       If m_known_by_count != 0, active object is not deleted, but static
+       data is still updated.
+
+       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),
+*/
+void ServerEnvironment::deactivateFarObjects(bool force_delete)
+{
+       std::vector<u16> objects_to_remove;
+       for(ActiveObjectMap::iterator i = m_active_objects.begin();
+               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;
+
+               // If pending deactivation, let removeRemovedObjects() do it
+               if(!force_delete && obj->m_pending_deactivation)
+                       continue;
+
+               u16 id = i->first;
+               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="<<id
+                                       <<" to it's current block "<<PP(blockpos_o)
+                                       <<std::endl;
+                               continue;
+                       }
+                       std::string staticdata_new = obj->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="<<id
+                                       <<" from it's previous block "<<PP(old_static_block)
+                                       <<std::endl;
+                               continue;
+                       }
+                       block->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;
+
+               verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
+                       <<"deactivating object id="<<id<<" on inactive block "
+                       <<PP(blockpos_o)<<std::endl;
+
+               // If known by some client, don't immediately delete.
+               bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
+
+               /*
+                       Update the static data
+               */
+
+               if(obj->isStaticAllowed())
+               {
+                       // 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)
+                                       stays_in_same_block = true;
+
+                               MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
+
+                               if (block) {
+                                       std::map<u16, StaticObject>::iterator n =
+                                               block->m_static_objects.m_active.find(id);
+                                       if (n != block->m_static_objects.m_active.end()) {
+                                               StaticObject static_old = n->second;
+
+                                               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(): "
+                                                       <<"id="<<id<<" m_static_exists=true but "
+                                                       <<"static data doesn't actually exist in "
+                                                       <<PP(obj->m_static_block)<<std::endl;
+                                       }
+                               }
+                       }
+
+                       bool shall_be_written = (!stays_in_same_block || data_changed);
+
+                       // Delete old static object
+                       if(obj->m_static_exists)
+                       {
+                               MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
+                               if(block)
+                               {
+                                       block->m_static_objects.remove(id);
+                                       obj->m_static_exists = false;
+                                       // Only mark block as modified if data changed considerably
+                                       if(shall_be_written)
+                                               block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                                       MOD_REASON_STATIC_DATA_CHANGED);
+                               }
+                       }
+
+                       // Add to the block where the object is located in
+                       v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
+                       // Get or generate the block
+                       MapBlock *block = NULL;
+                       try{
+                               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() >= g_settings->getU16("max_objects_per_block")) {
+                                       warningstream << "ServerEnv: Trying to store id = " << obj->getId()
+                                               << " statically but block " << PP(blockpos)
+                                               << " already contains "
+                                               << block->m_static_objects.m_stored.size()
+                                               << " objects."
+                                               << " Forcing delete." << std::endl;
+                                       force_delete = true;
+                               } else {
+                                       // If static counterpart already exists in target block,
+                                       // remove it first.
+                                       // This shouldn't happen because the object is removed from
+                                       // the previous block before this according to
+                                       // obj->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()){
+                                               warningstream<<"ServerEnv: Performing hack #83274"
+                                                       <<std::endl;
+                                               block->m_static_objects.remove(id);
+                                       }
+                                       // 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,
+                                                       MOD_REASON_STATIC_DATA_CHANGED);
+
+                                       obj->m_static_exists = true;
+                                       obj->m_static_block = block->getPos();
+                               }
+                       }
+                       else{
+                               if(!force_delete){
+                                       v3s16 p = floatToInt(objectpos, BS);
+                                       errorstream<<"ServerEnv: Could not find or generate "
+                                               <<"a block for storing id="<<obj->getId()
+                                               <<" statically (pos="<<PP(p)<<")"<<std::endl;
+                                       continue;
+                               }
+                       }
+               }
+
+               /*
+                       If known by some client, set pending deactivation.
+                       Otherwise delete it immediately.
+               */
+
+               if(pending_delete && !force_delete)
+               {
+                       verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
+                               <<"object id="<<id<<" is known by clients"
+                               <<"; not deleting yet"<<std::endl;
+
+                       obj->m_pending_deactivation = true;
+                       continue;
+               }
+
+               verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
+                       <<"object id="<<id<<" is not known by clients"
+                       <<"; deleting"<<std::endl;
+
+               // Tell the object about removal
+               obj->removingFromEnvironment();
+               // Deregister in scripting api
+               m_script->removeObjectReference(obj);
+
+               // Delete active object
+               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<u16>::iterator i = objects_to_remove.begin();
+               i != objects_to_remove.end(); ++i) {
+               m_active_objects.erase(*i);
+       }
+}
diff --git a/src/serverenvironment.h b/src/serverenvironment.h
new file mode 100644 (file)
index 0000000..20a783e
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+Minetest
+Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef SERVER_ENVIRONMENT_HEADER
+#define SERVER_ENVIRONMENT_HEADER
+
+#include "environment.h"
+
+class IGameDef;
+class ServerMap;
+class RemotePlayer;
+class PlayerSAO;
+class ServerEnvironment;
+class ActiveBlockModifier;
+class ServerActiveObject;
+
+/*
+       {Active, Loading} block modifier interface.
+
+       These are fed into ServerEnvironment at initialization time;
+       ServerEnvironment handles deleting them.
+*/
+
+class ActiveBlockModifier
+{
+public:
+       ActiveBlockModifier(){};
+       virtual ~ActiveBlockModifier(){};
+
+       // Set of contents to trigger on
+       virtual std::set<std::string> getTriggerContents()=0;
+       // Set of required neighbors (trigger doesn't happen if none are found)
+       // Empty = do not check neighbors
+       virtual std::set<std::string> getRequiredNeighbors()
+       { return std::set<std::string>(); }
+       // Trigger interval in seconds
+       virtual float getTriggerInterval() = 0;
+       // Random chance of (1 / return value), 0 is disallowed
+       virtual u32 getTriggerChance() = 0;
+       // Whether to modify chance to simulate time lost by an unnattended block
+       virtual bool getSimpleCatchUp() = 0;
+       // This is called usually at interval for 1/chance of the nodes
+       virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){};
+       virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n,
+               u32 active_object_count, u32 active_object_count_wider){};
+};
+
+struct ABMWithState
+{
+       ActiveBlockModifier *abm;
+       float timer;
+
+       ABMWithState(ActiveBlockModifier *abm_);
+};
+
+struct LoadingBlockModifierDef
+{
+       // Set of contents to trigger on
+       std::set<std::string> trigger_contents;
+       std::string name;
+       bool run_at_every_load;
+
+       virtual ~LoadingBlockModifierDef() {}
+       virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){};
+};
+
+struct LBMContentMapping
+{
+       typedef std::map<content_t, std::vector<LoadingBlockModifierDef *> > container_map;
+       container_map map;
+
+       std::vector<LoadingBlockModifierDef *> lbm_list;
+
+       // Needs to be separate method (not inside destructor),
+       // because the LBMContentMapping may be copied and destructed
+       // many times during operation in the lbm_lookup_map.
+       void deleteContents();
+       void addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef);
+       const std::vector<LoadingBlockModifierDef *> *lookup(content_t c) const;
+};
+
+class LBMManager
+{
+public:
+       LBMManager():
+               m_query_mode(false)
+       {}
+
+       ~LBMManager();
+
+       // Don't call this after loadIntroductionTimes() ran.
+       void addLBMDef(LoadingBlockModifierDef *lbm_def);
+
+       void loadIntroductionTimes(const std::string &times,
+               IGameDef *gamedef, u32 now);
+
+       // Don't call this before loadIntroductionTimes() ran.
+       std::string createIntroductionTimesString();
+
+       // Don't call this before loadIntroductionTimes() ran.
+       void applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp);
+
+       // Warning: do not make this std::unordered_map, order is relevant here
+       typedef std::map<u32, LBMContentMapping> lbm_lookup_map;
+
+private:
+       // Once we set this to true, we can only query,
+       // not modify
+       bool m_query_mode;
+
+       // For m_query_mode == false:
+       // The key of the map is the LBM def's name.
+       // TODO make this std::unordered_map
+       std::map<std::string, LoadingBlockModifierDef *> m_lbm_defs;
+
+       // For m_query_mode == true:
+       // The key of the map is the LBM def's first introduction time.
+       lbm_lookup_map m_lbm_lookup;
+
+       // Returns an iterator to the LBMs that were introduced
+       // after the given time. This is guaranteed to return
+       // valid values for everything
+       lbm_lookup_map::const_iterator getLBMsIntroducedAfter(u32 time)
+       { return m_lbm_lookup.lower_bound(time); }
+};
+
+/*
+       List of active blocks, used by ServerEnvironment
+*/
+
+class ActiveBlockList
+{
+public:
+       void update(std::vector<v3s16> &active_positions,
+               s16 radius,
+               std::set<v3s16> &blocks_removed,
+               std::set<v3s16> &blocks_added);
+
+       bool contains(v3s16 p){
+               return (m_list.find(p) != m_list.end());
+       }
+
+       void clear(){
+               m_list.clear();
+       }
+
+       std::set<v3s16> m_list;
+       std::set<v3s16> m_forceloaded_list;
+
+private:
+};
+
+/*
+       Operation mode for ServerEnvironment::clearObjects()
+*/
+enum ClearObjectsMode {
+       // Load and go through every mapblock, clearing objects
+               CLEAR_OBJECTS_MODE_FULL,
+
+       // Clear objects immediately in loaded mapblocks;
+       // clear objects in unloaded mapblocks only when the mapblocks are next activated.
+               CLEAR_OBJECTS_MODE_QUICK,
+};
+
+/*
+       The server-side environment.
+
+       This is not thread-safe. Server uses an environment mutex.
+*/
+
+typedef UNORDERED_MAP<u16, ServerActiveObject *> ActiveObjectMap;
+
+class ServerEnvironment : public Environment
+{
+public:
+       ServerEnvironment(ServerMap *map, GameScripting *scriptIface,
+               IGameDef *gamedef, const std::string &path_world);
+       ~ServerEnvironment();
+
+       Map & getMap();
+
+       ServerMap & getServerMap();
+
+       //TODO find way to remove this fct!
+       GameScripting* getScriptIface()
+       { return m_script; }
+
+       IGameDef *getGameDef()
+       { return m_gamedef; }
+
+       float getSendRecommendedInterval()
+       { return m_recommended_send_interval; }
+
+       void kickAllPlayers(AccessDeniedCode reason,
+               const std::string &str_reason, bool reconnect);
+       // Save players
+       void saveLoadedPlayers();
+       void savePlayer(RemotePlayer *player);
+       RemotePlayer *loadPlayer(const std::string &playername, PlayerSAO *sao);
+       void addPlayer(RemotePlayer *player);
+       void removePlayer(RemotePlayer *player);
+
+       /*
+               Save and load time of day and game timer
+       */
+       void saveMeta();
+       void loadMeta();
+       // to be called instead of loadMeta if
+       // env_meta.txt doesn't exist (e.g. new world)
+       void loadDefaultMeta();
+
+       u32 addParticleSpawner(float exptime);
+       u32 addParticleSpawner(float exptime, u16 attached_id);
+       void deleteParticleSpawner(u32 id, bool remove_from_object = true);
+
+       /*
+               External ActiveObject interface
+               -------------------------------------------
+       */
+
+       ServerActiveObject* getActiveObject(u16 id);
+
+       /*
+               Add an active object to the environment.
+               Environment handles deletion of object.
+               Object may be deleted by environment immediately.
+               If id of object is 0, assigns a free id to it.
+               Returns the id of the object.
+               Returns 0 if not added and thus deleted.
+       */
+       u16 addActiveObject(ServerActiveObject *object);
+
+       /*
+               Add an active object as a static object to the corresponding
+               MapBlock.
+               Caller allocates memory, ServerEnvironment frees memory.
+               Return value: true if succeeded, false if failed.
+               (note:  not used, pending removal from engine)
+       */
+       //bool addActiveObjectAsStatic(ServerActiveObject *object);
+
+       /*
+               Find out what new objects have been added to
+               inside a radius around a position
+       */
+       void getAddedActiveObjects(PlayerSAO *playersao, s16 radius,
+               s16 player_radius,
+               std::set<u16> &current_objects,
+               std::queue<u16> &added_objects);
+
+       /*
+               Find out what new objects have been removed from
+               inside a radius around a position
+       */
+       void getRemovedActiveObjects(PlayerSAO *playersao, s16 radius,
+               s16 player_radius,
+               std::set<u16> &current_objects,
+               std::queue<u16> &removed_objects);
+
+       /*
+               Get the next message emitted by some active object.
+               Returns a message with id=0 if no messages are available.
+       */
+       ActiveObjectMessage getActiveObjectMessage();
+
+       /*
+               Activate objects and dynamically modify for the dtime determined
+               from timestamp and additional_dtime
+       */
+       void activateBlock(MapBlock *block, u32 additional_dtime=0);
+
+       /*
+               {Active,Loading}BlockModifiers
+               -------------------------------------------
+       */
+
+       void addActiveBlockModifier(ActiveBlockModifier *abm);
+       void addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm);
+
+       /*
+               Other stuff
+               -------------------------------------------
+       */
+
+       // Script-aware node setters
+       bool setNode(v3s16 p, const MapNode &n);
+       bool removeNode(v3s16 p);
+       bool swapNode(v3s16 p, const MapNode &n);
+
+       // Find all active objects inside a radius around a point
+       void getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius);
+
+       // Clear objects, loading and going through every MapBlock
+       void clearObjects(ClearObjectsMode mode);
+
+       // This makes stuff happen
+       void step(f32 dtime);
+
+       //check if there's a line of sight between two positions
+       bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0, v3s16 *p=NULL);
+
+       u32 getGameTime() { return m_game_time; }
+
+       void reportMaxLagEstimate(float f) { m_max_lag_estimate = f; }
+       float getMaxLagEstimate() { return m_max_lag_estimate; }
+
+       std::set<v3s16>* getForceloadedBlocks() { return &m_active_blocks.m_forceloaded_list; };
+
+       // Sets the static object status all the active objects in the specified block
+       // This is only really needed for deleting blocks from the map
+       void setStaticForActiveObjectsInBlock(v3s16 blockpos,
+               bool static_exists, v3s16 static_block=v3s16(0,0,0));
+
+       RemotePlayer *getPlayer(const u16 peer_id);
+       RemotePlayer *getPlayer(const char* name);
+private:
+
+       /*
+               Internal ActiveObject interface
+               -------------------------------------------
+       */
+
+       /*
+               Add an active object to the environment.
+
+               Called by addActiveObject.
+
+               Object may be deleted by environment immediately.
+               If id of object is 0, assigns a free id to it.
+               Returns the id of the object.
+               Returns 0 if not added and thus deleted.
+       */
+       u16 addActiveObjectRaw(ServerActiveObject *object, bool set_changed, u32 dtime_s);
+
+       /*
+               Remove all objects that satisfy (m_removed && m_known_by_count==0)
+       */
+       void removeRemovedObjects();
+
+       /*
+               Convert stored objects from block to active
+       */
+       void activateObjects(MapBlock *block, u32 dtime_s);
+
+       /*
+               Convert objects that are not in active blocks to static.
+
+               If m_known_by_count != 0, active object is not deleted, but static
+               data is still updated.
+
+               If force_delete is set, active object is deleted nevertheless. It
+               shall only be set so in the destructor of the environment.
+       */
+       void deactivateFarObjects(bool force_delete);
+
+       /*
+               Member variables
+       */
+
+       // The map
+       ServerMap *m_map;
+       // Lua state
+       GameScripting* m_script;
+       // Game definition
+       IGameDef *m_gamedef;
+       // World path
+       const std::string m_path_world;
+       // Active object list
+       ActiveObjectMap m_active_objects;
+       // Outgoing network message buffer for active objects
+       std::queue<ActiveObjectMessage> m_active_object_messages;
+       // Some timers
+       float m_send_recommended_timer;
+       IntervalLimiter m_object_management_interval;
+       // List of active blocks
+       ActiveBlockList m_active_blocks;
+       IntervalLimiter m_active_blocks_management_interval;
+       IntervalLimiter m_active_block_modifier_interval;
+       IntervalLimiter m_active_blocks_nodemetadata_interval;
+       int m_active_block_interval_overload_skip;
+       // Time from the beginning of the game in seconds.
+       // Incremented in step().
+       u32 m_game_time;
+       // A helper variable for incrementing the latter
+       float m_game_time_fraction_counter;
+       // Time of last clearObjects call (game time).
+       // When a mapblock older than this is loaded, its objects are cleared.
+       u32 m_last_clear_objects_time;
+       // Active block modifiers
+       std::vector<ABMWithState> m_abms;
+       LBMManager m_lbm_mgr;
+       // An interval for generally sending object positions and stuff
+       float m_recommended_send_interval;
+       // Estimate for general maximum lag as determined by server.
+       // Can raise to high values like 15s with eg. map generation mods.
+       float m_max_lag_estimate;
+
+       // peer_ids in here should be unique, except that there may be many 0s
+       std::vector<RemotePlayer*> m_players;
+
+       // Particles
+       IntervalLimiter m_particle_management_interval;
+       UNORDERED_MAP<u32, float> m_particle_spawners;
+       UNORDERED_MAP<u32, u16> m_particle_spawner_attachments;
+};
+
+#endif
index f37bf0e869b49c7c41ac379f41682df6803e8c60..4df574f349307915a9033e15c805bb29752135b9 100644 (file)
@@ -21,9 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <stack>
 #include "util/pointer.h"
 #include "util/numeric.h"
-#include "util/mathconstants.h"
 #include "map.h"
-#include "environment.h"
+#include "serverenvironment.h"
 #include "nodedef.h"
 #include "treegen.h"