]> git.lizzy.rs Git - minetest.git/blobdiff - src/map.cpp
Get rid of unused variables
[minetest.git] / src / map.cpp
index cb0967c35143059294ca7b42c2ccdfd7f406f07d..092ce97fdcf030cca3b60c8af40a9b09a0b6a0fe 100644 (file)
@@ -18,17 +18,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "map.h"
+#include "mapsector.h"
+#include "mapblock.h"
 #include "main.h"
-#include "jmutexautolock.h"
 #include "client.h"
 #include "filesys.h"
 #include "utility.h"
 #include "voxel.h"
 #include "porting.h"
-#include "mineral.h"
-#include "noise.h"
-#include "serverobject.h"
-#include "content_mapnode.h"
+#include "mapgen.h"
+#include "nodemetadata.h"
 
 extern "C" {
        #include "sqlite3.h"
@@ -121,42 +120,23 @@ MapSector * Map::getSectorNoGenerate(v2s16 p)
        return sector;
 }
 
-MapBlock * Map::getBlockNoCreate(v3s16 p3d)
-{      
-       v2s16 p2d(p3d.X, p3d.Z);
-       MapSector * sector = getSectorNoGenerate(p2d);
-
-       MapBlock *block = sector->getBlockNoCreate(p3d.Y);
-
-       return block;
-}
-
 MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
 {
-       try
-       {
-               v2s16 p2d(p3d.X, p3d.Z);
-               MapSector * sector = getSectorNoGenerate(p2d);
-               MapBlock *block = sector->getBlockNoCreate(p3d.Y);
-               return block;
-       }
-       catch(InvalidPositionException &e)
-       {
+       v2s16 p2d(p3d.X, p3d.Z);
+       MapSector * sector = getSectorNoGenerateNoEx(p2d);
+       if(sector == NULL)
                return NULL;
-       }
+       MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
+       return block;
 }
 
-/*MapBlock * Map::getBlockCreate(v3s16 p3d)
-{
-       v2s16 p2d(p3d.X, p3d.Z);
-       MapSector * sector = getSectorCreate(p2d);
-       assert(sector);
-       MapBlock *block = sector->getBlockNoCreate(p3d.Y);
-       if(block)
-               return block;
-       block = sector->createBlankBlock(p3d.Y);
+MapBlock * Map::getBlockNoCreate(v3s16 p3d)
+{      
+       MapBlock *block = getBlockNoCreateNoEx(p3d);
+       if(block == NULL)
+               throw InvalidPositionException();
        return block;
-}*/
+}
 
 bool Map::isNodeUnderground(v3s16 p)
 {
@@ -171,6 +151,45 @@ bool Map::isNodeUnderground(v3s16 p)
        }
 }
 
+bool Map::isValidPosition(v3s16 p)
+{
+       v3s16 blockpos = getNodeBlockPos(p);
+       MapBlock *block = getBlockNoCreate(blockpos);
+       return (block != NULL);
+}
+
+// Returns a CONTENT_IGNORE node if not found
+MapNode Map::getNodeNoEx(v3s16 p)
+{
+       v3s16 blockpos = getNodeBlockPos(p);
+       MapBlock *block = getBlockNoCreateNoEx(blockpos);
+       if(block == NULL)
+               return MapNode(CONTENT_IGNORE);
+       v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
+       return block->getNodeNoCheck(relpos);
+}
+
+// throws InvalidPositionException if not found
+MapNode Map::getNode(v3s16 p)
+{
+       v3s16 blockpos = getNodeBlockPos(p);
+       MapBlock *block = getBlockNoCreateNoEx(blockpos);
+       if(block == NULL)
+               throw InvalidPositionException();
+       v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
+       return block->getNodeNoCheck(relpos);
+}
+
+// throws InvalidPositionException if not found
+void Map::setNode(v3s16 p, MapNode & n)
+{
+       v3s16 blockpos = getNodeBlockPos(p);
+       MapBlock *block = getBlockNoCreate(blockpos);
+       v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
+       block->setNodeNoCheck(relpos, n);
+}
+
+
 /*
        Goes recursively through the neighbours of the node.
 
@@ -611,9 +630,9 @@ s16 Map::propagateSunlight(v3s16 start,
                else
                {
                        /*// Turn mud into grass
-                       if(n.d == CONTENT_MUD)
+                       if(n.getContent() == CONTENT_MUD)
                        {
-                               n.d = CONTENT_GRASS;
+                               n.setContent(CONTENT_GRASS);
                                block->setNode(relpos, n);
                                modified_blocks.insert(blockpos, block);
                        }*/
@@ -736,6 +755,25 @@ void Map::updateLighting(enum LightBank bank,
 
                }
        }
+       
+       /*
+               Enable this to disable proper lighting for speeding up map
+               generation for testing or whatever
+       */
+#if 0
+       //if(g_settings.get(""))
+       {
+               core::map<v3s16, MapBlock*>::Iterator i;
+               i = blocks_to_update.getIterator();
+               for(; i.atEnd() == false; i++)
+               {
+                       MapBlock *block = i.getNode()->getValue();
+                       v3s16 p = block->getPos();
+                       block->setLightingExpired(false);
+               }
+               return;
+       }
+#endif
 
 #if 0
        {
@@ -878,19 +916,19 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
        {
        }
 
-#if 1
+#if 0
        /*
                If the new node is solid and there is grass below, change it to mud
        */
-       if(content_features(n.d).walkable == true)
+       if(content_features(n).walkable == true)
        {
                try{
                        MapNode bottomnode = getNode(bottompos);
 
-                       if(bottomnode.d == CONTENT_GRASS
-                                       || bottomnode.d == CONTENT_GRASS_FOOTSTEPS)
+                       if(bottomnode.getContent() == CONTENT_GRASS
+                                       || bottomnode.getContent() == CONTENT_GRASS_FOOTSTEPS)
                        {
-                               bottomnode.d = CONTENT_MUD;
+                               bottomnode.setContent(CONTENT_MUD);
                                setNode(bottompos, bottomnode);
                        }
                }
@@ -905,9 +943,9 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                If the new node is mud and it is under sunlight, change it
                to grass
        */
-       if(n.d == CONTENT_MUD && node_under_sunlight)
+       if(n.getContent() == CONTENT_MUD && node_under_sunlight)
        {
-               n.d = CONTENT_GRASS;
+               n.setContent(CONTENT_GRASS);
        }
 #endif
 
@@ -948,7 +986,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                If node lets sunlight through and is under sunlight, it has
                sunlight too.
        */
-       if(node_under_sunlight && content_features(n.d).sunlight_propagates)
+       if(node_under_sunlight && content_features(n).sunlight_propagates)
        {
                n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
        }
@@ -963,7 +1001,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                Add intial metadata
        */
 
-       NodeMetadata *meta_proto = content_features(n.d).initial_metadata;
+       NodeMetadata *meta_proto = content_features(n).initial_metadata;
        if(meta_proto)
        {
                NodeMetadata *meta = meta_proto->clone();
@@ -977,7 +1015,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                TODO: This could be optimized by mass-unlighting instead
                          of looping
        */
-       if(node_under_sunlight && !content_features(n.d).sunlight_propagates)
+       if(node_under_sunlight && !content_features(n).sunlight_propagates)
        {
                s16 y = p.Y - 1;
                for(;; y--){
@@ -1048,7 +1086,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                v3s16 p2 = p + dirs[i];
 
                MapNode n2 = getNode(p2);
-               if(content_liquid(n2.d))
+               if(content_liquid(n2.getContent()))
                {
                        m_transforming_liquid.push_back(p2);
                }
@@ -1073,7 +1111,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
        v3s16 toppos = p + v3s16(0,1,0);
 
        // Node will be replaced with this
-       u8 replace_material = CONTENT_AIR;
+       content_t replace_material = CONTENT_AIR;
 
        /*
                If there is a node at top and it doesn't have sunlight,
@@ -1120,7 +1158,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
        */
 
        MapNode n;
-       n.d = replace_material;
+       n.setContent(replace_material);
        setNode(p, n);
 
        for(s32 i=0; i<2; i++)
@@ -1202,17 +1240,19 @@ void Map::removeNodeAndUpdate(v3s16 p,
        }
 
        /*
-               Add neighboring liquid nodes to transform queue.
+               Add neighboring liquid nodes and this node to transform queue.
+               (it's vital for the node itself to get updated last.)
        */
-       v3s16 dirs[6] = {
+       v3s16 dirs[7] = {
                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
+               v3s16(0,0,0), // self
        };
-       for(u16 i=0; i<6; i++)
+       for(u16 i=0; i<7; i++)
        {
                try
                {
@@ -1220,7 +1260,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
                v3s16 p2 = p + dirs[i];
 
                MapNode n2 = getNode(p2);
-               if(content_liquid(n2.d))
+               if(content_liquid(n2.getContent()))
                {
                        m_transforming_liquid.push_back(p2);
                }
@@ -1348,9 +1388,14 @@ bool Map::dayNightDiffed(v3s16 blockpos)
 /*
        Updates usage timers
 */
-void Map::timerUpdate(float dtime)
+void Map::timerUpdate(float dtime, float unload_timeout,
+               core::list<v3s16> *unloaded_blocks)
 {
-       //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
+       bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
+       
+       core::list<v2s16> sector_deletion_queue;
+       u32 deleted_blocks_count = 0;
+       u32 saved_blocks_count = 0;
 
        core::map<v2s16, MapSector*>::Iterator si;
 
@@ -1359,48 +1404,85 @@ void Map::timerUpdate(float dtime)
        {
                MapSector *sector = si.getNode()->getValue();
 
+               bool all_blocks_deleted = true;
+
                core::list<MapBlock*> blocks;
                sector->getBlocks(blocks);
                for(core::list<MapBlock*>::Iterator i = blocks.begin();
                                i != blocks.end(); i++)
                {
-                       (*i)->incrementUsageTimer(dtime);
+                       MapBlock *block = (*i);
+                       
+                       block->incrementUsageTimer(dtime);
+                       
+                       if(block->getUsageTimer() > unload_timeout)
+                       {
+                               v3s16 p = block->getPos();
+
+                               // Save if modified
+                               if(block->getModified() != MOD_STATE_CLEAN
+                                               && save_before_unloading)
+                               {
+                                       saveBlock(block);
+                                       saved_blocks_count++;
+                               }
+
+                               // Delete from memory
+                               sector->deleteBlock(block);
+
+                               if(unloaded_blocks)
+                                       unloaded_blocks->push_back(p);
+
+                               deleted_blocks_count++;
+                       }
+                       else
+                       {
+                               all_blocks_deleted = false;
+                       }
+               }
+
+               if(all_blocks_deleted)
+               {
+                       sector_deletion_queue.push_back(si.getNode()->getKey());
                }
        }
+       
+       // Finally delete the empty sectors
+       deleteSectors(sector_deletion_queue);
+       
+       if(deleted_blocks_count != 0)
+       {
+               PrintInfo(dstream); // ServerMap/ClientMap:
+               dstream<<"Unloaded "<<deleted_blocks_count
+                               <<" blocks from memory";
+               if(save_before_unloading)
+                       dstream<<", of which "<<saved_blocks_count<<" were written";
+               dstream<<"."<<std::endl;
+       }
 }
 
-void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
+void Map::deleteSectors(core::list<v2s16> &list)
 {
        core::list<v2s16>::Iterator j;
        for(j=list.begin(); j!=list.end(); j++)
        {
                MapSector *sector = m_sectors[*j];
-               if(only_blocks)
-               {
-                       sector->deleteBlocks();
-               }
-               else
-               {
-                       /*
-                               If sector is in sector cache, remove it from there
-                       */
-                       if(m_sector_cache == sector)
-                       {
-                               m_sector_cache = NULL;
-                       }
-                       /*
-                               Remove from map and delete
-                       */
-                       m_sectors.remove(*j);
-                       delete sector;
-               }
+               // If sector is in sector cache, remove it from there
+               if(m_sector_cache == sector)
+                       m_sector_cache = NULL;
+               // Remove from map and delete
+               m_sectors.remove(*j);
+               delete sector;
        }
 }
 
-u32 Map::unloadUnusedData(float timeout, bool only_blocks,
+#if 0
+void Map::unloadUnusedData(float timeout,
                core::list<v3s16> *deleted_blocks)
 {
        core::list<v2s16> sector_deletion_queue;
+       u32 deleted_blocks_count = 0;
+       u32 saved_blocks_count = 0;
 
        core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
        for(; si.atEnd() == false; si++)
@@ -1415,15 +1497,18 @@ u32 Map::unloadUnusedData(float timeout, bool only_blocks,
                                i != blocks.end(); i++)
                {
                        MapBlock *block = (*i);
-
+                       
                        if(block->getUsageTimer() > timeout)
                        {
                                // Save if modified
                                if(block->getModified() != MOD_STATE_CLEAN)
+                               {
                                        saveBlock(block);
-                               // Unload
-                               sector->removeBlock(block);
-                               delete block;
+                                       saved_blocks_count++;
+                               }
+                               // Delete from memory
+                               sector->deleteBlock(block);
+                               deleted_blocks_count++;
                        }
                        else
                        {
@@ -1437,37 +1522,16 @@ u32 Map::unloadUnusedData(float timeout, bool only_blocks,
                }
        }
 
-#if 0
-       core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
-       for(; i.atEnd() == false; i++)
-       {
-               MapSector *sector = i.getNode()->getValue();
-               /*
-                       Delete sector from memory if it hasn't been used in a long time
-               */
-               if(sector->usage_timer > timeout)
-               {
-                       sector_deletion_queue.push_back(i.getNode()->getKey());
+       deleteSectors(sector_deletion_queue);
 
-                       if(deleted_blocks != NULL)
-                       {
-                               // Collect positions of blocks of sector
-                               MapSector *sector = i.getNode()->getValue();
-                               core::list<MapBlock*> blocks;
-                               sector->getBlocks(blocks);
-                               for(core::list<MapBlock*>::Iterator i = blocks.begin();
-                                               i != blocks.end(); i++)
-                               {
-                                       deleted_blocks->push_back((*i)->getPos());
-                               }
-                       }
-               }
-       }
-#endif
+       dstream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
+                       <<", of which "<<saved_blocks_count<<" were wr."
+                       <<std::endl;
 
-       deleteSectors(sector_deletion_queue, only_blocks);
-       return sector_deletion_queue.getSize();
+       //return sector_deletion_queue.getSize();
+       //return deleted_blocks_count;
 }
+#endif
 
 void Map::PrintInfo(std::ostream &out)
 {
@@ -1476,6 +1540,17 @@ void Map::PrintInfo(std::ostream &out)
 
 #define WATER_DROP_BOOST 4
 
+enum NeighborType {
+       NEIGHBOR_UPPER,
+       NEIGHBOR_SAME_LEVEL,
+       NEIGHBOR_LOWER
+};
+struct NodeNeighbor {
+       MapNode n;
+       NeighborType t;
+       v3s16 p;
+};
+
 void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
 {
        DSTACK(__FUNCTION_NAME);
@@ -1494,262 +1569,221 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                */
                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);
-
+               MapNode n0 = getNodeNoEx(p0);
+                               
                /*
-                       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;
+                       Collect information about current node
+                */
+               s8 liquid_level = -1;
+               u8 liquid_kind = CONTENT_IGNORE;
+               LiquidType liquid_type = content_features(n0.getContent()).liquid_type;
+               switch (liquid_type) {
+                       case LIQUID_SOURCE:
+                               liquid_level = 8;
+                               liquid_kind = content_features(n0.getContent()).liquid_alternative_flowing;
+                               break;
+                       case LIQUID_FLOWING:
+                               liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
+                               liquid_kind = n0.getContent();
+                               break;
+                       case LIQUID_NONE:
+                               // if this is an air node, it *could* be transformed into a liquid. otherwise,
+                               // continue with the next node.
+                               if (n0.getContent() != CONTENT_AIR)
+                                       continue;
+                               liquid_kind = CONTENT_AIR;
+                               break;
+               }
+               
+               /*
+                       Collect information about the environment
+                */
+               v3s16 dirs[6] = {
+                       v3s16( 0, 1, 0), // top
+                       v3s16( 0,-1, 0), // bottom
+                       v3s16( 1, 0, 0), // right
+                       v3s16(-1, 0, 0), // left
+                       v3s16( 0, 0, 1), // back
+                       v3s16( 0, 0,-1), // front
+               };
+               NodeNeighbor sources[6]; // surrounding sources
+               int num_sources = 0;
+               NodeNeighbor flows[6]; // surrounding flowing liquid nodes
+               int num_flows = 0;
+               NodeNeighbor airs[6]; // surrounding air
+               int num_airs = 0;
+               NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
+               int num_neutrals = 0;
+               bool flowing_down = false;
+               for (u16 i = 0; i < 6; i++) {
+                       NeighborType nt = NEIGHBOR_SAME_LEVEL;
+                       switch (i) {
+                               case 0:
+                                       nt = NEIGHBOR_UPPER;
+                                       break;
+                               case 1:
+                                       nt = NEIGHBOR_LOWER;
+                                       break;
+                       }
+                       v3s16 npos = p0 + dirs[i];
+                       NodeNeighbor nb = {getNodeNoEx(npos), nt, npos};
+                       switch (content_features(nb.n.getContent()).liquid_type) {
+                               case LIQUID_NONE:
+                                       if (nb.n.getContent() == CONTENT_AIR) {
+                                               airs[num_airs++] = nb;
+                                               // if the current node happens to be a flowing node, it will start to flow down here.
+                                               if (nb.t == NEIGHBOR_LOWER)
+                                                       flowing_down = true;
+                                       } else {
+                                               neutrals[num_neutrals++] = nb;
                                        }
-                                       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;
+                                       break;
+                               case LIQUID_SOURCE:
+                                       // if this node is not (yet) of a liquid type, choose the first liquid type we encounter 
+                                       if (liquid_kind == CONTENT_AIR)
+                                               liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
+                                       if (content_features(nb.n.getContent()).liquid_alternative_flowing !=liquid_kind) {
+                                               neutrals[num_neutrals++] = nb;
+                                       } else {
+                                               sources[num_sources++] = nb;
                                        }
-                                       else if(n2_liquid_level > 0)
-                                       {
-                                               new_liquid_level = n2_liquid_level - 1;
+                                       break;
+                               case LIQUID_FLOWING:
+                                       // if this node is not (yet) of a liquid type, choose the first liquid type we encounter
+                                       if (liquid_kind == CONTENT_AIR)
+                                               liquid_kind = content_features(nb.n.getContent()).liquid_alternative_flowing;
+                                       if (content_features(nb.n.getContent()).liquid_alternative_flowing != liquid_kind) {
+                                               neutrals[num_neutrals++] = nb;
+                                       } else {
+                                               flows[num_flows++] = nb;
+                                               if (nb.t == NEIGHBOR_LOWER)
+                                                       flowing_down = true;
                                        }
-
-                                       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);
+                                       break;
+                       }
+               }
+               
+               /*
+                       decide on the type (and possibly level) of the current node
+                */
+               u8 new_node_content;
+               s8 new_node_level = -1;
+               if (num_sources >= 2 || liquid_type == LIQUID_SOURCE) {
+                       // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
+                       // or the flowing alternative of the first of the surrounding sources (if it's air), so
+                       // it's perfectly safe to use liquid_kind here to determine the new node content.
+                       new_node_content = content_features(liquid_kind).liquid_alternative_source;
+               } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
+                       // liquid_kind is set properly, see above
+                       new_node_content = liquid_kind;
+                       new_node_level = 7;
+               } else {
+                       // no surrounding sources, so get the maximum level that can flow into this node
+                       for (u16 i = 0; i < num_flows; i++) {
+                               u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
+                               switch (flows[i].t) {
+                                       case NEIGHBOR_UPPER:
+                                               if (nb_liquid_level + WATER_DROP_BOOST > new_node_level) {
+                                                       new_node_level = 7;
+                                                       if (nb_liquid_level + WATER_DROP_BOOST < 7)
+                                                               new_node_level = nb_liquid_level + WATER_DROP_BOOST;
+                                               }
+                                               break;
+                                       case NEIGHBOR_LOWER:
+                                               break;
+                                       case NEIGHBOR_SAME_LEVEL:
+                                               if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
+                                                       nb_liquid_level > 0 && nb_liquid_level - 1 > new_node_level) {
+                                                       new_node_level = nb_liquid_level - 1;
+                                               }
+                                               break;
                                }
-
-                               /*
-                                       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)
-                                       {
+                       }
+                       // don't flow as far in open terrain - if there isn't at least one adjacent solid block,
+                       // substract another unit from the resulting water level.
+                       if (!flowing_down && new_node_level >= 1) {
+                               bool at_wall = false;
+                               for (u16 i = 0; i < num_neutrals; i++) {
+                                       if (neutrals[i].t == NEIGHBOR_SAME_LEVEL) {
+                                               at_wall = true;
+                                               break;
                                        }
                                }
+                               if (!at_wall)
+                                       new_node_level -= 1;
                        }
+                       
+                       if (new_node_level >= 0)
+                               new_node_content = liquid_kind;
+                       else
+                               new_node_content = CONTENT_AIR;
                }
-
-               // Get a new one from queue if the node has turned into non-water
-               if(content_liquid(n0.d) == false)
+               
+               /*
+                       check if anything has changed. if not, just continue with the next node.
+                */
+               if (new_node_content == n0.getContent() && (content_features(n0.getContent()).liquid_type != LIQUID_FLOWING ||
+                                                                                ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
+                                                                                ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
+                                                                                == flowing_down)))
                        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);
-                       //dstream<<"[1] n2.param="<<(int)n2.param<<std::endl;
-
-                       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;
+                       update the current node
+                */
+               bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
+               n0.setContent(new_node_content);
+               if (content_features(n0.getContent()).liquid_type == LIQUID_FLOWING) {
+                       // set level to last 3 bits, flowing down bit to 4th bit
+                       n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
+               } else {
+                       n0.param2 = 0;
+               }
+               setNode(p0, n0);
+               v3s16 blockpos = getNodeBlockPos(p0);
+               MapBlock *block = getBlockNoCreateNoEx(blockpos);
+               if(block != NULL)
+                       modified_blocks.insert(blockpos, block);
+               
+               /*
+                       enqueue neighbors for update if neccessary
+                */
+               switch (content_features(n0.getContent()).liquid_type) {
+                       case LIQUID_SOURCE:
+                               // make sure source flows into all neighboring nodes
+                               for (u16 i = 0; i < num_flows; i++)
+                                       if (flows[i].t != NEIGHBOR_UPPER)
+                                               m_transforming_liquid.push_back(flows[i].p);
+                               for (u16 i = 0; i < num_airs; i++)
+                                       if (airs[i].t != NEIGHBOR_UPPER)
+                                               m_transforming_liquid.push_back(airs[i].p);
+                               break;
+                       case LIQUID_NONE:
+                               // this flow has turned to air; neighboring flows might need to do the same
+                               for (u16 i = 0; i < num_flows; i++)
+                                       m_transforming_liquid.push_back(flows[i].p);
+                               break;
+                       case LIQUID_FLOWING:
+                               for (u16 i = 0; i < num_flows; i++) {
+                                       u8 flow_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
+                                       // liquid_level is still the ORIGINAL level of this node.
+                                       if (flows[i].t != NEIGHBOR_UPPER && ((flow_level < liquid_level || flow_level < new_node_level) ||
+                                               flow_down_enabled))
+                                               m_transforming_liquid.push_back(flows[i].p);
                                }
-
-                               if(n2_is_source)
-                               {
-                                       // Just flow into the source, nothing changes.
-                                       // n2_changed is not set because destination didn't change
-                                       flowed = true;
+                               for (u16 i = 0; i < num_airs; i++) {
+                                       if (airs[i].t != NEIGHBOR_UPPER && (airs[i].t == NEIGHBOR_LOWER || new_node_level > 0))
+                                               m_transforming_liquid.push_back(airs[i].p);
                                }
-                               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;
-                       }
-
-                       //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
-
-                       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 && !is_source)
                                break;
-
-                       }catch(InvalidPositionException &e)
-                       {
-                       }
                }
-
+               
                loopcount++;
                //if(loopcount >= 100000)
-               if(loopcount >= initial_size * 1)
+               if(loopcount >= initial_size * 10) {
                        break;
+               }
        }
        //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
 }
@@ -1876,1991 +1910,112 @@ ServerMap::ServerMap(std::string savedir):
                                        // Load map metadata (seed, chunksize)
                                        loadMapMeta();
                                }
-                               catch(FileNotGoodException &e){
-                                       dstream<<DTIME<<"WARNING: Could not load map metadata"
-                                                       //<<" Disabling chunk-based generator."
-                                                       <<std::endl;
-                                       //m_chunksize = 0;
-                               }
-
-                               /*try{
-                                       // Load chunk metadata
-                                       loadChunkMeta();
-                               }
-                               catch(FileNotGoodException &e){
-                                       dstream<<DTIME<<"WARNING: Could not load chunk metadata."
-                                                       <<" Disabling chunk-based generator."
-                                                       <<std::endl;
-                                       m_chunksize = 0;
-                               }*/
-
-                               /*dstream<<DTIME<<"Server: Successfully loaded chunk "
-                                               "metadata and sector (0,0) from "<<savedir<<
-                                               ", assuming valid save directory."
-                                               <<std::endl;*/
-
-                               dstream<<DTIME<<"INFO: Server: Successfully loaded map "
-                                               <<"and chunk metadata from "<<savedir
-                                               <<", assuming valid save directory."
-                                               <<std::endl;
-
-                               m_map_saving_enabled = true;
-                               // Map loaded, not creating new one
-                               return;
-                       }
-               }
-               // If directory doesn't exist, it is safe to save to it
-               else{
-                       m_map_saving_enabled = true;
-               }
-       }
-       catch(std::exception &e)
-       {
-               dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
-                               <<", exception: "<<e.what()<<std::endl;
-               dstream<<"Please remove the map or fix it."<<std::endl;
-               dstream<<"WARNING: Map saving will be disabled."<<std::endl;
-       }
-
-       dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
-
-       // Create zero sector
-       emergeSector(v2s16(0,0));
-
-       // Initially write whole map
-       save(false);
-}
-
-ServerMap::~ServerMap()
-{
-       dstream<<__FUNCTION_NAME<<std::endl;
-
-       try
-       {
-               if(m_map_saving_enabled)
-               {
-                       //save(false);
-                       // Save only changed parts
-                       save(true);
-                       dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
-               }
-               else
-               {
-                       dstream<<DTIME<<"Server: map not saved"<<std::endl;
-               }
-       }
-       catch(std::exception &e)
-       {
-               dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
-                               <<", exception: "<<e.what()<<std::endl;
-       }
-
-#if 0
-       /*
-               Free all MapChunks
-       */
-       core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
-       for(; i.atEnd() == false; i++)
-       {
-               MapChunk *chunk = i.getNode()->getValue();
-               delete chunk;
-       }
-#endif
-}
-
-/*
-       Some helper functions for the map generator
-*/
-
-s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
-{
-       v3s16 em = vmanip.m_area.getExtent();
-       s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
-       s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
-       u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
-       s16 y;
-       for(y=y_nodes_max; y>=y_nodes_min; y--)
-       {
-               MapNode &n = vmanip.m_data[i];
-               if(content_walkable(n.d))
-                       break;
-
-               vmanip.m_area.add_y(em, i, -1);
-       }
-       if(y >= y_nodes_min)
-               return y;
-       else
-               return y_nodes_min;
-}
-
-s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
-{
-       v3s16 em = vmanip.m_area.getExtent();
-       s16 y_nodes_max = vmanip.m_area.MaxEdge.Y;
-       s16 y_nodes_min = vmanip.m_area.MinEdge.Y;
-       u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
-       s16 y;
-       for(y=y_nodes_max; y>=y_nodes_min; y--)
-       {
-               MapNode &n = vmanip.m_data[i];
-               if(content_walkable(n.d)
-                               && n.d != CONTENT_TREE
-                               && n.d != CONTENT_LEAVES)
-                       break;
-
-               vmanip.m_area.add_y(em, i, -1);
-       }
-       if(y >= y_nodes_min)
-               return y;
-       else
-               return y_nodes_min;
-}
-
-void make_tree(VoxelManipulator &vmanip, v3s16 p0)
-{
-       MapNode treenode(CONTENT_TREE);
-       MapNode leavesnode(CONTENT_LEAVES);
-
-       s16 trunk_h = myrand_range(3, 6);
-       v3s16 p1 = p0;
-       for(s16 ii=0; ii<trunk_h; ii++)
-       {
-               if(vmanip.m_area.contains(p1))
-                       vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
-               p1.Y++;
-       }
-
-       // p1 is now the last piece of the trunk
-       p1.Y -= 1;
-
-       VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
-       //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
-       Buffer<u8> leaves_d(leaves_a.getVolume());
-       for(s32 i=0; i<leaves_a.getVolume(); i++)
-               leaves_d[i] = 0;
-
-       // Force leaves at near the end of the trunk
-       {
-               s16 d = 1;
-               for(s16 z=-d; z<=d; z++)
-               for(s16 y=-d; y<=d; y++)
-               for(s16 x=-d; x<=d; x++)
-               {
-                       leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
-               }
-       }
-
-       // Add leaves randomly
-       for(u32 iii=0; iii<7; iii++)
-       {
-               s16 d = 1;
-
-               v3s16 p(
-                       myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
-                       myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
-                       myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
-               );
-
-               for(s16 z=0; z<=d; z++)
-               for(s16 y=0; y<=d; y++)
-               for(s16 x=0; x<=d; x++)
-               {
-                       leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
-               }
-       }
-
-       // Blit leaves to vmanip
-       for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
-       for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
-       for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
-       {
-               v3s16 p(x,y,z);
-               p += p1;
-               if(vmanip.m_area.contains(p) == false)
-                       continue;
-               u32 vi = vmanip.m_area.index(p);
-               if(vmanip.m_data[vi].d != CONTENT_AIR
-                               && vmanip.m_data[vi].d != CONTENT_IGNORE)
-                       continue;
-               u32 i = leaves_a.index(x,y,z);
-               if(leaves_d[i] == 1)
-                       vmanip.m_data[vi] = leavesnode;
-       }
-}
-
-#if 0
-void make_randomstone(VoxelManipulator &vmanip, v3s16 p0)
-{
-       MapNode stonenode(CONTENT_STONE);
-
-       s16 size = myrand_range(3, 6);
-       
-       VoxelArea stone_a(v3s16(-2,0,-2), v3s16(2,size,2));
-       Buffer<u8> stone_d(stone_a.getVolume());
-       for(s32 i=0; i<stone_a.getVolume(); i++)
-               stone_d[i] = 0;
-
-       // Force stone at bottom to make it usually touch the ground
-       {
-               for(s16 z=0; z<=0; z++)
-               for(s16 y=0; y<=0; y++)
-               for(s16 x=0; x<=0; x++)
-               {
-                       stone_d[stone_a.index(v3s16(x,y,z))] = 1;
-               }
-       }
-
-       // Generate from perlin noise
-       for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
-       for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
-       for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
-       {
-               double d = noise3d_perlin((float)x/3.,(float)z/3.,(float)y/3.,
-                               p0.Z*4243+p0.Y*34+p0.X, 2, 0.5);
-               if(z == stone_a.MinEdge.Z || z == stone_a.MaxEdge.Z)
-                       d -= 0.3;
-               if(/*y == stone_a.MinEdge.Y ||*/ y == stone_a.MaxEdge.Y)
-                       d -= 0.3;
-               if(x == stone_a.MinEdge.X || x == stone_a.MaxEdge.X)
-                       d -= 0.3;
-               if(d > 0.0)
-               {
-                       u32 vi = stone_a.index(v3s16(x,y,z));
-                       stone_d[vi] = 1;
-               }
-       }
-
-       /*// Add stone randomly
-       for(u32 iii=0; iii<7; iii++)
-       {
-               s16 d = 1;
-
-               v3s16 p(
-                       myrand_range(stone_a.MinEdge.X, stone_a.MaxEdge.X-d),
-                       myrand_range(stone_a.MinEdge.Y, stone_a.MaxEdge.Y-d),
-                       myrand_range(stone_a.MinEdge.Z, stone_a.MaxEdge.Z-d)
-               );
-
-               for(s16 z=0; z<=d; z++)
-               for(s16 y=0; y<=d; y++)
-               for(s16 x=0; x<=d; x++)
-               {
-                       stone_d[stone_a.index(p+v3s16(x,y,z))] = 1;
-               }
-       }*/
-
-       // Blit stone to vmanip
-       for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
-       for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
-       for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
-       {
-               v3s16 p(x,y,z);
-               p += p0;
-               if(vmanip.m_area.contains(p) == false)
-                       continue;
-               u32 vi = vmanip.m_area.index(p);
-               if(vmanip.m_data[vi].d != CONTENT_AIR
-                               && vmanip.m_data[vi].d != CONTENT_IGNORE)
-                       continue;
-               u32 i = stone_a.index(x,y,z);
-               if(stone_d[i] == 1)
-                       vmanip.m_data[vi] = stonenode;
-       }
-}
-#endif
-
-#if 0
-void make_largestone(VoxelManipulator &vmanip, v3s16 p0)
-{
-       MapNode stonenode(CONTENT_STONE);
-
-       s16 size = myrand_range(8, 16);
-       
-       VoxelArea stone_a(v3s16(-size/2,0,-size/2), v3s16(size/2,size,size/2));
-       Buffer<u8> stone_d(stone_a.getVolume());
-       for(s32 i=0; i<stone_a.getVolume(); i++)
-               stone_d[i] = 0;
-
-       // Force stone at bottom to make it usually touch the ground
-       {
-               for(s16 z=0; z<=0; z++)
-               for(s16 y=0; y<=0; y++)
-               for(s16 x=0; x<=0; x++)
-               {
-                       stone_d[stone_a.index(v3s16(x,y,z))] = 1;
-               }
-       }
-
-       // Generate from perlin noise
-       for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
-       for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
-       for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
-       {
-               double d = 1.0;
-               d += noise3d_perlin((float)x/10.,(float)z/10.,(float)y/10.,
-                               p0.Z*5123+p0.Y*2439+p0.X, 2, 0.5);
-               double mid_z = (stone_a.MaxEdge.Z+stone_a.MinEdge.Z)/2;
-               double mid_x = (stone_a.MaxEdge.X+stone_a.MinEdge.X)/2;
-               double mid_y = (stone_a.MaxEdge.Y+stone_a.MinEdge.Y)/2;
-               double dz = (double)z-mid_z;
-               double dx = (double)x-mid_x;
-               double dy = MYMAX(0, (double)y-mid_y);
-               double r = sqrt(dz*dz+dx*dx+dy*dy);
-               d /= (2*r/size)*2 + 0.01;
-               if(d > 1.0)
-               {
-                       u32 vi = stone_a.index(v3s16(x,y,z));
-                       stone_d[vi] = 1;
-               }
-       }
-
-       /*// Add stone randomly
-       for(u32 iii=0; iii<7; iii++)
-       {
-               s16 d = 1;
-
-               v3s16 p(
-                       myrand_range(stone_a.MinEdge.X, stone_a.MaxEdge.X-d),
-                       myrand_range(stone_a.MinEdge.Y, stone_a.MaxEdge.Y-d),
-                       myrand_range(stone_a.MinEdge.Z, stone_a.MaxEdge.Z-d)
-               );
-
-               for(s16 z=0; z<=d; z++)
-               for(s16 y=0; y<=d; y++)
-               for(s16 x=0; x<=d; x++)
-               {
-                       stone_d[stone_a.index(p+v3s16(x,y,z))] = 1;
-               }
-       }*/
-
-       // Blit stone to vmanip
-       for(s16 z=stone_a.MinEdge.Z; z<=stone_a.MaxEdge.Z; z++)
-       for(s16 y=stone_a.MinEdge.Y; y<=stone_a.MaxEdge.Y; y++)
-       for(s16 x=stone_a.MinEdge.X; x<=stone_a.MaxEdge.X; x++)
-       {
-               v3s16 p(x,y,z);
-               p += p0;
-               if(vmanip.m_area.contains(p) == false)
-                       continue;
-               u32 vi = vmanip.m_area.index(p);
-               /*if(vmanip.m_data[vi].d != CONTENT_AIR
-                               && vmanip.m_data[vi].d != CONTENT_IGNORE)
-                       continue;*/
-               u32 i = stone_a.index(x,y,z);
-               if(stone_d[i] == 1)
-                       vmanip.m_data[vi] = stonenode;
-       }
-}
-#endif
-
-/*
-       Dungeon making routines
-*/
-
-#define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1
-#define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2
-#define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\
-               VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE)
-
-void make_room1(VoxelManipulator &vmanip, v3s16 roomsize, v3s16 roomplace)
-{
-       // Make +-X walls
-       for(s16 z=0; z<roomsize.Z; z++)
-       for(s16 y=0; y<roomsize.Y; y++)
-       {
-               {
-                       v3s16 p = roomplace + v3s16(0,y,z);
-                       if(vmanip.m_area.contains(p) == false)
-                               continue;
-                       u32 vi = vmanip.m_area.index(p);
-                       if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-                               continue;
-                       vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
-               }
-               {
-                       v3s16 p = roomplace + v3s16(roomsize.X-1,y,z);
-                       if(vmanip.m_area.contains(p) == false)
-                               continue;
-                       u32 vi = vmanip.m_area.index(p);
-                       if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-                               continue;
-                       vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
-               }
-       }
-       
-       // Make +-Z walls
-       for(s16 x=0; x<roomsize.X; x++)
-       for(s16 y=0; y<roomsize.Y; y++)
-       {
-               {
-                       v3s16 p = roomplace + v3s16(x,y,0);
-                       if(vmanip.m_area.contains(p) == false)
-                               continue;
-                       u32 vi = vmanip.m_area.index(p);
-                       if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-                               continue;
-                       vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
-               }
-               {
-                       v3s16 p = roomplace + v3s16(x,y,roomsize.Z-1);
-                       if(vmanip.m_area.contains(p) == false)
-                               continue;
-                       u32 vi = vmanip.m_area.index(p);
-                       if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-                               continue;
-                       vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
-               }
-       }
-       
-       // Make +-Y walls (floor and ceiling)
-       for(s16 z=0; z<roomsize.Z; z++)
-       for(s16 x=0; x<roomsize.X; x++)
-       {
-               {
-                       v3s16 p = roomplace + v3s16(x,0,z);
-                       if(vmanip.m_area.contains(p) == false)
-                               continue;
-                       u32 vi = vmanip.m_area.index(p);
-                       if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-                               continue;
-                       vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
-               }
-               {
-                       v3s16 p = roomplace + v3s16(x,roomsize.Y-1,z);
-                       if(vmanip.m_area.contains(p) == false)
-                               continue;
-                       u32 vi = vmanip.m_area.index(p);
-                       if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-                               continue;
-                       vmanip.m_data[vi] = MapNode(CONTENT_COBBLE);
-               }
-       }
-       
-       // Fill with air
-       for(s16 z=1; z<roomsize.Z-1; z++)
-       for(s16 y=1; y<roomsize.Y-1; y++)
-       for(s16 x=1; x<roomsize.X-1; x++)
-       {
-               v3s16 p = roomplace + v3s16(x,y,z);
-               if(vmanip.m_area.contains(p) == false)
-                       continue;
-               u32 vi = vmanip.m_area.index(p);
-               vmanip.m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
-               vmanip.m_data[vi] = MapNode(CONTENT_AIR);
-       }
-}
-
-void make_fill(VoxelManipulator &vmanip, v3s16 place, v3s16 size,
-               u8 avoid_flags, MapNode n, u8 or_flags)
-{
-       for(s16 z=0; z<size.Z; z++)
-       for(s16 y=0; y<size.Y; y++)
-       for(s16 x=0; x<size.X; x++)
-       {
-               v3s16 p = place + v3s16(x,y,z);
-               if(vmanip.m_area.contains(p) == false)
-                       continue;
-               u32 vi = vmanip.m_area.index(p);
-               if(vmanip.m_flags[vi] & avoid_flags)
-                       continue;
-               vmanip.m_flags[vi] |= or_flags;
-               vmanip.m_data[vi] = n;
-       }
-}
-
-void make_hole1(VoxelManipulator &vmanip, v3s16 place)
-{
-       make_fill(vmanip, place, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
-                       VMANIP_FLAG_DUNGEON_INSIDE);
-}
-
-void make_door1(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir)
-{
-       make_hole1(vmanip, doorplace);
-       // Place torch (for testing)
-       //vmanip.m_data[vmanip.m_area.index(doorplace)] = MapNode(CONTENT_TORCH);
-}
-
-v3s16 rand_ortho_dir(PseudoRandom &random)
-{
-       if(random.next()%2==0)
-               return random.next()%2 ? v3s16(-1,0,0) : v3s16(1,0,0);
-       else
-               return random.next()%2 ? v3s16(0,0,-1) : v3s16(0,0,1);
-}
-
-v3s16 turn_xz(v3s16 olddir, int t)
-{
-       v3s16 dir;
-       if(t == 0)
-       {
-               // Turn right
-               dir.X = olddir.Z;
-               dir.Z = -olddir.X;
-               dir.Y = olddir.Y;
-       }
-       else
-       {
-               // Turn left
-               dir.X = -olddir.Z;
-               dir.Z = olddir.X;
-               dir.Y = olddir.Y;
-       }
-       return dir;
-}
-
-v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
-{
-       int turn = random.range(0,2);
-       v3s16 dir;
-       if(turn == 0)
-       {
-               // Go straight
-               dir = olddir;
-       }
-       else if(turn == 1)
-               // Turn right
-               dir = turn_xz(olddir, 0);
-       else
-               // Turn left
-               dir = turn_xz(olddir, 1);
-       return dir;
-}
-
-void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir,
-               v3s16 &result_place, v3s16 &result_dir, PseudoRandom &random)
-{
-       make_hole1(vmanip, doorplace);
-       v3s16 p0 = doorplace;
-       v3s16 dir = doordir;
-       u32 length;
-       if(random.next()%2)
-               length = random.range(1,13);
-       else
-               length = random.range(1,6);
-       length = random.range(1,13);
-       u32 partlength = random.range(1,length);
-       u32 partcount = 0;
-       s16 make_stairs = 0;
-       if(random.next()%2 == 0 && partlength >= 3)
-               make_stairs = random.next()%2 ? 1 : -1;
-       for(u32 i=0; i<length; i++)
-       {
-               v3s16 p = p0 + dir;
-               if(partcount != 0)
-                       p.Y += make_stairs;
-
-               /*// If already empty
-               if(vmanip.getNodeNoExNoEmerge(p).d
-                               == CONTENT_AIR
-               && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
-                               == CONTENT_AIR)
-               {
-               }*/
-
-               if(vmanip.m_area.contains(p) == true
-                               && vmanip.m_area.contains(p+v3s16(0,1,0)) == true)
-               {
-                       if(make_stairs)
-                       {
-                               make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,5,3),
-                                               VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(CONTENT_COBBLE), 0);
-                               make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
-                                               VMANIP_FLAG_DUNGEON_INSIDE);
-                               make_fill(vmanip, p-dir, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
-                                               VMANIP_FLAG_DUNGEON_INSIDE);
-                       }
-                       else
-                       {
-                               make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,4,3),
-                                               VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(CONTENT_COBBLE), 0);
-                               make_hole1(vmanip, p);
-                               /*make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR),
-                                               VMANIP_FLAG_DUNGEON_INSIDE);*/
-                       }
-
-                       p0 = p;
-               }
-               else
-               {
-                       // Can't go here, turn away
-                       dir = turn_xz(dir, random.range(0,1));
-                       make_stairs = -make_stairs;
-                       partcount = 0;
-                       partlength = random.range(1,length);
-                       continue;
-               }
-
-               partcount++;
-               if(partcount >= partlength)
-               {
-                       partcount = 0;
-                       
-                       dir = random_turn(random, dir);
-                       
-                       partlength = random.range(1,length);
-
-                       make_stairs = 0;
-                       if(random.next()%2 == 0 && partlength >= 3)
-                               make_stairs = random.next()%2 ? 1 : -1;
-               }
-       }
-       result_place = p0;
-       result_dir = dir;
-}
-
-class RoomWalker
-{
-public:
-
-       RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random):
-                       vmanip(vmanip_),
-                       m_pos(pos),
-                       m_random(random)
-       {
-               randomizeDir();
-       }
-
-       void randomizeDir()
-       {
-               m_dir = rand_ortho_dir(m_random);
-       }
-
-       void setPos(v3s16 pos)
-       {
-               m_pos = pos;
-       }
-
-       void setDir(v3s16 dir)
-       {
-               m_dir = dir;
-       }
-       
-       bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
-       {
-               for(u32 i=0; i<100; i++)
-               {
-                       v3s16 p = m_pos + m_dir;
-                       v3s16 p1 = p + v3s16(0,1,0);
-                       if(vmanip.m_area.contains(p) == false
-                                       || vmanip.m_area.contains(p1) == false
-                                       || i % 4 == 0)
-                       {
-                               randomizeDir();
-                               continue;
-                       }
-                       if(vmanip.getNodeNoExNoEmerge(p).d
-                                       == CONTENT_COBBLE
-                       && vmanip.getNodeNoExNoEmerge(p1).d
-                                       == CONTENT_COBBLE)
-                       {
-                               // Found wall, this is a good place!
-                               result_place = p;
-                               result_dir = m_dir;
-                               // Randomize next direction
-                               randomizeDir();
-                               return true;
-                       }
-                       /*
-                               Determine where to move next
-                       */
-                       // Jump one up if the actual space is there
-                       if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).d
-                                       == CONTENT_COBBLE
-                       && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
-                                       == CONTENT_AIR
-                       && vmanip.getNodeNoExNoEmerge(p+v3s16(0,2,0)).d
-                                       == CONTENT_AIR)
-                               p += v3s16(0,1,0);
-                       // Jump one down if the actual space is there
-                       if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
-                                       == CONTENT_COBBLE
-                       && vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).d
-                                       == CONTENT_AIR
-                       && vmanip.getNodeNoExNoEmerge(p+v3s16(0,-1,0)).d
-                                       == CONTENT_AIR)
-                               p += v3s16(0,-1,0);
-                       // Check if walking is now possible
-                       if(vmanip.getNodeNoExNoEmerge(p).d
-                                       != CONTENT_AIR
-                       || vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).d
-                                       != CONTENT_AIR)
-                       {
-                               // Cannot continue walking here
-                               randomizeDir();
-                               continue;
-                       }
-                       // Move there
-                       m_pos = p;
-               }
-               return false;
-       }
-
-       bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
-                       v3s16 &result_doordir, v3s16 &result_roomplace)
-       {
-               for(s16 trycount=0; trycount<30; trycount++)
-               {
-                       v3s16 doorplace;
-                       v3s16 doordir;
-                       bool r = findPlaceForDoor(doorplace, doordir);
-                       if(r == false)
-                               continue;
-                       v3s16 roomplace;
-                       // X east, Z north, Y up
-                       if(doordir == v3s16(1,0,0)) // X+
-                               roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2+m_random.range(-roomsize.Z/2+1,roomsize.Z/2-1));
-                       if(doordir == v3s16(-1,0,0)) // X-
-                               roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2+m_random.range(-roomsize.Z/2+1,roomsize.Z/2-1));
-                       if(doordir == v3s16(0,0,1)) // Z+
-                               roomplace = doorplace + v3s16(-roomsize.X/2+m_random.range(-roomsize.X/2+1,roomsize.X/2-1),-1,0);
-                       if(doordir == v3s16(0,0,-1)) // Z-
-                               roomplace = doorplace + v3s16(-roomsize.X/2+m_random.range(-roomsize.X/2+1,roomsize.X/2-1),-1,-roomsize.Z+1);
-                       
-                       // Check fit
-                       bool fits = true;
-                       for(s16 z=1; z<roomsize.Z-1; z++)
-                       for(s16 y=1; y<roomsize.Y-1; y++)
-                       for(s16 x=1; x<roomsize.X-1; x++)
-                       {
-                               v3s16 p = roomplace + v3s16(x,y,z);
-                               if(vmanip.m_area.contains(p) == false)
-                               {
-                                       fits = false;
-                                       break;
-                               }
-                               if(vmanip.m_flags[vmanip.m_area.index(p)]
-                                               & VMANIP_FLAG_DUNGEON_INSIDE)
-                               {
-                                       fits = false;
-                                       break;
-                               }
-                       }
-                       if(fits == false)
-                       {
-                               // Find new place
-                               continue;
-                       }
-                       result_doorplace = doorplace;
-                       result_doordir = doordir;
-                       result_roomplace = roomplace;
-                       return true;
-               }
-               return false;
-       }
-
-private:
-       VoxelManipulator &vmanip;
-       v3s16 m_pos;
-       v3s16 m_dir;
-       PseudoRandom &m_random;
-};
-
-void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random)
-{
-       v3s16 areasize = vmanip.m_area.getExtent();
-       v3s16 roomsize;
-       v3s16 roomplace;
-       
-       /*
-               Find place for first room
-       */
-       bool fits = false;
-       for(u32 i=0; i<100; i++)
-       {
-               roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
-               roomplace = vmanip.m_area.MinEdge + v3s16(
-                               random.range(0,areasize.X-roomsize.X-1),
-                               random.range(0,areasize.Y-roomsize.Y-1),
-                               random.range(0,areasize.Z-roomsize.Z-1));
-               /*
-                       Check that we're not putting the room to an unknown place,
-                       otherwise it might end up floating in the air
-               */
-               fits = true;
-               for(s16 z=1; z<roomsize.Z-1; z++)
-               for(s16 y=1; y<roomsize.Y-1; y++)
-               for(s16 x=1; x<roomsize.X-1; x++)
-               {
-                       v3s16 p = roomplace + v3s16(x,y,z);
-                       u32 vi = vmanip.m_area.index(p);
-                       if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE)
-                       {
-                               fits = false;
-                               break;
-                       }
-                       if(vmanip.m_data[vi].d == CONTENT_IGNORE)
-                       {
-                               fits = false;
-                               break;
-                       }
-               }
-               if(fits)
-                       break;
-       }
-       // No place found
-       if(fits == false)
-               return;
-       
-       /*
-               Stores the center position of the last room made, so that
-               a new corridor can be started from the last room instead of
-               the new room, if chosen so.
-       */
-       v3s16 last_room_center = roomplace+v3s16(roomsize.X/2,1,roomsize.Z/2);
-       
-       u32 room_count = random.range(2,7);
-       for(u32 i=0; i<room_count; i++)
-       {
-               // Make a room to the determined place
-               make_room1(vmanip, roomsize, roomplace);
-               
-               v3s16 room_center = roomplace + v3s16(roomsize.X/2,1,roomsize.Z/2);
-
-               // Place torch at room center (for testing)
-               //vmanip.m_data[vmanip.m_area.index(room_center)] = MapNode(CONTENT_TORCH);
-
-               // Quit if last room
-               if(i == room_count-1)
-                       break;
-               
-               // Determine walker start position
-
-               bool start_in_last_room = (random.range(0,1)==0);
-               //bool start_in_last_room = true;
-
-               v3s16 walker_start_place;
-
-               if(start_in_last_room)
-               {
-                       walker_start_place = last_room_center;
-               }
-               else
-               {
-                       walker_start_place = room_center;
-                       // Store center of current room as the last one
-                       last_room_center = room_center;
-               }
-               
-               // Create walker and find a place for a door
-               RoomWalker walker(vmanip, walker_start_place, random);
-               v3s16 doorplace;
-               v3s16 doordir;
-               bool r = walker.findPlaceForDoor(doorplace, doordir);
-               if(r == false)
-                       return;
-               
-               if(random.range(0,1)==0)
-                       // Make the door
-                       make_door1(vmanip, doorplace, doordir);
-               else
-                       // Don't actually make a door
-                       doorplace -= doordir;
-               
-               // Make a random corridor starting from the door
-               v3s16 corridor_end;
-               v3s16 corridor_end_dir;
-               make_corridor(vmanip, doorplace, doordir, corridor_end,
-                               corridor_end_dir, random);
-               
-               // Find a place for a random sized room
-               roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
-               walker.setPos(corridor_end);
-               walker.setDir(corridor_end_dir);
-               r = walker.findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace);
-               if(r == false)
-                       return;
-
-               if(random.range(0,1)==0)
-                       // Make the door
-                       make_door1(vmanip, doorplace, doordir);
-               else
-                       // Don't actually make a door
-                       roomplace -= doordir;
-               
-       }
-}
-
-/*
-       Noise functions. Make sure seed is mangled differently in each one.
-*/
-
-// This affects the shape of the contour
-//#define CAVE_NOISE_SCALE 10.0
-//#define CAVE_NOISE_SCALE 7.5
-#define CAVE_NOISE_SCALE 5.0
-
-NoiseParams get_cave_noise1_params(u64 seed)
-{
-       /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.7,
-                       200, CAVE_NOISE_SCALE);*/
-       return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.7,
-                       100, CAVE_NOISE_SCALE);
-}
-
-NoiseParams get_cave_noise2_params(u64 seed)
-{
-       /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.7,
-                       200, CAVE_NOISE_SCALE);*/
-       return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.7,
-                       100, CAVE_NOISE_SCALE);
-}
-
-//#define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE)
-#define CAVE_NOISE_THRESHOLD (2.0/CAVE_NOISE_SCALE)
-
-NoiseParams get_ground_noise1_params(u64 seed)
-{
-       return NoiseParams(NOISE_PERLIN, seed+983240, 5,
-                       0.60, 100.0, 30.0);
-}
-
-NoiseParams get_ground_crumbleness_params(u64 seed)
-{
-       return NoiseParams(NOISE_PERLIN, seed+34413, 3,
-                       1.3, 20.0, 1.0);
-}
-
-NoiseParams get_ground_wetness_params(u64 seed)
-{
-       return NoiseParams(NOISE_PERLIN, seed+32474, 4,
-                       1.1, 40.0, 1.0);
-}
-
-bool is_cave(u64 seed, v3s16 p)
-{
-       double d1 = noise3d_param(get_cave_noise1_params(seed), p.X,p.Y,p.Z);
-       double d2 = noise3d_param(get_cave_noise2_params(seed), p.X,p.Y,p.Z);
-       return d1*d2 > CAVE_NOISE_THRESHOLD;
-}
-
-/*
-       Ground density noise shall be interpreted by using this.
-
-       TODO: No perlin noises here, they should be outsourced
-             and buffered
-*/
-bool val_is_ground(double ground_noise1_val, v3s16 p, u64 seed)
-{
-       //return ((double)p.Y < ground_noise1_val);
-
-       double f = 0.8 + noise2d_perlin(
-                       0.5+(float)p.X/250, 0.5+(float)p.Z/250,
-                       seed+920381, 3, 0.5);
-       if(f < 0.01)
-               f = 0.01;
-       else if(f >= 1.0)
-               f *= 2.0;
-       double h = WATER_LEVEL + 10 * noise2d_perlin(
-                       0.5+(float)p.X/250, 0.5+(float)p.Z/250,
-                       seed+84174, 4, 0.5);
-       return ((double)p.Y - h < ground_noise1_val * f);
-}
-
-/*
-       Queries whether a position is ground or not.
-*/
-bool is_ground(u64 seed, v3s16 p)
-{
-       double val1 = noise3d_param(get_ground_noise1_params(seed), p.X,p.Y,p.Z);
-       return val_is_ground(val1, p, seed);
-}
-
-// Amount of trees per area in nodes
-double tree_amount_2d(u64 seed, v2s16 p)
-{
-       /*double noise = noise2d_perlin(
-                       0.5+(float)p.X/250, 0.5+(float)p.Y/250,
-                       seed+2, 5, 0.66);*/
-       double noise = noise2d_perlin(
-                       0.5+(float)p.X/125, 0.5+(float)p.Y/125,
-                       seed+2, 4, 0.66);
-       double zeroval = -0.35;
-       if(noise < zeroval)
-               return 0;
-       else
-               return 0.04 * (noise-zeroval) / (1.0-zeroval);
-}
-
-#if 0
-double randomstone_amount_2d(u64 seed, v2s16 p)
-{
-       double noise = noise2d_perlin(
-                       0.5+(float)p.X/250, 0.5+(float)p.Y/250,
-                       seed+3829434, 5, 0.66);
-       double zeroval = 0.1;
-       if(noise < zeroval)
-               return 0;
-       else
-               return 0.01 * (noise-zeroval) / (1.0-zeroval);
-}
-#endif
-
-double largestone_amount_2d(u64 seed, v2s16 p)
-{
-       double noise = noise2d_perlin(
-                       0.5+(float)p.X/250, 0.5+(float)p.Y/250,
-                       seed+14143242, 5, 0.66);
-       double zeroval = 0.3;
-       if(noise < zeroval)
-               return 0;
-       else
-               return 0.005 * (noise-zeroval) / (1.0-zeroval);
-}
-
-/*
-       Incrementally find ground level from 3d noise
-*/
-s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
-{
-       // Start a bit fuzzy to make averaging lower precision values
-       // more useful
-       s16 level = myrand_range(-precision/2, precision/2);
-       s16 dec[] = {31000, 100, 20, 4, 1, 0};
-       s16 i;
-       for(i = 1; dec[i] != 0 && precision <= dec[i]; i++)
-       {
-               // First find non-ground by going upwards
-               // Don't stop in caves.
-               {
-                       s16 max = level+dec[i-1]*2;
-                       v3s16 p(p2d.X, level, p2d.Y);
-                       for(; p.Y < max; p.Y += dec[i])
-                       {
-                               if(!is_ground(seed, p))
-                               {
-                                       level = p.Y;
-                                       break;
-                               }
-                       }
-               }
-               // Then find ground by going downwards from there.
-               // Go in caves, too, when precision is 1.
-               {
-                       s16 min = level-dec[i-1]*2;
-                       v3s16 p(p2d.X, level, p2d.Y);
-                       for(; p.Y>min; p.Y-=dec[i])
-                       {
-                               bool ground = is_ground(seed, p);
-                               /*if(dec[i] == 1 && is_cave(seed, p))
-                                       ground = false;*/
-                               if(ground)
-                               {
-                                       level = p.Y;
-                                       break;
-                               }
-                       }
-               }
-       }
-       
-       // This is more like the actual ground level
-       level += dec[i-1]/2;
-
-       return level;
-}
-
-double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p=4);
-
-double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p)
-{
-       v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
-       v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
-       double a = 0;
-       a += find_ground_level_from_noise(seed,
-                       v2s16(node_min.X, node_min.Y), p);
-       a += find_ground_level_from_noise(seed,
-                       v2s16(node_min.X, node_max.Y), p);
-       a += find_ground_level_from_noise(seed,
-                       v2s16(node_max.X, node_max.Y), p);
-       a += find_ground_level_from_noise(seed,
-                       v2s16(node_max.X, node_min.Y), p);
-       a += find_ground_level_from_noise(seed,
-                       v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p);
-       a /= 5;
-       return a;
-}
-
-double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p=4);
-
-double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p)
-{
-       v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
-       v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
-       double a = -31000;
-       a = MYMAX(a, find_ground_level_from_noise(seed,
-                       v2s16(node_min.X, node_min.Y), p));
-       a = MYMAX(a, find_ground_level_from_noise(seed,
-                       v2s16(node_min.X, node_max.Y), p));
-       a = MYMAX(a, find_ground_level_from_noise(seed,
-                       v2s16(node_max.X, node_max.Y), p));
-       a = MYMAX(a, find_ground_level_from_noise(seed,
-                       v2s16(node_min.X, node_min.Y), p));
-       a = MYMAX(a, find_ground_level_from_noise(seed,
-                       v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
-       return a;
-}
-
-double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p=4);
-
-double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p)
-{
-       v2s16 node_min = sectorpos*MAP_BLOCKSIZE;
-       v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1);
-       double a = 31000;
-       a = MYMIN(a, find_ground_level_from_noise(seed,
-                       v2s16(node_min.X, node_min.Y), p));
-       a = MYMIN(a, find_ground_level_from_noise(seed,
-                       v2s16(node_min.X, node_max.Y), p));
-       a = MYMIN(a, find_ground_level_from_noise(seed,
-                       v2s16(node_max.X, node_max.Y), p));
-       a = MYMIN(a, find_ground_level_from_noise(seed,
-                       v2s16(node_min.X, node_min.Y), p));
-       a = MYMIN(a, find_ground_level_from_noise(seed,
-                       v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p));
-       return a;
-}
-
-#if 0
-#define AVERAGE_MUD_AMOUNT 4
-
-double base_rock_level_2d(u64 seed, v2s16 p)
-{
-       // The base ground level
-       double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
-                       + 20. * noise2d_perlin(
-                       0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
-                       (seed>>32)+654879876, 6, 0.6);
-
-       /*// A bit hillier one
-       double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin(
-                       0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-                       (seed>>27)+90340, 6, 0.69);
-       if(base2 > base)
-               base = base2;*/
-#if 1
-       // Higher ground level
-       double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin(
-                       0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-                       seed+85039, 5, 0.69);
-       //higher = 30; // For debugging
-
-       // Limit higher to at least base
-       if(higher < base)
-               higher = base;
-
-       // Steepness factor of cliffs
-       double b = 1.0 + 1.0 * noise2d_perlin(
-                       0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-                       seed-932, 7, 0.7);
-       b = rangelim(b, 0.0, 1000.0);
-       b = pow(b, 5);
-       b *= 7;
-       b = rangelim(b, 3.0, 1000.0);
-       //dstream<<"b="<<b<<std::endl;
-       //double b = 20;
-
-       // Offset to more low
-       double a_off = -0.2;
-       // High/low selector
-       /*double a = 0.5 + b * (a_off + noise2d_perlin(
-                       0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
-                       seed-359, 6, 0.7));*/
-       double a = (double)0.5 + b * (a_off + noise2d_perlin(
-                       0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
-                       seed-359, 5, 0.60));
-       // Limit
-       a = rangelim(a, 0.0, 1.0);
-
-       //dstream<<"a="<<a<<std::endl;
-
-       double h = base*(1.0-a) + higher*a;
-#else
-       double h = base;
-#endif
-       return h;
-}
-
-double get_mud_add_amount(u64 seed, v2s16 p)
-{
-       return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
-                       0.5+(float)p.X/200, 0.5+(float)p.Y/200,
-                       seed+91013, 3, 0.55));
-}
-#endif
-
-bool get_have_sand(u64 seed, v2s16 p2d)
-{
-       // Determine whether to have sand here
-       double sandnoise = noise2d_perlin(
-                       0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
-                       seed+59420, 3, 0.50);
-
-       return (sandnoise > -0.15);
-}
-
-/*
-       Adds random objects to block, depending on the content of the block
-*/
-void addRandomObjects(MapBlock *block)
-{
-       for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
-       for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
-       {
-               bool last_node_walkable = false;
-               for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
-               {
-                       v3s16 p(x0,y0,z0);
-                       MapNode n = block->getNodeNoEx(p);
-                       if(n.d == CONTENT_IGNORE)
-                               continue;
-                       if(content_features(n.d).liquid_type != LIQUID_NONE)
-                               continue;
-                       if(content_features(n.d).walkable)
-                       {
-                               last_node_walkable = true;
-                               continue;
-                       }
-                       if(last_node_walkable)
-                       {
-                               // If block contains light information
-                               if(content_features(n.d).param_type == CPT_LIGHT)
-                               {
-                                       if(n.getLight(LIGHTBANK_DAY) <= 3)
-                                       {
-                                               if(myrand() % 300 == 0)
-                                               {
-                                                       v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
-                                                       pos_f.Y -= BS*0.4;
-                                                       ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
-                                                       std::string data = obj->getStaticData();
-                                                       StaticObject s_obj(obj->getType(),
-                                                                       obj->getBasePosition(), data);
-                                                       // Add some
-                                                       block->m_static_objects.insert(0, s_obj);
-                                                       block->m_static_objects.insert(0, s_obj);
-                                                       block->m_static_objects.insert(0, s_obj);
-                                                       block->m_static_objects.insert(0, s_obj);
-                                                       block->m_static_objects.insert(0, s_obj);
-                                                       block->m_static_objects.insert(0, s_obj);
-                                                       delete obj;
-                                               }
-                                               if(myrand() % 1000 == 0)
-                                               {
-                                                       v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
-                                                       pos_f.Y -= BS*0.4;
-                                                       ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
-                                                       std::string data = obj->getStaticData();
-                                                       StaticObject s_obj(obj->getType(),
-                                                                       obj->getBasePosition(), data);
-                                                       // Add one
-                                                       block->m_static_objects.insert(0, s_obj);
-                                                       delete obj;
-                                               }
-                                       }
-                               }
-                       }
-                       last_node_walkable = false;
-               }
-       }
-       block->setChangedFlag();
-}
-
-void makeBlock(BlockMakeData *data)
-{
-       if(data->no_op)
-       {
-               dstream<<"makeBlock: no-op"<<std::endl;
-               return;
-       }
-
-       v3s16 blockpos = data->blockpos;
-       
-       /*dstream<<"makeBlock(): ("<<blockpos.X<<","<<blockpos.Y<<","
-                       <<blockpos.Z<<")"<<std::endl;*/
-
-       ManualMapVoxelManipulator &vmanip = data->vmanip;
-       v3s16 blockpos_min = blockpos - v3s16(1,1,1);
-       v3s16 blockpos_max = blockpos + v3s16(1,1,1);
-       // Area of center block
-       v3s16 node_min = blockpos*MAP_BLOCKSIZE;
-       v3s16 node_max = (blockpos+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1);
-       // Full allocated area
-       v3s16 full_node_min = (blockpos-1)*MAP_BLOCKSIZE;
-       v3s16 full_node_max = (blockpos+2)*MAP_BLOCKSIZE-v3s16(1,1,1);
-       // Area of a block
-       double block_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE;
-
-       v2s16 p2d_center(node_min.X+MAP_BLOCKSIZE/2, node_min.Z+MAP_BLOCKSIZE/2);
-
-       /*
-               Get average ground level from noise
-       */
-       
-       s16 approx_groundlevel = (s16)get_sector_average_ground_level(
-                       data->seed, v2s16(blockpos.X, blockpos.Z));
-       //dstream<<"approx_groundlevel="<<approx_groundlevel<<std::endl;
-       
-       s16 approx_ground_depth = approx_groundlevel - (node_min.Y+MAP_BLOCKSIZE/2);
-       
-       s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
-                       data->seed, v2s16(blockpos.X, blockpos.Z));
-       // Minimum amount of ground above the top of the central block
-       s16 minimum_ground_depth = minimum_groundlevel - node_max.Y;
-
-       s16 maximum_groundlevel = (s16)get_sector_maximum_ground_level(
-                       data->seed, v2s16(blockpos.X, blockpos.Z), 1);
-       // Maximum amount of ground above the bottom of the central block
-       s16 maximum_ground_depth = maximum_groundlevel - node_min.Y;
-       
-       /*
-               Special case for high air or water: Just fill with air and water.
-       */
-       if(maximum_ground_depth < -20)
-       {
-               for(s16 x=node_min.X; x<=node_max.X; x++)
-               for(s16 z=node_min.Z; z<=node_max.Z; z++)
-               {
-                       // Node position
-                       v2s16 p2d(x,z);
-                       {
-                               // Use fast index incrementing
-                               v3s16 em = vmanip.m_area.getExtent();
-                               u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
-                               for(s16 y=node_min.Y; y<=node_max.Y; y++)
-                               {
-                                       // Only modify places that have no content
-                                       if(vmanip.m_data[i].d == CONTENT_IGNORE)
-                                       {
-                                               if(y <= WATER_LEVEL)
-                                                       vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE);
-                                               else
-                                                       vmanip.m_data[i] = MapNode(CONTENT_AIR);
-                                       }
-                               
-                                       data->vmanip.m_area.add_y(em, i, 1);
-                               }
-                       }
-               }
-               
-               // We're done
-               return;
-       }
-
-       /*
-               If block is deep underground, this is set to true and ground
-               density noise is not generated, for speed optimization.
-       */
-       bool all_is_ground_except_caves = (minimum_ground_depth > 16);
-       
-       /*
-               Create a block-specific seed
-       */
-       u32 blockseed = (data->seed%0x100000000) + full_node_min.Z*38134234
-                       + full_node_min.Y*42123 + full_node_min.X*23;
-       
-       /*
-               Make some 3D noise
-       */
-       
-       //NoiseBuffer noisebuf1;
-       //NoiseBuffer noisebuf2;
-       NoiseBuffer noisebuf_cave;
-       NoiseBuffer noisebuf_ground;
-       NoiseBuffer noisebuf_ground_crumbleness;
-       NoiseBuffer noisebuf_ground_wetness;
-       {
-               v3f minpos_f(node_min.X, node_min.Y, node_min.Z);
-               v3f maxpos_f(node_max.X, node_max.Y, node_max.Z);
-
-               //TimeTaker timer("noisebuf.create");
-
-               /*
-                       Cave noise
-               */
-
-               noisebuf_cave.create(get_cave_noise1_params(data->seed),
-                               minpos_f.X, minpos_f.Y, minpos_f.Z,
-                               maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
-                               4, 3, 4);
-               
-               noisebuf_cave.multiply(get_cave_noise2_params(data->seed));
-
-               /*
-                       Ground noise
-               */
-               
-               // Sample length
-               v3f sl = v3f(4.0, 4.0, 4.0);
-               
-               /*
-                       Density noise
-               */
-               if(all_is_ground_except_caves == false)
-                       //noisebuf_ground.create(data->seed+983240, 6, 0.60, false,
-                       noisebuf_ground.create(get_ground_noise1_params(data->seed),
-                                       minpos_f.X, minpos_f.Y, minpos_f.Z,
-                                       maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
-                                       sl.X, sl.Y, sl.Z);
-               
-               /*
-                       Ground property noise
-               */
-               sl = v3f(2.5, 2.5, 2.5);
-               noisebuf_ground_crumbleness.create(
-                               get_ground_crumbleness_params(data->seed),
-                               minpos_f.X, minpos_f.Y, minpos_f.Z,
-                               maxpos_f.X, maxpos_f.Y+5, maxpos_f.Z,
-                               sl.X, sl.Y, sl.Z);
-               noisebuf_ground_wetness.create(
-                               get_ground_wetness_params(data->seed),
-                               minpos_f.X, minpos_f.Y, minpos_f.Z,
-                               maxpos_f.X, maxpos_f.Y+5, maxpos_f.Z,
-                               sl.X, sl.Y, sl.Z);
-       }
-       
-       /*
-               Make base ground level
-       */
-
-       for(s16 x=node_min.X; x<=node_max.X; x++)
-       for(s16 z=node_min.Z; z<=node_max.Z; z++)
-       {
-               // Node position
-               v2s16 p2d(x,z);
-               {
-                       // Use fast index incrementing
-                       v3s16 em = vmanip.m_area.getExtent();
-                       u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y));
-                       for(s16 y=node_min.Y; y<=node_max.Y; y++)
-                       {
-                               // Only modify places that have no content
-                               if(vmanip.m_data[i].d == CONTENT_IGNORE)
-                               {
-                                       // First priority: make air and water.
-                                       // This avoids caves inside water.
-                                       if(all_is_ground_except_caves == false
-                                                       && val_is_ground(noisebuf_ground.get(x,y,z),
-                                                       v3s16(x,y,z), data->seed) == false)
-                                       {
-                                               if(y <= WATER_LEVEL)
-                                                       vmanip.m_data[i] = MapNode(CONTENT_WATERSOURCE);
-                                               else
-                                                       vmanip.m_data[i] = MapNode(CONTENT_AIR);
-                                       }
-                                       else if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD)
-                                               vmanip.m_data[i] = MapNode(CONTENT_AIR);
-                                       else
-                                               vmanip.m_data[i] = MapNode(CONTENT_STONE);
-                               }
-                       
-                               data->vmanip.m_area.add_y(em, i, 1);
-                       }
-               }
-       }
-
-       /*
-               Add minerals
-       */
-
-       {
-               PseudoRandom mineralrandom(blockseed);
-
-               /*
-                       Add meseblocks
-               */
-               for(s16 i=0; i<approx_ground_depth/4; i++)
-               {
-                       if(mineralrandom.next()%50 == 0)
-                       {
-                               s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
-                               s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
-                               s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
-                               for(u16 i=0; i<27; i++)
-                               {
-                                       v3s16 p = v3s16(x,y,z) + g_27dirs[i];
-                                       u32 vi = vmanip.m_area.index(p);
-                                       if(vmanip.m_data[vi].d == CONTENT_STONE)
-                                               if(mineralrandom.next()%8 == 0)
-                                                       vmanip.m_data[vi] = MapNode(CONTENT_MESE);
-                               }
-                                       
-                       }
-               }
-               /*
-                       Add others
-               */
-               {
-                       u16 a = mineralrandom.range(0,15);
-                       a = a*a*a;
-                       u16 amount = 20 * a/1000;
-                       for(s16 i=0; i<amount; i++)
-                       {
-                               s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
-                               s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
-                               s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
-
-                               u8 base_content = CONTENT_STONE;
-                               MapNode new_content(CONTENT_IGNORE);
-                               u32 sparseness = 6;
-
-                               if(noisebuf_ground_crumbleness.get(x,y+5,z) < -0.1)
-                               {
-                                       new_content = MapNode(CONTENT_STONE, MINERAL_COAL);
-                               }
-                               else
-                               {
-                                       if(noisebuf_ground_wetness.get(x,y+5,z) > 0.0)
-                                               new_content = MapNode(CONTENT_STONE, MINERAL_IRON);
-                                       /*if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
-                                               vmanip.m_data[i] = MapNode(CONTENT_MUD);
-                                       else
-                                               vmanip.m_data[i] = MapNode(CONTENT_SAND);*/
-                               }
-                               /*else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.1)
-                               {
-                               }*/
-
-                               if(new_content.d != CONTENT_IGNORE)
-                               {
-                                       for(u16 i=0; i<27; i++)
-                                       {
-                                               v3s16 p = v3s16(x,y,z) + g_27dirs[i];
-                                               u32 vi = vmanip.m_area.index(p);
-                                               if(vmanip.m_data[vi].d == base_content)
-                                               {
-                                                       if(mineralrandom.next()%sparseness == 0)
-                                                               vmanip.m_data[vi] = new_content;
-                                               }
-                                       }
-                               }
-                       }
-               }
-               /*
-                       Add coal
-               */
-               //for(s16 i=0; i < MYMAX(0, 50 - abs(node_min.Y+8 - (-30))); i++)
-               //for(s16 i=0; i<50; i++)
-               u16 coal_amount = 30;
-               u16 coal_rareness = 60 / coal_amount;
-               if(coal_rareness == 0)
-                       coal_rareness = 1;
-               if(mineralrandom.next()%coal_rareness == 0)
-               {
-                       u16 a = mineralrandom.next() % 16;
-                       u16 amount = coal_amount * a*a*a / 1000;
-                       for(s16 i=0; i<amount; i++)
-                       {
-                               s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
-                               s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
-                               s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
-                               for(u16 i=0; i<27; i++)
-                               {
-                                       v3s16 p = v3s16(x,y,z) + g_27dirs[i];
-                                       u32 vi = vmanip.m_area.index(p);
-                                       if(vmanip.m_data[vi].d == CONTENT_STONE)
-                                               if(mineralrandom.next()%8 == 0)
-                                                       vmanip.m_data[vi] = MapNode(CONTENT_STONE, MINERAL_COAL);
-                               }
-                       }
-               }
-               /*
-                       Add iron
-               */
-               u16 iron_amount = 8;
-               u16 iron_rareness = 60 / iron_amount;
-               if(iron_rareness == 0)
-                       iron_rareness = 1;
-               if(mineralrandom.next()%iron_rareness == 0)
-               {
-                       u16 a = mineralrandom.next() % 16;
-                       u16 amount = iron_amount * a*a*a / 1000;
-                       for(s16 i=0; i<amount; i++)
-                       {
-                               s16 x = mineralrandom.range(node_min.X+1, node_max.X-1);
-                               s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1);
-                               s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1);
-                               for(u16 i=0; i<27; i++)
-                               {
-                                       v3s16 p = v3s16(x,y,z) + g_27dirs[i];
-                                       u32 vi = vmanip.m_area.index(p);
-                                       if(vmanip.m_data[vi].d == CONTENT_STONE)
-                                               if(mineralrandom.next()%8 == 0)
-                                                       vmanip.m_data[vi] = MapNode(CONTENT_STONE, MINERAL_IRON);
-                               }
-                       }
-               }
-       }
-
-       /*
-               Add mud and sand and others underground (in place of stone)
-       */
-
-       for(s16 x=node_min.X; x<=node_max.X; x++)
-       for(s16 z=node_min.Z; z<=node_max.Z; z++)
-       {
-               // Node position
-               v2s16 p2d(x,z);
-               {
-                       // Use fast index incrementing
-                       v3s16 em = vmanip.m_area.getExtent();
-                       u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
-                       for(s16 y=node_max.Y; y>=node_min.Y; y--)
-                       {
-                               if(vmanip.m_data[i].d == CONTENT_STONE)
-                               {
-                                       if(noisebuf_ground_crumbleness.get(x,y,z) > 1.3)
-                                       {
-                                               if(noisebuf_ground_wetness.get(x,y,z) > 0.0)
-                                                       vmanip.m_data[i] = MapNode(CONTENT_MUD);
-                                               else
-                                                       vmanip.m_data[i] = MapNode(CONTENT_SAND);
-                                       }
-                                       else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.7)
-                                       {
-                                               if(noisebuf_ground_wetness.get(x,y,z) < -0.6)
-                                                       vmanip.m_data[i] = MapNode(CONTENT_GRAVEL);
-                                       }
-                               }
-
-                               data->vmanip.m_area.add_y(em, i, -1);
-                       }
-               }
-       }
-
-       /*
-               Add dungeons
-       */
-       
-       //if(node_min.Y < approx_groundlevel)
-       //if(myrand() % 3 == 0)
-       //if(myrand() % 3 == 0 && node_min.Y < approx_groundlevel)
-       //if(myrand() % 100 == 0 && node_min.Y < approx_groundlevel)
-       //float dungeon_rarity = g_settings.getFloat("dungeon_rarity");
-       float dungeon_rarity = 0.02;
-       if(((noise3d(blockpos.X,blockpos.Y,blockpos.Z,data->seed)+1.0)/2.0)
-                       < dungeon_rarity
-                       && node_min.Y < approx_groundlevel)
-       {
-               // Dungeon generator doesn't modify places which have this set
-               data->vmanip.clearFlag(VMANIP_FLAG_DUNGEON_INSIDE
-                               | VMANIP_FLAG_DUNGEON_PRESERVE);
-               
-               // Set all air and water to be untouchable to make dungeons open
-               // to caves and open air
-               for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
-               for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
-               {
-                       // Node position
-                       v2s16 p2d(x,z);
-                       {
-                               // Use fast index incrementing
-                               v3s16 em = vmanip.m_area.getExtent();
-                               u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
-                               for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
-                               {
-                                       if(vmanip.m_data[i].d == CONTENT_AIR)
-                                               vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
-                                       else if(vmanip.m_data[i].d == CONTENT_WATERSOURCE)
-                                               vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
-                                       data->vmanip.m_area.add_y(em, i, -1);
-                               }
-                       }
-               }
-               
-               PseudoRandom random(blockseed+2);
-
-               // Add it
-               make_dungeon1(data->vmanip, random);
-               
-               // Convert some cobble to mossy cobble
-               for(s16 x=full_node_min.X; x<=full_node_max.X; x++)
-               for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++)
-               {
-                       // Node position
-                       v2s16 p2d(x,z);
-                       {
-                               // Use fast index incrementing
-                               v3s16 em = vmanip.m_area.getExtent();
-                               u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y));
-                               for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--)
-                               {
-                                       // (noisebuf not used because it doesn't contain the
-                                       //  full area)
-                                       double wetness = noise3d_param(
-                                                       get_ground_wetness_params(data->seed), x,y,z);
-                                       double d = noise3d_perlin((float)x/2.5,
-                                                       (float)y/2.5,(float)z/2.5,
-                                                       blockseed, 2, 1.4);
-                                       if(vmanip.m_data[i].d == CONTENT_COBBLE)
-                                       {
-                                               if(d < wetness/3.0)
-                                               {
-                                                       vmanip.m_data[i].d = CONTENT_MOSSYCOBBLE;
-                                               }
-                                       }
-                                       /*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE)
-                                       {
-                                               if(wetness > 1.2)
-                                                       vmanip.m_data[i].d = CONTENT_MUD;
-                                       }*/
-                                       data->vmanip.m_area.add_y(em, i, -1);
-                               }
-                       }
-               }
-       }
-       
-       /*
-               Add top and bottom side of water to transforming_liquid queue
-       */
-
-       for(s16 x=node_min.X; x<=node_max.X; x++)
-       for(s16 z=node_min.Z; z<=node_max.Z; z++)
-       {
-               // Node position
-               v2s16 p2d(x,z);
-               {
-                       bool water_found = false;
-                       // Use fast index incrementing
-                       v3s16 em = vmanip.m_area.getExtent();
-                       u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y));
-                       for(s16 y=node_max.Y; y>=node_min.Y; y--)
-                       {
-                               if(water_found == false)
-                               {
-                                       if(vmanip.m_data[i].d == CONTENT_WATERSOURCE)
-                                       {
-                                               v3s16 p = v3s16(p2d.X, y, p2d.Y);
-                                               data->transforming_liquid.push_back(p);
-                                               water_found = true;
-                                       }
-                               }
-                               else
-                               {
-                                       // This can be done because water_found can only
-                                       // turn to true and end up here after going through
-                                       // a single block.
-                                       if(vmanip.m_data[i+1].d != CONTENT_WATERSOURCE)
-                                       {
-                                               v3s16 p = v3s16(p2d.X, y+1, p2d.Y);
-                                               data->transforming_liquid.push_back(p);
-                                               water_found = false;
-                                       }
+                               catch(FileNotGoodException &e){
+                                       dstream<<DTIME<<"WARNING: Could not load map metadata"
+                                                       //<<" Disabling chunk-based generator."
+                                                       <<std::endl;
+                                       //m_chunksize = 0;
+                               }
+
+                               /*try{
+                                       // Load chunk metadata
+                                       loadChunkMeta();
                                }
+                               catch(FileNotGoodException &e){
+                                       dstream<<DTIME<<"WARNING: Could not load chunk metadata."
+                                                       <<" Disabling chunk-based generator."
+                                                       <<std::endl;
+                                       m_chunksize = 0;
+                               }*/
+
+                               /*dstream<<DTIME<<"Server: Successfully loaded chunk "
+                                               "metadata and sector (0,0) from "<<savedir<<
+                                               ", assuming valid save directory."
+                                               <<std::endl;*/
+
+                               dstream<<DTIME<<"INFO: Server: Successfully loaded map "
+                                               <<"and chunk metadata from "<<savedir
+                                               <<", assuming valid save directory."
+                                               <<std::endl;
 
-                               data->vmanip.m_area.add_y(em, i, -1);
+                               m_map_saving_enabled = true;
+                               // Map loaded, not creating new one
+                               return;
                        }
                }
+               // If directory doesn't exist, it is safe to save to it
+               else{
+                       m_map_saving_enabled = true;
+               }
        }
-
-       /*
-               If close to ground level
-       */
-
-       //if(abs(approx_ground_depth) < 30)
-       if(minimum_ground_depth < 5 && maximum_ground_depth > -5)
+       catch(std::exception &e)
        {
-               /*
-                       Add grass and mud
-               */
+               dstream<<DTIME<<"WARNING: Server: Failed to load map from "<<savedir
+                               <<", exception: "<<e.what()<<std::endl;
+               dstream<<"Please remove the map or fix it."<<std::endl;
+               dstream<<"WARNING: Map saving will be disabled."<<std::endl;
+       }
 
-               for(s16 x=node_min.X; x<=node_max.X; x++)
-               for(s16 z=node_min.Z; z<=node_max.Z; z++)
-               {
-                       // Node position
-                       v2s16 p2d(x,z);
-                       {
-                               bool possibly_have_sand = get_have_sand(data->seed, p2d);
-                               bool have_sand = false;
-                               u32 current_depth = 0;
-                               bool air_detected = false;
-                               bool water_detected = false;
-                               // Use fast index incrementing
-                               s16 start_y = node_max.Y+2;
-                               v3s16 em = vmanip.m_area.getExtent();
-                               u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y));
-                               for(s16 y=start_y; y>=node_min.Y-3; y--)
-                               {
-                                       if(vmanip.m_data[i].d == CONTENT_WATERSOURCE)
-                                               water_detected = true;
-                                       if(vmanip.m_data[i].d == CONTENT_AIR)
-                                               air_detected = true;
-
-                                       if((vmanip.m_data[i].d == CONTENT_STONE
-                                                       || vmanip.m_data[i].d == CONTENT_GRASS
-                                                       || vmanip.m_data[i].d == CONTENT_MUD
-                                                       || vmanip.m_data[i].d == CONTENT_SAND
-                                                       || vmanip.m_data[i].d == CONTENT_GRAVEL
-                                                       ) && (air_detected || water_detected))
-                                       {
-                                               if(current_depth == 0 && y <= WATER_LEVEL+2
-                                                               && possibly_have_sand)
-                                                       have_sand = true;
-                                               
-                                               if(current_depth < 4)
-                                               {
-                                                       if(have_sand)
-                                                       {
-                                                               vmanip.m_data[i] = MapNode(CONTENT_SAND);
-                                                       }
-                                                       #if 1
-                                                       else if(current_depth==0 && !water_detected
-                                                                       && y >= WATER_LEVEL && air_detected)
-                                                               vmanip.m_data[i] = MapNode(CONTENT_GRASS);
-                                                       #endif
-                                                       else
-                                                               vmanip.m_data[i] = MapNode(CONTENT_MUD);
-                                               }
-                                               else
-                                               {
-                                                       if(vmanip.m_data[i].d == CONTENT_MUD
-                                                               || vmanip.m_data[i].d == CONTENT_GRASS)
-                                                               vmanip.m_data[i] = MapNode(CONTENT_STONE);
-                                               }
+       dstream<<DTIME<<"INFO: Initializing new map."<<std::endl;
 
-                                               current_depth++;
+       // Create zero sector
+       emergeSector(v2s16(0,0));
 
-                                               if(current_depth >= 8)
-                                                       break;
-                                       }
-                                       else if(current_depth != 0)
-                                               break;
+       // Initially write whole map
+       save(false);
+}
 
-                                       data->vmanip.m_area.add_y(em, i, -1);
-                               }
-                       }
-               }
+ServerMap::~ServerMap()
+{
+       dstream<<__FUNCTION_NAME<<std::endl;
 
-               /*
-                       Add trees
-               */
-               
-               // Amount of trees
-               u32 tree_count = block_area_nodes * tree_amount_2d(data->seed, p2d_center);
-               PseudoRandom treerandom(blockseed);
-               // Put trees in random places on part of division
-               for(u32 i=0; i<tree_count; i++)
+       try
+       {
+               if(m_map_saving_enabled)
                {
-                       s16 x = treerandom.range(node_min.X, node_max.X);
-                       s16 z = treerandom.range(node_min.Z, node_max.Z);
-                       //s16 y = find_ground_level(data->vmanip, v2s16(x,z));
-                       s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4);
-                       // Don't make a tree under water level
-                       if(y < WATER_LEVEL)
-                               continue;
-                       // Make sure tree fits (only trees whose starting point is
-                       // at this block are added)
-                       if(y < node_min.Y || y > node_max.Y)
-                               continue;
-                       /*
-                               Find exact ground level
-                       */
-                       v3s16 p(x,y+6,z);
-                       bool found = false;
-                       for(; p.Y >= y-6; p.Y--)
-                       {
-                               u32 i = data->vmanip.m_area.index(p);
-                               MapNode *n = &data->vmanip.m_data[i];
-                               if(n->d != CONTENT_AIR && n->d != CONTENT_IGNORE)
-                               {
-                                       found = true;
-                                       break;
-                               }
-                       }
-                       // If not found, handle next one
-                       if(found == false)
-                               continue;
-                       /*
-                               Trees grow only on mud and grass
-                       */
-                       {
-                               u32 i = data->vmanip.m_area.index(p);
-                               MapNode *n = &data->vmanip.m_data[i];
-                               if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
-                                       continue;
-                       }
-                       // Tree will be placed one higher
-                       p.Y++;
-                       // Make a tree
-                       make_tree(data->vmanip, p);
+                       // Save only changed parts
+                       save(true);
+                       dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
                }
-
-#if 0
-               /*
-                       Add some kind of random stones
-               */
-               
-               u32 random_stone_count = block_area_nodes *
-                               randomstone_amount_2d(data->seed, p2d_center);
-               // Put in random places on part of division
-               for(u32 i=0; i<random_stone_count; i++)
+               else
                {
-                       s16 x = myrand_range(node_min.X, node_max.X);
-                       s16 z = myrand_range(node_min.Z, node_max.Z);
-                       s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
-                       // Don't add under water level
-                       /*if(y < WATER_LEVEL)
-                               continue;*/
-                       // Don't add if doesn't belong to this block
-                       if(y < node_min.Y || y > node_max.Y)
-                               continue;
-                       v3s16 p(x,y,z);
-                       // Filter placement
-                       /*{
-                               u32 i = data->vmanip.m_area.index(v3s16(p));
-                               MapNode *n = &data->vmanip.m_data[i];
-                               if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
-                                       continue;
-                       }*/
-                       // Will be placed one higher
-                       p.Y++;
-                       // Add it
-                       make_randomstone(data->vmanip, p);
+                       dstream<<DTIME<<"Server: map not saved"<<std::endl;
                }
-#endif
+       }
+       catch(std::exception &e)
+       {
+               dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
+                               <<", exception: "<<e.what()<<std::endl;
+       }
 
 #if 0
-               /*
-                       Add larger stones
-               */
-               
-               u32 large_stone_count = block_area_nodes *
-                               largestone_amount_2d(data->seed, p2d_center);
-               //u32 large_stone_count = 1;
-               // Put in random places on part of division
-               for(u32 i=0; i<large_stone_count; i++)
-               {
-                       s16 x = myrand_range(node_min.X, node_max.X);
-                       s16 z = myrand_range(node_min.Z, node_max.Z);
-                       s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1);
-                       // Don't add under water level
-                       /*if(y < WATER_LEVEL)
-                               continue;*/
-                       // Don't add if doesn't belong to this block
-                       if(y < node_min.Y || y > node_max.Y)
-                               continue;
-                       v3s16 p(x,y,z);
-                       // Filter placement
-                       /*{
-                               u32 i = data->vmanip.m_area.index(v3s16(p));
-                               MapNode *n = &data->vmanip.m_data[i];
-                               if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
-                                       continue;
-                       }*/
-                       // Will be placed one lower
-                       p.Y--;
-                       // Add it
-                       make_largestone(data->vmanip, p);
-               }
-#endif
+       /*
+               Free all MapChunks
+       */
+       core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
+       for(; i.atEnd() == false; i++)
+       {
+               MapChunk *chunk = i.getNode()->getValue();
+               delete chunk;
        }
-
+#endif
 }
 
-void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
+void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
 {
-       /*dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
-                       <<blockpos.Z<<")"<<std::endl;*/
-
+       bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+       if(enable_mapgen_debug_info)
+               dstream<<"initBlockMake(): ("<<blockpos.X<<","<<blockpos.Y<<","
+                               <<blockpos.Z<<")"<<std::endl;
+       
+       // Do nothing if not inside limits (+-1 because of neighbors)
+       if(blockpos_over_limit(blockpos - v3s16(1,1,1)) ||
+               blockpos_over_limit(blockpos + v3s16(1,1,1)))
+       {
+               data->no_op = true;
+               return;
+       }
+       
        data->no_op = false;
        data->seed = m_seed;
        data->blockpos = blockpos;
@@ -3881,20 +2036,28 @@ void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
 
                        for(s16 y=-1; y<=1; y++)
                        {
-                               MapBlock *block = createBlock(blockpos);
+                               v3s16 p(blockpos.X+x, blockpos.Y+y, blockpos.Z+z);
+                               //MapBlock *block = createBlock(p);
+                               // 1) get from memory, 2) load from disk
+                               MapBlock *block = emergeBlock(p, false);
+                               // 3) create a blank one
+                               if(block == NULL)
+                               {
+                                       block = createBlock(p);
+
+                                       /*
+                                               Block gets sunlight if this is true.
 
-                               // Lighting won't be calculated
+                                               Refer to the map generator heuristics.
+                                       */
+                                       bool ug = mapgen::block_is_underground(data->seed, p);
+                                       block->setIsUnderground(ug);
+                               }
+
+                               // Lighting will not be valid after make_chunk is called
                                block->setLightingExpired(true);
                                // Lighting will be calculated
                                //block->setLightingExpired(false);
-
-                               /*
-                                       Block gets sunlight if this is true.
-
-                                       This should be set to true when the top side of a block
-                                       is completely exposed to the sky.
-                               */
-                               block->setIsUnderground(false);
                        }
                }
        }
@@ -3906,21 +2069,23 @@ void ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
                neighboring blocks
        */
        
+       // The area that contains this block and it's neighbors
        v3s16 bigarea_blocks_min = blockpos - v3s16(1,1,1);
        v3s16 bigarea_blocks_max = blockpos + v3s16(1,1,1);
        
-       data->vmanip.setMap(this);
+       data->vmanip = new ManualMapVoxelManipulator(this);
+       //data->vmanip->setMap(this);
 
        // Add the area
        {
                //TimeTaker timer("initBlockMake() initialEmerge");
-               data->vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
+               data->vmanip->initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
        }
 
        // Data is ready now.
 }
 
-MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
+MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
                core::map<v3s16, MapBlock*> &changed_blocks)
 {
        v3s16 blockpos = data->blockpos;
@@ -3929,10 +2094,12 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
 
        if(data->no_op)
        {
-               dstream<<"finishBlockMake(): no-op"<<std::endl;
+               //dstream<<"finishBlockMake(): no-op"<<std::endl;
                return NULL;
        }
 
+       bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+
        /*dstream<<"Resulting vmanip:"<<std::endl;
        data->vmanip.print(dstream);*/
        
@@ -3943,12 +2110,13 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
        {
                // 70ms @cs=8
                //TimeTaker timer("finishBlockMake() blitBackAll");
-               data->vmanip.blitBackAll(&changed_blocks);
+               data->vmanip->blitBackAll(&changed_blocks);
        }
-#if 1
-       dstream<<"finishBlockMake: changed_blocks.size()="
-                       <<changed_blocks.size()<<std::endl;
-#endif
+
+       if(enable_mapgen_debug_info)
+               dstream<<"finishBlockMake: changed_blocks.size()="
+                               <<changed_blocks.size()<<std::endl;
+
        /*
                Copy transforming liquid information
        */
@@ -3965,16 +2133,14 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
        assert(block);
 
        /*
-               Set is_underground flag for lighting with sunlight
+               Set is_underground flag for lighting with sunlight.
+
+               Refer to map generator heuristics.
+
+               NOTE: This is done in initChunkMake
        */
+       //block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
 
-       s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
-                       data->seed, v2s16(blockpos.X, blockpos.Z));
-       
-       if(blockpos.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
-               block->setIsUnderground(true);
-       else
-               block->setIsUnderground(false);
 
        /*
                Add sunlight to central block.
@@ -3988,32 +2154,77 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
        /*
                NOTE: Lighting and object adding shouldn't really be here, but
                lighting is a bit tricky to move properly to makeBlock.
-               TODO: Do this the right way anyway.
+               TODO: Do this the right way anyway, that is, move it to makeBlock.
+                     - There needs to be some way for makeBlock to report back if
+                           the lighting update is going further down because of the
+                               new block blocking light
        */
 
        /*
                Update lighting
+               NOTE: This takes ~60ms, TODO: Investigate why
        */
+       {
+               TimeTaker t("finishBlockMake lighting update");
 
-       core::map<v3s16, MapBlock*> lighting_update_blocks;
-       // Center block
-       lighting_update_blocks.insert(block->getPos(), block);
+               core::map<v3s16, MapBlock*> lighting_update_blocks;
+#if 1
+               // Center block
+               lighting_update_blocks.insert(block->getPos(), block);
+
+               /*{
+                       s16 x = 0;
+                       s16 z = 0;
+                       v3s16 p = block->getPos()+v3s16(x,1,z);
+                       lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
+               }*/
+#endif
 #if 0
-       // All modified blocks
-       for(core::map<v3s16, MapBlock*>::Iterator
-                       i = changed_blocks.getIterator();
-                       i.atEnd() == false; i++)
-       {
-               lighting_update_blocks.insert(i.getNode()->getKey(),
-                               i.getNode()->getValue());
-       }
+               // All modified blocks
+               // NOTE: Should this be done? If this is not done, then the lighting
+               // of the others will be updated in a different place, one by one, i
+               // think... or they might not? Well, at least they are left marked as
+               // "lighting expired"; it seems that is not handled at all anywhere,
+               // so enabling this will slow it down A LOT because otherwise it
+               // would not do this at all. This causes the black trees.
+               for(core::map<v3s16, MapBlock*>::Iterator
+                               i = changed_blocks.getIterator();
+                               i.atEnd() == false; i++)
+               {
+                       lighting_update_blocks.insert(i.getNode()->getKey(),
+                                       i.getNode()->getValue());
+               }
+               /*// Also force-add all the upmost blocks for proper sunlight
+               for(s16 x=-1; x<=1; x++)
+               for(s16 z=-1; z<=1; z++)
+               {
+                       v3s16 p = block->getPos()+v3s16(x,1,z);
+                       lighting_update_blocks[p] = getBlockNoCreateNoEx(p);
+               }*/
 #endif
-       updateLighting(lighting_update_blocks, changed_blocks);
-       
+               updateLighting(lighting_update_blocks, changed_blocks);
+               
+               /*
+                       Set lighting to non-expired state in all of them.
+                       This is cheating, but it is not fast enough if all of them
+                       would actually be updated.
+               */
+               for(s16 x=-1; x<=1; x++)
+               for(s16 y=-1; y<=1; y++)
+               for(s16 z=-1; z<=1; z++)
+               {
+                       v3s16 p = block->getPos()+v3s16(x,y,z);
+                       getBlockNoCreateNoEx(p)->setLightingExpired(false);
+               }
+
+               if(enable_mapgen_debug_info == false)
+                       t.stop(true); // Hide output
+       }
+
        /*
                Add random objects to block
        */
-       addRandomObjects(block);
+       mapgen::add_random_objects(block);
 
        /*
                Go through changed blocks
@@ -4046,7 +2257,26 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
 
        /*dstream<<"finishBlockMake() done for ("<<blockpos.X<<","<<blockpos.Y<<","
                        <<blockpos.Z<<")"<<std::endl;*/
-       
+#if 0
+       if(enable_mapgen_debug_info)
+       {
+               /*
+                       Analyze resulting blocks
+               */
+               for(s16 x=-1; x<=1; x++)
+               for(s16 y=-1; y<=1; y++)
+               for(s16 z=-1; z<=1; z++)
+               {
+                       v3s16 p = block->getPos()+v3s16(x,y,z);
+                       MapBlock *block = getBlockNoCreateNoEx(p);
+                       char spos[20];
+                       snprintf(spos, 20, "(%2d,%2d,%2d)", x, y, z);
+                       dstream<<"Generated "<<spos<<": "
+                                       <<analyze_block(block)<<std::endl;
+               }
+       }
+#endif
+
        return block;
 }
 
@@ -4122,6 +2352,8 @@ MapBlock * ServerMap::generateBlock(
                        <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
                        <<std::endl;*/
        
+       bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+
        TimeTaker timer("generateBlock");
        
        //MapBlock *block = original_dummy;
@@ -4141,15 +2373,18 @@ MapBlock * ServerMap::generateBlock(
        /*
                Create block make data
        */
-       BlockMakeData data;
+       mapgen::BlockMakeData data;
        initBlockMake(&data, p);
 
        /*
                Generate block
        */
        {
-               TimeTaker t("makeBlock()");
-               makeBlock(&data);
+               TimeTaker t("mapgen::make_block()");
+               mapgen::make_block(&data);
+
+               if(enable_mapgen_debug_info == false)
+                       t.stop(true); // Hide output
        }
 
        /*
@@ -4161,53 +2396,61 @@ MapBlock * ServerMap::generateBlock(
                Get central block
        */
        MapBlock *block = getBlockNoCreateNoEx(p);
-       assert(block);
 
 #if 0
        /*
                Check result
        */
-       bool erroneus_content = false;
-       for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
-       for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
-       for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
-       {
-               v3s16 p(x0,y0,z0);
-               MapNode n = block->getNode(p);
-               if(n.d == CONTENT_IGNORE)
+       if(block)
+       {
+               bool erroneus_content = false;
+               for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
+               for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+               for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+               {
+                       v3s16 p(x0,y0,z0);
+                       MapNode n = block->getNode(p);
+                       if(n.getContent() == CONTENT_IGNORE)
+                       {
+                               dstream<<"CONTENT_IGNORE at "
+                                               <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+                                               <<std::endl;
+                               erroneus_content = true;
+                               assert(0);
+                       }
+               }
+               if(erroneus_content)
                {
-                       dstream<<"CONTENT_IGNORE at "
-                                       <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
-                                       <<std::endl;
-                       erroneus_content = true;
                        assert(0);
                }
        }
-       if(erroneus_content)
-       {
-               assert(0);
-       }
 #endif
 
 #if 0
        /*
                Generate a completely empty block
        */
-       for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
-       for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+       if(block)
        {
-               for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+               for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
+               for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
                {
-                       MapNode n;
-                       if(y0%2==0)
-                               n.d = CONTENT_AIR;
-                       else
-                               n.d = CONTENT_STONE;
-                       block->setNode(v3s16(x0,y0,z0), n);
+                       for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+                       {
+                               MapNode n;
+                               if(y0%2==0)
+                                       n.setContent(CONTENT_AIR);
+                               else
+                                       n.setContent(CONTENT_STONE);
+                               block->setNode(v3s16(x0,y0,z0), n);
+                       }
                }
        }
 #endif
 
+       if(enable_mapgen_debug_info == false)
+               timer.stop(true); // Hide output
+
        return block;
 }
 
@@ -4274,23 +2517,51 @@ MapBlock * ServerMap::createBlock(v3s16 p)
        return block;
 }
 
-#if 0
-MapBlock * ServerMap::emergeBlock(
-               v3s16 p,
-               bool only_from_disk,
-               core::map<v3s16, MapBlock*> &changed_blocks,
-               core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
-)
+MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
 {
-       DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
+       DSTACKF("%s: p=(%d,%d,%d), allow_generate=%d",
                        __FUNCTION_NAME,
-                       p.X, p.Y, p.Z, only_from_disk);
+                       p.X, p.Y, p.Z, allow_generate);
        
-       // This has to be redone or removed
-       assert(0);
+       {
+               MapBlock *block = getBlockNoCreateNoEx(p);
+               if(block && block->isDummy() == false)
+                       return block;
+       }
+
+       {
+               MapBlock *block = loadBlock(p);
+               if(block)
+                       return block;
+       }
+
+       if(allow_generate)
+       {
+               core::map<v3s16, MapBlock*> modified_blocks;
+               MapBlock *block = generateBlock(p, modified_blocks);
+               if(block)
+               {
+                       MapEditEvent event;
+                       event.type = MEET_OTHER;
+                       event.p = p;
+
+                       // Copy modified_blocks to event
+                       for(core::map<v3s16, MapBlock*>::Iterator
+                                       i = modified_blocks.getIterator();
+                                       i.atEnd()==false; i++)
+                       {
+                               event.modified_blocks.insert(i.getNode()->getKey(), false);
+                       }
+
+                       // Queue event
+                       dispatchEvent(&event);
+                                                               
+                       return block;
+               }
+       }
+
        return NULL;
 }
-#endif
 
 #if 0
        /*
@@ -4451,19 +2722,19 @@ s16 ServerMap::findGroundLevel(v2s16 p2d)
        for(; p.Y>min; p.Y--)
        {
                MapNode n = getNodeNoEx(p);
-               if(n.d != CONTENT_IGNORE)
+               if(n.getContent() != CONTENT_IGNORE)
                        break;
        }
        if(p.Y == min)
                goto plan_b;
        // If this node is not air, go to plan b
-       if(getNodeNoEx(p).d != CONTENT_AIR)
+       if(getNodeNoEx(p).getContent() != CONTENT_AIR)
                goto plan_b;
        // Search existing walkable and return it
        for(; p.Y>min; p.Y--)
        {
                MapNode n = getNodeNoEx(p);
-               if(content_walkable(n.d) && n.d != CONTENT_IGNORE)
+               if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
                        return p.Y;
        }
 
@@ -4475,7 +2746,7 @@ s16 ServerMap::findGroundLevel(v2s16 p2d)
                Determine from map generator noise functions
        */
        
-       s16 level = find_ground_level_from_noise(m_seed, p2d, 1);
+       s16 level = mapgen::find_ground_level_from_noise(m_seed, p2d, 1);
        return level;
 
        //double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
@@ -4737,10 +3008,10 @@ MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load
                // format. Just go ahead and create the sector.
                if(fs::PathExists(sectordir))
                {
-                       dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
+                       /*dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
                                        <<fullpath<<" doesn't exist but directory does."
                                        <<" Continuing with a sector with no metadata."
-                                       <<std::endl;
+                                       <<std::endl;*/
                        sector = new ServerMapSector(this, p2d);
                        m_sectors.insert(p2d, sector);
                }
@@ -4954,10 +3225,8 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
 
                MapBlock *block = NULL;
                bool created_new = false;
-               try{
-                       block = sector->getBlockNoCreate(p3d.Y);
-               }
-               catch(InvalidPositionException &e)
+               block = sector->getBlockNoCreateNoEx(p3d.Y);
+               if(block == NULL)
                {
                        block = sector->createBlankBlockNoInsert(p3d.Y);
                        created_new = true;
@@ -5127,6 +3396,7 @@ MapSector * ClientMap::emergeSector(v2s16 p2d)
        return sector;
 }
 
+#if 0
 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
 {
        DSTACK(__FUNCTION_NAME);
@@ -5152,6 +3422,7 @@ void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
 
        sector->deSerialize(is);
 }
+#endif
 
 void ClientMap::OnRegisterSceneNode()
 {
@@ -5210,13 +3481,13 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
 
        // Take a fair amount as we will be dropping more out later
        v3s16 p_blocks_min(
-                       p_nodes_min.X / MAP_BLOCKSIZE - 1,
-                       p_nodes_min.Y / MAP_BLOCKSIZE - 1,
-                       p_nodes_min.Z / MAP_BLOCKSIZE - 1);
+                       p_nodes_min.X / MAP_BLOCKSIZE - 2,
+                       p_nodes_min.Y / MAP_BLOCKSIZE - 2,
+                       p_nodes_min.Z / MAP_BLOCKSIZE - 2);
        v3s16 p_blocks_max(
-                       p_nodes_max.X / MAP_BLOCKSIZE,
-                       p_nodes_max.Y / MAP_BLOCKSIZE,
-                       p_nodes_max.Z / MAP_BLOCKSIZE);
+                       p_nodes_max.X / MAP_BLOCKSIZE + 1,
+                       p_nodes_max.Y / MAP_BLOCKSIZE + 1,
+                       p_nodes_max.Z / MAP_BLOCKSIZE + 1);
        
        u32 vertex_count = 0;
        
@@ -5288,6 +3559,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        {
                                continue;
                        }
+
+                       // Okay, this block will be drawn. Reset usage timer.
+                       block->resetUsageTimer();
                        
                        // This is ugly (spherical distance limit?)
                        /*if(m_control.range_all == false &&
@@ -5853,10 +4127,16 @@ void ManualMapVoxelManipulator::blitBackAll(
                        i = m_loaded_blocks.getIterator();
                        i.atEnd() == false; i++)
        {
+               v3s16 p = i.getNode()->getKey();
                bool existed = i.getNode()->getValue();
                if(existed == false)
+               {
+                       // The Great Bug was found using this
+                       /*dstream<<"ManualMapVoxelManipulator::blitBackAll: "
+                                       <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
+                                       <<std::endl;*/
                        continue;
-               v3s16 p = i.getNode()->getKey();
+               }
                MapBlock *block = m_map->getBlockNoCreateNoEx(p);
                if(block == NULL)
                {