]> git.lizzy.rs Git - minetest.git/blobdiff - src/environment.cpp
Mapgen V6: Re-enable liquid flowing
[minetest.git] / src / environment.cpp
index 5249dd9ec5757de6b1a8afb981feaa4e34ef83d7..ee4488476455293e06d41296b4c925e31034324e 100644 (file)
@@ -36,12 +36,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #ifndef SERVER
 #include "clientmap.h"
 #include "localplayer.h"
+#include "mapblock_mesh.h"
 #include "event.h"
 #endif
 #include "daynightratio.h"
 #include "map.h"
 #include "emerge.h"
 #include "util/serialize.h"
+#include "jthread/jmutexautolock.h"
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -49,8 +51,11 @@ 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)
 {
+       m_cache_enable_shaders = g_settings->getBool("enable_shaders");
 }
 
 Environment::~Environment()
@@ -83,19 +88,29 @@ 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;
+               }
+       }
+}
+
+void Environment::removePlayer(const char *name)
+{
+       for (std::list<Player*>::iterator it = m_players.begin();
+                       it != m_players.end(); ++it) {
+               if (strcmp((*it)->getName(), name) == 0) {
+                       delete *it;
+                       m_players.erase(it);
+                       return;
+               }
        }
 }
 
@@ -190,16 +205,35 @@ std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
 
 u32 Environment::getDayNightRatio()
 {
-       bool smooth = g_settings->getBool("enable_shaders");
-       return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
+       if(m_enable_day_night_ratio_override)
+               return m_day_night_ratio_override;
+       return time_to_daynight_ratio(m_time_of_day_f*24000, m_cache_enable_shaders);
+}
+
+void Environment::setTimeOfDaySpeed(float speed)
+{
+       JMutexAutoLock(this->m_lock);
+       m_time_of_day_speed = speed;
+}
+
+float Environment::getTimeOfDaySpeed()
+{
+       JMutexAutoLock(this->m_lock);
+       float retval = m_time_of_day_speed;
+       return retval;
 }
 
 void Environment::stepTimeOfDay(float dtime)
 {
+       float day_speed = 0;
+       {
+               JMutexAutoLock(this->m_lock);
+               day_speed = m_time_of_day_speed;
+       }
+       
        m_time_counter += dtime;
-       f32 speed = m_time_of_day_speed * 24000./(24.*3600);
+       f32 speed = 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
@@ -209,8 +243,11 @@ void Environment::stepTimeOfDay(float dtime)
                if(sync_f)
                        m_time_of_day_f = (float)m_time_of_day / 24000.0;
        }
+       if (speed > 0) {
+               m_time_counter -= (f32)units / speed;
+       }
        if(!sync_f){
-               m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
+               m_time_of_day_f += 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)
@@ -308,13 +345,12 @@ void ActiveBlockList::update(std::list<v3s16> &active_positions,
 */
 
 ServerEnvironment::ServerEnvironment(ServerMap *map,
-               GameScripting *scriptIface,
-               IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
+               GameScripting *scriptIface, IGameDef *gamedef,
+               const std::string &path_world) :
        m_map(map),
        m_script(scriptIface),
        m_gamedef(gamedef),
-       m_emerger(emerger),
-       m_random_spawn_timer(3),
+       m_path_world(path_world),
        m_send_recommended_timer(0),
        m_active_block_interval_overload_skip(0),
        m_game_time(0),
@@ -322,7 +358,6 @@ ServerEnvironment::ServerEnvironment(ServerMap *map,
        m_recommended_send_interval(0.1),
        m_max_lag_estimate(0.1)
 {
-       m_use_weather = g_settings->getBool("weather");
 }
 
 ServerEnvironment::~ServerEnvironment()
@@ -381,196 +416,76 @@ bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16
        return true;
 }
 
-void ServerEnvironment::serializePlayers(const std::string &savedir)
+void ServerEnvironment::saveLoadedPlayers()
 {
-       std::string players_path = savedir + "/players";
+       std::string players_path = m_path_world + DIR_DELIM "players";
        fs::CreateDir(players_path);
 
-       std::set<Player*> saved_players;
-
-       std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
-       for(u32 i=0; i<player_files.size(); i++)
-       {
-               if(player_files[i].dir || player_files[i].name[0] == '.')
-                       continue;
-               
-               // Full path to this file
-               std::string path = players_path + "/" + player_files[i].name;
-
-               //infostream<<"Checking player file "<<path<<std::endl;
-
-               // Load player to see what is its name
-               RemotePlayer testplayer(m_gamedef);
-               {
-                       // Open file and deserialize
-                       std::ifstream is(path.c_str(), std::ios_base::binary);
-                       if(is.good() == false)
-                       {
-                               infostream<<"Failed to read "<<path<<std::endl;
-                               continue;
-                       }
-                       testplayer.deSerialize(is, player_files[i].name);
-               }
-
-               //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
-               
-               // Search for the player
-               std::string playername = testplayer.getName();
-               Player *player = getPlayer(playername.c_str());
-               if(player == NULL)
-               {
-                       infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
-                       continue;
-               }
-
-               //infostream<<"Found matching player, overwriting."<<std::endl;
-
-               // OK, found. Save player there.
-               if(player->checkModified())
-               {
-                       // Open file and serialize
-                       std::ostringstream ss(std::ios_base::binary);
-                       player->serialize(ss);
-                       if(!fs::safeWriteToFile(path, ss.str()))
-                       {
-                               infostream<<"Failed to write "<<path<<std::endl;
-                               continue;
-                       }
-                       saved_players.insert(player);
-               } else {
-                       saved_players.insert(player);
+       for (std::list<Player*>::iterator it = m_players.begin();
+                       it != m_players.end();
+                       ++it) {
+               RemotePlayer *player = static_cast<RemotePlayer*>(*it);
+               if (player->checkModified()) {
+                       player->save(players_path);
                }
        }
+}
 
-       for(std::list<Player*>::iterator i = m_players.begin();
-                       i != m_players.end(); ++i)
-       {
-               Player *player = *i;
-               if(saved_players.find(player) != saved_players.end())
-               {
-                       /*infostream<<"Player "<<player->getName()
-                                       <<" was already saved."<<std::endl;*/
-                       continue;
-               }
-               std::string playername = player->getName();
-               // Don't save unnamed player
-               if(playername == "")
-               {
-                       //infostream<<"Not saving unnamed player."<<std::endl;
-                       continue;
-               }
-               /*
-                       Find a sane filename
-               */
-               if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
-                       playername = "player";
-               std::string path = players_path + "/" + playername;
-               bool found = false;
-               for(u32 i=0; i<1000; i++)
-               {
-                       if(fs::PathExists(path) == false)
-                       {
-                               found = true;
-                               break;
-                       }
-                       path = players_path + "/" + playername + itos(i);
-               }
-               if(found == false)
-               {
-                       infostream<<"Didn't find free file for player"<<std::endl;
-                       continue;
-               }
+void ServerEnvironment::savePlayer(const std::string &playername)
+{
+       std::string players_path = m_path_world + DIR_DELIM "players";
+       fs::CreateDir(players_path);
 
-               {
-                       /*infostream<<"Saving player "<<player->getName()<<" to "
-                                       <<path<<std::endl;*/
-                       // Open file and serialize
-                       std::ostringstream ss(std::ios_base::binary);
-                       player->serialize(ss);
-                       if(!fs::safeWriteToFile(path, ss.str()))
-                       {
-                               infostream<<"Failed to write "<<path<<std::endl;
-                               continue;
-                       }
-                       saved_players.insert(player);
-               }
+       RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
+       if (player) {
+               player->save(players_path);
        }
-
-       //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
 }
 
-void ServerEnvironment::deSerializePlayers(const std::string &savedir)
+Player *ServerEnvironment::loadPlayer(const std::string &playername)
 {
-       std::string players_path = savedir + "/players";
-
-       std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
-       for(u32 i=0; i<player_files.size(); i++)
-       {
-               if(player_files[i].dir)
-                       continue;
-               
-               // Full path to this file
-               std::string path = players_path + "/" + player_files[i].name;
+       std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
 
-               //infostream<<"Checking player file "<<path<<std::endl;
-
-               // Load player to see what is its name
-               RemotePlayer testplayer(m_gamedef);
-               {
-                       // Open file and deserialize
-                       std::ifstream is(path.c_str(), std::ios_base::binary);
-                       if(is.good() == false)
-                       {
-                               infostream<<"Failed to read "<<path<<std::endl;
-                               continue;
-                       }
-                       testplayer.deSerialize(is, player_files[i].name);
-               }
-
-               if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
-               {
-                       infostream<<"Not loading player with invalid name: "
-                                       <<testplayer.getName()<<std::endl;
-               }
+       RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
+       bool newplayer = false;
+       bool found = false;
+       if (!player) {
+               player = new RemotePlayer(m_gamedef, playername.c_str());
+               newplayer = true;
+       }
 
-               /*infostream<<"Loaded test player with name "<<testplayer.getName()
-                               <<std::endl;*/
-               
-               // Search for the player
-               std::string playername = testplayer.getName();
-               Player *player = getPlayer(playername.c_str());
-               bool newplayer = false;
-               if(player == NULL)
-               {
-                       //infostream<<"Is a new player"<<std::endl;
-                       player = new RemotePlayer(m_gamedef);
-                       newplayer = true;
+       RemotePlayer testplayer(m_gamedef, "");
+       std::string path = players_path + playername;
+       for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
+               // Open file and deserialize
+               std::ifstream is(path.c_str(), std::ios_base::binary);
+               if (!is.good()) {
+                       return NULL;
                }
-
-               // Load player
-               {
-                       verbosestream<<"Reading player "<<testplayer.getName()<<" from "
-                                       <<path<<std::endl;
-                       // Open file and deserialize
-                       std::ifstream is(path.c_str(), std::ios_base::binary);
-                       if(is.good() == false)
-                       {
-                               infostream<<"Failed to read "<<path<<std::endl;
-                               continue;
-                       }
-                       player->deSerialize(is, player_files[i].name);
-               }
-
-               if(newplayer)
-               {
-                       addPlayer(player);
+               testplayer.deSerialize(is, path);
+               is.close();
+               if (testplayer.getName() == playername) {
+                       *player = testplayer;
+                       found = true;
+                       break;
                }
+               path = players_path + playername + itos(i);
+       }
+       if (!found) {
+               infostream << "Player file for player " << playername
+                               << " not found" << std::endl;
+               return NULL;
        }
+       if (newplayer) {
+               addPlayer(player);
+       }
+       player->setModified(false);
+       return player;
 }
 
-void ServerEnvironment::saveMeta(const std::string &savedir)
+void ServerEnvironment::saveMeta()
 {
-       std::string path = savedir + "/env_meta.txt";
+       std::string path = m_path_world + DIR_DELIM "env_meta.txt";
 
        // Open file and serialize
        std::ostringstream ss(std::ios_base::binary);
@@ -589,44 +504,35 @@ void ServerEnvironment::saveMeta(const std::string &savedir)
        }
 }
 
-void ServerEnvironment::loadMeta(const std::string &savedir)
+void ServerEnvironment::loadMeta()
 {
-       std::string path = savedir + "/env_meta.txt";
+       std::string path = m_path_world + DIR_DELIM "env_meta.txt";
 
        // Open file and deserialize
        std::ifstream is(path.c_str(), std::ios_base::binary);
-       if(is.good() == false)
-       {
-               infostream<<"ServerEnvironment::loadMeta(): Failed to open "
-                               <<path<<std::endl;
+       if (!is.good()) {
+               infostream << "ServerEnvironment::loadMeta(): Failed to open "
+                               << path << std::endl;
                throw SerializationError("Couldn't load env meta");
        }
 
        Settings args;
-       
-       for(;;)
-       {
-               if(is.eof())
-                       throw SerializationError
-                                       ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
-               std::string line;
-               std::getline(is, line);
-               std::string trimmedline = trim(line);
-               if(trimmedline == "EnvArgsEnd")
-                       break;
-               args.parseConfigLine(line);
+
+       if (!args.parseConfigLines(is, "EnvArgsEnd")) {
+               throw SerializationError("ServerEnvironment::loadMeta(): "
+                               "EnvArgsEnd not found!");
        }
-       
-       try{
+
+       try {
                m_game_time = args.getU64("game_time");
-       }catch(SettingNotFoundException &e){
+       } catch (SettingNotFoundException &e) {
                // Getting this is crucial, otherwise timestamps are useless
                throw SerializationError("Couldn't load env meta game_time");
        }
 
-       try{
+       try {
                m_time_of_day = args.getU64("time_of_day");
-       }catch(SettingNotFoundException &e){
+       } catch (SettingNotFoundException &e) {
                // This is not as important
                m_time_of_day = 9000;
        }
@@ -710,6 +616,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())
@@ -717,6 +651,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++)
@@ -760,33 +698,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;
+                               }
                        }
                }
        }
@@ -794,6 +715,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();
@@ -842,19 +771,26 @@ bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
 {
        INodeDefManager *ndef = m_gamedef->ndef();
        MapNode n_old = m_map->getNodeNoEx(p);
+
        // Call destructor
-       if(ndef->get(n_old).has_on_destruct)
+       if (ndef->get(n_old).has_on_destruct)
                m_script->node_on_destruct(p, n_old);
+
        // Replace node
-       bool succeeded = m_map->addNodeWithEvent(p, n);
-       if(!succeeded)
+       if (!m_map->addNodeWithEvent(p, n))
                return false;
+
+       // Update active VoxelManipulator if a mapgen thread
+       m_map->updateVManip(p);
+
        // Call post-destructor
-       if(ndef->get(n_old).has_after_destruct)
+       if (ndef->get(n_old).has_after_destruct)
                m_script->node_after_destruct(p, n_old);
+
        // Call constructor
-       if(ndef->get(n).has_on_construct)
+       if (ndef->get(n).has_on_construct)
                m_script->node_on_construct(p, n);
+
        return true;
 }
 
@@ -862,24 +798,36 @@ bool ServerEnvironment::removeNode(v3s16 p)
 {
        INodeDefManager *ndef = m_gamedef->ndef();
        MapNode n_old = m_map->getNodeNoEx(p);
+
        // Call destructor
-       if(ndef->get(n_old).has_on_destruct)
+       if (ndef->get(n_old).has_on_destruct)
                m_script->node_on_destruct(p, n_old);
+
        // Replace with air
        // This is slightly optimized compared to addNodeWithEvent(air)
-       bool succeeded = m_map->removeNodeWithEvent(p);
-       if(!succeeded)
+       if (!m_map->removeNodeWithEvent(p))
                return false;
+
+       // Update active VoxelManipulator if a mapgen thread
+       m_map->updateVManip(p);
+
        // Call post-destructor
-       if(ndef->get(n_old).has_after_destruct)
+       if (ndef->get(n_old).has_after_destruct)
                m_script->node_after_destruct(p, n_old);
+
        // Air doesn't require constructor
        return true;
 }
 
 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
 {
-       return m_map->addNodeWithEvent(p, n, false);
+       if (!m_map->addNodeWithEvent(p, n, false))
+               return false;
+
+       // Update active VoxelManipulator if a mapgen thread
+       m_map->updateVManip(p);
+
+       return true;
 }
 
 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
@@ -1074,7 +1022,7 @@ void ServerEnvironment::step(float dtime)
                                continue;
                        
                        // Move
-                       player->move(dtime, *m_map, 100*BS);
+                       player->move(dtime, this, 100*BS);
                }
        }
 
@@ -1144,11 +1092,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->enqueueBlockEmerge(
-                                               PEER_ID_INEXISTENT, p, false);
                                m_active_blocks.m_list.erase(p);
                                continue;
                        }
@@ -1281,11 +1226,6 @@ void ServerEnvironment::step(float dtime)
                                i != m_active_objects.end(); ++i)
                {
                        ServerActiveObject* obj = i->second;
-                       // Remove non-peaceful mobs on peaceful mode
-                       if(g_settings->getBool("only_peaceful_mobs")){
-                               if(!obj->isPeaceful())
-                                       obj->m_removed = true;
-                       }
                        // Don't step if is to be removed or stored statically
                        if(obj->m_removed || obj->m_pending_deactivation)
                                continue;
@@ -1351,6 +1291,7 @@ u16 getFreeServerActiveObjectId(
 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
 {
        assert(object);
+       m_added_objects++;
        u16 id = addActiveObjectRaw(object, true, 0);
        return id;
 }
@@ -1360,7 +1301,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));
@@ -1405,11 +1346,17 @@ bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
        inside a radius around a position
 */
 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
+               s16 player_radius,
                std::set<u16> &current_objects,
                std::set<u16> &added_objects)
 {
        v3f pos_f = intToFloat(pos, BS);
        f32 radius_f = radius * BS;
+       f32 player_radius_f = player_radius * BS;
+
+       if (player_radius_f < 0)
+               player_radius_f = 0;
+
        /*
                Go through the object list,
                - discard m_removed objects,
@@ -1429,12 +1376,15 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
                // Discard if removed or deactivating
                if(object->m_removed || object->m_pending_deactivation)
                        continue;
-               if(object->unlimitedTransferDistance() == false){
+
+               f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
+               if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
                        // Discard if too far
-                       f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
-                       if(distance_f > radius_f)
+                       if (distance_f > player_radius_f && player_radius_f != 0)
                                continue;
-               }
+               } else if (distance_f > radius_f)
+                       continue;
+
                // Discard if already on current_objects
                std::set<u16>::iterator n;
                n = current_objects.find(id);
@@ -1450,11 +1400,17 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
        inside a radius around a position
 */
 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
+               s16 player_radius,
                std::set<u16> &current_objects,
                std::set<u16> &removed_objects)
 {
        v3f pos_f = intToFloat(pos, BS);
        f32 radius_f = radius * BS;
+       f32 player_radius_f = player_radius * BS;
+
+       if (player_radius_f < 0)
+               player_radius_f = 0;
+
        /*
                Go through current_objects; object is removed if:
                - object is not found in m_active_objects (this is actually an
@@ -1483,19 +1439,15 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
                        continue;
                }
                
-               // If transfer distance is unlimited, don't remove
-               if(object->unlimitedTransferDistance())
-                       continue;
-
                f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
-
-               if(distance_f >= radius_f)
-               {
-                       removed_objects.insert(id);
+               if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+                       if (distance_f <= player_radius_f || player_radius_f == 0)
+                               continue;
+               } else if (distance_f <= radius_f)
                        continue;
-               }
-               
-               // Not removed
+
+               // Object is no longer visible
+               removed_objects.insert(id);
        }
 }
 
@@ -1572,7 +1524,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
                        object->m_static_block = blockpos;
 
                        if(set_changed)
-                               block->raiseModified(MOD_STATE_WRITE_NEEDED, 
+                               block->raiseModified(MOD_STATE_WRITE_NEEDED,
                                                "addActiveObjectRaw");
                } else {
                        v3s16 p = floatToInt(objectpos, BS);
@@ -1717,7 +1669,7 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
        if(block==NULL)
                return;
        // Ignore if no stored objects (to not set changed flag)
-       if(block->m_static_objects.m_stored.size() == 0)
+       if(block->m_static_objects.m_stored.empty())
                return;
        verbosestream<<"ServerEnvironment::activateObjects(): "
                        <<"activating objects of block "<<PP(block->getPos())
@@ -1809,7 +1761,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)
 {
@@ -1830,7 +1782,7 @@ 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));
@@ -2059,6 +2011,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()
@@ -2374,24 +2328,29 @@ void ClientEnvironment::step(float dtime)
                if(player->isLocal() == false)
                {
                        // Move
-                       player->move(dtime, *m_map, 100*BS);
+                       player->move(dtime, this, 100*BS);
 
                }
-               
-               // Update lighting on all players on client
-               float light = 1.0;
-               try{
-                       // Get node at head
-                       v3s16 p = player->getLightPosition();
-                       MapNode n = m_map->getNode(p);
-                       light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
-               }
-               catch(InvalidPositionException &e){
-                       light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
-               }
-               player->light = light;
        }
-       
+
+       // Update lighting on local player (used for wield item)
+       u32 day_night_ratio = getDayNightRatio();
+       {
+               // Get node at head
+
+               // On InvalidPositionException, use this as default
+               // (day: LIGHT_SUN, night: 0)
+               MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0);
+
+               v3s16 p = lplayer->getLightPosition();
+               node_at_lplayer = m_map->getNodeNoEx(p);
+
+               u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef());
+               u8 day = light & 0xff;
+               u8 night = (light >> 8) & 0xff;
+               finalColorBlend(lplayer->light_color, day, night, day_night_ratio);
+       }
+
        /*
                Step active objects and update lighting of them
        */
@@ -2410,15 +2369,16 @@ void ClientEnvironment::step(float dtime)
                {
                        // Update lighting
                        u8 light = 0;
-                       try{
-                               // Get node at head
-                               v3s16 p = obj->getLightPosition();
-                               MapNode n = m_map->getNode(p);
-                               light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
-                       }
-                       catch(InvalidPositionException &e){
-                               light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
-                       }
+                       bool pos_ok;
+
+                       // Get node at head
+                       v3s16 p = obj->getLightPosition();
+                       MapNode n = m_map->getNodeNoEx(p, &pos_ok);
+                       if (pos_ok)
+                               light = n.getLightBlend(day_night_ratio, m_gamedef->ndef());
+                       else
+                               light = blend_light(day_night_ratio, LIGHT_SUN, 0);
+
                        obj->updateLight(light);
                }
        }
@@ -2509,15 +2469,16 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
        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);
+               bool pos_ok;
+
+               // Get node at head
+               v3s16 p = object->getLightPosition();
+               MapNode n = m_map->getNodeNoEx(p, &pos_ok);
+               if (pos_ok)
                        light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
-               }
-               catch(InvalidPositionException &e){
+               else
                        light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
-               }
+
                object->updateLight(light);
        }
        return object->getId();