]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/map.cpp
working good
[dragonfireclient.git] / src / map.cpp
index caebb516182441b02244f7ce54a33fc5728d02e4..1bd4f299f05f6eb75884641e30170151a930a825 100644 (file)
@@ -18,58 +18,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "map.h"
-//#include "player.h"
 #include "main.h"
 #include "jmutexautolock.h"
 #include "client.h"
 #include "filesys.h"
 #include "utility.h"
 #include "voxel.h"
-
-#ifdef _WIN32
-       #include <windows.h>
-       #define sleep_ms(x) Sleep(x)
-#else
-       #include <unistd.h>
-       #define sleep_ms(x) usleep(x*1000)
-#endif
-
-MapBlockPointerCache::MapBlockPointerCache(Map *map)
-{
-       m_map = map;
-       m_map->m_blockcachelock.cacheCreated();
-
-       m_from_cache_count = 0;
-       m_from_map_count = 0;
-}
-
-MapBlockPointerCache::~MapBlockPointerCache()
-{
-       m_map->m_blockcachelock.cacheRemoved();
-
-       /*dstream<<"MapBlockPointerCache:"
-                       <<" from_cache_count="<<m_from_cache_count
-                       <<" from_map_count="<<m_from_map_count
-                       <<std::endl;*/
-}
-
-MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
-{
-       core::map<v3s16, MapBlock*>::Node *n = NULL;
-       n = m_blocks.find(p);
-       if(n != NULL)
-       {
-               m_from_cache_count++;
-               return n->getValue();
-       }
-       
-       m_from_map_count++;
-       
-       // Throws InvalidPositionException if not found
-       MapBlock *b = m_map->getBlockNoCreate(p);
-       m_blocks[p] = b;
-       return b;
-}
+#include "porting.h"
 
 /*
        Map
@@ -112,13 +67,6 @@ Map::~Map()
        }
 }
 
-/*bool Map::sectorExists(v2s16 p)
-{
-       JMutexAutoLock lock(m_sector_mutex);
-       core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
-       return (n != NULL);
-}*/
-
 MapSector * Map::getSectorNoGenerate(v2s16 p)
 {
        JMutexAutoLock lock(m_sector_mutex);
@@ -160,19 +108,20 @@ MapBlock * Map::getBlockNoCreate(v3s16 p3d)
        return block;
 }
 
-/*MapBlock * Map::getBlock(v3s16 p3d, bool generate)
+MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
 {
-       dstream<<"Map::getBlock() with generate=true called"
-                       <<std::endl;
-       v2s16 p2d(p3d.X, p3d.Z);
-       //MapSector * sector = getSector(p2d, generate);
-       MapSector * sector = getSectorNoGenerate(p2d);
-
-       if(sector == NULL)
-               throw InvalidPositionException();
-
-       return sector->getBlockNoCreate(p3d.Y);
-}*/
+       try
+       {
+               v2s16 p2d(p3d.X, p3d.Z);
+               MapSector * sector = getSectorNoGenerate(p2d);
+               MapBlock *block = sector->getBlockNoCreate(p3d.Y);
+               return block;
+       }
+       catch(InvalidPositionException &e)
+       {
+               return NULL;
+       }
+}
 
 f32 Map::getGroundHeight(v2s16 p, bool generate)
 {
@@ -215,156 +164,6 @@ bool Map::isNodeUnderground(v3s16 p)
        }
 }
 
-#if 0
-void Map::interpolate(v3s16 block,
-               core::map<v3s16, MapBlock*> & modified_blocks)
-{
-       const v3s16 dirs[6] = {
-               v3s16(0,0,1), // back
-               v3s16(0,1,0), // top
-               v3s16(1,0,0), // right
-               v3s16(0,0,-1), // front
-               v3s16(0,-1,0), // bottom
-               v3s16(-1,0,0), // left
-       };
-
-       if(from_nodes.size() == 0)
-               return;
-       
-       u32 blockchangecount = 0;
-
-       core::map<v3s16, bool> lighted_nodes;
-       core::map<v3s16, bool>::Iterator j;
-       j = from_nodes.getIterator();
-
-       /*
-               Initialize block cache
-       */
-       v3s16 blockpos_last;
-       MapBlock *block = NULL;
-       // Cache this a bit, too
-       bool block_checked_in_modified = false;
-       
-       for(; j.atEnd() == false; j++)
-       //for(; j != from_nodes.end(); j++)
-       {
-               v3s16 pos = j.getNode()->getKey();
-               //v3s16 pos = *j;
-               //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
-               v3s16 blockpos = getNodeBlockPos(pos);
-               
-               // Only fetch a new block if the block position has changed
-               try{
-                       if(block == NULL || blockpos != blockpos_last){
-                               block = getBlockNoCreate(blockpos);
-                               blockpos_last = blockpos;
-
-                               block_checked_in_modified = false;
-                               blockchangecount++;
-                       }
-               }
-               catch(InvalidPositionException &e)
-               {
-                       continue;
-               }
-
-               if(block->isDummy())
-                       continue;
-
-               // Calculate relative position in block
-               v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
-
-               // Get node straight from the block
-               MapNode n = block->getNode(relpos);
-
-               u8 oldlight = n.getLight();
-               u8 newlight = diminish_light(oldlight);
-
-               // Loop through 6 neighbors
-               for(u16 i=0; i<6; i++){
-                       // Get the position of the neighbor node
-                       v3s16 n2pos = pos + dirs[i];
-                       
-                       // Get the block where the node is located
-                       v3s16 blockpos = getNodeBlockPos(n2pos);
-
-                       try
-                       {
-                               // Only fetch a new block if the block position has changed
-                               try{
-                                       if(block == NULL || blockpos != blockpos_last){
-                                               block = getBlockNoCreate(blockpos);
-                                               blockpos_last = blockpos;
-
-                                               block_checked_in_modified = false;
-                                               blockchangecount++;
-                                       }
-                               }
-                               catch(InvalidPositionException &e)
-                               {
-                                       continue;
-                               }
-                               
-                               // Calculate relative position in block
-                               v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
-                               // Get node straight from the block
-                               MapNode n2 = block->getNode(relpos);
-                               
-                               bool changed = false;
-                               /*
-                                       If the neighbor is brighter than the current node,
-                                       add to list (it will light up this node on its turn)
-                               */
-                               if(n2.getLight() > undiminish_light(oldlight))
-                               {
-                                       lighted_nodes.insert(n2pos, true);
-                                       //lighted_nodes.push_back(n2pos);
-                                       changed = true;
-                               }
-                               /*
-                                       If the neighbor is dimmer than how much light this node
-                                       would spread on it, add to list
-                               */
-                               if(n2.getLight() < newlight)
-                               {
-                                       if(n2.light_propagates())
-                                       {
-                                               n2.setLight(newlight);
-                                               block->setNode(relpos, n2);
-                                               lighted_nodes.insert(n2pos, true);
-                                               //lighted_nodes.push_back(n2pos);
-                                               changed = true;
-                                       }
-                               }
-
-                               // Add to modified_blocks
-                               if(changed == true && block_checked_in_modified == false)
-                               {
-                                       // If the block is not found in modified_blocks, add.
-                                       if(modified_blocks.find(blockpos) == NULL)
-                                       {
-                                               modified_blocks.insert(blockpos, block);
-                                       }
-                                       block_checked_in_modified = true;
-                               }
-                       }
-                       catch(InvalidPositionException &e)
-                       {
-                               continue;
-                       }
-               }
-       }
-
-       /*dstream<<"spreadLight(): Changed block "
-                       <<blockchangecount<<" times"
-                       <<" for "<<from_nodes.size()<<" nodes"
-                       <<std::endl;*/
-       
-       if(lighted_nodes.size() > 0)
-               spreadLight(lighted_nodes, modified_blocks);
-}
-#endif
-
 /*
        Goes recursively through the neighbours of the node.
 
@@ -382,7 +181,8 @@ void Map::interpolate(v3s16 block,
 
        values of from_nodes are lighting values.
 */
-void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
+void Map::unspreadLight(enum LightBank bank,
+               core::map<v3s16, u8> & from_nodes,
                core::map<v3s16, bool> & light_sources,
                core::map<v3s16, MapBlock*>  & modified_blocks)
 {
@@ -482,19 +282,19 @@ void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
                                        If the neighbor is dimmer than what was specified
                                        as oldlight (the light of the previous node)
                                */
-                               if(n2.getLight() < oldlight)
+                               if(n2.getLight(bank) < oldlight)
                                {
                                        /*
                                                And the neighbor is transparent and it has some light
                                        */
-                                       if(n2.light_propagates() && n2.getLight() != 0)
+                                       if(n2.light_propagates() && n2.getLight(bank) != 0)
                                        {
                                                /*
                                                        Set light to 0 and add to queue
                                                */
 
-                                               u8 current_light = n2.getLight();
-                                               n2.setLight(0);
+                                               u8 current_light = n2.getLight(bank);
+                                               n2.setLight(bank, 0);
                                                block->setNode(relpos, n2);
 
                                                unlighted_nodes.insert(n2pos, current_light);
@@ -543,27 +343,29 @@ void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
                        <<std::endl;*/
        
        if(unlighted_nodes.size() > 0)
-               unspreadLight(unlighted_nodes, light_sources, modified_blocks);
+               unspreadLight(bank, unlighted_nodes, light_sources, modified_blocks);
 }
 
 /*
        A single-node wrapper of the above
 */
-void Map::unLightNeighbors(v3s16 pos, u8 lightwas,
+void Map::unLightNeighbors(enum LightBank bank,
+               v3s16 pos, u8 lightwas,
                core::map<v3s16, bool> & light_sources,
                core::map<v3s16, MapBlock*>  & modified_blocks)
 {
        core::map<v3s16, u8> from_nodes;
        from_nodes.insert(pos, lightwas);
 
-       unspreadLight(from_nodes, light_sources, modified_blocks);
+       unspreadLight(bank, from_nodes, light_sources, modified_blocks);
 }
 
 /*
        Lights neighbors of from_nodes, collects all them and then
        goes on recursively.
 */
-void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
+void Map::spreadLight(enum LightBank bank,
+               core::map<v3s16, bool> & from_nodes,
                core::map<v3s16, MapBlock*> & modified_blocks)
 {
        const v3s16 dirs[6] = {
@@ -624,7 +426,7 @@ void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
                // Get node straight from the block
                MapNode n = block->getNode(relpos);
 
-               u8 oldlight = n.getLight();
+               u8 oldlight = n.getLight(bank);
                u8 newlight = diminish_light(oldlight);
 
                // Loop through 6 neighbors
@@ -662,7 +464,7 @@ void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
                                        If the neighbor is brighter than the current node,
                                        add to list (it will light up this node on its turn)
                                */
-                               if(n2.getLight() > undiminish_light(oldlight))
+                               if(n2.getLight(bank) > undiminish_light(oldlight))
                                {
                                        lighted_nodes.insert(n2pos, true);
                                        //lighted_nodes.push_back(n2pos);
@@ -672,11 +474,11 @@ void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
                                        If the neighbor is dimmer than how much light this node
                                        would spread on it, add to list
                                */
-                               if(n2.getLight() < newlight)
+                               if(n2.getLight(bank) < newlight)
                                {
                                        if(n2.light_propagates())
                                        {
-                                               n2.setLight(newlight);
+                                               n2.setLight(bank, newlight);
                                                block->setNode(relpos, n2);
                                                lighted_nodes.insert(n2pos, true);
                                                //lighted_nodes.push_back(n2pos);
@@ -708,21 +510,22 @@ void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
                        <<std::endl;*/
        
        if(lighted_nodes.size() > 0)
-               spreadLight(lighted_nodes, modified_blocks);
+               spreadLight(bank, lighted_nodes, modified_blocks);
 }
 
 /*
        A single-node source variation of the above.
 */
-void Map::lightNeighbors(v3s16 pos,
+void Map::lightNeighbors(enum LightBank bank,
+               v3s16 pos,
                core::map<v3s16, MapBlock*> & modified_blocks)
 {
        core::map<v3s16, bool> from_nodes;
        from_nodes.insert(pos, true);
-       spreadLight(from_nodes, modified_blocks);
+       spreadLight(bank, from_nodes, modified_blocks);
 }
 
-v3s16 Map::getBrightestNeighbour(v3s16 p)
+v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p)
 {
        v3s16 dirs[6] = {
                v3s16(0,0,1), // back
@@ -749,8 +552,8 @@ v3s16 Map::getBrightestNeighbour(v3s16 p)
                {
                        continue;
                }
-               if(n2.getLight() > brightest_light || found_something == false){
-                       brightest_light = n2.getLight();
+               if(n2.getLight(bank) > brightest_light || found_something == false){
+                       brightest_light = n2.getLight(bank);
                        brightest_pos = n2pos;
                        found_something = true;
                }
@@ -791,7 +594,7 @@ s16 Map::propagateSunlight(v3s16 start,
 
                if(n.sunlight_propagates())
                {
-                       n.setLight(LIGHT_SUN);
+                       n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
                        block->setNode(relpos, n);
 
                        modified_blocks.insert(blockpos, block);
@@ -803,7 +606,8 @@ s16 Map::propagateSunlight(v3s16 start,
        return y + 1;
 }
 
-void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
+void Map::updateLighting(enum LightBank bank,
+               core::map<v3s16, MapBlock*> & a_blocks,
                core::map<v3s16, MapBlock*> & modified_blocks)
 {
        /*m_dout<<DTIME<<"Map::updateLighting(): "
@@ -813,11 +617,6 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
        bool debug=false;
        u32 count_was = modified_blocks.size();
 
-       /*core::list<MapBlock *>::Iterator i = a_blocks.begin();
-       for(; i != a_blocks.end(); i++)
-       {
-               MapBlock *block = *i;*/
-
        core::map<v3s16, bool> light_sources;
        
        core::map<v3s16, u8> unlight_from;
@@ -848,8 +647,8 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
                                try{
                                        v3s16 p(x,y,z);
                                        MapNode n = block->getNode(v3s16(x,y,z));
-                                       u8 oldlight = n.getLight();
-                                       n.setLight(0);
+                                       u8 oldlight = n.getLight(bank);
+                                       n.setLight(bank, 0);
                                        block->setNode(v3s16(x,y,z), n);
                                        
                                        // Collect borders for unlighting
@@ -876,11 +675,22 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
                                }
                        }
                        
-                       bool bottom_valid = block->propagateSunlight(light_sources);
+                       if(bank == LIGHTBANK_DAY)
+                       {
+                               bool bottom_valid = block->propagateSunlight(light_sources);
 
-                       // If bottom is valid, we're done.
-                       if(bottom_valid)
+                               // If bottom is valid, we're done.
+                               if(bottom_valid)
+                                       break;
+                       }
+                       else if(bank == LIGHTBANK_NIGHT)
+                       {
                                break;
+                       }
+                       else
+                       {
+                               assert(0);
+                       }
                                
                        /*dstream<<"Bottom for sunlight-propagated block ("
                                        <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
@@ -901,8 +711,8 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
        }
        
        {
-               //TimeTaker timer("unspreadLight", g_device);
-               unspreadLight(unlight_from, light_sources, modified_blocks);
+               //TimeTaker timer("unspreadLight");
+               unspreadLight(bank, unlight_from, light_sources, modified_blocks);
        }
        
        if(debug)
@@ -916,12 +726,12 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
        // Yes, add it to light_sources... somehow.
        // It has to be added at somewhere above, in the loop.
        // TODO
-       // NOTE: This actually works quite fine without it
+       // NOTE: This actually works fine without doing so
        //       - Find out why it works
 
        {
-               //TimeTaker timer("spreadLight", g_device);
-               spreadLight(light_sources, modified_blocks);
+               //TimeTaker timer("spreadLight");
+               spreadLight(bank, light_sources, modified_blocks);
        }
        
        if(debug)
@@ -934,6 +744,24 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
        //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
 }
 
+void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
+               core::map<v3s16, MapBlock*> & modified_blocks)
+{
+       updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks);
+       updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks);
+       
+       /*
+               Update information about whether day and night light differ
+       */
+       for(core::map<v3s16, MapBlock*>::Iterator
+                       i = modified_blocks.getIterator();
+                       i.atEnd() == false; i++)
+       {
+               MapBlock *block = i.getNode()->getValue();
+               block->updateDayNightDiff();
+       }
+}
+
 /*
        This is called after changing a node from transparent to opaque.
        The lighting value of the node should be left as-is after changing
@@ -948,12 +776,6 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
        m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
                        <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
 
-       u8 lightwas = getNode(p).getLight();
-
-       //core::list<v3s16> light_sources;
-       core::map<v3s16, bool> light_sources;
-       //MapNode n = getNode(p);
-
        /*
                From this node to nodes underneath:
                If lighting is sunlight (1.0), unlight neighbours and
@@ -961,9 +783,11 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                Else discontinue.
        */
 
-       bool node_under_sunlight = true;
-       
        v3s16 toppos = p + v3s16(0,1,0);
+       v3s16 bottompos = p + v3s16(0,-1,0);
+
+       bool node_under_sunlight = true;
+       core::map<v3s16, bool> light_sources;
 
        /*
                If there is a node at top and it doesn't have sunlight,
@@ -974,36 +798,71 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
        try{
                MapNode topnode = getNode(toppos);
 
-               if(topnode.getLight() != LIGHT_SUN)
+               if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
                        node_under_sunlight = false;
        }
        catch(InvalidPositionException &e)
        {
        }
-
-       // Add the block of the added node to modified_blocks
-       v3s16 blockpos = getNodeBlockPos(p);
-       MapBlock * block = getBlockNoCreate(blockpos);
-       assert(block != NULL);
-       modified_blocks.insert(blockpos, block);
        
-       if(isValidPosition(p) == false)
-               throw;
+       if(n.d != CONTENT_TORCH)
+       {
+               /*
+                       If there is grass below, change it to mud
+               */
+               try{
+                       MapNode bottomnode = getNode(bottompos);
+                       
+                       if(bottomnode.d == CONTENT_GRASS
+                                       || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
+                       {
+                               bottomnode.d = CONTENT_MUD;
+                               setNode(bottompos, bottomnode);
+                       }
+               }
+               catch(InvalidPositionException &e)
+               {
+               }
+       }
+
+       enum LightBank banks[] =
+       {
+               LIGHTBANK_DAY,
+               LIGHTBANK_NIGHT
+       };
+       for(s32 i=0; i<2; i++)
+       {
+               enum LightBank bank = banks[i];
+
+               u8 lightwas = getNode(p).getLight(bank);
+
+               // Add the block of the added node to modified_blocks
+               v3s16 blockpos = getNodeBlockPos(p);
+               MapBlock * block = getBlockNoCreate(blockpos);
+               assert(block != NULL);
+               modified_blocks.insert(blockpos, block);
                
-       // Unlight neighbours of node.
-       // This means setting light of all consequent dimmer nodes
-       // to 0.
-       // This also collects the nodes at the border which will spread
-       // light again into this.
-       unLightNeighbors(p, lightwas, light_sources, modified_blocks);
-
-       n.setLight(0);
+               if(isValidPosition(p) == false)
+                       throw;
+                       
+               // Unlight neighbours of node.
+               // This means setting light of all consequent dimmer nodes
+               // to 0.
+               // This also collects the nodes at the border which will spread
+               // light again into this.
+               unLightNeighbors(bank, p, lightwas, light_sources, modified_blocks);
+
+               n.setLight(bank, 0);
+       }
+       
        setNode(p, n);
        
        /*
                If node is under sunlight, take all sunlighted nodes under
                it and clear light from them and from where the light has
                been spread.
+               TODO: This could be optimized by mass-unlighting instead
+                     of looping
        */
        if(node_under_sunlight)
        {
@@ -1021,11 +880,13 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                                break;
                        }
 
-                       if(n2.getLight() == LIGHT_SUN)
+                       if(n2.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
                        {
                                //m_dout<<DTIME<<"doing"<<std::endl;
-                               unLightNeighbors(n2pos, n2.getLight(), light_sources, modified_blocks);
-                               n2.setLight(0);
+                               unLightNeighbors(LIGHTBANK_DAY,
+                                               n2pos, n2.getLight(LIGHTBANK_DAY),
+                                               light_sources, modified_blocks);
+                               n2.setLight(LIGHTBANK_DAY, 0);
                                setNode(n2pos, n2);
                        }
                        else
@@ -1033,11 +894,27 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                }
        }
        
+       for(s32 i=0; i<2; i++)
+       {
+               enum LightBank bank = banks[i];
+               
+               /*
+                       Spread light from all nodes that might be capable of doing so
+                       TODO: Convert to spreadLight
+               */
+               spreadLight(bank, light_sources, modified_blocks);
+       }
+
        /*
-               Spread light from all nodes that might be capable of doing so
-               TODO: Convert to spreadLight
+               Update information about whether day and night light differ
        */
-       spreadLight(light_sources, modified_blocks);
+       for(core::map<v3s16, MapBlock*>::Iterator
+                       i = modified_blocks.getIterator();
+                       i.atEnd() == false; i++)
+       {
+               MapBlock *block = i.getNode()->getValue();
+               block->updateDayNightDiff();
+       }
 }
 
 /*
@@ -1056,67 +933,6 @@ void Map::removeNodeAndUpdate(v3s16 p,
        // Node will be replaced with this
        u8 replace_material = CONTENT_AIR;
        
-       // NOTE: Water is now managed elsewhere
-#if 0
-       {
-               /*
-                       Find out with what material the node will be replaced.
-                       It will be replaced with the mostly seen buildable_to.
-               */
-
-               v3s16 dirs[6] = {
-                       v3s16(0,0,1), // back
-                       v3s16(0,1,0), // top
-                       v3s16(1,0,0), // right
-                       v3s16(0,0,-1), // front
-                       v3s16(0,-1,0), // bottom
-                       v3s16(-1,0,0), // left
-               };
-
-               core::map<u8, u16> neighbor_rankings;
-               
-               for(u32 i=0; i<sizeof(dirs)/sizeof(dirs[0]); i++)
-               {
-                       try{
-                               MapNode n2 = getNode(p + dirs[i]);
-
-                               if(material_buildable_to(n2.d))
-                               {
-                                       if(neighbor_rankings.find(n2.d) == NULL)
-                                               neighbor_rankings[n2.d] = 1;
-                                       else
-                                               neighbor_rankings[n2.d]
-                                                       = neighbor_rankings[n2.d] + 1;
-                               }
-                       }
-                       catch(InvalidPositionException &e)
-                       {
-                       }
-               }
-
-               u16 highest_ranking = 0;
-               
-               for(core::map<u8, u16>::Iterator
-                               i = neighbor_rankings.getIterator();
-                               i.atEnd() == false; i++)
-               {
-                       u8 m = i.getNode()->getKey();
-                       u8 c = i.getNode()->getValue();
-                       if(
-                                       c > highest_ranking ||
-                                       // Prefer something else than air
-                                       (c >= highest_ranking && m != CONTENT_AIR)
-
-                       )
-                       {
-                               replace_material = m;
-                               highest_ranking = c;
-                       }
-               }
-       }
-
-#endif
-
        /*
                If there is a node at top and it doesn't have sunlight,
                there will be no sunlight going down.
@@ -1124,33 +940,50 @@ void Map::removeNodeAndUpdate(v3s16 p,
        try{
                MapNode topnode = getNode(toppos);
 
-               if(topnode.getLight() != LIGHT_SUN)
+               if(topnode.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
                        node_under_sunlight = false;
        }
        catch(InvalidPositionException &e)
        {
        }
 
-       /*
-               Unlight neighbors (in case the node is a light source)
-       */
-       //core::list<v3s16> light_sources;
        core::map<v3s16, bool> light_sources;
-       unLightNeighbors(p, getNode(p).getLight(),
-                       light_sources, modified_blocks);
+
+       enum LightBank banks[] =
+       {
+               LIGHTBANK_DAY,
+               LIGHTBANK_NIGHT
+       };
+       for(s32 i=0; i<2; i++)
+       {
+               enum LightBank bank = banks[i];
+       
+               /*
+                       Unlight neighbors (in case the node is a light source)
+               */
+               unLightNeighbors(bank, p,
+                               getNode(p).getLight(bank),
+                               light_sources, modified_blocks);
+       }
 
        /*
-               Remove the node
+               Remove the node.
+               This also clears the lighting.
        */
+
        MapNode n;
        n.d = replace_material;
-       n.setLight(0);
        setNode(p, n);
        
-       /*
-               Recalculate lighting
-       */
-       spreadLight(light_sources, modified_blocks);
+       for(s32 i=0; i<2; i++)
+       {
+               enum LightBank bank = banks[i];
+       
+               /*
+                       Recalculate lighting
+               */
+               spreadLight(bank, light_sources, modified_blocks);
+       }
 
        // Add the block of the removed node to modified_blocks
        v3s16 blockpos = getNodeBlockPos(p);
@@ -1176,62 +1009,195 @@ void Map::removeNodeAndUpdate(v3s16 p,
                        /*m_dout<<DTIME<<"lighting neighbors of node ("
                                        <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
                                        <<std::endl;*/
-                       lightNeighbors(p2, modified_blocks);
+                       lightNeighbors(LIGHTBANK_DAY, p2, modified_blocks);
+               }
+       }
+       else
+       {
+               // Set the lighting of this node to 0
+               // TODO: Is this needed? Lighting is cleared up there already.
+               try{
+                       MapNode n = getNode(p);
+                       n.setLight(LIGHTBANK_DAY, 0);
+                       setNode(p, n);
+               }
+               catch(InvalidPositionException &e)
+               {
+                       throw;
+               }
+       }
+
+       for(s32 i=0; i<2; i++)
+       {
+               enum LightBank bank = banks[i];
+       
+               // Get the brightest neighbour node and propagate light from it
+               v3s16 n2p = getBrightestNeighbour(bank, p);
+               try{
+                       MapNode n2 = getNode(n2p);
+                       lightNeighbors(bank, n2p, modified_blocks);
+               }
+               catch(InvalidPositionException &e)
+               {
                }
        }
-       else
-       {
-               // Set the lighting of this node to 0
-               try{
-                       MapNode n = getNode(p);
-                       n.setLight(0);
-                       setNode(p, n);
-               }
-               catch(InvalidPositionException &e)
-               {
-                       throw;
-               }
+
+       /*
+               Update information about whether day and night light differ
+       */
+       for(core::map<v3s16, MapBlock*>::Iterator
+                       i = modified_blocks.getIterator();
+                       i.atEnd() == false; i++)
+       {
+               MapBlock *block = i.getNode()->getValue();
+               block->updateDayNightDiff();
+       }
+}
+
+#ifndef SERVER
+void Map::expireMeshes(bool only_daynight_diffed)
+{
+       TimeTaker timer("expireMeshes()");
+
+       core::map<v2s16, MapSector*>::Iterator si;
+       si = m_sectors.getIterator();
+       for(; si.atEnd() == false; si++)
+       {
+               MapSector *sector = si.getNode()->getValue();
+
+               core::list< MapBlock * > sectorblocks;
+               sector->getBlocks(sectorblocks);
+               
+               core::list< MapBlock * >::Iterator i;
+               for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
+               {
+                       MapBlock *block = *i;
+
+                       if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
+                       {
+                               continue;
+                       }
+                       
+                       {
+                               JMutexAutoLock lock(block->mesh_mutex);
+                               if(block->mesh != NULL)
+                               {
+                                       /*block->mesh->drop();
+                                       block->mesh = NULL;*/
+                                       block->setMeshExpired(true);
+                               }
+                       }
+               }
+       }
+}
+
+void Map::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
+{
+       assert(mapType() == MAPTYPE_CLIENT);
+
+       try{
+               v3s16 p = blockpos + v3s16(0,0,0);
+               MapBlock *b = getBlockNoCreate(p);
+               b->updateMesh(daynight_ratio);
+       }
+       catch(InvalidPositionException &e){}
+       // Leading edge
+       try{
+               v3s16 p = blockpos + v3s16(-1,0,0);
+               MapBlock *b = getBlockNoCreate(p);
+               b->updateMesh(daynight_ratio);
+       }
+       catch(InvalidPositionException &e){}
+       try{
+               v3s16 p = blockpos + v3s16(0,-1,0);
+               MapBlock *b = getBlockNoCreate(p);
+               b->updateMesh(daynight_ratio);
+       }
+       catch(InvalidPositionException &e){}
+       try{
+               v3s16 p = blockpos + v3s16(0,0,-1);
+               MapBlock *b = getBlockNoCreate(p);
+               b->updateMesh(daynight_ratio);
        }
-
-       // Get the brightest neighbour node and propagate light from it
-       v3s16 n2p = getBrightestNeighbour(p);
+       catch(InvalidPositionException &e){}
+       /*// Trailing edge
        try{
-               MapNode n2 = getNode(n2p);
-               lightNeighbors(n2p, modified_blocks);
+               v3s16 p = blockpos + v3s16(1,0,0);
+               MapBlock *b = getBlockNoCreate(p);
+               b->updateMesh(daynight_ratio);
        }
-       catch(InvalidPositionException &e)
-       {
+       catch(InvalidPositionException &e){}
+       try{
+               v3s16 p = blockpos + v3s16(0,1,0);
+               MapBlock *b = getBlockNoCreate(p);
+               b->updateMesh(daynight_ratio);
+       }
+       catch(InvalidPositionException &e){}
+       try{
+               v3s16 p = blockpos + v3s16(0,0,1);
+               MapBlock *b = getBlockNoCreate(p);
+               b->updateMesh(daynight_ratio);
        }
+       catch(InvalidPositionException &e){}*/
 }
 
-void Map::updateMeshes(v3s16 blockpos)
-{
-       assert(mapType() == MAPTYPE_CLIENT);
+#endif
 
+bool Map::dayNightDiffed(v3s16 blockpos)
+{
        try{
                v3s16 p = blockpos + v3s16(0,0,0);
                MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh();
+               if(b->dayNightDiffed())
+                       return true;
        }
        catch(InvalidPositionException &e){}
+       // Leading edges
        try{
                v3s16 p = blockpos + v3s16(-1,0,0);
                MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh();
+               if(b->dayNightDiffed())
+                       return true;
        }
        catch(InvalidPositionException &e){}
        try{
                v3s16 p = blockpos + v3s16(0,-1,0);
                MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh();
+               if(b->dayNightDiffed())
+                       return true;
        }
        catch(InvalidPositionException &e){}
        try{
                v3s16 p = blockpos + v3s16(0,0,-1);
                MapBlock *b = getBlockNoCreate(p);
-               b->updateMesh();
+               if(b->dayNightDiffed())
+                       return true;
+       }
+       catch(InvalidPositionException &e){}
+       // Trailing edges
+       try{
+               v3s16 p = blockpos + v3s16(1,0,0);
+               MapBlock *b = getBlockNoCreate(p);
+               if(b->dayNightDiffed())
+                       return true;
+       }
+       catch(InvalidPositionException &e){}
+       try{
+               v3s16 p = blockpos + v3s16(0,1,0);
+               MapBlock *b = getBlockNoCreate(p);
+               if(b->dayNightDiffed())
+                       return true;
+       }
+       catch(InvalidPositionException &e){}
+       try{
+               v3s16 p = blockpos + v3s16(0,0,1);
+               MapBlock *b = getBlockNoCreate(p);
+               if(b->dayNightDiffed())
+                       return true;
        }
        catch(InvalidPositionException &e){}
+
+       return false;
 }
 
 /*
@@ -1568,12 +1534,12 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
                        tree_max = a / (t/0.03);
                else
                        tree_max = a;
-               u32 count = (rand()%(tree_max+1));
+               u32 count = (myrand()%(tree_max+1));
                //u32 count = tree_max;
                for(u32 i=0; i<count; i++)
                {
-                       s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
-                       s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
+                       s16 x = (myrand()%(MAP_BLOCKSIZE-2))+1;
+                       s16 z = (myrand()%(MAP_BLOCKSIZE-2))+1;
                        s16 y = sector->getGroundHeight(v2s16(x,z))+1;
                        if(y < WATER_LEVEL)
                                continue;
@@ -1592,11 +1558,11 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
                        bush_max = (pitness*a*4);
                if(bush_max > a)
                        bush_max = a;
-               u32 count = (rand()%(bush_max+1));
+               u32 count = (myrand()%(bush_max+1));
                for(u32 i=0; i<count; i++)
                {
-                       s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
-                       s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
+                       s16 x = myrand()%(MAP_BLOCKSIZE-0)+0;
+                       s16 z = myrand()%(MAP_BLOCKSIZE-0)+0;
                        s16 y = sector->getGroundHeight(v2s16(x,z))+1;
                        if(y < WATER_LEVEL)
                                continue;
@@ -1609,11 +1575,11 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
        */
        if(m_params.ravines_amount != 0)
        {
-               if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
+               if(myrand()%(s32)(20.0 / m_params.ravines_amount) == 0)
                {
                        s16 s = 6;
-                       s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
-                       s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
+                       s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
+                       s16 z = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
                        /*s16 x = 8;
                        s16 z = 8;*/
                        s16 y = sector->getGroundHeight(v2s16(x,z))+1;
@@ -1691,6 +1657,7 @@ MapBlock * ServerMap::emergeBlock(
        }
 
        //dstream<<"Not found on disk, generating."<<std::endl;
+       //TimeTaker("emergeBlock()", g_irrlicht);
 
        /*
                Do not generate over-limit
@@ -1723,24 +1690,25 @@ MapBlock * ServerMap::emergeBlock(
                // Allocate the block to be a proper one.
                block->unDummify();
        }
-
-       // Randomize a bit. This makes dungeons.
-       /*bool low_block_is_empty = false;
-       if(rand() % 4 == 0)
-               low_block_is_empty = true;*/
        
-       const s32 ued = 4;
-       //const s32 ued = 8;
-       bool underground_emptiness[ued*ued*ued];
+#if 0
+       /*
+               Initialize dungeon making by creating a random table
+       */
+       const s32 ued_max = 5;
+       const s32 ued_min = 3;
+       bool underground_emptiness[ued_max*ued_max*ued_max];
+       s32 ued = (myrand()%(ued_max-ued_min+1))+1;
+       //s32 ued = ued_max;
        for(s32 i=0; i<ued*ued*ued; i++)
        {
-               underground_emptiness[i] = ((rand() % 5) == 0);
+               underground_emptiness[i] = ((myrand() % 5) == 0);
        }
 
-#if 0
        /*
                This is a messy hack to sort the emptiness a bit
        */
+       // Iterator through a few times
        for(s32 j=0; j<2; j++)
        for(s32 y0=0; y0<ued; y0++)
        for(s32 z0=0; z0<ued; z0++)
@@ -1760,6 +1728,7 @@ MapBlock * ServerMap::emergeBlock(
                        /*v3s16(0,1,0), // top
                        v3s16(0,-1,0), // bottom*/
                };
+
                for(s32 i=0; i<4; i++)
                {
                        v3s16 p1 = p0 + dirs[i];
@@ -1804,6 +1773,144 @@ MapBlock * ServerMap::emergeBlock(
                }
        }
 #endif
+
+       /*
+               Create dungeon making table
+       */
+       const s32 ued = MAP_BLOCKSIZE;
+       bool underground_emptiness[ued*ued*ued];
+       for(s32 i=0; i<ued*ued*ued; i++)
+       {
+               underground_emptiness[i] = 0;
+       }
+       // Generate dungeons
+       {
+               /*
+                       Initialize orp and ors. Try to find if some neighboring
+                       MapBlock has a tunnel ended in its side
+               */
+
+               v3f orp(
+                       (float)(myrand()%ued)+0.5,
+                       (float)(myrand()%ued)+0.5,
+                       (float)(myrand()%ued)+0.5
+               );
+
+               // Check z-
+               try
+               {
+                       s16 z = -1;
+                       for(s16 y=0; y<ued; y++)
+                       for(s16 x=0; x<ued; x++)
+                       {
+                               v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
+                               if(getNode(ap).d == CONTENT_AIR)
+                               {
+                                       orp = v3f(x+1,y+1,0);
+                                       goto continue_generating;
+                               }
+                       }
+               }
+               catch(InvalidPositionException &e){}
+               
+               // Check z+
+               try
+               {
+                       s16 z = ued;
+                       for(s16 y=0; y<ued; y++)
+                       for(s16 x=0; x<ued; x++)
+                       {
+                               v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
+                               if(getNode(ap).d == CONTENT_AIR)
+                               {
+                                       orp = v3f(x+1,y+1,ued-1);
+                                       goto continue_generating;
+                               }
+                       }
+               }
+               catch(InvalidPositionException &e){}
+               
+               // Check x-
+               try
+               {
+                       s16 x = -1;
+                       for(s16 y=0; y<ued; y++)
+                       for(s16 z=0; z<ued; z++)
+                       {
+                               v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
+                               if(getNode(ap).d == CONTENT_AIR)
+                               {
+                                       orp = v3f(0,y+1,z+1);
+                                       goto continue_generating;
+                               }
+                       }
+               }
+               catch(InvalidPositionException &e){}
+               
+               // Check x+
+               try
+               {
+                       s16 x = ued;
+                       for(s16 y=0; y<ued; y++)
+                       for(s16 z=0; z<ued; z++)
+                       {
+                               v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
+                               if(getNode(ap).d == CONTENT_AIR)
+                               {
+                                       orp = v3f(ued-1,y+1,z+1);
+                                       goto continue_generating;
+                               }
+                       }
+               }
+               catch(InvalidPositionException &e){}
+
+continue_generating:
+               
+               /*
+                       Generate some tunnel starting from orp and ors
+               */
+               for(u16 i=0; i<3; i++)
+               {
+                       v3f rp(
+                               (float)(myrand()%ued)+0.5,
+                               (float)(myrand()%ued)+0.5,
+                               (float)(myrand()%ued)+0.5
+                       );
+                       s16 min_d = 0;
+                       s16 max_d = 4;
+                       s16 rs = (myrand()%(max_d-min_d+1))+min_d;
+                       
+                       v3f vec = rp - orp;
+
+                       for(float f=0; f<1.0; f+=0.04)
+                       {
+                               v3f fp = orp + vec * f;
+                               v3s16 cp(fp.X, fp.Y, fp.Z);
+                               s16 d0 = -rs/2;
+                               s16 d1 = d0 + rs - 1;
+                               for(s16 z0=d0; z0<=d1; z0++)
+                               {
+                                       s16 si = rs - abs(z0);
+                                       for(s16 x0=-si; x0<=si-1; x0++)
+                                       {
+                                               s16 si2 = rs - abs(x0);
+                                               for(s16 y0=-si2+1; y0<=si2-1; y0++)
+                                               {
+                                                       s16 z = cp.Z + z0;
+                                                       s16 y = cp.Y + y0;
+                                                       s16 x = cp.X + x0;
+                                                       v3s16 p(x,y,z);
+                                                       if(isInArea(p, ued) == false)
+                                                               continue;
+                                                       underground_emptiness[ued*ued*z + ued*y + x] = 1;
+                                               }
+                                       }
+                               }
+                       }
+
+                       orp = rp;
+               }
+       }
        
        // This is the basic material of what the visible flat ground
        // will consist of
@@ -1868,32 +1975,12 @@ MapBlock * ServerMap::emergeBlock(
                                newly created block, they won't be taken into account.
                        */
                        if(real_y > surface_y)
-                               n.setLight(LIGHT_SUN);
+                               n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
 
                        /*
                                Calculate material
                        */
 
-                       // If node is very low
-                       /*if(real_y <= surface_y - 7)
-                       {
-                               // Create dungeons
-                               if(underground_emptiness[
-                                               ued*ued*(z0*ued/MAP_BLOCKSIZE)
-                                               +ued*(y0*ued/MAP_BLOCKSIZE)
-                                               +(x0*ued/MAP_BLOCKSIZE)])
-                               {
-                                       n.d = CONTENT_AIR;
-                               }
-                               else
-                               {
-                                       n.d = CONTENT_STONE;
-                               }
-                       }
-                       // If node is under surface level
-                       else if(real_y <= surface_y - surface_depth)
-                               n.d = CONTENT_STONE;
-                       */
                        if(real_y <= surface_y - surface_depth)
                        {
                                // Create dungeons
@@ -1928,7 +2015,8 @@ MapBlock * ServerMap::emergeBlock(
                                if(real_y < WATER_LEVEL)
                                {
                                        n.d = water_material;
-                                       n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
+                                       n.setLight(LIGHTBANK_DAY,
+                                                       diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
                                }
                                // else air
                                else
@@ -1962,18 +2050,21 @@ MapBlock * ServerMap::emergeBlock(
                Add some minerals
        */
 
-       //if(is_underground)
        if(some_part_underground)
        {
                s16 underground_level = (lowest_ground_y/MAP_BLOCKSIZE - block_y)+1;
-               for(s16 i=0; i<underground_level*3; i++)
+
+               /*
+                       Add meseblocks
+               */
+               for(s16 i=0; i<underground_level*1; i++)
                {
-                       if(rand()%2 == 0)
+                       if(myrand()%2 == 0)
                        {
                                v3s16 cp(
-                                       (rand()%(MAP_BLOCKSIZE-2))+1,
-                                       (rand()%(MAP_BLOCKSIZE-2))+1,
-                                       (rand()%(MAP_BLOCKSIZE-2))+1
+                                       (myrand()%(MAP_BLOCKSIZE-2))+1,
+                                       (myrand()%(MAP_BLOCKSIZE-2))+1,
+                                       (myrand()%(MAP_BLOCKSIZE-2))+1
                                );
 
                                MapNode n;
@@ -1981,14 +2072,53 @@ MapBlock * ServerMap::emergeBlock(
                                
                                //if(is_ground_content(block->getNode(cp).d))
                                if(block->getNode(cp).d == CONTENT_STONE)
-                                       if(rand()%8 == 0)
+                                       if(myrand()%8 == 0)
+                                               block->setNode(cp, n);
+
+                               for(u16 i=0; i<26; i++)
+                               {
+                                       //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
+                                       if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
+                                               if(myrand()%8 == 0)
+                                                       block->setNode(cp+g_26dirs[i], n);
+                               }
+                       }
+               }
+
+               /*
+                       Add coal
+               */
+               u16 coal_amount = 30.0 * g_settings.getFloat("coal_amount");
+               u16 coal_rareness = 60 / coal_amount;
+               if(coal_rareness == 0)
+                       coal_rareness = 1;
+               if(myrand()%coal_rareness == 0)
+               {
+                       u16 a = myrand() % 16;
+                       u16 amount = coal_amount * a*a*a / 1000;
+                       for(s16 i=0; i<amount; i++)
+                       {
+                               v3s16 cp(
+                                       (myrand()%(MAP_BLOCKSIZE-2))+1,
+                                       (myrand()%(MAP_BLOCKSIZE-2))+1,
+                                       (myrand()%(MAP_BLOCKSIZE-2))+1
+                               );
+
+                               MapNode n;
+                               n.d = CONTENT_COALSTONE;
+
+                               //dstream<<"Adding coalstone"<<std::endl;
+                               
+                               //if(is_ground_content(block->getNode(cp).d))
+                               if(block->getNode(cp).d == CONTENT_STONE)
+                                       if(myrand()%8 == 0)
                                                block->setNode(cp, n);
 
                                for(u16 i=0; i<26; i++)
                                {
                                        //if(is_ground_content(block->getNode(cp+g_26dirs[i]).d))
                                        if(block->getNode(cp+g_26dirs[i]).d == CONTENT_STONE)
-                                               if(rand()%8 == 0)
+                                               if(myrand()%8 == 0)
                                                        block->setNode(cp+g_26dirs[i], n);
                                }
                        }
@@ -2003,9 +2133,9 @@ MapBlock * ServerMap::emergeBlock(
                //for(u16 i=0; i<2; i++)
                {
                        v3s16 cp(
-                               (rand()%(MAP_BLOCKSIZE-2))+1,
-                               (rand()%(MAP_BLOCKSIZE-2))+1,
-                               (rand()%(MAP_BLOCKSIZE-2))+1
+                               (myrand()%(MAP_BLOCKSIZE-2))+1,
+                               (myrand()%(MAP_BLOCKSIZE-2))+1,
+                               (myrand()%(MAP_BLOCKSIZE-2))+1
                        );
 
                        // Check that the place is empty
@@ -2023,42 +2153,6 @@ MapBlock * ServerMap::emergeBlock(
        */
        sector->insertBlock(block);
        
-       /*
-               Do some interpolation for dungeons
-       */
-
-#if 0  
-       {
-       TimeTaker timer("interpolation", g_device);
-       
-       MapVoxelManipulator vmanip(this);
-       
-       v3s16 relpos = block->getPosRelative();
-
-       vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
-                       relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
-       /*vmanip.interpolate(VoxelArea(relpos,
-                       relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
-       
-       core::map<v3s16, MapBlock*> modified_blocks;
-       vmanip.blitBack(modified_blocks);
-       dstream<<"blitBack modified "<<modified_blocks.size()
-                       <<" blocks"<<std::endl;
-
-       // Add modified blocks to changed_blocks and lighting_invalidated_blocks
-       for(core::map<v3s16, MapBlock*>::Iterator
-                       i = modified_blocks.getIterator();
-                       i.atEnd() == false; i++)
-       {
-               MapBlock *block = i.getNode()->getValue();
-
-               changed_blocks.insert(block->getPos(), block);
-               //lighting_invalidated_blocks.insert(block->getPos(), block);
-       }
-
-       }
-#endif
-
        /*
                Sector object stuff
        */
@@ -2162,39 +2256,43 @@ MapBlock * ServerMap::emergeBlock(
                                n.d = CONTENT_STONE;
                                MapNode n2;
                                n2.d = CONTENT_AIR;
-                               s16 depth = maxdepth + (rand()%10);
+                               s16 depth = maxdepth + (myrand()%10);
                                s16 z = 0;
                                s16 minz = -6 - (-2);
                                s16 maxz = 6 -1;
                                for(s16 x=-6; x<=6; x++)
                                {
-                                       z += -1 + (rand()%3);
+                                       z += -1 + (myrand()%3);
                                        if(z < minz)
                                                z = minz;
                                        if(z > maxz)
                                                z = maxz;
-                                       for(s16 y=depth+(rand()%2); y<=6; y++)
+                                       for(s16 y=depth+(myrand()%2); y<=6; y++)
                                        {
                                                /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
                                                                <<std::endl;*/
                                                {
                                                        v3s16 p2 = p + v3s16(x,y,z-2);
-                                                       if(is_ground_content(sector->getNode(p2).d))
+                                                       if(is_ground_content(sector->getNode(p2).d)
+                                                                       && !is_mineral(sector->getNode(p2).d))
                                                                sector->setNode(p2, n);
                                                }
                                                {
                                                        v3s16 p2 = p + v3s16(x,y,z-1);
-                                                       if(is_ground_content(sector->getNode(p2).d))
+                                                       if(is_ground_content(sector->getNode(p2).d)
+                                                                       && !is_mineral(sector->getNode(p2).d))
                                                                sector->setNode(p2, n2);
                                                }
                                                {
                                                        v3s16 p2 = p + v3s16(x,y,z+0);
-                                                       if(is_ground_content(sector->getNode(p2).d))
+                                                       if(is_ground_content(sector->getNode(p2).d)
+                                                                       && !is_mineral(sector->getNode(p2).d))
                                                                sector->setNode(p2, n2);
                                                }
                                                {
                                                        v3s16 p2 = p + v3s16(x,y,z+1);
-                                                       if(is_ground_content(sector->getNode(p2).d))
+                                                       if(is_ground_content(sector->getNode(p2).d)
+                                                                       && !is_mineral(sector->getNode(p2).d))
                                                                sector->setNode(p2, n);
                                                }
 
@@ -2359,20 +2457,15 @@ void ServerMap::save(bool only_changed)
 
        }//sectorlock
        
-       u32 deleted_count = 0;
-       deleted_count = deleteUnusedSectors
-                       (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
-       
        /*
                Only print if something happened or saved whole map
        */
        if(only_changed == false || sector_meta_count != 0
-                       || block_count != 0 || deleted_count != 0)
+                       || block_count != 0)
        {
                dstream<<DTIME<<"ServerMap: Written: "
                                <<sector_meta_count<<" sector metadata files, "
-                               <<block_count<<" block files, "
-                               <<deleted_count<<" sectors unloaded from memory."
+                               <<block_count<<" block files"
                                <<std::endl;
        }
 }
@@ -2695,7 +2788,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
        */
        if(version >= 9)
        {
-               block->updateObjects(is, version, NULL);
+               block->updateObjects(is, version, NULL, 0);
        }
 
        if(created_new)
@@ -2749,12 +2842,15 @@ void ServerMap::PrintInfo(std::ostream &out)
        out<<"ServerMap: ";
 }
 
+#ifndef SERVER
+
 /*
        ClientMap
 */
 
 ClientMap::ClientMap(
                Client *client,
+               MapDrawControl &control,
                scene::ISceneNode* parent,
                scene::ISceneManager* mgr,
                s32 id
@@ -2762,8 +2858,11 @@ ClientMap::ClientMap(
        Map(dout_client),
        scene::ISceneNode(parent, mgr, id),
        m_client(client),
-       mesh(NULL)
+       mesh(NULL),
+       m_control(control)
 {
+       mesh_mutex.Init();
+
        /*m_box = core::aabbox3d<f32>(0,0,0,
                        map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
        /*m_box = core::aabbox3d<f32>(0,0,0,
@@ -2772,8 +2871,8 @@ ClientMap::ClientMap(
                        map->getSizeNodes().Z * BS);*/
        m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
                        BS*1000000,BS*1000000,BS*1000000);
-
-       mesh_mutex.Init();
+       
+       //setPosition(v3f(BS,BS,BS));
 }
 
 ClientMap::~ClientMap()
@@ -2861,20 +2960,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
        */
        int time1 = time(0);
 
-       /*
-               Collect all blocks that are in the view range
-
-               Should not optimize more here as we want to auto-update
-               all changed nodes in viewing range at the next step.
-       */
-
-       s16 viewing_range_nodes;
-       bool viewing_range_all;
-       {
-               JMutexAutoLock lock(g_range_mutex);
-               viewing_range_nodes = g_viewing_range_nodes;
-               viewing_range_all = g_viewing_range_all;
-       }
+       u32 daynight_ratio = m_client->getDayNightRatio();
 
        m_camera_mutex.Lock();
        v3f camera_position = m_camera_position;
@@ -2890,7 +2976,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        camera_position.Y / BS,
                        camera_position.Z / BS);
 
-       v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
+       v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
 
        v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
        v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
@@ -2907,16 +2993,21 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
        
        u32 vertex_count = 0;
        
-       core::map<v2s16, MapSector*>::Iterator si;
+       // For limiting number of mesh updates per frame
+       u32 mesh_update_count = 0;
+       
+       u32 blocks_would_have_drawn = 0;
+       u32 blocks_drawn = 0;
 
        //NOTE: The sectors map should be locked but we're not doing it
        // because it'd cause too much delays
 
+       int timecheck_counter = 0;
+       core::map<v2s16, MapSector*>::Iterator si;
        si = m_sectors.getIterator();
        for(; si.atEnd() == false; si++)
        {
                {
-                       static int timecheck_counter = 0;
                        timecheck_counter++;
                        if(timecheck_counter > 50)
                        {
@@ -2934,7 +3025,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                MapSector *sector = si.getNode()->getValue();
                v2s16 sp = sector->getPos();
                
-               if(viewing_range_all == false)
+               if(m_control.range_all == false)
                {
                        if(sp.X < p_blocks_min.X
                        || sp.X > p_blocks_max.X
@@ -2978,12 +3069,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        // Total distance
                        f32 d = blockpos_relative.getLength();
                        
-                       if(viewing_range_all == false)
+                       if(m_control.range_all == false)
                        {
                                // If block is far away, don't draw it
-                               if(d > viewing_range_nodes * BS)
+                               if(d > m_control.wanted_range * BS)
                                // This is nicer when fog is used
-                               //if((dforward+d)/2 > viewing_range_nodes * BS)
+                               //if((dforward+d)/2 > m_control.wanted_range * BS)
                                        continue;
                        }
                        
@@ -3012,19 +3103,76 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        /*
                                Draw the faces of the block
                        */
+#if 1
+                       bool mesh_expired = false;
+                       
+                       {
+                               JMutexAutoLock lock(block->mesh_mutex);
+
+                               mesh_expired = block->getMeshExpired();
+
+                               // Mesh has not been expired and there is no mesh:
+                               // block has no content
+                               if(block->mesh == NULL && mesh_expired == false)
+                                       continue;
+                       }
+
+                       f32 faraway = BS*50;
+                       //f32 faraway = m_control.wanted_range * BS;
+                       
+                       /*
+                               This has to be done with the mesh_mutex unlocked
+                       */
+                       if(mesh_expired && mesh_update_count < 6
+                                       && (d < faraway || mesh_update_count < 3))
+                       //if(mesh_expired && mesh_update_count < 4)
+                       {
+                               mesh_update_count++;
+
+                               // Mesh has been expired: generate new mesh
+                               //block->updateMeshes(daynight_i);
+                               block->updateMesh(daynight_ratio);
+
+                               mesh_expired = false;
+                       }
                        
+                       /*
+                               Don't draw an expired mesh that is far away
+                       */
+                       /*if(mesh_expired && d >= faraway)
+                       //if(mesh_expired)
+                       {
+                               // Instead, delete it
+                               JMutexAutoLock lock(block->mesh_mutex);
+                               if(block->mesh)
+                               {
+                                       block->mesh->drop();
+                                       block->mesh = NULL;
+                               }
+                               // And continue to next block
+                               continue;
+                       }*/
+#endif
                        {
                                JMutexAutoLock lock(block->mesh_mutex);
 
-                               // Cancel if block has no mesh
-                               if(block->mesh == NULL)
+                               scene::SMesh *mesh = block->mesh;
+
+                               if(mesh == NULL)
+                                       continue;
+                               
+                               blocks_would_have_drawn++;
+                               if(blocks_drawn >= m_control.wanted_max_blocks
+                                               && m_control.range_all == false
+                                               && d > m_control.wanted_min_range * BS)
                                        continue;
+                               blocks_drawn++;
 
-                               u32 c = block->mesh->getMeshBufferCount();
+                               u32 c = mesh->getMeshBufferCount();
 
                                for(u32 i=0; i<c; i++)
                                {
-                                       scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
+                                       scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
                                        const video::SMaterial& material = buf->getMaterial();
                                        video::IMaterialRenderer* rnd =
                                                        driver->getMaterialRenderer(material.MaterialType);
@@ -3040,14 +3188,66 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        }
                } // foreach sectorblocks
        }
+       
+       m_control.blocks_drawn = blocks_drawn;
+       m_control.blocks_would_have_drawn = blocks_would_have_drawn;
 
        /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
                        <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
 }
 
-void ClientMap::updateMesh()
+v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
+{
+       /*
+               Add it to all blocks touching it
+       */
+       v3s16 dirs[7] = {
+               v3s16(0,0,0), // this
+               v3s16(0,0,1), // back
+               v3s16(0,1,0), // top
+               v3s16(1,0,0), // right
+               v3s16(0,0,-1), // front
+               v3s16(0,-1,0), // bottom
+               v3s16(-1,0,0), // left
+       };
+       for(u16 i=0; i<7; i++)
+       {
+               v3s16 p2 = p + dirs[i];
+               // Block position of neighbor (or requested) node
+               v3s16 blockpos = getNodeBlockPos(p2);
+               MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
+               if(blockref == NULL)
+                       continue;
+               // Relative position of requested node
+               v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
+               blockref->setTempMod(relpos, mod);
+       }
+       return getNodeBlockPos(p);
+}
+v3s16 ClientMap::clearTempMod(v3s16 p)
 {
-       //TODO: Remove this
+       v3s16 dirs[7] = {
+               v3s16(0,0,0), // this
+               v3s16(0,0,1), // back
+               v3s16(0,1,0), // top
+               v3s16(1,0,0), // right
+               v3s16(0,0,-1), // front
+               v3s16(0,-1,0), // bottom
+               v3s16(-1,0,0), // left
+       };
+       for(u16 i=0; i<7; i++)
+       {
+               v3s16 p2 = p + dirs[i];
+               // Block position of neighbor (or requested) node
+               v3s16 blockpos = getNodeBlockPos(p2);
+               MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
+               if(blockref == NULL)
+                       continue;
+               // Relative position of requested node
+               v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
+               blockref->clearTempMod(relpos);
+       }
+       return getNodeBlockPos(p);
 }
 
 void ClientMap::PrintInfo(std::ostream &out)
@@ -3055,6 +3255,7 @@ void ClientMap::PrintInfo(std::ostream &out)
        out<<"ClientMap: ";
 }
 
+#endif // !SERVER
 
 /*
        MapVoxelManipulator
@@ -3074,7 +3275,7 @@ MapVoxelManipulator::~MapVoxelManipulator()
 #if 1
 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
 {
-       TimeTaker timer1("emerge", g_device, &emerge_time);
+       TimeTaker timer1("emerge", &emerge_time);
 
        // Units of these are MapBlocks
        v3s16 p_min = getNodeBlockPos(a.MinEdge);
@@ -3098,7 +3299,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
                bool block_data_inexistent = false;
                try
                {
-                       TimeTaker timer1("emerge load", g_device, &emerge_load_time);
+                       TimeTaker timer1("emerge load", &emerge_load_time);
 
                        /*dstream<<"Loading block (caller_id="<<caller_id<<")"
                                        <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
@@ -3139,7 +3340,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
 #if 0
 void MapVoxelManipulator::emerge(VoxelArea a)
 {
-       TimeTaker timer1("emerge", g_device, &emerge_time);
+       TimeTaker timer1("emerge", &emerge_time);
        
        v3s16 size = a.getExtent();
        
@@ -3158,7 +3359,7 @@ void MapVoxelManipulator::emerge(VoxelArea a)
                        continue;
                try
                {
-                       TimeTaker timer1("emerge load", g_device, &emerge_load_time);
+                       TimeTaker timer1("emerge load", &emerge_load_time);
                        MapNode n = m_map->getNode(a.MinEdge + p);
                        m_data[i] = n;
                        m_flags[i] = 0;
@@ -3183,7 +3384,7 @@ void MapVoxelManipulator::blitBack
        if(m_area.getExtent() == v3s16(0,0,0))
                return;
        
-       //TimeTaker timer1("blitBack", g_device);
+       //TimeTaker timer1("blitBack");
        
        /*
                Initialize block cache