]> git.lizzy.rs Git - minetest.git/blobdiff - src/environment.cpp
Install minetest_game without .git and other unnecessary things
[minetest.git] / src / environment.cpp
index e03007341978a9fd564b94b9b017901afb801dc9..0c6d829f5b44b557676fec9ed66d1f6f0b2201f6 100644 (file)
@@ -17,6 +17,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
+#include <set>
+#include <list>
+#include <map>
 #include "environment.h"
 #include "filesys.h"
 #include "porting.h"
 #include "environment.h"
 #include "filesys.h"
 #include "porting.h"
@@ -34,11 +37,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nodemetadata.h"
 #include "main.h" // For g_settings, g_profiler
 #include "gamedef.h"
 #include "nodemetadata.h"
 #include "main.h" // For g_settings, g_profiler
 #include "gamedef.h"
+#ifndef SERVER
+#include "clientmap.h"
+#endif
+#include "daynightratio.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
 Environment::Environment():
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
 Environment::Environment():
-       m_time_of_day(9000)
+       m_time_of_day(9000),
+       m_time_of_day_f(9000./24000),
+       m_time_of_day_speed(0),
+       m_time_counter(0)
 {
 }
 
 {
 }
 
@@ -188,17 +198,51 @@ void Environment::printPlayers(std::ostream &o)
        }
 }
 
        }
 }
 
-/*void Environment::setDayNightRatio(u32 r)
-{
-       getDayNightRatio() = r;
-}*/
-
 u32 Environment::getDayNightRatio()
 {
 u32 Environment::getDayNightRatio()
 {
-       //return getDayNightRatio();
        return time_to_daynight_ratio(m_time_of_day);
 }
 
        return time_to_daynight_ratio(m_time_of_day);
 }
 
+void Environment::stepTimeOfDay(float dtime)
+{
+       m_time_counter += dtime;
+       f32 speed = m_time_of_day_speed * 24000./(24.*3600);
+       u32 units = (u32)(m_time_counter*speed);
+       m_time_counter -= (f32)units / speed;
+       bool sync_f = false;
+       if(units > 0){
+               // Sync at overflow
+               if(m_time_of_day + units >= 24000)
+                       sync_f = true;
+               m_time_of_day = (m_time_of_day + units) % 24000;
+               if(sync_f)
+                       m_time_of_day_f = (float)m_time_of_day / 24000.0;
+       }
+       if(!sync_f){
+               m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
+               if(m_time_of_day_f > 1.0)
+                       m_time_of_day_f -= 1.0;
+               if(m_time_of_day_f < 0.0)
+                       m_time_of_day_f += 1.0;
+       }
+}
+
+/*
+       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);
+}
+
 /*
        ActiveBlockList
 */
 /*
        ActiveBlockList
 */
@@ -273,10 +317,11 @@ void ActiveBlockList::update(core::list<v3s16> &active_positions,
 */
 
 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
 */
 
 ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
-               IGameDef *gamedef):
+               IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
        m_map(map),
        m_lua(L),
        m_gamedef(gamedef),
        m_map(map),
        m_lua(L),
        m_gamedef(gamedef),
+       m_emerger(emerger),
        m_random_spawn_timer(3),
        m_send_recommended_timer(0),
        m_game_time(0),
        m_random_spawn_timer(3),
        m_send_recommended_timer(0),
        m_game_time(0),
@@ -295,6 +340,12 @@ ServerEnvironment::~ServerEnvironment()
 
        // Drop/delete map
        m_map->drop();
 
        // Drop/delete map
        m_map->drop();
+
+       // Delete ActiveBlockModifiers
+       for(core::list<ABMWithState>::Iterator
+                       i = m_abms.begin(); i != m_abms.end(); i++){
+               delete i->abm;
+       }
 }
 
 void ServerEnvironment::serializePlayers(const std::string &savedir)
 }
 
 void ServerEnvironment::serializePlayers(const std::string &savedir)
@@ -316,7 +367,7 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
                //infostream<<"Checking player file "<<path<<std::endl;
 
                // Load player to see what is its name
                //infostream<<"Checking player file "<<path<<std::endl;
 
                // Load player to see what is its name
-               ServerRemotePlayer testplayer(this);
+               RemotePlayer testplayer(m_gamedef);
                {
                        // Open file and deserialize
                        std::ifstream is(path.c_str(), std::ios_base::binary);
                {
                        // Open file and deserialize
                        std::ifstream is(path.c_str(), std::ios_base::binary);
@@ -427,10 +478,10 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
                // Full path to this file
                std::string path = players_path + "/" + player_files[i].name;
 
                // Full path to this file
                std::string path = players_path + "/" + player_files[i].name;
 
-               infostream<<"Checking player file "<<path<<std::endl;
+               //infostream<<"Checking player file "<<path<<std::endl;
 
                // Load player to see what is its name
 
                // Load player to see what is its name
-               ServerRemotePlayer testplayer(this);
+               RemotePlayer testplayer(m_gamedef);
                {
                        // Open file and deserialize
                        std::ifstream is(path.c_str(), std::ios_base::binary);
                {
                        // Open file and deserialize
                        std::ifstream is(path.c_str(), std::ios_base::binary);
@@ -448,8 +499,8 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
                                        <<testplayer.getName()<<std::endl;
                }
 
                                        <<testplayer.getName()<<std::endl;
                }
 
-               infostream<<"Loaded test player with name "<<testplayer.getName()
-                               <<std::endl;
+               /*infostream<<"Loaded test player with name "<<testplayer.getName()
+                               <<std::endl;*/
                
                // Search for the player
                std::string playername = testplayer.getName();
                
                // Search for the player
                std::string playername = testplayer.getName();
@@ -457,14 +508,14 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
                bool newplayer = false;
                if(player == NULL)
                {
                bool newplayer = false;
                if(player == NULL)
                {
-                       infostream<<"Is a new player"<<std::endl;
-                       player = new ServerRemotePlayer(this);
+                       //infostream<<"Is a new player"<<std::endl;
+                       player = new RemotePlayer(m_gamedef);
                        newplayer = true;
                }
 
                // Load player
                {
                        newplayer = true;
                }
 
                // Load player
                {
-                       infostream<<"Reading player "<<testplayer.getName()<<" from "
+                       verbosestream<<"Reading player "<<testplayer.getName()<<" from "
                                        <<path<<std::endl;
                        // Open file and deserialize
                        std::ifstream is(path.c_str(), std::ios_base::binary);
                                        <<path<<std::endl;
                        // Open file and deserialize
                        std::ifstream is(path.c_str(), std::ios_base::binary);
@@ -477,7 +528,9 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
                }
 
                if(newplayer)
                }
 
                if(newplayer)
+               {
                        addPlayer(player);
                        addPlayer(player);
+               }
        }
 }
 
        }
 }
 
@@ -544,55 +597,154 @@ void ServerEnvironment::loadMeta(const std::string &savedir)
        }
 }
 
        }
 }
 
-#if 0
-// This is probably very useless
-void spawnRandomObjects(MapBlock *block)
+struct ActiveABM
+{
+       ActiveBlockModifier *abm;
+       int chance;
+       std::set<content_t> required_neighbors;
+};
+
+class ABMHandler
 {
 {
-       for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
-       for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+private:
+       ServerEnvironment *m_env;
+       std::map<content_t, std::list<ActiveABM> > m_aabms;
+public:
+       ABMHandler(core::list<ABMWithState> &abms,
+                       float dtime_s, ServerEnvironment *env,
+                       bool use_timers):
+               m_env(env)
        {
        {
-               bool last_node_walkable = false;
-               for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+               if(dtime_s < 0.001)
+                       return;
+               INodeDefManager *ndef = env->getGameDef()->ndef();
+               for(core::list<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;
+                       }
+                       ActiveABM aabm;
+                       aabm.abm = abm;
+                       float intervals = actual_interval / trigger_interval;
+                       float chance = abm->getTriggerChance();
+                       if(chance == 0)
+                               chance = 1;
+                       aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
+                       if(aabm.chance == 0)
+                               aabm.chance = 1;
+                       // 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++){
+                               content_t c = ndef->getId(*i);
+                               if(c == CONTENT_IGNORE)
+                                       continue;
+                               aabm.required_neighbors.insert(c);
+                       }
+                       // 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++){
+                               content_t c = ndef->getId(*i);
+                               if(c == CONTENT_IGNORE)
+                                       continue;
+                               std::map<content_t, std::list<ActiveABM> >::iterator j;
+                               j = m_aabms.find(c);
+                               if(j == m_aabms.end()){
+                                       std::list<ActiveABM> aabmlist;
+                                       m_aabms[c] = aabmlist;
+                                       j = m_aabms.find(c);
+                               }
+                               j->second.push_back(aabm);
+                       }
+               }
+       }
+       void apply(MapBlock *block)
+       {
+               if(m_aabms.empty())
+                       return;
+
+               ServerMap *map = &m_env->getServerMap();
+
+               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++)
                {
                {
-                       v3s16 p(x0,y0,z0);
-                       MapNode n = block->getNodeNoEx(p);
-                       if(n.getContent() == CONTENT_IGNORE)
-                               continue;
-                       if(m_gamedef->ndef()->get(n).liquid_type != LIQUID_NONE)
-                               continue;
-                       if(m_gamedef->ndef()->get(n).walkable)
-                       {
-                               last_node_walkable = true;
+                       MapNode n = block->getNodeNoEx(p0);
+                       content_t c = n.getContent();
+                       v3s16 p = p0 + block->getPosRelative();
+
+                       std::map<content_t, std::list<ActiveABM> >::iterator j;
+                       j = m_aabms.find(c);
+                       if(j == m_aabms.end())
                                continue;
                                continue;
-                       }
-                       if(last_node_walkable)
+
+                       for(std::list<ActiveABM>::iterator
+                                       i = j->second.begin(); i != j->second.end(); i++)
                        {
                        {
-                               // If block contains light information
-                               if(m_gamedef->ndef()->get(n).param_type == CPT_LIGHT)
+                               if(myrand() % i->chance != 0)
+                                       continue;
+
+                               // Check neighbors
+                               if(!i->required_neighbors.empty())
                                {
                                {
-                                       if(n.getLight(LIGHTBANK_DAY) <= 5)
+                                       v3s16 p1;
+                                       for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
+                                       for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
+                                       for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
                                        {
                                        {
-                                               if(myrand() % 1000 == 0)
-                                               {
-                                                       v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
-                                                       pos_f.Y -= BS*0.4;
-                                                       ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
-                                                       std::string data = obj->getStaticData();
-                                                       StaticObject s_obj(obj->getType(),
-                                                                       obj->getBasePosition(), data);
-                                                       // Add one
-                                                       block->m_static_objects.insert(0, s_obj);
-                                                       delete obj;
-                                                       block->setChangedFlag();
+                                               if(p1 == p)
+                                                       continue;
+                                               MapNode n = map->getNodeNoEx(p1);
+                                               content_t 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:
+
+                               // Find out how many objects the block contains
+                               u32 active_object_count = block->m_static_objects.m_active.size();
+                               // Find out how many objects this and all the neighbors contain
+                               u32 active_object_count_wider = 0;
+                               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)
+                                               continue;
+                                       active_object_count_wider +=
+                                                       block2->m_static_objects.m_active.size()
+                                                       + block2->m_static_objects.m_stored.size();
+                               }
+
+                               // 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);
                        }
                        }
-                       last_node_walkable = false;
                }
        }
                }
        }
-}
-#endif
+};
 
 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
 {
 
 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
 {
@@ -603,10 +755,14 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
                dtime_s = m_game_time - block->getTimestamp();
        dtime_s += additional_dtime;
 
                dtime_s = m_game_time - block->getTimestamp();
        dtime_s += additional_dtime;
 
-       // Set current time as timestamp (and let it set ChangedFlag)
-       block->setTimestamp(m_game_time);
+       /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
+                       <<stamp<<", game time: "<<m_game_time<<std::endl;*/
 
 
-       //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
+       // 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);
        
        // Activate stored objects
        activateObjects(block);
@@ -620,38 +776,35 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
                event.p = block->getPos();
                m_map->dispatchEvent(&event);
 
                event.p = block->getPos();
                m_map->dispatchEvent(&event);
 
-               block->setChangedFlag();
+               block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                               "node metadata modified in activateBlock");
        }
 
        }
 
-       // TODO: Do something
-       // TODO: Implement usage of ActiveBlockModifier
-       
-       // Here's a quick demonstration
-       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++)
-       {
-               v3s16 p = p0 + block->getPosRelative();
-               MapNode n = block->getNodeNoEx(p0);
-#if 1
-               // Test something:
-               // Convert all mud under proper day lighting to grass
-               if(n.getContent() == CONTENT_MUD)
-               {
-                       if(dtime_s > 300)
-                       {
-                               MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0));
-                               if(m_gamedef->ndef()->get(n_top).air_equivalent &&
-                                               n_top.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) >= 13)
-                               {
-                                       n.setContent(CONTENT_GRASS);
-                                       m_map->addNodeWithEvent(p, n);
-                               }
-                       }
-               }
-#endif
+       /* Handle ActiveBlockModifiers */
+       ABMHandler abmhandler(m_abms, dtime_s, this, false);
+       abmhandler.apply(block);
+}
+
+void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
+{
+       m_abms.push_back(ABMWithState(abm));
+}
+
+std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
+{
+       std::set<u16> objects;
+       for(core::map<u16, ServerActiveObject*>::Iterator
+                       i = m_active_objects.getIterator();
+                       i.atEnd()==false; i++)
+       {
+               ServerActiveObject* obj = i.getNode()->getValue();
+               u16 id = i.getNode()->getKey();
+               v3f objectpos = obj->getBasePosition();
+               if(objectpos.getDistanceFrom(pos) > radius)
+                       continue;
+               objects.insert(id);
        }
        }
+       return objects;
 }
 
 void ServerEnvironment::clearAllObjects()
 }
 
 void ServerEnvironment::clearAllObjects()
@@ -664,6 +817,8 @@ void ServerEnvironment::clearAllObjects()
                        i.atEnd()==false; i++)
        {
                ServerActiveObject* obj = i.getNode()->getValue();
                        i.atEnd()==false; i++)
        {
                ServerActiveObject* obj = i.getNode()->getValue();
+               if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
+                       continue;
                u16 id = i.getNode()->getKey();         
                v3f objectpos = obj->getBasePosition(); 
                // Delete static object if block is loaded
                u16 id = i.getNode()->getKey();         
                v3f objectpos = obj->getBasePosition(); 
                // Delete static object if block is loaded
@@ -671,7 +826,8 @@ void ServerEnvironment::clearAllObjects()
                        MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
                        if(block){
                                block->m_static_objects.remove(id);
                        MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
                        if(block){
                                block->m_static_objects.remove(id);
-                               block->raiseModified(MOD_STATE_WRITE_NEEDED);
+                               block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                               "clearAllObjects");
                                obj->m_static_exists = false;
                        }
                }
                                obj->m_static_exists = false;
                        }
                }
@@ -681,10 +837,15 @@ void ServerEnvironment::clearAllObjects()
                        obj->m_removed = true;
                        continue;
                }
                        obj->m_removed = true;
                        continue;
                }
+
+               // Tell the object about removal
+               obj->removingFromEnvironment();
                // Deregister in scripting api
                scriptapi_rm_object_reference(m_lua, obj);
                // Deregister in scripting api
                scriptapi_rm_object_reference(m_lua, obj);
+
                // Delete active object
                // Delete active object
-               delete obj;
+               if(obj->environmentDeletes())
+                       delete obj;
                // Id to be removed from m_active_objects
                objects_to_remove.push_back(id);
        }
                // Id to be removed from m_active_objects
                objects_to_remove.push_back(id);
        }
@@ -722,7 +883,8 @@ void ServerEnvironment::clearAllObjects()
                if(num_stored != 0 || num_active != 0){
                        block->m_static_objects.m_stored.clear();
                        block->m_static_objects.m_active.clear();
                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);
+                       block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                       "clearAllObjects");
                        num_objs_cleared += num_stored + num_active;
                        num_blocks_cleared++;
                }
                        num_objs_cleared += num_stored + num_active;
                        num_blocks_cleared++;
                }
@@ -742,28 +904,14 @@ void ServerEnvironment::clearAllObjects()
                        <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
 }
 
                        <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
 }
 
-static void getMob_dungeon_master(Settings &properties)
-{
-       properties.set("looks", "dungeon_master");
-       properties.setFloat("yaw", 1.57);
-       properties.setFloat("hp", 30);
-       properties.setBool("bright_shooting", true);
-       properties.set("shoot_type", "fireball");
-       properties.set("shoot_y", "0.7");
-       properties.set("player_hit_damage", "1");
-       properties.set("player_hit_distance", "1.0");
-       properties.set("player_hit_interval", "0.5");
-       properties.setBool("mindless_rage", myrand_range(0,100)==0);
-}
-
 void ServerEnvironment::step(float dtime)
 {
        DSTACK(__FUNCTION_NAME);
        
        //TimeTaker timer("ServerEnv step");
 
 void ServerEnvironment::step(float dtime)
 {
        DSTACK(__FUNCTION_NAME);
        
        //TimeTaker timer("ServerEnv step");
 
-       // Get some settings
-       bool footprints = g_settings->getBool("footprints");
+       /* Step time of day */
+       stepTimeOfDay(dtime);
 
        /*
                Increment game time
 
        /*
                Increment game time
@@ -793,26 +941,6 @@ void ServerEnvironment::step(float dtime)
                        
                        // Move
                        player->move(dtime, *m_map, 100*BS);
                        
                        // Move
                        player->move(dtime, *m_map, 100*BS);
-                       
-                       /*
-                               Add footsteps to grass
-                       */
-                       if(footprints)
-                       {
-                               // Get node that is at BS/4 under player
-                               v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
-                               try{
-                                       MapNode n = m_map->getNode(bottompos);
-                                       if(n.getContent() == CONTENT_GRASS)
-                                       {
-                                               n.setContent(CONTENT_GRASS_FOOTSTEPS);
-                                               m_map->setNode(bottompos, n);
-                                       }
-                               }
-                               catch(InvalidPositionException &e)
-                               {
-                               }
-                       }
                }
        }
 
                }
        }
 
@@ -886,8 +1014,12 @@ void ServerEnvironment::step(float dtime)
                                        <<") became active"<<std::endl;*/
 
                        MapBlock *block = m_map->getBlockNoCreateNoEx(p);
                                        <<") became active"<<std::endl;*/
 
                        MapBlock *block = m_map->getBlockNoCreateNoEx(p);
-                       if(block==NULL)
+                       if(block==NULL){
+                               // Block needs to be fetched first
+                               m_emerger->queueBlockEmerge(p, false);
+                               m_active_blocks.m_list.remove(p);
                                continue;
                                continue;
+                       }
 
                        activateBlock(block);
                }
 
                        activateBlock(block);
                }
@@ -920,6 +1052,11 @@ void ServerEnvironment::step(float dtime)
                        
                        // Set current time as timestamp
                        block->setTimestampNoChangedFlag(m_game_time);
                        
                        // Set current time as timestamp
                        block->setTimestampNoChangedFlag(m_game_time);
+                       // If time has changed much from the one on disk,
+                       // set block to be saved when it is unloaded
+                       if(block->getTimestamp() > block->getDiskTimestamp() + 60)
+                               block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
+                                               "Timestamp older than 60s (step)");
 
                        // Run node metadata
                        bool changed = block->m_node_metadata->step(dtime);
 
                        // Run node metadata
                        bool changed = block->m_node_metadata->step(dtime);
@@ -930,16 +1067,21 @@ void ServerEnvironment::step(float dtime)
                                event.p = p;
                                m_map->dispatchEvent(&event);
 
                                event.p = p;
                                m_map->dispatchEvent(&event);
 
-                               block->setChangedFlag();
+                               block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                               "node metadata modified in step");
                        }
                }
        }
        
                        }
                }
        }
        
-       if(m_active_blocks_test_interval.step(dtime, 10.0))
+       const float abm_interval = 1.0;
+       if(m_active_block_modifier_interval.step(dtime, abm_interval))
        {
        {
-               ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
-               //float dtime = 10.0;
+               ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
+               TimeTaker timer("modify in active blocks");
                
                
+               // Initialize handling of ActiveBlockModifiers
+               ABMHandler abmhandler(m_abms, abm_interval, this, true);
+
                for(core::map<v3s16, bool>::Iterator
                                i = m_active_blocks.m_list.getIterator();
                                i.atEnd()==false; i++)
                for(core::map<v3s16, bool>::Iterator
                                i = m_active_blocks.m_list.getIterator();
                                i.atEnd()==false; i++)
@@ -956,196 +1098,16 @@ void ServerEnvironment::step(float dtime)
                        // Set current time as timestamp
                        block->setTimestampNoChangedFlag(m_game_time);
 
                        // Set current time as timestamp
                        block->setTimestampNoChangedFlag(m_game_time);
 
-                       /*
-                               Do stuff!
-
-                               Note that map modifications should be done using the event-
-                               making map methods so that the server gets information
-                               about them.
-
-                               Reading can be done quickly directly from the block.
-
-                               Everything should bind to inside this single content
-                               searching loop to keep things fast.
-                       */
-                       // TODO: Implement usage of ActiveBlockModifier
-                       
-                       // Find out how many objects the block contains
-                       //u32 active_object_count = block->m_static_objects.m_active.size();
-                       // Find out how many objects this and all the neighbors contain
-                       u32 active_object_count_wider = 0;
-                       for(s16 x=-1; x<=1; x++)
-                       for(s16 y=-1; y<=1; y++)
-                       for(s16 z=-1; z<=1; z++)
-                       {
-                               MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z));
-                               if(block==NULL)
-                                       continue;
-                               active_object_count_wider +=
-                                               block->m_static_objects.m_active.size()
-                                               + block->m_static_objects.m_stored.size();
-                               
-                               /*if(block->m_static_objects.m_stored.size() != 0){
-                                       errorstream<<"ServerEnvironment::step(): "
-                                                       <<PP(block->getPos())<<" contains "
-                                                       <<block->m_static_objects.m_stored.size()
-                                                       <<" stored objects; "
-                                                       <<"when spawning objects, when counting active "
-                                                       <<"objects in wide area. relative position: "
-                                                       <<"("<<x<<","<<y<<","<<z<<")"<<std::endl;
-                               }*/
-                       }
+                       /* Handle ActiveBlockModifiers */
+                       abmhandler.apply(block);
+               }
 
 
-                       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++)
-                       {
-                               v3s16 p = p0 + block->getPosRelative();
-                               MapNode n = block->getNodeNoEx(p0);
-
-                               /*
-                                       Test something:
-                                       Convert mud under proper lighting to grass
-                               */
-                               if(n.getContent() == CONTENT_MUD)
-                               {
-                                       if(myrand()%20 == 0)
-                                       {
-                                               MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
-                                               if(m_gamedef->ndef()->get(n_top).air_equivalent &&
-                                                               n_top.getLightBlend(getDayNightRatio(),
-                                                                               m_gamedef->ndef()) >= 13)
-                                               {
-                                                       n.setContent(CONTENT_GRASS);
-                                                       m_map->addNodeWithEvent(p, n);
-                                               }
-                                       }
-                               }
-                               /*
-                                       Convert grass into mud if under something else than air
-                               */
-                               if(n.getContent() == CONTENT_GRASS)
-                               {
-                                       //if(myrand()%20 == 0)
-                                       {
-                                               MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0));
-                                               if(m_gamedef->ndef()->get(n_top).air_equivalent == false)
-                                               {
-                                                       n.setContent(CONTENT_MUD);
-                                                       m_map->addNodeWithEvent(p, n);
-                                               }
-                                       }
-                               }
-                               /*
-                                       Rats spawn around regular trees
-                               */
-                               if(n.getContent() == CONTENT_TREE ||
-                                               n.getContent() == CONTENT_JUNGLETREE)
-                               {
-                                       if(myrand()%200 == 0 && active_object_count_wider == 0)
-                                       {
-                                               v3s16 p1 = p + v3s16(myrand_range(-2, 2),
-                                                               0, myrand_range(-2, 2));
-                                               MapNode n1 = m_map->getNodeNoEx(p1);
-                                               MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0));
-                                               if(n1b.getContent() == CONTENT_GRASS &&
-                                                               n1.getContent() == CONTENT_AIR)
-                                               {
-                                                       v3f pos = intToFloat(p1, BS);
-                                                       ServerActiveObject *obj = new RatSAO(this, pos);
-                                                       addActiveObject(obj);
-                                               }
-                                       }
-                               }
-                               /*
-                                       Fun things spawn in caves and dungeons
-                               */
-                               if(n.getContent() == CONTENT_STONE ||
-                                               n.getContent() == CONTENT_MOSSYCOBBLE)
-                               {
-                                       if(myrand()%200 == 0 && active_object_count_wider == 0)
-                                       {
-                                               v3s16 p1 = p + v3s16(0,1,0);
-                                               MapNode n1a = m_map->getNodeNoEx(p1+v3s16(0,0,0));
-                                               if(n1a.getLightBlend(getDayNightRatio(),
-                                                               m_gamedef->ndef()) <= 3){
-                                                       MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,1,0));
-                                                       if(n1a.getContent() == CONTENT_AIR &&
-                                                                       n1b.getContent() == CONTENT_AIR)
-                                                       {
-                                                               v3f pos = intToFloat(p1, BS);
-                                                               int i = myrand()%5;
-                                                               if(i == 0 || i == 1){
-                                                                       actionstream<<"A dungeon master spawns at "
-                                                                                       <<PP(p1)<<std::endl;
-                                                                       Settings properties;
-                                                                       getMob_dungeon_master(properties);
-                                                                       ServerActiveObject *obj = new MobV2SAO(
-                                                                                       this, pos, &properties);
-                                                                       addActiveObject(obj);
-                                                               } else if(i == 2 || i == 3){
-                                                                       actionstream<<"Rats spawn at "
-                                                                                       <<PP(p1)<<std::endl;
-                                                                       for(int j=0; j<3; j++){
-                                                                               ServerActiveObject *obj = new RatSAO(
-                                                                                               this, pos);
-                                                                               addActiveObject(obj);
-                                                                       }
-                                                               } else {
-                                                                       actionstream<<"An oerkki spawns at "
-                                                                                       <<PP(p1)<<std::endl;
-                                                                       ServerActiveObject *obj = new Oerkki1SAO(
-                                                                                       this, pos);
-                                                                       addActiveObject(obj);
-                                                               }
-                                                       }
-                                               }
-                                       }
-                               }
-                               /*
-                                       Make trees from saplings!
-                               */
-                               if(n.getContent() == CONTENT_SAPLING)
-                               {
-                                       if(myrand()%50 == 0)
-                                       {
-                                               actionstream<<"A sapling grows into a tree at "
-                                                               <<PP(p)<<std::endl;
-
-                                               core::map<v3s16, MapBlock*> modified_blocks;
-                                               v3s16 tree_p = p;
-                                               ManualMapVoxelManipulator vmanip(m_map);
-                                               v3s16 tree_blockp = getNodeBlockPos(tree_p);
-                                               vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1));
-                                               bool is_apple_tree = myrand()%4 == 0;
-                                               mapgen::make_tree(vmanip, tree_p, is_apple_tree);
-                                               vmanip.blitBackAll(&modified_blocks);
-
-                                               // update lighting
-                                               core::map<v3s16, MapBlock*> lighting_modified_blocks;
-                                               for(core::map<v3s16, MapBlock*>::Iterator
-                                                       i = modified_blocks.getIterator();
-                                                       i.atEnd() == false; i++)
-                                               {
-                                                       lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue());
-                                               }
-                                               m_map->updateLighting(lighting_modified_blocks, modified_blocks);
-
-                                               // Send a MEET_OTHER event
-                                               MapEditEvent event;
-                                               event.type = MEET_OTHER;
-                                               for(core::map<v3s16, MapBlock*>::Iterator
-                                                       i = modified_blocks.getIterator();
-                                                       i.atEnd() == false; i++)
-                                               {
-                                                       v3s16 p = i.getNode()->getKey();
-                                                       event.modified_blocks.insert(p, true);
-                                               }
-                                               m_map->dispatchEvent(&event);
-                                       }
-                               }
-                       }
+               u32 time_ms = timer.stop(true);
+               u32 max_time_ms = 200;
+               if(time_ms > max_time_ms){
+                       infostream<<"WARNING: active block modifiers took "
+                                       <<time_ms<<"ms (longer than "
+                                       <<max_time_ms<<"ms)"<<std::endl;
                }
        }
        
                }
        }
        
@@ -1207,60 +1169,6 @@ void ServerEnvironment::step(float dtime)
                */
                removeRemovedObjects();
        }
                */
                removeRemovedObjects();
        }
-
-       if(g_settings->getBool("enable_experimental"))
-       {
-
-       /*
-               TEST CODE
-       */
-#if 0
-       m_random_spawn_timer -= dtime;
-       if(m_random_spawn_timer < 0)
-       {
-               //m_random_spawn_timer += myrand_range(2.0, 20.0);
-               //m_random_spawn_timer += 2.0;
-               m_random_spawn_timer += 200.0;
-
-               /*
-                       Find some position
-               */
-
-               /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5));
-               s16 y = 1 + getServerMap().findGroundLevel(p2d);
-               v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/
-               
-               Player *player = getRandomConnectedPlayer();
-               v3f pos(0,0,0);
-               if(player)
-                       pos = player->getPosition();
-               pos += v3f(
-                       myrand_range(-3,3)*BS,
-                       5,
-                       myrand_range(-3,3)*BS
-               );
-
-               /*
-                       Create a ServerActiveObject
-               */
-
-               //TestSAO *obj = new TestSAO(this, pos);
-               //ServerActiveObject *obj = new ItemSAO(this, pos, "CraftItem Stick 1");
-               //ServerActiveObject *obj = new RatSAO(this, pos);
-               //ServerActiveObject *obj = new Oerkki1SAO(this, pos);
-               //ServerActiveObject *obj = new FireflySAO(this, pos);
-
-               infostream<<"Server: Spawning MobV2SAO at "
-                               <<"("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
-               
-               Settings properties;
-               getMob_dungeon_master(properties);
-               ServerActiveObject *obj = new MobV2SAO(this, pos, &properties);
-               addActiveObject(obj);
-       }
-#endif
-
-       } // enable_experimental
 }
 
 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
 }
 
 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
@@ -1337,7 +1245,8 @@ bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
        if(block)
        {
                block->m_static_objects.insert(0, s_obj);
        if(block)
        {
                block->m_static_objects.insert(0, s_obj);
-               block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD);
+               block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
+                               "addActiveObjectAsStatic");
                succeeded = true;
        }
        else{
                succeeded = true;
        }
        else{
@@ -1347,7 +1256,8 @@ bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
                succeeded = false;
        }
 
                succeeded = false;
        }
 
-       delete obj;
+       if(obj->environmentDeletes())
+               delete obj;
 
        return succeeded;
 }
 
        return succeeded;
 }
@@ -1381,10 +1291,12 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
                // Discard if removed
                if(object->m_removed)
                        continue;
                // Discard if removed
                if(object->m_removed)
                        continue;
-               // Discard if too far
-               f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
-               if(distance_f > radius_f)
-                       continue;
+               if(object->unlimitedTransferDistance() == false){
+                       // Discard if too far
+                       f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
+                       if(distance_f > radius_f)
+                               continue;
+               }
                // Discard if already on current_objects
                core::map<u16, bool>::Node *n;
                n = current_objects.find(id);
                // Discard if already on current_objects
                core::map<u16, bool>::Node *n;
                n = current_objects.find(id);
@@ -1419,24 +1331,33 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
        {
                u16 id = i.getNode()->getKey();
                ServerActiveObject *object = getActiveObject(id);
        {
                u16 id = i.getNode()->getKey();
                ServerActiveObject *object = getActiveObject(id);
-               if(object == NULL)
-               {
+
+               if(object == NULL){
                        infostream<<"ServerEnvironment::getRemovedActiveObjects():"
                                        <<" object in current_objects is NULL"<<std::endl;
                        infostream<<"ServerEnvironment::getRemovedActiveObjects():"
                                        <<" object in current_objects is NULL"<<std::endl;
+                       removed_objects.insert(id, false);
+                       continue;
                }
                }
-               else if(object->m_removed == false)
+
+               if(object->m_removed)
                {
                {
-                       f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
-                       /*infostream<<"removed == false"
-                                       <<"distance_f = "<<distance_f
-                                       <<", radius_f = "<<radius_f<<std::endl;*/
-                       if(distance_f < radius_f)
-                       {
-                               // Not removed
-                               continue;
-                       }
+                       removed_objects.insert(id, false);
+                       continue;
+               }
+               
+               // If transfer distance is unlimited, don't remove
+               if(object->unlimitedTransferDistance())
+                       continue;
+
+               f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
+
+               if(distance_f >= radius_f)
+               {
+                       removed_objects.insert(id, false);
+                       continue;
                }
                }
-               removed_objects.insert(id, false);
+               
+               // Not removed
        }
 }
 
        }
 }
 
@@ -1462,7 +1383,8 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
                {
                        errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
                                        <<"no free ids available"<<std::endl;
                {
                        errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
                                        <<"no free ids available"<<std::endl;
-                       delete object;
+                       if(object->environmentDeletes())
+                               delete object;
                        return 0;
                }
                object->setId(new_id);
                        return 0;
                }
                object->setId(new_id);
@@ -1475,7 +1397,8 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
        {
                errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
                                <<"id is not free ("<<object->getId()<<")"<<std::endl;
        {
                errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
                                <<"id is not free ("<<object->getId()<<")"<<std::endl;
-               delete object;
+               if(object->environmentDeletes())
+                       delete object;
                return 0;
        }
        /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
                return 0;
        }
        /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
@@ -1488,33 +1411,38 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
                        <<m_active_objects.size()<<" active objects."
                        <<std::endl;
        
                        <<m_active_objects.size()<<" active objects."
                        <<std::endl;
        
-       // 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->getBlockNoCreateNoEx(blockpos);
-       if(block)
-       {
-               block->m_static_objects.m_active.insert(object->getId(), s_obj);
-               object->m_static_exists = true;
-               object->m_static_block = blockpos;
-
-               if(set_changed)
-                       block->raiseModified(MOD_STATE_WRITE_NEEDED);
-       }
-       else{
-               errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
-                               <<"could not find block for storing id="<<object->getId()
-                               <<" statically"<<std::endl;
-       }
-       
        // Register reference in scripting api (must be done before post-init)
        scriptapi_add_object_reference(m_lua, object);
        // Post-initialize object
        object->addedToEnvironment();
        // Register reference in scripting api (must be done before post-init)
        scriptapi_add_object_reference(m_lua, object);
        // Post-initialize object
        object->addedToEnvironment();
+       
+       // 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->getBlockNoCreateNoEx(blockpos);
+               if(block)
+               {
+                       block->m_static_objects.m_active.insert(object->getId(), s_obj);
+                       object->m_static_exists = true;
+                       object->m_static_block = blockpos;
 
 
+                       if(set_changed)
+                               block->raiseModified(MOD_STATE_WRITE_NEEDED, 
+                                               "addActiveObjectRaw");
+               }
+               else{
+                       errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
+                                       <<"could not find block for storing id="<<object->getId()
+                                       <<" statically"<<std::endl;
+               }
+       }
+       
        return object->getId();
 }
 
        return object->getId();
 }
 
@@ -1556,7 +1484,8 @@ void ServerEnvironment::removeRemovedObjects()
                        if(block)
                        {
                                block->m_static_objects.remove(id);
                        if(block)
                        {
                                block->m_static_objects.remove(id);
-                               block->raiseModified(MOD_STATE_WRITE_NEEDED);
+                               block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                               "removeRemovedObjects");
                                obj->m_static_exists = false;
                        }
                }
                                obj->m_static_exists = false;
                        }
                }
@@ -1565,11 +1494,14 @@ void ServerEnvironment::removeRemovedObjects()
                if(obj->m_known_by_count > 0)
                        continue;
                
                if(obj->m_known_by_count > 0)
                        continue;
                
+               // Tell the object about removal
+               obj->removingFromEnvironment();
                // Deregister in scripting api
                scriptapi_rm_object_reference(m_lua, obj);
 
                // Delete
                // Deregister in scripting api
                scriptapi_rm_object_reference(m_lua, obj);
 
                // Delete
-               delete obj;
+               if(obj->environmentDeletes())
+                       delete obj;
                // Id to be removed from m_active_objects
                objects_to_remove.push_back(id);
        }
                // Id to be removed from m_active_objects
                objects_to_remove.push_back(id);
        }
@@ -1637,7 +1569,9 @@ void ServerEnvironment::activateObjects(MapBlock *block)
                                <<"; removing all of them."<<std::endl;
                // Clear stored list
                block->m_static_objects.m_stored.clear();
                                <<"; removing all of them."<<std::endl;
                // Clear stored list
                block->m_static_objects.m_stored.clear();
-               block->raiseModified(MOD_STATE_WRITE_NEEDED);
+               block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                               "stored list cleared in activateObjects due to "
+                               "large amount of objects");
                return;
        }
        // A list for objects that couldn't be converted to static for some
                return;
        }
        // A list for objects that couldn't be converted to static for some
@@ -1687,7 +1621,7 @@ void ServerEnvironment::activateObjects(MapBlock *block)
                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.
                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->setChangedFlag().
+               Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
                Otherwise there would be a huge amount of unnecessary I/O.
        */
 }
                Otherwise there would be a huge amount of unnecessary I/O.
        */
 }
@@ -1709,18 +1643,14 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
                        i.atEnd()==false; i++)
        {
                ServerActiveObject* obj = i.getNode()->getValue();
                        i.atEnd()==false; i++)
        {
                ServerActiveObject* obj = i.getNode()->getValue();
-
-               // This shouldn't happen but check it
-               if(obj == NULL)
-               {
-                       errorstream<<"NULL object found in ServerEnvironment"
-                                       <<std::endl;
-                       assert(0);
+               assert(obj);
+               
+               // Do not deactivate if static data creation not allowed
+               if(!force_delete && !obj->isStaticAllowed())
                        continue;
                        continue;
-               }
 
                // If pending deactivation, let removeRemovedObjects() do it
 
                // If pending deactivation, let removeRemovedObjects() do it
-               if(obj->m_pending_deactivation)
+               if(!force_delete && obj->m_pending_deactivation)
                        continue;
 
                u16 id = i.getNode()->getKey();         
                        continue;
 
                u16 id = i.getNode()->getKey();         
@@ -1730,7 +1660,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
                v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
 
                // If block is active, don't remove
                v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
 
                // If block is active, don't remove
-               if(m_active_blocks.contains(blockpos_o))
+               if(!force_delete && m_active_blocks.contains(blockpos_o))
                        continue;
 
                verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
                        continue;
 
                verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
@@ -1744,85 +1674,94 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
                        Update the static data
                */
 
                        Update the static data
                */
 
-               // 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->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;
+                       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);
-                       
-                       core::map<u16, StaticObject>::Node *n =
-                                       block->m_static_objects.m_active.find(id);
-                       if(n){
-                               StaticObject static_old = n->getValue();
-
-                               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;
+                               MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
+                               
+                               core::map<u16, StaticObject>::Node *n =
+                                               block->m_static_objects.m_active.find(id);
+                               if(n){
+                                       StaticObject static_old = n->getValue();
+
+                                       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)
+                       bool shall_be_written = (!stays_in_same_block || data_changed);
+                       
+                       // Delete old static object
+                       if(obj->m_static_exists)
                        {
                        {
-                               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);
+                               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,
+                                                               "deactivateFarObjects: Static data "
+                                                               "changed considerably");
+                               }
                        }
                        }
-               }
 
 
-               // Add to the block where the object is located in
-               v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
-               // Get or generate the block
-               MapBlock *block = m_map->emergeBlock(blockpos);
+                       // Add to the block where the object is located in
+                       v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
+                       // Get or generate the block
+                       MapBlock *block = m_map->emergeBlock(blockpos);
 
 
-               if(block)
-               {
-                       if(block->m_static_objects.m_stored.size() >= 49){
-                               errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
-                                               <<" statically but block "<<PP(blockpos)
-                                               <<" already contains "
-                                               <<block->m_static_objects.m_stored.size()
-                                               <<" (over 49) objects."
-                                               <<" Forcing delete."<<std::endl;
-                               force_delete = true;
-                       } else {
-                               u16 new_id = pending_delete ? id : 0;
-                               block->m_static_objects.insert(new_id, s_obj);
-                               
-                               // Only mark block as modified if data changed considerably
-                               if(shall_be_written)
-                                       block->raiseModified(MOD_STATE_WRITE_NEEDED);
-                               
-                               obj->m_static_exists = true;
-                               obj->m_static_block = block->getPos();
+                       if(block)
+                       {
+                               if(block->m_static_objects.m_stored.size() >= 49){
+                                       errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
+                                                       <<" statically but block "<<PP(blockpos)
+                                                       <<" already contains "
+                                                       <<block->m_static_objects.m_stored.size()
+                                                       <<" (over 49) objects."
+                                                       <<" Forcing delete."<<std::endl;
+                                       force_delete = true;
+                               } else {
+                                       u16 new_id = pending_delete ? id : 0;
+                                       block->m_static_objects.insert(new_id, s_obj);
+                                       
+                                       // Only mark block as modified if data changed considerably
+                                       if(shall_be_written)
+                                               block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                                               "deactivateFarObjects: Static data "
+                                                               "changed considerably");
+                                       
+                                       obj->m_static_exists = true;
+                                       obj->m_static_block = block->getPos();
+                               }
+                       }
+                       else{
+                               if(!force_delete){
+                                       errorstream<<"ServerEnv: Could not find or generate "
+                                                       <<"a block for storing id="<<obj->getId()
+                                                       <<" statically"<<std::endl;
+                                       continue;
+                               }
                        }
                        }
-               }
-               else{
-                       errorstream<<"ServerEnv: Could not find or generate "
-                                       <<"a block for storing id="<<obj->getId()
-                                       <<" statically"<<std::endl;
-                       continue;
                }
 
                /*
                }
 
                /*
@@ -1830,7 +1769,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
                        Otherwise delete it immediately.
                */
 
                        Otherwise delete it immediately.
                */
 
-               if(pending_delete)
+               if(pending_delete && !force_delete)
                {
                        verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
                                        <<"object id="<<id<<" is known by clients"
                {
                        verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
                                        <<"object id="<<id<<" is known by clients"
@@ -1844,11 +1783,14 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
                                <<"object id="<<id<<" is not known by clients"
                                <<"; deleting"<<std::endl;
 
                                <<"object id="<<id<<" is not known by clients"
                                <<"; deleting"<<std::endl;
 
+               // Tell the object about removal
+               obj->removingFromEnvironment();
                // Deregister in scripting api
                scriptapi_rm_object_reference(m_lua, obj);
 
                // Delete active object
                // Deregister in scripting api
                scriptapi_rm_object_reference(m_lua, obj);
 
                // Delete active object
-               delete obj;
+               if(obj->environmentDeletes())
+                       delete obj;
                // Id to be removed from m_active_objects
                objects_to_remove.push_back(id);
        }
                // Id to be removed from m_active_objects
                objects_to_remove.push_back(id);
        }
@@ -1864,19 +1806,21 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
 
 #ifndef SERVER
 
 
 #ifndef SERVER
 
+#include "clientsimpleobject.h"
+
 /*
        ClientEnvironment
 */
 
 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
 /*
        ClientEnvironment
 */
 
 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
-               ITextureSource *texturesource, IGameDef *gamedef):
+               ITextureSource *texturesource, IGameDef *gamedef,
+               IrrlichtDevice *irr):
        m_map(map),
        m_smgr(smgr),
        m_texturesource(texturesource),
        m_map(map),
        m_smgr(smgr),
        m_texturesource(texturesource),
-       m_gamedef(gamedef)
+       m_gamedef(gamedef),
+       m_irr(irr)
 {
 {
-       assert(m_map);
-       assert(m_smgr);
 }
 
 ClientEnvironment::~ClientEnvironment()
 }
 
 ClientEnvironment::~ClientEnvironment()
@@ -1889,10 +1833,26 @@ ClientEnvironment::~ClientEnvironment()
                delete i.getNode()->getValue();
        }
 
                delete i.getNode()->getValue();
        }
 
+       for(core::list<ClientSimpleObject*>::Iterator
+                       i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
+       {
+               delete *i;
+       }
+
        // Drop/delete map
        m_map->drop();
 }
 
        // Drop/delete map
        m_map->drop();
 }
 
+Map & ClientEnvironment::getMap()
+{
+       return *m_map;
+}
+
+ClientMap & ClientEnvironment::getClientMap()
+{
+       return *m_map;
+}
+
 void ClientEnvironment::addPlayer(Player *player)
 {
        DSTACK(__FUNCTION_NAME);
 void ClientEnvironment::addPlayer(Player *player)
 {
        DSTACK(__FUNCTION_NAME);
@@ -1921,9 +1881,11 @@ void ClientEnvironment::step(float dtime)
 {
        DSTACK(__FUNCTION_NAME);
 
 {
        DSTACK(__FUNCTION_NAME);
 
+       /* Step time of day */
+       stepTimeOfDay(dtime);
+
        // Get some settings
        bool free_move = g_settings->getBool("free_move");
        // Get some settings
        bool free_move = g_settings->getBool("free_move");
-       bool footprints = g_settings->getBool("footprints");
 
        // Get local player
        LocalPlayer *lplayer = getLocalPlayer();
 
        // Get local player
        LocalPlayer *lplayer = getLocalPlayer();
@@ -1936,8 +1898,7 @@ void ClientEnvironment::step(float dtime)
        */
        bool is_climbing = lplayer->is_climbing;
        
        */
        bool is_climbing = lplayer->is_climbing;
        
-       f32 player_speed = 0.001; // just some small value
-       player_speed = lplayer->getSpeed().getLength();
+       f32 player_speed = lplayer->getSpeed().getLength();
        
        /*
                Maximum position increment
        
        /*
                Maximum position increment
@@ -1947,7 +1908,9 @@ void ClientEnvironment::step(float dtime)
 
        // Maximum time increment (for collision detection etc)
        // time = distance / speed
 
        // Maximum time increment (for collision detection etc)
        // time = distance / speed
-       f32 dtime_max_increment = position_max_increment / player_speed;
+       f32 dtime_max_increment = 1;
+       if(player_speed > 0.001)
+               dtime_max_increment = position_max_increment / player_speed;
        
        // Maximum time increment is 10ms or lower
        if(dtime_max_increment > 0.01)
        
        // Maximum time increment is 10ms or lower
        if(dtime_max_increment > 0.01)
@@ -2036,21 +1999,15 @@ void ClientEnvironment::step(float dtime)
                if(info.t == COLLISION_FALL)
                {
                        //f32 tolerance = BS*10; // 2 without damage
                if(info.t == COLLISION_FALL)
                {
                        //f32 tolerance = BS*10; // 2 without damage
-                       f32 tolerance = BS*12; // 3 without damage
+                       //f32 tolerance = BS*12; // 3 without damage
+                       f32 tolerance = BS*14; // 5 without damage
                        f32 factor = 1;
                        if(info.speed > tolerance)
                        {
                                f32 damage_f = (info.speed - tolerance)/BS*factor;
                                u16 damage = (u16)(damage_f+0.5);
                        f32 factor = 1;
                        if(info.speed > tolerance)
                        {
                                f32 damage_f = (info.speed - tolerance)/BS*factor;
                                u16 damage = (u16)(damage_f+0.5);
-                               if(lplayer->hp > damage)
-                                       lplayer->hp -= damage;
-                               else
-                                       lplayer->hp = 0;
-
-                               ClientEnvEvent event;
-                               event.type = CEE_PLAYER_DAMAGE;
-                               event.player_damage.amount = damage;
-                               m_client_event_queue.push_back(event);
+                               if(damage != 0)
+                                       damageLocalPlayer(damage, true);
                        }
                }
        }
                        }
                }
        }
@@ -2080,10 +2037,7 @@ void ClientEnvironment::step(float dtime)
                
                if(damage_per_second != 0)
                {
                
                if(damage_per_second != 0)
                {
-                       ClientEnvEvent event;
-                       event.type = CEE_PLAYER_DAMAGE;
-                       event.player_damage.amount = damage_per_second;
-                       m_client_event_queue.push_back(event);
+                       damageLocalPlayer(damage_per_second, true);
                }
        }
        
                }
        }
        
@@ -2114,36 +2068,10 @@ void ClientEnvironment::step(float dtime)
                        MapNode n = m_map->getNode(p);
                        light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
                }
                        MapNode n = m_map->getNode(p);
                        light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
                }
-               catch(InvalidPositionException &e) {}
-               player->updateLight(light);
-
-               /*
-                       Add footsteps to grass
-               */
-               if(footprints)
-               {
-                       // Get node that is at BS/4 under player
-                       v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
-                       try{
-                               MapNode n = m_map->getNode(bottompos);
-                               if(n.getContent() == CONTENT_GRASS)
-                               {
-                                       n.setContent(CONTENT_GRASS_FOOTSTEPS);
-                                       m_map->setNode(bottompos, n);
-                                       // Update mesh on client
-                                       if(m_map->mapType() == MAPTYPE_CLIENT)
-                                       {
-                                               v3s16 p_blocks = getNodeBlockPos(bottompos);
-                                               MapBlock *b = m_map->getBlockNoCreate(p_blocks);
-                                               //b->updateMesh(getDayNightRatio());
-                                               b->setMeshExpired(true);
-                                       }
-                               }
-                       }
-                       catch(InvalidPositionException &e)
-                       {
-                       }
+               catch(InvalidPositionException &e){
+                       light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
                }
                }
+               player->light = light;
        }
        
        /*
        }
        
        /*
@@ -2161,7 +2089,6 @@ void ClientEnvironment::step(float dtime)
                if(m_active_object_light_update_interval.step(dtime, 0.21))
                {
                        // Update lighting
                if(m_active_object_light_update_interval.step(dtime, 0.21))
                {
                        // Update lighting
-                       //u8 light = LIGHT_MAX;
                        u8 light = 0;
                        try{
                                // Get node at head
                        u8 light = 0;
                        try{
                                // Get node at head
@@ -2169,20 +2096,33 @@ void ClientEnvironment::step(float dtime)
                                MapNode n = m_map->getNode(p);
                                light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
                        }
                                MapNode n = m_map->getNode(p);
                                light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
                        }
-                       catch(InvalidPositionException &e) {}
+                       catch(InvalidPositionException &e){
+                               light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
+                       }
                        obj->updateLight(light);
                }
        }
                        obj->updateLight(light);
                }
        }
-}
 
 
-void ClientEnvironment::updateMeshes(v3s16 blockpos)
-{
-       m_map->updateMeshes(blockpos, getDayNightRatio());
+       /*
+               Step and handle simple objects
+       */
+       for(core::list<ClientSimpleObject*>::Iterator
+                       i = m_simple_objects.begin(); i != m_simple_objects.end();)
+       {
+               ClientSimpleObject *simple = *i;
+               core::list<ClientSimpleObject*>::Iterator cur = i;
+               i++;
+               simple->step(dtime);
+               if(simple->m_to_be_removed){
+                       delete simple;
+                       m_simple_objects.erase(cur);
+               }
+       }
 }
 }
-
-void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
+       
+void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
 {
 {
-       m_map->expireMeshes(only_daynight_diffed);
+       m_simple_objects.push_back(simple);
 }
 
 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
 }
 
 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
@@ -2251,15 +2191,28 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
        infostream<<"ClientEnvironment::addActiveObject(): "
                        <<"added (id="<<object->getId()<<")"<<std::endl;
        m_active_objects.insert(object->getId(), object);
        infostream<<"ClientEnvironment::addActiveObject(): "
                        <<"added (id="<<object->getId()<<")"<<std::endl;
        m_active_objects.insert(object->getId(), object);
-       // TODO: Make g_texturesource non-global
-       object->addToScene(m_smgr, m_texturesource);
+       object->addToScene(m_smgr, m_texturesource, m_irr);
+       { // Update lighting immediately
+               u8 light = 0;
+               try{
+                       // Get node at head
+                       v3s16 p = object->getLightPosition();
+                       MapNode n = m_map->getNode(p);
+                       light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
+               }
+               catch(InvalidPositionException &e){
+                       light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
+               }
+               object->updateLight(light);
+       }
        return object->getId();
 }
 
 void ClientEnvironment::addActiveObject(u16 id, u8 type,
                const std::string &init_data)
 {
        return object->getId();
 }
 
 void ClientEnvironment::addActiveObject(u16 id, u8 type,
                const std::string &init_data)
 {
-       ClientActiveObject* obj = ClientActiveObject::create(type, m_gamedef);
+       ClientActiveObject* obj =
+                       ClientActiveObject::create(type, m_gamedef, this);
        if(obj == NULL)
        {
                infostream<<"ClientEnvironment::addActiveObject(): "
        if(obj == NULL)
        {
                infostream<<"ClientEnvironment::addActiveObject(): "
@@ -2270,14 +2223,25 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type,
        
        obj->setId(id);
 
        
        obj->setId(id);
 
-       obj->initialize(init_data);
-       
+       try
+       {
+               obj->initialize(init_data);
+       }
+       catch(SerializationError &e)
+       {
+               errorstream<<"ClientEnvironment::addActiveObject():"
+                               <<" id="<<id<<" type="<<type
+                               <<": SerializationError in initialize(),"
+                               <<" init_data="<<serializeJsonString(init_data)
+                               <<std::endl;
+       }
+
        addActiveObject(obj);
 }
 
 void ClientEnvironment::removeActiveObject(u16 id)
 {
        addActiveObject(obj);
 }
 
 void ClientEnvironment::removeActiveObject(u16 id)
 {
-       infostream<<"ClientEnvironment::removeActiveObject(): "
+       verbosestream<<"ClientEnvironment::removeActiveObject(): "
                        <<"id="<<id<<std::endl;
        ClientActiveObject* obj = getActiveObject(id);
        if(obj == NULL)
                        <<"id="<<id<<std::endl;
        ClientActiveObject* obj = getActiveObject(id);
        if(obj == NULL)
@@ -2302,26 +2266,40 @@ void ClientEnvironment::processActiveObjectMessage(u16 id,
                                <<std::endl;
                return;
        }
                                <<std::endl;
                return;
        }
-       obj->processMessage(data);
+       try
+       {
+               obj->processMessage(data);
+       }
+       catch(SerializationError &e)
+       {
+               errorstream<<"ClientEnvironment::processActiveObjectMessage():"
+                               <<" id="<<id<<" type="<<obj->getType()
+                               <<" SerializationError in processMessage(),"
+                               <<" message="<<serializeJsonString(data)
+                               <<std::endl;
+       }
 }
 
 /*
        Callbacks for activeobjects
 */
 
 }
 
 /*
        Callbacks for activeobjects
 */
 
-void ClientEnvironment::damageLocalPlayer(u8 damage)
+void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
 {
        LocalPlayer *lplayer = getLocalPlayer();
        assert(lplayer);
 {
        LocalPlayer *lplayer = getLocalPlayer();
        assert(lplayer);
-
-       if(lplayer->hp > damage)
-               lplayer->hp -= damage;
-       else
-               lplayer->hp = 0;
+       
+       if(handle_hp){
+               if(lplayer->hp > damage)
+                       lplayer->hp -= damage;
+               else
+                       lplayer->hp = 0;
+       }
 
        ClientEnvEvent event;
        event.type = CEE_PLAYER_DAMAGE;
        event.player_damage.amount = damage;
 
        ClientEnvEvent event;
        event.type = CEE_PLAYER_DAMAGE;
        event.player_damage.amount = damage;
+       event.player_damage.send_to_server = handle_hp;
        m_client_event_queue.push_back(event);
 }
 
        m_client_event_queue.push_back(event);
 }