Add congestion control settings to minetest.conf
[oweals/minetest.git] / src / environment.cpp
index ed45cee69be7b1a617ae3fa41ab7dfd17afb6b1b..e70cb39b784d42a38a6e93f118e408a9e3c3e5f9 100644 (file)
@@ -3,20 +3,23 @@ Minetest-c55
 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
 
 This program is free software; you can redistribute it and/or modify
 Copyright (C) 2010-2011 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 General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
+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
 (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 General Public License for more details.
+GNU Lesser General Public License for more details.
 
 
-You should have received a copy of the GNU General Public License along
+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.
 */
 
 with this program; if not, write to the Free Software Foundation, Inc.,
 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"
@@ -30,11 +33,25 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "profiler.h"
 #include "scriptapi.h"
 #include "log.h"
 #include "profiler.h"
 #include "scriptapi.h"
+#include "nodedef.h"
+#include "nodemetadata.h"
+#include "main.h" // For g_settings, g_profiler
+#include "gamedef.h"
+#ifndef SERVER
+#include "clientmap.h"
+#include "localplayer.h"
+#endif
+#include "daynightratio.h"
+#include "map.h"
+#include "util/serialize.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)
 {
 }
 
 {
 }
 
@@ -184,17 +201,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
 */
@@ -268,13 +319,18 @@ void ActiveBlockList::update(core::list<v3s16> &active_positions,
        ServerEnvironment
 */
 
        ServerEnvironment
 */
 
-ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L):
+ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L,
+               IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
        m_map(map),
        m_lua(L),
        m_map(map),
        m_lua(L),
+       m_gamedef(gamedef),
+       m_emerger(emerger),
        m_random_spawn_timer(3),
        m_send_recommended_timer(0),
        m_random_spawn_timer(3),
        m_send_recommended_timer(0),
+       m_active_block_interval_overload_skip(0),
        m_game_time(0),
        m_game_time(0),
-       m_game_time_fraction_counter(0)
+       m_game_time_fraction_counter(0),
+       m_recommended_send_interval(0.1)
 {
 }
 
 {
 }
 
@@ -289,8 +345,25 @@ 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;
+       }
+}
+
+Map & ServerEnvironment::getMap()
+{
+       return *m_map;
+}
+
+ServerMap & ServerEnvironment::getServerMap()
+{
+       return *m_map;
 }
 
 }
 
+
 void ServerEnvironment::serializePlayers(const std::string &savedir)
 {
        std::string players_path = savedir + "/players";
 void ServerEnvironment::serializePlayers(const std::string &savedir)
 {
        std::string players_path = savedir + "/players";
@@ -310,7 +383,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;
+               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);
@@ -421,10 +494,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;
+               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);
@@ -442,8 +515,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();
@@ -451,14 +524,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();
+                       //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);
@@ -471,7 +544,9 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
                }
 
                if(newplayer)
                }
 
                if(newplayer)
+               {
                        addPlayer(player);
                        addPlayer(player);
+               }
        }
 }
 
        }
 }
 
@@ -538,55 +613,165 @@ void ServerEnvironment::loadMeta(const std::string &savedir)
        }
 }
 
        }
 }
 
-#if 0
-// This is probably very useless
-void spawnRandomObjects(MapBlock *block)
+struct ActiveABM
 {
 {
-       for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
-       for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+       ActiveBlockModifier *abm;
+       int chance;
+       std::set<content_t> required_neighbors;
+};
+
+class ABMHandler
+{
+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++)
-               {
-                       v3s16 p(x0,y0,z0);
-                       MapNode n = block->getNodeNoEx(p);
-                       if(n.getContent() == CONTENT_IGNORE)
-                               continue;
-                       if(content_features(n).liquid_type != LIQUID_NONE)
+               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;
+                       }
+                       float intervals = actual_interval / trigger_interval;
+                       if(intervals == 0)
                                continue;
                                continue;
-                       if(content_features(n).walkable)
+                       float chance = abm->getTriggerChance();
+                       if(chance == 0)
+                               chance = 1;
+                       ActiveABM aabm;
+                       aabm.abm = abm;
+                       aabm.chance = chance / intervals;
+                       if(aabm.chance == 0)
+                               aabm.chance = 1;
+                       // 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++)
                        {
                        {
-                               last_node_walkable = true;
-                               continue;
+                               ndef->getIds(*i, aabm.required_neighbors);
                        }
                        }
-                       if(last_node_walkable)
+                       // 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++)
                        {
                        {
-                               // If block contains light information
-                               if(content_features(n).param_type == CPT_LIGHT)
+                               std::set<content_t> ids;
+                               ndef->getIds(*i, ids);
+                               for(std::set<content_t>::const_iterator k = ids.begin();
+                                               k != ids.end(); k++)
                                {
                                {
-                                       if(n.getLight(LIGHTBANK_DAY) <= 5)
+                                       content_t c = *k;
+                                       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++)
+               {
+                       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;
+
+                       for(std::list<ActiveABM>::iterator
+                                       i = j->second.begin(); i != j->second.end(); i++)
+                       {
+                               if(myrand() % i->chance != 0)
+                                       continue;
+
+                               // Check neighbors
+                               if(!i->required_neighbors.empty())
+                               {
+                                       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;
+                               u32 wider_unknown_count = 0;
+                               for(s16 x=-1; x<=1; x++)
+                               for(s16 y=-1; y<=1; y++)
+                               for(s16 z=-1; z<=1; z++)
+                               {
+                                       MapBlock *block2 = map->getBlockNoCreateNoEx(
+                                                       block->getPos() + v3s16(x,y,z));
+                                       if(block2==NULL){
+                                               wider_unknown_count = 0;
+                                               continue;
+                                       }
+                                       active_object_count_wider +=
+                                                       block2->m_static_objects.m_active.size()
+                                                       + block2->m_static_objects.m_stored.size();
+                               }
+                               // Extrapolate
+                               u32 wider_known_count = 3*3*3 - wider_unknown_count;
+                               active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
+                               
+                               // Call all the trigger variations
+                               i->abm->trigger(m_env, p, n);
+                               i->abm->trigger(m_env, p, n,
+                                               active_object_count, active_object_count_wider);
                        }
                        }
-                       last_node_walkable = false;
                }
        }
                }
        }
-}
-#endif
+};
 
 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
 {
 
 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
 {
@@ -597,55 +782,57 @@ 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;*/
+
+       // Set current time as timestamp
+       block->setTimestampNoChangedFlag(m_game_time);
 
 
-       //infostream<<"Block is "<<dtime_s<<" seconds old."<<std::endl;
+       /*infostream<<"ServerEnvironment::activateBlock(): block is "
+                       <<dtime_s<<" seconds old."<<std::endl;*/
        
        // Activate stored objects
        
        // Activate stored objects
-       activateObjects(block);
+       activateObjects(block, dtime_s);
+
+       // Run node timers
+       std::map<v3s16, NodeTimer> elapsed_timers =
+               block->m_node_timers.step((float)dtime_s);
+       if(!elapsed_timers.empty()){
+               MapNode n;
+               for(std::map<v3s16, NodeTimer>::iterator
+                               i = elapsed_timers.begin();
+                               i != elapsed_timers.end(); i++){
+                       n = block->getNodeNoEx(i->first);
+                       if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
+                               block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
+               }
+       }
 
 
-       // Run node metadata
-       bool changed = block->m_node_metadata.step((float)dtime_s);
-       if(changed)
-       {
-               MapEditEvent event;
-               event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
-               event.p = block->getPos();
-               m_map->dispatchEvent(&event);
+       /* Handle ActiveBlockModifiers */
+       ABMHandler abmhandler(m_abms, dtime_s, this, false);
+       abmhandler.apply(block);
+}
 
 
-               block->setChangedFlag();
-       }
+void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
+{
+       m_abms.push_back(ABMWithState(abm));
+}
 
 
-       // 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(content_features(n_top).air_equivalent &&
-                                               n_top.getLight(LIGHTBANK_DAY) >= 13)
-                               {
-                                       n.setContent(CONTENT_GRASS);
-                                       m_map->addNodeWithEvent(p, n);
-                               }
-                       }
-               }
-#endif
+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()
@@ -658,6 +845,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
@@ -665,7 +854,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;
                        }
                }
@@ -675,10 +865,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);
        }
@@ -716,7 +911,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++;
                }
@@ -736,28 +932,19 @@ 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);
+
+       // Update this one
+       // NOTE: This is kind of funny on a singleplayer game, but doesn't
+       // really matter that much.
+       m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
 
        /*
                Increment game time
 
        /*
                Increment game time
@@ -787,26 +974,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)
-                               {
-                               }
-                       }
                }
        }
 
                }
        }
 
@@ -880,8 +1047,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);
                }
@@ -914,26 +1085,43 @@ 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);
-
-                       // Run node metadata
-                       bool changed = block->m_node_metadata.step(dtime);
-                       if(changed)
-                       {
-                               MapEditEvent event;
-                               event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
-                               event.p = p;
-                               m_map->dispatchEvent(&event);
-
-                               block->setChangedFlag();
+                       // 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 timers
+                       std::map<v3s16, NodeTimer> elapsed_timers =
+                               block->m_node_timers.step((float)dtime);
+                       if(!elapsed_timers.empty()){
+                               MapNode n;
+                               for(std::map<v3s16, NodeTimer>::iterator
+                                               i = elapsed_timers.begin();
+                                               i != elapsed_timers.end(); i++){
+                                       n = block->getNodeNoEx(i->first);
+                                       p = i->first + block->getPosRelative();
+                                       if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
+                                               block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
+                               }
                        }
                }
        }
        
                        }
                }
        }
        
-       if(m_active_blocks_test_interval.step(dtime, 10.0))
-       {
-               ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /10s", SPT_AVG);
-               //float dtime = 10.0;
+       const float abm_interval = 1.0;
+       if(m_active_block_modifier_interval.step(dtime, abm_interval))
+       do{ // breakable
+               if(m_active_block_interval_overload_skip > 0){
+                       ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
+                       m_active_block_interval_overload_skip--;
+                       break;
+               }
+               ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
+               TimeTaker timer("modify in active blocks");
                
                
+               // 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++)
@@ -950,197 +1138,25 @@ 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(content_features(n_top).air_equivalent &&
-                                                               n_top.getLightBlend(getDayNightRatio()) >= 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(content_features(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()) <= 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;
+                       m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
                }
                }
-       }
+       }while(0);
        
        
+       /*
+               Step script environment (run global on_step())
+       */
+       scriptapi_environment_step(m_lua, dtime);
+
        /*
                Step active objects
        */
        /*
                Step active objects
        */
@@ -1153,9 +1169,9 @@ void ServerEnvironment::step(float dtime)
                // This helps the objects to send data at the same time
                bool send_recommended = false;
                m_send_recommended_timer += dtime;
                // 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 > 0.10)
+               if(m_send_recommended_timer > getSendRecommendedInterval())
                {
                {
-                       m_send_recommended_timer = 0;
+                       m_send_recommended_timer -= getSendRecommendedInterval();
                        send_recommended = true;
                }
 
                        send_recommended = true;
                }
 
@@ -1194,60 +1210,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)
@@ -1294,7 +1256,7 @@ u16 getFreeServerActiveObjectId(
 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
 {
        assert(object);
 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
 {
        assert(object);
-       u16 id = addActiveObjectRaw(object, true);
+       u16 id = addActiveObjectRaw(object, true, 0);
        return id;
 }
 
        return id;
 }
 
@@ -1324,7 +1286,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{
@@ -1334,7 +1297,8 @@ bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
                succeeded = false;
        }
 
                succeeded = false;
        }
 
-       delete obj;
+       if(obj->environmentDeletes())
+               delete obj;
 
        return succeeded;
 }
 
        return succeeded;
 }
@@ -1368,10 +1332,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);
@@ -1406,24 +1372,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;
+               }
+
+               if(object->m_removed)
+               {
+                       removed_objects.insert(id, false);
+                       continue;
                }
                }
-               else if(object->m_removed == false)
+               
+               // 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)
                {
                {
-                       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;
                }
                }
-               removed_objects.insert(id, false);
+               
+               // Not removed
        }
 }
 
        }
 }
 
@@ -1440,7 +1415,7 @@ ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
 */
 
 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
 */
 
 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
-               bool set_changed)
+               bool set_changed, u32 dtime_s)
 {
        assert(object);
        if(object->getId() == 0){
 {
        assert(object);
        if(object->getId() == 0){
@@ -1449,7 +1424,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);
@@ -1462,7 +1438,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(): "
@@ -1475,33 +1452,39 @@ 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
        // Register reference in scripting api (must be done before post-init)
        scriptapi_add_object_reference(m_lua, object);
        // Post-initialize object
-       object->addedToEnvironment(object->getId());
+       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->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{
+                       v3s16 p = floatToInt(objectpos, BS);
+                       errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
+                                       <<"could not find block for storing id="<<object->getId()
+                                       <<" statically (pos="<<PP(p)<<")"<<std::endl;
+               }
+       }
+       
        return object->getId();
 }
 
        return object->getId();
 }
 
@@ -1543,7 +1526,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;
                        }
                }
@@ -1552,11 +1536,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);
        }
@@ -1605,7 +1592,7 @@ static void print_hexdump(std::ostream &o, const std::string &data)
 /*
        Convert stored objects from blocks near the players to active.
 */
 /*
        Convert stored objects from blocks near the players to active.
 */
-void ServerEnvironment::activateObjects(MapBlock *block)
+void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
 {
        if(block==NULL)
                return;
 {
        if(block==NULL)
                return;
@@ -1624,10 +1611,12 @@ 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;
        }
                return;
        }
-       // A list for objects that couldn't be converted to static for some
+       // A list for objects that couldn't be converted to active for some
        // reason. They will be stored back.
        core::list<StaticObject> new_stored;
        // Loop through stored static objects
        // reason. They will be stored back.
        core::list<StaticObject> new_stored;
        // Loop through stored static objects
@@ -1657,7 +1646,7 @@ void ServerEnvironment::activateObjects(MapBlock *block)
                                <<"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
                                <<"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);
+               addActiveObjectRaw(obj, false, dtime_s);
        }
        // Clear stored list
        block->m_static_objects.m_stored.clear();
        }
        // Clear stored list
        block->m_static_objects.m_stored.clear();
@@ -1674,7 +1663,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.
        */
 }
@@ -1696,18 +1685,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();         
@@ -1717,7 +1702,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(): "
@@ -1731,81 +1716,109 @@ 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();
-
-                               if(static_old.data == staticdata_new &&
-                                               (static_old.pos - objectpos).getLength() < 2*BS)
-                                       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;
+                               }
                        }
                        }
-               }
-               
-               // 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(!stays_in_same_block || data_changed)
-                                       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 = NULL;
+                       try{
+                               block = m_map->emergeBlock(blockpos);
+                       } catch(InvalidPositionException &e){
+                               // Handled via NULL pointer
+                       }
 
 
-               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(!stays_in_same_block || data_changed)
-                                       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;
+                                       // If static counterpart already exists, remove it first.
+                                       // This shouldn't happen, but happens rarely for some
+                                       // unknown reason. Unsuccessful attempts have been made to
+                                       // find said reason.
+                                       if(new_id && block->m_static_objects.m_active.find(new_id)){
+                                               infostream<<"ServerEnv: WARNING: Performing hack #83274"
+                                                               <<std::endl;
+                                               block->m_static_objects.remove(new_id);
+                                       }
+                                       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){
+                                       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;
+                               }
                        }
                        }
-               }
-               else{
-                       errorstream<<"ServerEnv: Could not find or generate "
-                                       <<"a block for storing id="<<obj->getId()
-                                       <<" statically"<<std::endl;
-                       continue;
                }
 
                /*
                }
 
                /*
@@ -1813,7 +1826,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"
@@ -1827,11 +1840,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);
        }
@@ -1847,16 +1863,21 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
 
 #ifndef SERVER
 
 
 #ifndef SERVER
 
+#include "clientsimpleobject.h"
+
 /*
        ClientEnvironment
 */
 
 /*
        ClientEnvironment
 */
 
-ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr):
+ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
+               ITextureSource *texturesource, IGameDef *gamedef,
+               IrrlichtDevice *irr):
        m_map(map),
        m_map(map),
-       m_smgr(smgr)
+       m_smgr(smgr),
+       m_texturesource(texturesource),
+       m_gamedef(gamedef),
+       m_irr(irr)
 {
 {
-       assert(m_map);
-       assert(m_smgr);
 }
 
 ClientEnvironment::~ClientEnvironment()
 }
 
 ClientEnvironment::~ClientEnvironment()
@@ -1869,10 +1890,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);
@@ -1901,9 +1938,12 @@ void ClientEnvironment::step(float dtime)
 {
        DSTACK(__FUNCTION_NAME);
 
 {
        DSTACK(__FUNCTION_NAME);
 
+       /* Step time of day */
+       stepTimeOfDay(dtime);
+
        // Get some settings
        // Get some settings
-       bool free_move = g_settings->getBool("free_move");
-       bool footprints = g_settings->getBool("footprints");
+       bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
+       bool free_move = fly_allowed && g_settings->getBool("free_move");
 
        // Get local player
        LocalPlayer *lplayer = getLocalPlayer();
 
        // Get local player
        LocalPlayer *lplayer = getLocalPlayer();
@@ -1916,8 +1956,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
@@ -1927,7 +1966,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)
@@ -2013,25 +2054,32 @@ void ClientEnvironment::step(float dtime)
                        i != player_collisions.end(); i++)
        {
                CollisionInfo &info = *i;
                        i != player_collisions.end(); i++)
        {
                CollisionInfo &info = *i;
-               if(info.t == COLLISION_FALL)
+               v3f speed_diff = info.new_speed - info.old_speed;;
+               // Handle only fall damage
+               // (because otherwise walking against something in fast_move kills you)
+               if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
+                       continue;
+               // Get rid of other components
+               speed_diff.X = 0;
+               speed_diff.Z = 0;
+               f32 pre_factor = 1; // 1 hp per node/s
+               f32 tolerance = BS*14; // 5 without damage
+               f32 post_factor = 1; // 1 hp per node/s
+               if(info.type == COLLISION_NODE)
                {
                {
-                       //f32 tolerance = BS*10; // 2 without damage
-                       f32 tolerance = BS*12; // 3 without damage
-                       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);
-                       }
+                       const ContentFeatures &f = m_gamedef->ndef()->
+                                       get(m_map->getNodeNoEx(info.node_p));
+                       // Determine fall damage multiplier
+                       int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
+                       pre_factor = 1.0 + (float)addp/100.0;
+               }
+               float speed = pre_factor * speed_diff.getLength();
+               if(speed > tolerance)
+               {
+                       f32 damage_f = (speed - tolerance)/BS * post_factor;
+                       u16 damage = (u16)(damage_f+0.5);
+                       if(damage != 0)
+                               damageLocalPlayer(damage, true);
                }
        }
        
                }
        }
        
@@ -2052,18 +2100,15 @@ void ClientEnvironment::step(float dtime)
 
                u32 damage_per_second = 0;
                damage_per_second = MYMAX(damage_per_second,
 
                u32 damage_per_second = 0;
                damage_per_second = MYMAX(damage_per_second,
-                               content_features(n1).damage_per_second);
+                               m_gamedef->ndef()->get(n1).damage_per_second);
                damage_per_second = MYMAX(damage_per_second,
                damage_per_second = MYMAX(damage_per_second,
-                               content_features(n2).damage_per_second);
+                               m_gamedef->ndef()->get(n2).damage_per_second);
                damage_per_second = MYMAX(damage_per_second,
                damage_per_second = MYMAX(damage_per_second,
-                               content_features(n3).damage_per_second);
+                               m_gamedef->ndef()->get(n3).damage_per_second);
                
                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);
                }
        }
        
                }
        }
        
@@ -2092,44 +2137,19 @@ void ClientEnvironment::step(float dtime)
                        // Get node at head
                        v3s16 p = player->getLightPosition();
                        MapNode n = m_map->getNode(p);
                        // Get node at head
                        v3s16 p = player->getLightPosition();
                        MapNode n = m_map->getNode(p);
-                       light = n.getLightBlend(getDayNightRatio());
+                       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;
        }
        
        /*
                Step active objects and update lighting of them
        */
        
        }
        
        /*
                Step active objects and update lighting of them
        */
        
+       bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
        for(core::map<u16, ClientActiveObject*>::Iterator
                        i = m_active_objects.getIterator();
                        i.atEnd()==false; i++)
        for(core::map<u16, ClientActiveObject*>::Iterator
                        i = m_active_objects.getIterator();
                        i.atEnd()==false; i++)
@@ -2138,31 +2158,43 @@ void ClientEnvironment::step(float dtime)
                // Step object
                obj->step(dtime, this);
 
                // Step object
                obj->step(dtime, this);
 
-               if(m_active_object_light_update_interval.step(dtime, 0.21))
+               if(update_lighting)
                {
                        // Update lighting
                {
                        // Update lighting
-                       //u8 light = LIGHT_MAX;
                        u8 light = 0;
                        try{
                                // Get node at head
                                v3s16 p = obj->getLightPosition();
                                MapNode n = m_map->getNode(p);
                        u8 light = 0;
                        try{
                                // Get node at head
                                v3s16 p = obj->getLightPosition();
                                MapNode n = m_map->getNode(p);
-                               light = n.getLightBlend(getDayNightRatio());
+                               light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
+                       }
+                       catch(InvalidPositionException &e){
+                               light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
                        }
                        }
-                       catch(InvalidPositionException &e) {}
                        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)
@@ -2231,14 +2263,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);
-       object->addToScene(m_smgr);
+       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);
+       ClientActiveObject* obj =
+                       ClientActiveObject::create(type, m_gamedef, this);
        if(obj == NULL)
        {
                infostream<<"ClientEnvironment::addActiveObject(): "
        if(obj == NULL)
        {
                infostream<<"ClientEnvironment::addActiveObject(): "
@@ -2249,14 +2295,26 @@ 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(): "
+                               <<e.what()
+                               <<": 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)
@@ -2265,7 +2323,7 @@ void ClientEnvironment::removeActiveObject(u16 id)
                                <<"id="<<id<<" not found"<<std::endl;
                return;
        }
                                <<"id="<<id<<" not found"<<std::endl;
                return;
        }
-       obj->removeFromScene();
+       obj->removeFromScene(true);
        delete obj;
        m_active_objects.remove(id);
 }
        delete obj;
        m_active_objects.remove(id);
 }
@@ -2281,26 +2339,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);
 }