]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/map.cpp
.
[dragonfireclient.git] / src / map.cpp
index f5e490ad4ccaaa44c833b98d30b6edba1b24a5e9..1889cccf69cd2511efcafcc73dd0a362eada80f2 100644 (file)
@@ -35,8 +35,7 @@ Map::Map(std::ostream &dout):
        m_camera_position(0,0,0),
        m_camera_direction(0,0,1),
        m_sector_cache(NULL),
-       m_hwrapper(this),
-       drawoffset(0,0,0)
+       m_hwrapper(this)
 {
        m_sector_mutex.Init();
        m_camera_mutex.Init();
@@ -927,6 +926,37 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                MapBlock *block = i.getNode()->getValue();
                block->updateDayNightDiff();
        }
+
+       /*
+               Add neighboring liquid nodes and the node itself if it is
+               liquid (=water node was added) to transform queue.
+       */
+       v3s16 dirs[7] = {
+               v3s16(0,0,0), // self
+               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++)
+       {
+               try
+               {
+
+               v3s16 p2 = p + dirs[i];
+               
+               MapNode n2 = getNode(p2);
+               if(content_liquid(n2.d))
+               {
+                       m_transforming_liquid.push_back(p2);
+               }
+               
+               }catch(InvalidPositionException &e)
+               {
+               }
+       }
 }
 
 /*
@@ -1064,6 +1094,35 @@ void Map::removeNodeAndUpdate(v3s16 p,
                MapBlock *block = i.getNode()->getValue();
                block->updateDayNightDiff();
        }
+
+       /*
+               Add neighboring liquid nodes to transform queue.
+       */
+       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
+       };
+       for(u16 i=0; i<6; i++)
+       {
+               try
+               {
+
+               v3s16 p2 = p + dirs[i];
+               
+               MapNode n2 = getNode(p2);
+               if(content_liquid(n2.d))
+               {
+                       m_transforming_liquid.push_back(p2);
+               }
+               
+               }catch(InvalidPositionException &e)
+               {
+               }
+       }
 }
 
 #ifndef SERVER
@@ -1236,7 +1295,7 @@ void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
                
                This disables the existence of caches while locked
        */
-       SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
+       //SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
 
        core::list<v2s16>::Iterator j;
        for(j=list.begin(); j!=list.end(); j++)
@@ -1304,6 +1363,276 @@ void Map::PrintInfo(std::ostream &out)
        out<<"Map: ";
 }
 
+#define WATER_DROP_BOOST 4
+
+void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
+{
+       DSTACK(__FUNCTION_NAME);
+       //TimeTaker timer("transformLiquids()");
+
+       u32 loopcount = 0;
+       u32 initial_size = m_transforming_liquid.size();
+       while(m_transforming_liquid.size() != 0)
+       {
+               v3s16 p0 = m_transforming_liquid.pop_front();
+
+               MapNode n0 = getNode(p0);
+               
+               // Don't deal with non-liquids
+               if(content_liquid(n0.d) == false)
+                       continue;
+
+               bool is_source = !content_flowing_liquid(n0.d);
+               
+               u8 liquid_level = 8;
+               if(is_source == false)
+                       liquid_level = n0.param2 & 0x0f;
+               
+               // Turn possible source into non-source
+               u8 nonsource_c = make_liquid_flowing(n0.d);
+
+               /*
+                       If not source, check that some node flows into this one
+                       and what is the level of liquid in this one
+               */
+               if(is_source == false)
+               {
+                       s8 new_liquid_level_max = -1;
+
+                       v3s16 dirs_from[5] = {
+                               v3s16(0,1,0), // top
+                               v3s16(0,0,1), // back
+                               v3s16(1,0,0), // right
+                               v3s16(0,0,-1), // front
+                               v3s16(-1,0,0), // left
+                       };
+                       for(u16 i=0; i<5; i++)
+                       {
+                               try
+                               {
+
+                               bool from_top = (i==0);
+
+                               v3s16 p2 = p0 + dirs_from[i];
+                               MapNode n2 = getNode(p2);
+
+                               if(content_liquid(n2.d))
+                               {
+                                       u8 n2_nonsource_c = make_liquid_flowing(n2.d);
+                                       // Check that the liquids are the same type
+                                       if(n2_nonsource_c != nonsource_c)
+                                       {
+                                               dstream<<"WARNING: Not handling: different liquids"
+                                                               " collide"<<std::endl;
+                                               continue;
+                                       }
+                                       bool n2_is_source = !content_flowing_liquid(n2.d);
+                                       s8 n2_liquid_level = 8;
+                                       if(n2_is_source == false)
+                                               n2_liquid_level = n2.param2 & 0x07;
+                                       
+                                       s8 new_liquid_level = -1;
+                                       if(from_top)
+                                       {
+                                               //new_liquid_level = 7;
+                                               if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
+                                                       new_liquid_level = 7;
+                                               else
+                                                       new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
+                                       }
+                                       else if(n2_liquid_level > 0)
+                                       {
+                                               new_liquid_level = n2_liquid_level - 1;
+                                       }
+
+                                       if(new_liquid_level > new_liquid_level_max)
+                                               new_liquid_level_max = new_liquid_level;
+                               }
+
+                               }catch(InvalidPositionException &e)
+                               {
+                               }
+                       } //for
+                       
+                       /*
+                               If liquid level should be something else, update it and
+                               add all the neighboring water nodes to the transform queue.
+                       */
+                       if(new_liquid_level_max != liquid_level)
+                       {
+                               if(new_liquid_level_max == -1)
+                               {
+                                       // Remove water alltoghether
+                                       n0.d = CONTENT_AIR;
+                                       n0.param2 = 0;
+                                       setNode(p0, n0);
+                               }
+                               else
+                               {
+                                       n0.param2 = new_liquid_level_max;
+                                       setNode(p0, n0);
+                               }
+                               
+                               // Block has been modified
+                               {
+                                       v3s16 blockpos = getNodeBlockPos(p0);
+                                       MapBlock *block = getBlockNoCreateNoEx(blockpos);
+                                       if(block != NULL)
+                                               modified_blocks.insert(blockpos, block);
+                               }
+                               
+                               /*
+                                       Add neighboring non-source liquid nodes to transform queue.
+                               */
+                               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
+                               };
+                               for(u16 i=0; i<6; i++)
+                               {
+                                       try
+                                       {
+
+                                       v3s16 p2 = p0 + dirs[i];
+                                       
+                                       MapNode n2 = getNode(p2);
+                                       if(content_flowing_liquid(n2.d))
+                                       {
+                                               m_transforming_liquid.push_back(p2);
+                                       }
+                                       
+                                       }catch(InvalidPositionException &e)
+                                       {
+                                       }
+                               }
+                       }
+               }
+               
+               // Get a new one from queue if the node has turned into non-water
+               if(content_liquid(n0.d) == false)
+                       continue;
+
+               /*
+                       Flow water from this node
+               */
+               v3s16 dirs_to[5] = {
+                       v3s16(0,-1,0), // bottom
+                       v3s16(0,0,1), // back
+                       v3s16(1,0,0), // right
+                       v3s16(0,0,-1), // front
+                       v3s16(-1,0,0), // left
+               };
+               for(u16 i=0; i<5; i++)
+               {
+                       try
+                       {
+
+                       bool to_bottom = (i == 0);
+
+                       // If liquid is at lowest possible height, it's not going
+                       // anywhere except down
+                       if(liquid_level == 0 && to_bottom == false)
+                               continue;
+                       
+                       u8 liquid_next_level = 0;
+                       // If going to bottom
+                       if(to_bottom)
+                       {
+                               //liquid_next_level = 7;
+                               if(liquid_level >= 7 - WATER_DROP_BOOST)
+                                       liquid_next_level = 7;
+                               else
+                                       liquid_next_level = liquid_level + WATER_DROP_BOOST;
+                       }
+                       else
+                               liquid_next_level = liquid_level - 1;
+
+                       bool n2_changed = false;
+                       bool flowed = false;
+                       
+                       v3s16 p2 = p0 + dirs_to[i];
+
+                       MapNode n2 = getNode(p2);
+
+                       if(content_liquid(n2.d))
+                       {
+                               u8 n2_nonsource_c = make_liquid_flowing(n2.d);
+                               // Check that the liquids are the same type
+                               if(n2_nonsource_c != nonsource_c)
+                               {
+                                       dstream<<"WARNING: Not handling: different liquids"
+                                                       " collide"<<std::endl;
+                                       continue;
+                               }
+                               bool n2_is_source = !content_flowing_liquid(n2.d);
+                               u8 n2_liquid_level = 8;
+                               if(n2_is_source == false)
+                                       n2_liquid_level = n2.param2 & 0x07;
+                               
+                               if(to_bottom)
+                               {
+                                       flowed = true;
+                               }
+
+                               if(n2_is_source)
+                               {
+                                       // Just flow into the source, nothing changes.
+                                       // n2_changed is not set because destination didn't change
+                                       flowed = true;
+                               }
+                               else
+                               {
+                                       if(liquid_next_level > liquid_level)
+                                       {
+                                               n2.param2 = liquid_next_level;
+                                               setNode(p2, n2);
+
+                                               n2_changed = true;
+                                               flowed = true;
+                                       }
+                               }
+                       }
+                       else if(n2.d == CONTENT_AIR)
+                       {
+                               n2.d = nonsource_c;
+                               n2.param2 = liquid_next_level;
+                               setNode(p2, n2);
+                               
+                               n2_changed = true;
+                               flowed = true;
+                       }
+
+                       if(n2_changed)
+                       {
+                               m_transforming_liquid.push_back(p2);
+                               
+                               v3s16 blockpos = getNodeBlockPos(p2);
+                               MapBlock *block = getBlockNoCreateNoEx(blockpos);
+                               if(block != NULL)
+                                       modified_blocks.insert(blockpos, block);
+                       }
+                       
+                       // If n2_changed to bottom, don't flow anywhere else
+                       if(to_bottom && flowed)
+                               break;
+                               
+                       }catch(InvalidPositionException &e)
+                       {
+                       }
+               }
+
+               loopcount++;
+               //if(loopcount >= 100000)
+               if(loopcount >= initial_size * 1)
+                       break;
+       }
+       //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
+}
+
 /*
        ServerMap
 */
@@ -1312,6 +1641,178 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
        Map(dout_server),
        m_heightmap(NULL)
 {
+       /*
+               Experimental and debug stuff
+       */
+       
+       {
+               dstream<<"Generating map point attribute lists"<<std::endl;
+               
+               PointAttributeList *list_baseheight = m_padb.getList("hm_baseheight");
+               PointAttributeList *list_randmax = m_padb.getList("hm_randmax");
+               PointAttributeList *list_randfactor = m_padb.getList("hm_randfactor");
+               PointAttributeList *list_plants_amount = m_padb.getList("plants_amount");
+               PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
+
+               /*
+                       NOTE: BEWARE: Too big amount of these will make map generation
+                       slow. Especially those that are read by every block emerge.
+                       
+                       Fetch times:
+                       1000 points: 2-3ms
+                       5000 points: 15ms
+                       15000 points: 40ms
+               */
+               
+               for(u32 i=0; i<5000; i++)
+               {
+                       /*u32 lim = MAP_GENERATION_LIMIT;
+                       if(i < 400)
+                               lim = 2000;*/
+
+                       u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
+
+                       v3s16 p(
+                               -lim + myrand()%(lim*2),
+                               0,
+                               -lim + myrand()%(lim*2)
+                       );
+                       /*float plants_amount = (float)(myrand()%1050) / 1000.0;
+                       plants_amount = pow(plants_amount, 5);
+                       list_plants_amount->addPoint(p, Attribute(plants_amount));*/
+                       
+                       float plants_amount = 0;
+                       if(myrand()%5 == 0)
+                       {
+                               plants_amount = 1.5;
+                       }
+                       else if(myrand()%4 == 0)
+                       {
+                               plants_amount = 0.5;
+                       }
+                       else if(myrand()%2 == 0)
+                       {
+                               plants_amount = 0.03;
+                       }
+                       else
+                       {
+                               plants_amount = 0.0;
+                       }
+
+
+                       list_plants_amount->addPoint(p, Attribute(plants_amount));
+               }
+
+               for(u32 i=0; i<1000; i++)
+               {
+                       /*u32 lim = MAP_GENERATION_LIMIT;
+                       if(i < 400)
+                               lim = 2000;*/
+
+                       u32 lim = 500 + MAP_GENERATION_LIMIT * i / 1000;
+
+                       v3s16 p(
+                               -lim + myrand()%(lim*2),
+                               0,
+                               -lim + myrand()%(lim*2)
+                       );
+
+                       float caves_amount = 0;
+                       if(myrand()%5 == 0)
+                       {
+                               caves_amount = 1.0;
+                       }
+                       else if(myrand()%3 == 0)
+                       {
+                               caves_amount = 0.3;
+                       }
+                       else
+                       {
+                               caves_amount = 0.05;
+                       }
+
+                       list_caves_amount->addPoint(p, Attribute(caves_amount));
+               }
+               
+               for(u32 i=0; i<5000; i++)
+               {
+                       /*u32 lim = MAP_GENERATION_LIMIT;
+                       if(i < 400)
+                               lim = 2000;*/
+
+                       u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000;
+
+                       v3s16 p(
+                               -lim + (myrand()%(lim*2)),
+                               0,
+                               -lim + (myrand()%(lim*2))
+                       );
+                       
+                       /*s32 bh_i = (myrand()%200) - 50;
+                       float baseheight = (float)bh_i;
+                       
+                       float m = 100.;
+                       float e = 3.;
+                       float randmax = (float)(myrand()%(int)(10.*pow(m, 1./e)))/10.;
+                       randmax = pow(randmax, e);
+
+                       //float randmax = (float)(myrand()%60);
+                       float randfactor = (float)(myrand()%450) / 1000.0 + 0.4;*/
+
+                       float baseheight = 0;
+                       float randmax = 0;
+                       float randfactor = 0;
+
+                       if(myrand()%4 == 0)
+                       {
+                               baseheight = 100;
+                               randmax = 50;
+                               randfactor = 0.63;
+                       }
+                       else if(myrand()%6 == 0)
+                       {
+                               baseheight = 200;
+                               randmax = 100;
+                               randfactor = 0.66;
+                       }
+                       else if(myrand()%4 == 0)
+                       {
+                               baseheight = -3;
+                               randmax = 30;
+                               randfactor = 0.7;
+                       }
+                       else if(myrand()%3 == 0)
+                       {
+                               baseheight = 0;
+                               randmax = 30;
+                               randfactor = 0.63;
+                       }
+                       else
+                       {
+                               baseheight = -3;
+                               randmax = 20;
+                               randfactor = 0.5;
+                       }
+
+                       list_baseheight->addPoint(p, Attribute(baseheight));
+                       list_randmax->addPoint(p, Attribute(randmax));
+                       list_randfactor->addPoint(p, Attribute(randfactor));
+               }
+
+               /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(5));
+               list_randmax->addPoint(v3s16(0,0,0), Attribute(20));
+               list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.6));*/
+
+               // Easy spawn point
+               /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0));
+               list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
+               list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
+       }
+
+       /*
+               Try to load map; if not found, create a new one.
+       */
+
        m_savedir = savedir;
        m_map_saving_enabled = false;
        
@@ -1362,14 +1863,20 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
        dstream<<DTIME<<"Initializing new map."<<std::endl;
        
        // Create master heightmap
-       ValueGenerator *maxgen =
+       /*ValueGenerator *maxgen =
                        ValueGenerator::deSerialize(hmp.randmax);
        ValueGenerator *factorgen =
                        ValueGenerator::deSerialize(hmp.randfactor);
        ValueGenerator *basegen =
                        ValueGenerator::deSerialize(hmp.base);
        m_heightmap = new UnlimitedHeightmap
-                       (hmp.blocksize, maxgen, factorgen, basegen);
+                       (hmp.blocksize, maxgen, factorgen, basegen, &m_padb);*/
+
+       /*m_heightmap = new UnlimitedHeightmap
+                       (hmp.blocksize, &m_padb);*/
+
+       m_heightmap = new UnlimitedHeightmap
+                       (32, &m_padb);
        
        // Set map parameters
        m_params = mp;
@@ -1456,48 +1963,17 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
        s16 hm_d = MAP_BLOCKSIZE / hm_split;
 
        ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
+       
+       // Sector position on map in nodes
+       v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
 
        /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
                        " heightmaps and objects"<<std::endl;*/
        
-       // Loop through sub-heightmaps
-       for(s16 y=0; y<hm_split; y++)
-       for(s16 x=0; x<hm_split; x++)
-       {
-               v2s16 p_in_sector = v2s16(x,y);
-               v2s16 mhm_p = p2d * hm_split + p_in_sector;
-               f32 corners[4] = {
-                       m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
-                       m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
-                       m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
-                       m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
-               };
-
-               /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
-                               <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
-                               <<std::endl;*/
-
-               FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
-                               mhm_p, hm_d);
-               sector->setHeightmap(p_in_sector, hm);
-
-               //TODO: Make these values configurable
-               //hm->generateContinued(0.0, 0.0, corners);
-               hm->generateContinued(0.5, 0.2, corners);
-               //hm->generateContinued(1.0, 0.2, corners);
-               //hm->generateContinued(2.0, 0.2, corners);
-
-               //hm->print();
-               
-       }
-
        /*
-               Generate objects
+               Calculate some information about local properties
        */
        
-       core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
-       sector->setObjects(objects);
-       
        v2s16 mhm_p = p2d * hm_split;
        f32 corners[4] = {
                m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
@@ -1533,19 +2009,87 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
        pitness /= 4.0;
        pitness /= MAP_BLOCKSIZE;
        //dstream<<"pitness="<<pitness<<std::endl;
+
+       /*
+               Get local attributes
+       */
+       
+       float local_plants_amount = 0.0;
+       {
+               //dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
+               //TimeTaker attrtimer("emergeSector() attribute fetch");
+               
+               // Get plant amount from attributes
+               PointAttributeList *palist = m_padb.getList("plants_amount");
+               assert(palist);
+               /*local_plants_amount =
+                               palist->getNearAttr(nodepos2d).getFloat();*/
+               local_plants_amount =
+                               palist->getInterpolatedFloat(nodepos2d);
+       }
+
+       /*
+               Generate sector heightmap
+       */
+
+       // Loop through sub-heightmaps
+       for(s16 y=0; y<hm_split; y++)
+       for(s16 x=0; x<hm_split; x++)
+       {
+               v2s16 p_in_sector = v2s16(x,y);
+               v2s16 mhm_p = p2d * hm_split + p_in_sector;
+               f32 corners[4] = {
+                       m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
+                       m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
+                       m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
+                       m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
+               };
+
+               /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
+                               <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
+                               <<std::endl;*/
+
+               FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
+                               mhm_p, hm_d);
+               sector->setHeightmap(p_in_sector, hm);
+
+               //TODO: Make these values configurable
+               //hm->generateContinued(0.0, 0.0, corners);
+               //hm->generateContinued(0.25, 0.2, corners);
+               //hm->generateContinued(0.5, 0.2, corners);
+               //hm->generateContinued(1.0, 0.2, corners);
+               //hm->generateContinued(2.0, 0.2, corners);
+               //hm->generateContinued(2.0 * avgslope, 0.5, corners);
+               hm->generateContinued(avgslope * MAP_BLOCKSIZE/8, 0.5, corners);
+
+               //hm->print();
+       }
+
+       /*
+               Generate objects
+       */
        
+       core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
+       sector->setObjects(objects);
+
+       float area = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
+
        /*
                Plant some trees if there is not much slope
        */
        {
                // Avgslope is the derivative of a hill
-               float t = avgslope * avgslope;
-               float a = MAP_BLOCKSIZE * m_params.plants_amount;
+               //float t = avgslope * avgslope;
+               float t = avgslope;
+               float a = area/16 * m_params.plants_amount * local_plants_amount;
                u32 tree_max;
-               if(t > 0.03)
-                       tree_max = a / (t/0.03);
+               //float something = 0.17*0.17;
+               float something = 0.3;
+               if(t > something)
+                       tree_max = a / (t/something);
                else
                        tree_max = a;
+               
                u32 count = (myrand()%(tree_max+1));
                //u32 count = tree_max;
                for(u32 i=0; i<count; i++)
@@ -1565,7 +2109,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
        {
                // Pitness usually goes at around -0.5...0.5
                u32 bush_max = 0;
-               u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
+               u32 a = area/16 * 3.0 * m_params.plants_amount * local_plants_amount;
                if(pitness > 0)
                        bush_max = (pitness*a*4);
                if(bush_max > a)
@@ -1585,9 +2129,9 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
        /*
                Add ravine (randomly)
        */
-       if(m_params.ravines_amount != 0)
+       if(m_params.ravines_amount > 0.001)
        {
-               if(myrand()%(s32)(20.0 / m_params.ravines_amount) == 0)
+               if(myrand()%(s32)(200.0 / m_params.ravines_amount) == 0)
                {
                        s16 s = 6;
                        s16 x = myrand()%(MAP_BLOCKSIZE-s*2-1)+s;
@@ -1688,31 +2232,202 @@ MapBlock * ServerMap::emergeBlock(
        */
        
        /*
-               If block doesn't exist, create one.
-               If it exists, it is a dummy. In that case unDummify() it.
+               If block doesn't exist, create one.
+               If it exists, it is a dummy. In that case unDummify() it.
+
+               NOTE: This already sets the map as the parent of the block
+       */
+       if(block == NULL)
+       {
+               block = sector->createBlankBlockNoInsert(block_y);
+       }
+       else
+       {
+               // Remove the block so that nobody can get a half-generated one.
+               sector->removeBlock(block);
+               // Allocate the block to contain the generated data
+               block->unDummify();
+       }
+       
+       /*u8 water_material = CONTENT_WATER;
+       if(g_settings.getBool("endless_water"))
+               water_material = CONTENT_WATERSOURCE;*/
+       u8 water_material = CONTENT_WATERSOURCE;
+       
+       s32 lowest_ground_y = 32767;
+       s32 highest_ground_y = -32768;
+       
+       // DEBUG
+       //sector->printHeightmaps();
+
+       for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
+       for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+       {
+               //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
+
+               float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
+               //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
+               if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
+               {
+                       dstream<<"WARNING: Surface height not found in sector "
+                                       "for block that is being emerged"<<std::endl;
+                       surface_y_f = 0.0;
+               }
+
+               s16 surface_y = surface_y_f;
+               //avg_ground_y += surface_y;
+               if(surface_y < lowest_ground_y)
+                       lowest_ground_y = surface_y;
+               if(surface_y > highest_ground_y)
+                       highest_ground_y = surface_y;
+
+               s32 surface_depth = 0;
+               
+               float slope = sector->getSlope(v2s16(x0,z0)).getLength();
+               
+               //float min_slope = 0.45;
+               //float max_slope = 0.85;
+               float min_slope = 0.60;
+               float max_slope = 1.20;
+               float min_slope_depth = 5.0;
+               float max_slope_depth = 0;
+
+               if(slope < min_slope)
+                       surface_depth = min_slope_depth;
+               else if(slope > max_slope)
+                       surface_depth = max_slope_depth;
+               else
+                       surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
+
+               for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+               {
+                       s16 real_y = block_y * MAP_BLOCKSIZE + y0;
+                       MapNode n;
+                       /*
+                               Calculate lighting
+                               
+                               NOTE: If there are some man-made structures above the
+                               newly created block, they won't be taken into account.
+                       */
+                       if(real_y > surface_y)
+                               n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
+
+                       /*
+                               Calculate material
+                       */
+
+                       // If node is over heightmap y, it's air or water
+                       if(real_y > surface_y)
+                       {
+                               // If under water level, it's water
+                               if(real_y < WATER_LEVEL)
+                               {
+                                       n.d = water_material;
+                                       n.setLight(LIGHTBANK_DAY,
+                                                       diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
+                                       /*
+                                               Add to transforming liquid queue (in case it'd
+                                               start flowing)
+                                       */
+                                       v3s16 real_pos = v3s16(x0,y0,z0) + p*MAP_BLOCKSIZE;
+                                       m_transforming_liquid.push_back(real_pos);
+                               }
+                               // else air
+                               else
+                                       n.d = CONTENT_AIR;
+                       }
+                       // Else it's ground or dungeons (air)
+                       else
+                       {
+                               // If it's surface_depth under ground, it's stone
+                               if(real_y <= surface_y - surface_depth)
+                               {
+                                       n.d = CONTENT_STONE;
+                               }
+                               else
+                               {
+                                       // It is mud if it is under the first ground
+                                       // level or under water
+                                       if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
+                                       {
+                                               n.d = CONTENT_MUD;
+                                       }
+                                       else
+                                       {
+                                               n.d = CONTENT_GRASS;
+                                       }
+
+                                       //n.d = CONTENT_MUD;
+                                       
+                                       /*// If under water level, it's mud
+                                       if(real_y < WATER_LEVEL)
+                                               n.d = CONTENT_MUD;
+                                       // Only the topmost node is grass
+                                       else if(real_y <= surface_y - 1)
+                                               n.d = CONTENT_MUD;
+                                       else
+                                               n.d = CONTENT_GRASS;*/
+                               }
+                       }
+
+                       block->setNode(v3s16(x0,y0,z0), n);
+               }
+       }
+       
+       /*
+               Calculate some helper variables
+       */
+       
+       // Completely underground if the highest part of block is under lowest
+       // ground height.
+       // This has to be very sure; it's probably one too strict now but
+       // that's just better.
+       bool completely_underground =
+                       block_y * MAP_BLOCKSIZE + MAP_BLOCKSIZE < lowest_ground_y;
+
+       bool some_part_underground = block_y * MAP_BLOCKSIZE <= highest_ground_y;
+
+       bool mostly_underwater_surface = false;
+       if(highest_ground_y < WATER_LEVEL
+                       && some_part_underground && !completely_underground)
+               mostly_underwater_surface = true;
+
+       /*
+               Get local attributes
        */
-       if(block == NULL)
-       {
-               block = sector->createBlankBlockNoInsert(block_y);
-       }
-       else
+
+       //dstream<<"emergeBlock(): Getting local attributes"<<std::endl;
+
+       float caves_amount = 0;
+       
        {
-               // Remove the block so that nobody can get a half-generated one.
-               sector->removeBlock(block);
-               // Allocate the block to be a proper one.
-               block->unDummify();
+               /*
+                       NOTE: BEWARE: Too big amount of attribute points slows verything
+                       down by a lot.
+                       1 interpolation from 5000 points takes 2-3ms.
+               */
+               //TimeTaker timer("emergeBlock() local attribute retrieval");
+               v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
+               PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
+               caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
        }
-       
+
+       //dstream<<"emergeBlock(): Done"<<std::endl;
+
        /*
-               Create dungeon making table
+               Generate dungeons
        */
+
+       // Initialize temporary 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
+       
+       // Fill table
+#if 1
        {
                /*
                        Initialize orp and ors. Try to find if some neighboring
@@ -1799,12 +2514,75 @@ MapBlock * ServerMap::emergeBlock(
                }
                catch(InvalidPositionException &e){}
 
+               // Check y-
+               try
+               {
+                       s16 y = -1;
+                       for(s16 x=0; x<ued; x++)
+                       for(s16 z=0; z<ued; z++)
+                       {
+                               v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
+                               if(getNode(ap).d == CONTENT_AIR)
+                               {
+                                       orp = v3f(x+1,0,z+1);
+                                       found_existing = true;
+                                       goto continue_generating;
+                               }
+                       }
+               }
+               catch(InvalidPositionException &e){}
+               
+               // Check y+
+               try
+               {
+                       s16 y = ued;
+                       for(s16 x=0; x<ued; x++)
+                       for(s16 z=0; z<ued; z++)
+                       {
+                               v3s16 ap = v3s16(x,y,z) + block->getPosRelative();
+                               if(getNode(ap).d == CONTENT_AIR)
+                               {
+                                       orp = v3f(x+1,ued-1,z+1);
+                                       found_existing = true;
+                                       goto continue_generating;
+                               }
+                       }
+               }
+               catch(InvalidPositionException &e){}
+
 continue_generating:
                
                /*
-                       Don't always generate dungeon
+                       Choose whether to actually generate dungeon
                */
-               if(found_existing || rand() % 2 == 0)
+               bool do_generate_dungeons = true;
+               // Don't generate if no part is underground
+               if(!some_part_underground)
+               {
+                       do_generate_dungeons = false;
+               }
+               // Don't generate if mostly underwater surface
+               else if(mostly_underwater_surface)
+               {
+                       do_generate_dungeons = false;
+               }
+               // Partly underground = cave
+               else if(!completely_underground)
+               {
+                       do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
+               }
+               // Found existing dungeon underground
+               else if(found_existing && completely_underground)
+               {
+                       do_generate_dungeons = (rand() % 100 <= (s32)(caves_amount*100));
+               }
+               // Underground and no dungeons found
+               else
+               {
+                       do_generate_dungeons = (rand() % 300 <= (s32)(caves_amount*100));
+               }
+
+               if(do_generate_dungeons)
                {
                        /*
                                Generate some tunnel starting from orp and ors
@@ -1817,7 +2595,7 @@ MapBlock * ServerMap::emergeBlock(
                                        (float)(myrand()%ued)+0.5
                                );
                                s16 min_d = 0;
-                               s16 max_d = 4;
+                               s16 max_d = 6;
                                s16 rs = (myrand()%(max_d-min_d+1))+min_d;
                                
                                v3f vec = rp - orp;
@@ -1852,184 +2630,61 @@ MapBlock * ServerMap::emergeBlock(
                        }
                }
        }
-       
-       u8 water_material = CONTENT_WATER;
-       if(g_settings.getBool("endless_water"))
-               water_material = CONTENT_OCEAN;
-       
-       s32 lowest_ground_y = 32767;
-       s32 highest_ground_y = -32768;
-       
-       // DEBUG
-       //sector->printHeightmaps();
+#endif
+
+       // Set to true if has caves.
+       // Set when some non-air is changed to air when making caves.
+       bool has_dungeons = false;
+
+       /*
+               Apply temporary cave data to block
+       */
 
        for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
        for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
        {
-               //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
-
-               float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
-               //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
-               if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
-               {
-                       dstream<<"WARNING: Surface height not found in sector "
-                                       "for block that is being emerged"<<std::endl;
-                       surface_y_f = 0.0;
-               }
-
-               s16 surface_y = surface_y_f;
-               //avg_ground_y += surface_y;
-               if(surface_y < lowest_ground_y)
-                       lowest_ground_y = surface_y;
-               if(surface_y > highest_ground_y)
-                       highest_ground_y = surface_y;
-
-               s32 surface_depth = 0;
-               
-               float slope = sector->getSlope(v2s16(x0,z0)).getLength();
-               
-               //float min_slope = 0.45;
-               //float max_slope = 0.85;
-               float min_slope = 0.60;
-               float max_slope = 1.20;
-               float min_slope_depth = 5.0;
-               float max_slope_depth = 0;
-               if(slope < min_slope)
-                       surface_depth = min_slope_depth;
-               else if(slope > max_slope)
-                       surface_depth = max_slope_depth;
-               else
-                       surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
-
                for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
                {
-                       s16 real_y = block_y * MAP_BLOCKSIZE + y0;
-                       MapNode n;
-                       /*
-                               Calculate lighting
-                               
-                               NOTE: If there are some man-made structures above the
-                               newly created block, they won't be taken into account.
-                       */
-                       if(real_y > surface_y)
-                               n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
-
-                       /*
-                               Calculate material
-                       */
-
-                       // If node is over heightmap y, it's air or water
-                       if(real_y > surface_y)
-                       {
-                               // If under water level, it's water
-                               if(real_y < WATER_LEVEL)
-                               {
-                                       n.d = water_material;
-                                       n.setLight(LIGHTBANK_DAY,
-                                                       diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
-                               }
-                               // else air
-                               else
-                                       n.d = CONTENT_AIR;
-                       }
-                       // Else it's ground or dungeons (air)
-                       else
-                       {
-                               // 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
-                               {
-                                       // If it's surface_depth under ground, it's stone
-                                       if(real_y <= surface_y - surface_depth)
-                                       {
-                                               n.d = CONTENT_STONE;
-                                       }
-                                       else
-                                       {
-                                               // It is mud if it is under the first ground
-                                               // level or under water
-                                               if(real_y < WATER_LEVEL || real_y <= surface_y - 1)
-                                               {
-                                                       n.d = CONTENT_MUD;
-                                               }
-                                               else
-                                               {
-                                                       n.d = CONTENT_GRASS;
-                                               }
+                       MapNode n = block->getNode(v3s16(x0,y0,z0));
 
-                                               //n.d = CONTENT_MUD;
-                                               
-                                               /*// If under water level, it's mud
-                                               if(real_y < WATER_LEVEL)
-                                                       n.d = CONTENT_MUD;
-                                               // Only the topmost node is grass
-                                               else if(real_y <= surface_y - 1)
-                                                       n.d = CONTENT_MUD;
-                                               else
-                                                       n.d = CONTENT_GRASS;*/
-                                       }
-                               }
-                       }
-#if 0
-                       else if(real_y <= surface_y - surface_depth)
+                       // Create dungeons
+                       if(underground_emptiness[
+                                       ued*ued*(z0*ued/MAP_BLOCKSIZE)
+                                       +ued*(y0*ued/MAP_BLOCKSIZE)
+                                       +(x0*ued/MAP_BLOCKSIZE)])
                        {
-                               // Create dungeons
-                               if(underground_emptiness[
-                                               ued*ued*(z0*ued/MAP_BLOCKSIZE)
-                                               +ued*(y0*ued/MAP_BLOCKSIZE)
-                                               +(x0*ued/MAP_BLOCKSIZE)])
+                               if(is_ground_content(n.d))
                                {
+                                       // Has now caves
+                                       has_dungeons = true;
+                                       // Set air to node
                                        n.d = CONTENT_AIR;
                                }
-                               else
-                               {
-                                       n.d = CONTENT_STONE;
-                               }
                        }
-                       // If node is at or under heightmap y
-                       else if(real_y <= surface_y)
-                       {
-                               // If under water level, it's mud
-                               if(real_y < WATER_LEVEL)
-                                       n.d = CONTENT_MUD;
-                               // Only the topmost node is grass
-                               else if(real_y <= surface_y - 1)
-                                       n.d = CONTENT_MUD;
-                               // Else it's the main material
-                               else
-                                       n.d = material;
-                       }
-#endif
+
                        block->setNode(v3s16(x0,y0,z0), n);
                }
        }
-
+       
        /*
-               Calculate is_underground
+               This is used for guessing whether or not the block should
+               receive sunlight from the top if the top block doesn't exist
        */
-       // Probably underground if the highest part of block is under lowest
-       // ground height
-       bool is_underground = (block_y+1) * MAP_BLOCKSIZE <= lowest_ground_y;
-       block->setIsUnderground(is_underground);
+       block->setIsUnderground(completely_underground);
 
        /*
-               Force lighting update if some part of block is underground
-               This is needed because of caves.
+               Force lighting update if some part of block is partly
+               underground and has caves.
        */
-       
-       bool some_part_underground = (block_y+0) * MAP_BLOCKSIZE < highest_ground_y;
-       if(some_part_underground)
-       //if(is_underground)
+       /*if(some_part_underground && !completely_underground && has_dungeons)
        {
+               //dstream<<"Half-ground caves"<<std::endl;
                lighting_invalidated_blocks[block->getPos()] = block;
-       }
+       }*/
        
+       // DEBUG: Always update lighting
+       //lighting_invalidated_blocks[block->getPos()] = block;
+
        /*
                Add some minerals
        */
@@ -2041,9 +2696,9 @@ MapBlock * ServerMap::emergeBlock(
                /*
                        Add meseblocks
                */
-               for(s16 i=0; i<underground_level*1; i++)
+               for(s16 i=0; i<underground_level/4 + 1; i++)
                {
-                       if(myrand()%2 == 0)
+                       if(myrand()%50 == 0)
                        {
                                v3s16 cp(
                                        (myrand()%(MAP_BLOCKSIZE-2))+1,
@@ -2112,7 +2767,7 @@ MapBlock * ServerMap::emergeBlock(
        /*
                Create a few rats in empty blocks underground
        */
-       if(is_underground)
+       if(completely_underground)
        {
                //for(u16 i=0; i<2; i++)
                {
@@ -2157,8 +2812,34 @@ MapBlock * ServerMap::emergeBlock(
                v2s16 p2d(p.X,p.Z);
                u8 d = i.getNode()->getValue();
 
-               //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
+               // Ground level point (user for stuff that is on ground)
+               v3s16 gp = p;
+               bool ground_found = true;
                
+               // Search real ground level
+               try{
+                       for(;;)
+                       {
+                               MapNode n = sector->getNode(gp);
+
+                               // If not air, go one up and continue to placing the tree
+                               if(n.d != CONTENT_AIR)
+                               {
+                                       gp += v3s16(0,1,0);
+                                       break;
+                               }
+
+                               // If air, go one down
+                               gp += v3s16(0,-1,0);
+                       }
+               }catch(InvalidPositionException &e)
+               {
+                       // Ground not found.
+                       ground_found = false;
+                       // This is most close to ground
+                       gp += v3s16(0,1,0);
+               }
+
                try
                {
 
@@ -2175,40 +2856,64 @@ MapBlock * ServerMap::emergeBlock(
                }
                else if(d == SECTOR_OBJECT_TREE_1)
                {
-                       v3s16 p_min = p + v3s16(-1,0,-1);
-                       v3s16 p_max = p + v3s16(1,4,1);
+                       if(ground_found == false)
+                               continue;
+
+                       v3s16 p_min = gp + v3s16(-1,0,-1);
+                       v3s16 p_max = gp + v3s16(1,5,1);
                        if(sector->isValidArea(p_min, p_max,
                                        &changed_blocks_sector))
                        {
                                MapNode n;
                                n.d = CONTENT_TREE;
-                               sector->setNode(p+v3s16(0,0,0), n);
-                               sector->setNode(p+v3s16(0,1,0), n);
-                               sector->setNode(p+v3s16(0,2,0), n);
-                               sector->setNode(p+v3s16(0,3,0), n);
+                               sector->setNode(gp+v3s16(0,0,0), n);
+                               sector->setNode(gp+v3s16(0,1,0), n);
+                               sector->setNode(gp+v3s16(0,2,0), n);
+                               sector->setNode(gp+v3s16(0,3,0), n);
 
                                n.d = CONTENT_LEAVES;
 
-                               sector->setNode(p+v3s16(0,4,0), n);
+                               if(rand()%4!=0) sector->setNode(gp+v3s16(0,5,0), n);
+
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,0), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,0), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(0,5,-1), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(0,5,1), n);
+                               /*if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,1), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,1), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(-1,5,-1), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(1,5,-1), n);*/
+
+                               sector->setNode(gp+v3s16(0,4,0), n);
+                               
+                               sector->setNode(gp+v3s16(-1,4,0), n);
+                               sector->setNode(gp+v3s16(1,4,0), n);
+                               sector->setNode(gp+v3s16(0,4,-1), n);
+                               sector->setNode(gp+v3s16(0,4,1), n);
+                               sector->setNode(gp+v3s16(1,4,1), n);
+                               sector->setNode(gp+v3s16(-1,4,1), n);
+                               sector->setNode(gp+v3s16(-1,4,-1), n);
+                               sector->setNode(gp+v3s16(1,4,-1), n);
+
+                               sector->setNode(gp+v3s16(-1,3,0), n);
+                               sector->setNode(gp+v3s16(1,3,0), n);
+                               sector->setNode(gp+v3s16(0,3,-1), n);
+                               sector->setNode(gp+v3s16(0,3,1), n);
+                               sector->setNode(gp+v3s16(1,3,1), n);
+                               sector->setNode(gp+v3s16(-1,3,1), n);
+                               sector->setNode(gp+v3s16(-1,3,-1), n);
+                               sector->setNode(gp+v3s16(1,3,-1), n);
                                
-                               sector->setNode(p+v3s16(-1,4,0), n);
-                               sector->setNode(p+v3s16(1,4,0), n);
-                               sector->setNode(p+v3s16(0,4,-1), n);
-                               sector->setNode(p+v3s16(0,4,1), n);
-                               sector->setNode(p+v3s16(1,4,1), n);
-                               sector->setNode(p+v3s16(-1,4,1), n);
-                               sector->setNode(p+v3s16(-1,4,-1), n);
-                               sector->setNode(p+v3s16(1,4,-1), n);
-
-                               sector->setNode(p+v3s16(-1,3,0), n);
-                               sector->setNode(p+v3s16(1,3,0), n);
-                               sector->setNode(p+v3s16(0,3,-1), n);
-                               sector->setNode(p+v3s16(0,3,1), n);
-                               sector->setNode(p+v3s16(1,3,1), n);
-                               sector->setNode(p+v3s16(-1,3,1), n);
-                               sector->setNode(p+v3s16(-1,3,-1), n);
-                               sector->setNode(p+v3s16(1,3,-1), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,0), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,0), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(0,2,-1), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(0,2,1), n);
+                               /*if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,1), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,1), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(-1,2,-1), n);
+                               if(rand()%3!=0) sector->setNode(gp+v3s16(1,2,-1), n);*/
                                
+                               // Objects are identified by wanted position
                                objects_to_remove.push_back(p);
                                
                                // Lighting has to be recalculated for this one.
@@ -2218,13 +2923,17 @@ MapBlock * ServerMap::emergeBlock(
                }
                else if(d == SECTOR_OBJECT_BUSH_1)
                {
-                       if(sector->isValidArea(p + v3s16(0,0,0),
-                                       p + v3s16(0,0,0), &changed_blocks_sector))
+                       if(ground_found == false)
+                               continue;
+                       
+                       if(sector->isValidArea(gp + v3s16(0,0,0),
+                                       gp + v3s16(0,0,0), &changed_blocks_sector))
                        {
                                MapNode n;
                                n.d = CONTENT_LEAVES;
-                               sector->setNode(p+v3s16(0,0,0), n);
+                               sector->setNode(gp+v3s16(0,0,0), n);
                                
+                               // Objects are identified by wanted position
                                objects_to_remove.push_back(p);
                        }
                }
@@ -2318,6 +3027,34 @@ MapBlock * ServerMap::emergeBlock(
                objects->remove(*i);
        }
 
+       /*
+               Initially update sunlight
+       */
+       
+       {
+               core::map<v3s16, bool> light_sources;
+               bool black_air_left = false;
+               bool bottom_invalid =
+                               block->propagateSunlight(light_sources, true,
+                               &black_air_left, true);
+
+               // If sunlight didn't reach everywhere and part of block is
+               // above ground, lighting has to be properly updated
+               if(black_air_left && some_part_underground)
+               {
+                       lighting_invalidated_blocks[block->getPos()] = block;
+               }
+
+               if(bottom_invalid)
+               {
+                       lighting_invalidated_blocks[block->getPos()] = block;
+               }
+       }
+
+       /*
+               Translate sector's changed blocks to global changed blocks
+       */
+       
        for(core::map<s16, MapBlock*>::Iterator
                        i = changed_blocks_sector.getIterator();
                        i.atEnd() == false; i++)
@@ -2327,6 +3064,33 @@ MapBlock * ServerMap::emergeBlock(
                changed_blocks.insert(block->getPos(), block);
        }
 
+       /*
+               Debug information
+       */
+       if(0)
+       {
+               dstream
+               <<"lighting_invalidated_blocks.size()"
+               <<", has_dungeons"
+               <<", completely_ug"
+               <<", some_part_ug"
+               <<"  "<<lighting_invalidated_blocks.size()
+               <<", "<<has_dungeons
+               <<", "<<completely_underground
+               <<", "<<some_part_underground
+               <<std::endl;
+       }
+
+       /*
+               Debug mode operation
+       */
+       bool haxmode = g_settings.getBool("haxmode");
+       if(haxmode)
+       {
+               // Don't calculate lighting at all
+               //lighting_invalidated_blocks.clear();
+       }
+
        return block;
 }
 
@@ -2559,7 +3323,7 @@ void ServerMap::loadMasterHeightmap()
        if(m_heightmap != NULL)
                delete m_heightmap;
                
-       m_heightmap = UnlimitedHeightmap::deSerialize(is);
+       m_heightmap = UnlimitedHeightmap::deSerialize(is, &m_padb);
 }
 
 void ServerMap::saveSectorMeta(ServerMapSector *sector)
@@ -3035,6 +3799,17 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                                if not seen on display
                        */
                        
+                       float range = 100000 * BS;
+                       if(m_control.range_all == false)
+                               range = m_control.wanted_range * BS;
+
+                       if(isBlockInSight(block->getPos(), camera_position,
+                                       camera_direction, range) == false)
+                       {
+                               continue;
+                       }
+
+#if 0                  
                        v3s16 blockpos_nodes = block->getPosRelative();
                        
                        // Block center position
@@ -3057,8 +3832,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        {
                                // If block is far away, don't draw it
                                if(d > m_control.wanted_range * BS)
-                               // This is nicer when fog is used
-                               //if((dforward+d)/2 > m_control.wanted_range * BS)
                                        continue;
                        }
                        
@@ -3083,11 +3856,28 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                                if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
                                        continue;
                        }
+#endif                 
+
+                       v3s16 blockpos_nodes = block->getPosRelative();
+                       
+                       // Block center position
+                       v3f blockpos(
+                                       ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
+                                       ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
+                                       ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
+                       );
+
+                       // Block position relative to camera
+                       v3f blockpos_relative = blockpos - camera_position;
+
+                       // Total distance
+                       f32 d = blockpos_relative.getLength();
                        
+#if 1
                        /*
-                               Draw the faces of the block
+                               Update expired mesh
                        */
-#if 1
+
                        bool mesh_expired = false;
                        
                        {
@@ -3145,6 +3935,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                                continue;
                        }*/
 #endif
+                       /*
+                               Draw the faces of the block
+                       */
                        {
                                JMutexAutoLock lock(block->mesh_mutex);
 
@@ -3188,7 +3981,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
 }
 
-v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
+v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod, bool *changed)
 {
        /*
                Add it to all blocks touching it
@@ -3212,11 +4005,15 @@ v3s16 ClientMap::setTempMod(v3s16 p, NodeMod mod)
                        continue;
                // Relative position of requested node
                v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
-               blockref->setTempMod(relpos, mod);
+               if(blockref->setTempMod(relpos, mod))
+               {
+                       if(changed != NULL)
+                               *changed = true;
+               }
        }
        return getNodeBlockPos(p);
 }
-v3s16 ClientMap::clearTempMod(v3s16 p)
+v3s16 ClientMap::clearTempMod(v3s16 p, bool *changed)
 {
        v3s16 dirs[7] = {
                v3s16(0,0,0), // this
@@ -3237,7 +4034,11 @@ v3s16 ClientMap::clearTempMod(v3s16 p)
                        continue;
                // Relative position of requested node
                v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
-               blockref->clearTempMod(relpos);
+               if(blockref->clearTempMod(relpos))
+               {
+                       if(changed != NULL)
+                               *changed = true;
+               }
        }
        return getNodeBlockPos(p);
 }