]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/environment.cpp
Speedup attachement handling by replacing vector search by direct array access and...
[dragonfireclient.git] / src / environment.cpp
index 63718f3fc85a9413b150566daf8fd6e64a187fae..8103998c3c847d219caecc9bb40763b5ee44fe88 100644 (file)
@@ -17,9 +17,6 @@ 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"
@@ -28,11 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapblock.h"
 #include "serverobject.h"
 #include "content_sao.h"
-#include "mapgen.h"
 #include "settings.h"
 #include "log.h"
 #include "profiler.h"
-#include "cpp_api/scriptapi.h"
+#include "scripting_game.h"
 #include "nodedef.h"
 #include "nodemetadata.h"
 #include "main.h" // For g_settings, g_profiler
@@ -44,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #endif
 #include "daynightratio.h"
 #include "map.h"
+#include "emerge.h"
 #include "util/serialize.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@@ -52,7 +49,9 @@ Environment::Environment():
        m_time_of_day(9000),
        m_time_of_day_f(9000./24000),
        m_time_of_day_speed(0),
-       m_time_counter(0)
+       m_time_counter(0),
+       m_enable_day_night_ratio_override(false),
+       m_day_night_ratio_override(0.0f)
 {
 }
 
@@ -86,19 +85,17 @@ void Environment::addPlayer(Player *player)
 void Environment::removePlayer(u16 peer_id)
 {
        DSTACK(__FUNCTION_NAME);
-re_search:
+
        for(std::list<Player*>::iterator i = m_players.begin();
-                       i != m_players.end(); ++i)
+                       i != m_players.end();)
        {
                Player *player = *i;
-               if(player->peer_id != peer_id)
-                       continue;
-               
-               delete player;
-               m_players.erase(i);
-               // See if there is an another one
-               // (shouldn't be, but just to be sure)
-               goto re_search;
+               if(player->peer_id == peer_id) {
+                       delete player;
+                       i = m_players.erase(i);
+               } else {
+                       ++i;
+               }
        }
 }
 
@@ -191,19 +188,10 @@ std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
        return newlist;
 }
 
-void Environment::printPlayers(std::ostream &o)
-{
-       o<<"Players in environment:"<<std::endl;
-       for(std::list<Player*>::iterator i = m_players.begin();
-                       i != m_players.end(); i++)
-       {
-               Player *player = *i;
-               o<<"Player peer_id="<<player->peer_id<<std::endl;
-       }
-}
-
 u32 Environment::getDayNightRatio()
 {
+       if(m_enable_day_night_ratio_override)
+               return m_day_night_ratio_override;
        bool smooth = g_settings->getBool("enable_shaders");
        return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
 }
@@ -272,7 +260,7 @@ void ActiveBlockList::update(std::list<v3s16> &active_positions,
        /*
                Create the new list
        */
-       std::set<v3s16> newlist;
+       std::set<v3s16> newlist = m_forceloaded_list;
        for(std::list<v3s16>::iterator i = active_positions.begin();
                        i != active_positions.end(); ++i)
        {
@@ -321,13 +309,11 @@ void ActiveBlockList::update(std::list<v3s16> &active_positions,
        ServerEnvironment
 */
 
-ServerEnvironment::ServerEnvironment(ServerMap *map, ScriptApi *scriptIface,
-               IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
+ServerEnvironment::ServerEnvironment(ServerMap *map,
+               GameScripting *scriptIface, IGameDef *gamedef):
        m_map(map),
        m_script(scriptIface),
        m_gamedef(gamedef),
-       m_emerger(emerger),
-       m_random_spawn_timer(3),
        m_send_recommended_timer(0),
        m_active_block_interval_overload_skip(0),
        m_game_time(0),
@@ -366,7 +352,7 @@ ServerMap & ServerEnvironment::getServerMap()
        return *m_map;
 }
 
-bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
+bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
 {
        float distance = pos1.getDistanceFrom(pos2);
 
@@ -384,6 +370,9 @@ bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
                MapNode n = getMap().getNodeNoEx(pos);
 
                if(n.param0 != CONTENT_AIR) {
+                       if (p) {
+                               *p = pos;
+                       }
                        return false;
                }
        }
@@ -438,13 +427,13 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
                if(player->checkModified())
                {
                        // Open file and serialize
-                       std::ofstream os(path.c_str(), std::ios_base::binary);
-                       if(os.good() == false)
+                       std::ostringstream ss(std::ios_base::binary);
+                       player->serialize(ss);
+                       if(!fs::safeWriteToFile(path, ss.str()))
                        {
-                               infostream<<"Failed to overwrite "<<path<<std::endl;
+                               infostream<<"Failed to write "<<path<<std::endl;
                                continue;
                        }
-                       player->serialize(os);
                        saved_players.insert(player);
                } else {
                        saved_players.insert(player);
@@ -494,13 +483,13 @@ void ServerEnvironment::serializePlayers(const std::string &savedir)
                        /*infostream<<"Saving player "<<player->getName()<<" to "
                                        <<path<<std::endl;*/
                        // Open file and serialize
-                       std::ofstream os(path.c_str(), std::ios_base::binary);
-                       if(os.good() == false)
+                       std::ostringstream ss(std::ios_base::binary);
+                       player->serialize(ss);
+                       if(!fs::safeWriteToFile(path, ss.str()))
                        {
-                               infostream<<"Failed to overwrite "<<path<<std::endl;
+                               infostream<<"Failed to write "<<path<<std::endl;
                                continue;
                        }
-                       player->serialize(os);
                        saved_players.insert(player);
                }
        }
@@ -582,19 +571,20 @@ void ServerEnvironment::saveMeta(const std::string &savedir)
        std::string path = savedir + "/env_meta.txt";
 
        // Open file and serialize
-       std::ofstream os(path.c_str(), std::ios_base::binary);
-       if(os.good() == false)
-       {
-               infostream<<"ServerEnvironment::saveMeta(): Failed to open "
-                               <<path<<std::endl;
-               throw SerializationError("Couldn't save env meta");
-       }
+       std::ostringstream ss(std::ios_base::binary);
 
        Settings args;
        args.setU64("game_time", m_game_time);
        args.setU64("time_of_day", getTimeOfDay());
-       args.writeLines(os);
-       os<<"EnvArgsEnd\n";
+       args.writeLines(ss);
+       ss<<"EnvArgsEnd\n";
+
+       if(!fs::safeWriteToFile(path, ss.str()))
+       {
+               infostream<<"ServerEnvironment::saveMeta(): Failed to write "
+                               <<path<<std::endl;
+               throw SerializationError("Couldn't save env meta");
+       }
 }
 
 void ServerEnvironment::loadMeta(const std::string &savedir)
@@ -718,6 +708,34 @@ class ABMHandler
                        }
                }
        }
+       // Find out how many objects the given block and its neighbours contain.
+       // Returns the number of objects in the block, and also in 'wider' the
+       // number of objects in the block and all its neighbours. The latter
+       // may an estimate if any neighbours are unloaded.
+       u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
+       {
+               wider = 0;
+               u32 wider_unknown_count = 0;
+               for(s16 x=-1; x<=1; x++)
+               for(s16 y=-1; y<=1; y++)
+               for(s16 z=-1; z<=1; z++)
+               {
+                       MapBlock *block2 = map->getBlockNoCreateNoEx(
+                                       block->getPos() + v3s16(x,y,z));
+                       if(block2==NULL){
+                               wider_unknown_count++;
+                               continue;
+                       }
+                       wider += block2->m_static_objects.m_active.size()
+                                       + block2->m_static_objects.m_stored.size();
+               }
+               // Extrapolate
+               u32 active_object_count = block->m_static_objects.m_active.size();
+               u32 wider_known_count = 3*3*3 - wider_unknown_count;
+               wider += wider_unknown_count * wider / wider_known_count;
+               return active_object_count;
+
+       }
        void apply(MapBlock *block)
        {
                if(m_aabms.empty())
@@ -725,6 +743,10 @@ class ABMHandler
 
                ServerMap *map = &m_env->getServerMap();
 
+               u32 active_object_count_wider;
+               u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
+               m_env->m_added_objects = 0;
+
                v3s16 p0;
                for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
                for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
@@ -768,33 +790,16 @@ class ABMHandler
                                }
 neighbor_found:
 
-                               // Find out how many objects the block contains
-                               u32 active_object_count = block->m_static_objects.m_active.size();
-                               // Find out how many objects this and all the neighbors contain
-                               u32 active_object_count_wider = 0;
-                               u32 wider_unknown_count = 0;
-                               for(s16 x=-1; x<=1; x++)
-                               for(s16 y=-1; y<=1; y++)
-                               for(s16 z=-1; z<=1; z++)
-                               {
-                                       MapBlock *block2 = map->getBlockNoCreateNoEx(
-                                                       block->getPos() + v3s16(x,y,z));
-                                       if(block2==NULL){
-                                               wider_unknown_count = 0;
-                                               continue;
-                                       }
-                                       active_object_count_wider +=
-                                                       block2->m_static_objects.m_active.size()
-                                                       + block2->m_static_objects.m_stored.size();
-                               }
-                               // Extrapolate
-                               u32 wider_known_count = 3*3*3 - wider_unknown_count;
-                               active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
-                               
                                // Call all the trigger variations
                                i->abm->trigger(m_env, p, n);
                                i->abm->trigger(m_env, p, n,
                                                active_object_count, active_object_count_wider);
+
+                               // Count surrounding objects again if the abms added any
+                               if(m_env->m_added_objects > 0) {
+                                       active_object_count = countObjects(block, map, active_object_count_wider);
+                                       m_env->m_added_objects = 0;
+                               }
                        }
                }
        }
@@ -802,6 +807,14 @@ class ABMHandler
 
 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
 {
+       // Reset usage timer immediately, otherwise a block that becomes active
+       // again at around the same time as it would normally be unloaded will
+       // get unloaded incorrectly. (I think this still leaves a small possibility
+       // of a race condition between this and server::AsyncRunStep, which only
+       // some kind of synchronisation will fix, but it at least reduces the window
+       // of opportunity for it to break from seconds to nanoseconds)
+       block->resetUsageTimer();
+
        // Get time difference
        u32 dtime_s = 0;
        u32 stamp = block->getTimestamp();
@@ -885,6 +898,11 @@ bool ServerEnvironment::removeNode(v3s16 p)
        return true;
 }
 
+bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
+{
+       return m_map->addNodeWithEvent(p, n, false);
+}
+
 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
 {
        std::set<u16> objects;
@@ -1008,7 +1026,8 @@ void ServerEnvironment::clearAllObjects()
                }
                num_blocks_checked++;
 
-               if(num_blocks_checked % report_interval == 0){
+               if(report_interval != 0 &&
+                               num_blocks_checked % report_interval == 0){
                        float percent = 100.0 * (float)num_blocks_checked /
                                        loadable_blocks.size();
                        infostream<<"ServerEnvironment::clearAllObjects(): "
@@ -1076,7 +1095,7 @@ void ServerEnvironment::step(float dtime)
                                continue;
                        
                        // Move
-                       player->move(dtime, *m_map, 100*BS);
+                       player->move(dtime, this, 100*BS);
                }
        }
 
@@ -1146,10 +1165,8 @@ void ServerEnvironment::step(float dtime)
                {
                        v3s16 p = *i;
 
-                       MapBlock *block = m_map->getBlockNoCreateNoEx(p);
+                       MapBlock *block = m_map->getBlockOrEmerge(p);
                        if(block==NULL){
-                               // Block needs to be fetched first
-                               m_emerger->queueBlockEmerge(p, false);
                                m_active_blocks.m_list.erase(p);
                                continue;
                        }
@@ -1352,6 +1369,7 @@ u16 getFreeServerActiveObjectId(
 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
 {
        assert(object);
+       m_added_objects++;
        u16 id = addActiveObjectRaw(object, true, 0);
        return id;
 }
@@ -1361,7 +1379,7 @@ bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
 {
        assert(obj);
 
-       v3f objectpos = obj->getBasePosition(); 
+       v3f objectpos = obj->getBasePosition();
 
        // The block in which the object resides in
        v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
@@ -1427,8 +1445,8 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
                ServerActiveObject *object = i->second;
                if(object == NULL)
                        continue;
-               // Discard if removed
-               if(object->m_removed)
+               // Discard if removed or deactivating
+               if(object->m_removed || object->m_pending_deactivation)
                        continue;
                if(object->unlimitedTransferDistance() == false){
                        // Discard if too far
@@ -1478,7 +1496,7 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
                        continue;
                }
 
-               if(object->m_removed)
+               if(object->m_removed || object->m_pending_deactivation)
                {
                        removed_objects.insert(id);
                        continue;
@@ -1505,7 +1523,9 @@ ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
        if(m_active_object_messages.empty())
                return ActiveObjectMessage(0);
        
-       return m_active_object_messages.pop_front();
+       ActiveObjectMessage message = m_active_object_messages.front();
+       m_active_object_messages.pop_front();
+       return message;
 }
 
 /*
@@ -1564,21 +1584,19 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
                StaticObject s_obj(object->getType(), objectpos, staticdata);
                // Add to the block where the object is located in
                v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
-               MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
-               if(block)
-               {
+               MapBlock *block = m_map->emergeBlock(blockpos);
+               if(block){
                        block->m_static_objects.m_active[object->getId()] = s_obj;
                        object->m_static_exists = true;
                        object->m_static_block = blockpos;
 
                        if(set_changed)
-                               block->raiseModified(MOD_STATE_WRITE_NEEDED, 
+                               block->raiseModified(MOD_STATE_WRITE_NEEDED,
                                                "addActiveObjectRaw");
-               }
-               else{
+               } else {
                        v3s16 p = floatToInt(objectpos, BS);
                        errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
-                                       <<"could not find block for storing id="<<object->getId()
+                                       <<"could not emerge block for storing id="<<object->getId()
                                        <<" statically (pos="<<PP(p)<<")"<<std::endl;
                }
        }
@@ -1624,18 +1642,39 @@ void ServerEnvironment::removeRemovedObjects()
                        if (block) {
                                block->m_static_objects.remove(id);
                                block->raiseModified(MOD_STATE_WRITE_NEEDED,
-                                               "removeRemovedObjects");
+                                               "removeRemovedObjects/remove");
                                obj->m_static_exists = false;
                        } else {
-                               infostream << "failed to emerge block from which "
-                                       "an object to be removed was loaded from. id="<<id<<std::endl;
+                               infostream<<"Failed to emerge block from which an object to "
+                                               <<"be removed was loaded from. id="<<id<<std::endl;
                        }
                }
 
-               // If m_known_by_count > 0, don't actually remove.
+               // If m_known_by_count > 0, don't actually remove. On some future
+               // invocation this will be 0, which is when removal will continue.
                if(obj->m_known_by_count > 0)
                        continue;
-               
+
+               /*
+                       Move static data from active to stored if not marked as removed
+               */
+               if(obj->m_static_exists && !obj->m_removed){
+                       MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
+                       if (block) {
+                               std::map<u16, StaticObject>::iterator i =
+                                               block->m_static_objects.m_active.find(id);
+                               if(i != block->m_static_objects.m_active.end()){
+                                       block->m_static_objects.m_stored.push_back(i->second);
+                                       block->m_static_objects.m_active.erase(id);
+                                       block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                                       "removeRemovedObjects/deactivate");
+                               }
+                       } else {
+                               infostream<<"Failed to emerge block from which an object to "
+                                               <<"be deactivated was loaded from. id="<<id<<std::endl;
+                       }
+               }
+
                // Tell the object about removal
                obj->removingFromEnvironment();
                // Deregister in scripting api
@@ -1703,7 +1742,7 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
                        <<"activating objects of block "<<PP(block->getPos())
                        <<" ("<<block->m_static_objects.m_stored.size()
                        <<" objects)"<<std::endl;
-       bool large_amount = (block->m_static_objects.m_stored.size() > 49);
+       bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
        if(large_amount){
                errorstream<<"suspiciously large amount of objects detected: "
                                <<block->m_static_objects.m_stored.size()<<" in "
@@ -1716,10 +1755,9 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
                                "large amount of objects");
                return;
        }
-       // A list for objects that couldn't be converted to active for some
-       // reason. They will be stored back.
+
+       // Activate stored objects
        std::list<StaticObject> new_stored;
-       // Loop through stored static objects
        for(std::list<StaticObject>::iterator
                        i = block->m_static_objects.m_stored.begin();
                        i != block->m_static_objects.m_stored.end(); ++i)
@@ -1758,6 +1796,19 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
                StaticObject &s_obj = *i;
                block->m_static_objects.m_stored.push_back(s_obj);
        }
+
+       // Turn the active counterparts of activated objects not pending for
+       // deactivation
+       for(std::map<u16, StaticObject>::iterator
+                       i = block->m_static_objects.m_active.begin();
+                       i != block->m_static_objects.m_active.end(); ++i)
+       {
+               u16 id = i->first;
+               ServerActiveObject *object = getActiveObject(id);
+               assert(object);
+               object->m_pending_deactivation = false;
+       }
+
        /*
                Note: Block hasn't really been modified here.
                The objects have just been activated and moved from the stored
@@ -1777,7 +1828,7 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
        If force_delete is set, active object is deleted nevertheless. It
        shall only be set so in the destructor of the environment.
 
-       If block wasn't generated (not in memory or on disk), 
+       If block wasn't generated (not in memory or on disk),
 */
 void ServerEnvironment::deactivateFarObjects(bool force_delete)
 {
@@ -1798,11 +1849,52 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
                        continue;
 
                u16 id = i->first;
-               v3f objectpos = obj->getBasePosition(); 
+               v3f objectpos = obj->getBasePosition();
 
                // The block in which the object resides in
                v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
 
+               // If object's static data is stored in a deactivated block and object
+               // is actually located in an active block, re-save to the block in
+               // which the object is actually located in.
+               if(!force_delete &&
+                               obj->m_static_exists &&
+                               !m_active_blocks.contains(obj->m_static_block) &&
+                                m_active_blocks.contains(blockpos_o))
+               {
+                       v3s16 old_static_block = obj->m_static_block;
+
+                       // Save to block where object is located
+                       MapBlock *block = m_map->emergeBlock(blockpos_o, false);
+                       if(!block){
+                               errorstream<<"ServerEnvironment::deactivateFarObjects(): "
+                                               <<"Could not save object id="<<id
+                                               <<" to it's current block "<<PP(blockpos_o)
+                                               <<std::endl;
+                               continue;
+                       }
+                       std::string staticdata_new = obj->getStaticData();
+                       StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
+                       block->m_static_objects.insert(id, s_obj);
+                       obj->m_static_block = blockpos_o;
+                       block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                       "deactivateFarObjects: Static data moved in");
+
+                       // Delete from block where object was located
+                       block = m_map->emergeBlock(old_static_block, false);
+                       if(!block){
+                               errorstream<<"ServerEnvironment::deactivateFarObjects(): "
+                                               <<"Could not delete object id="<<id
+                                               <<" from it's previous block "<<PP(old_static_block)
+                                               <<std::endl;
+                               continue;
+                       }
+                       block->m_static_objects.remove(id);
+                       block->raiseModified(MOD_STATE_WRITE_NEEDED,
+                                       "deactivateFarObjects: Static data moved out");
+                       continue;
+               }
+
                // If block is active, don't remove
                if(!force_delete && m_active_blocks.contains(blockpos_o))
                        continue;
@@ -1877,30 +1969,36 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
                                block = m_map->emergeBlock(blockpos);
                        } catch(InvalidPositionException &e){
                                // Handled via NULL pointer
+                               // NOTE: emergeBlock's failure is usually determined by it
+                               //       actually returning NULL
                        }
 
                        if(block)
                        {
-                               if(block->m_static_objects.m_stored.size() >= 49){
+                               if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
                                        errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
                                                        <<" statically but block "<<PP(blockpos)
                                                        <<" already contains "
                                                        <<block->m_static_objects.m_stored.size()
-                                                       <<" (over 49) objects."
+                                                       <<" objects."
                                                        <<" Forcing delete."<<std::endl;
                                        force_delete = true;
                                } else {
-                                       // 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 static counterpart already exists in target block,
+                                       // remove it first.
+                                       // This shouldn't happen because the object is removed from
+                                       // the previous block before this according to
+                                       // obj->m_static_block, but happens rarely for some unknown
+                                       // reason. Unsuccessful attempts have been made to find
+                                       // said reason.
                                        if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
                                                infostream<<"ServerEnv: WARNING: Performing hack #83274"
                                                                <<std::endl;
                                                block->m_static_objects.remove(id);
                                        }
-                                       //store static data
-                                       block->m_static_objects.insert(0, s_obj);
+                                       // Store static data
+                                       u16 store_id = pending_delete ? id : 0;
+                                       block->m_static_objects.insert(store_id, s_obj);
                                        
                                        // Only mark block as modified if data changed considerably
                                        if(shall_be_written)
@@ -1980,6 +2078,8 @@ ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
        m_gamedef(gamedef),
        m_irr(irr)
 {
+       char zero = 0;
+       memset(m_attachements, zero, sizeof(m_attachements));
 }
 
 ClientEnvironment::~ClientEnvironment()
@@ -2243,7 +2343,8 @@ void ClientEnvironment::step(float dtime)
                v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
                MapNode n = m_map->getNodeNoEx(p);
                ContentFeatures c = m_gamedef->ndef()->get(n);
-               if(c.isLiquid() && c.drowning && lplayer->hp > 0){
+               u8 drowning_damage = c.drowning;
+               if(drowning_damage > 0 && lplayer->hp > 0){
                        u16 breath = lplayer->getBreath();
                        if(breath > 10){
                                breath = 11;
@@ -2255,8 +2356,8 @@ void ClientEnvironment::step(float dtime)
                        updateLocalPlayerBreath(breath);
                }
 
-               if(lplayer->getBreath() == 0){
-                       damageLocalPlayer(1, true);
+               if(lplayer->getBreath() == 0 && drowning_damage > 0){
+                       damageLocalPlayer(drowning_damage, true);
                }
        }
        if(m_breathing_interval.step(dtime, 0.5))
@@ -2270,7 +2371,7 @@ void ClientEnvironment::step(float dtime)
                if (!lplayer->hp){
                        lplayer->setBreath(11);
                }
-               else if(!c.isLiquid() || !c.drowning){
+               else if(c.drowning == 0){
                        u16 breath = lplayer->getBreath();
                        if(breath <= 10){
                                breath += 1;
@@ -2294,7 +2395,7 @@ void ClientEnvironment::step(float dtime)
                if(player->isLocal() == false)
                {
                        // Move
-                       player->move(dtime, *m_map, 100*BS);
+                       player->move(dtime, this, 100*BS);
 
                }
                
@@ -2573,13 +2674,14 @@ void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
 
 ClientEnvEvent ClientEnvironment::getClientEvent()
 {
+       ClientEnvEvent event;
        if(m_client_event_queue.empty())
-       {
-               ClientEnvEvent event;
                event.type = CEE_NONE;
-               return event;
+       else {
+               event = m_client_event_queue.front();
+               m_client_event_queue.pop_front();
        }
-       return m_client_event_queue.pop_front();
+       return event;
 }
 
 #endif // #ifndef SERVER