]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/map.cpp
+ clay and associated items
[dragonfireclient.git] / src / map.cpp
index 334de40defc41fc437c20955d1910d2fbcdacaca..bf9f38c877598d4a39af4975d2cd33fc937b89a3 100644 (file)
@@ -1,6 +1,6 @@
 /*
 Minetest-c55
-Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "porting.h"
 #include "mineral.h"
 #include "noise.h"
+#include "serverobject.h"
 
 /*
        Map
@@ -36,8 +37,8 @@ Map::Map(std::ostream &dout):
        m_dout(dout),
        m_sector_cache(NULL)
 {
-       m_sector_mutex.Init();
-       assert(m_sector_mutex.IsInitialized());
+       /*m_sector_mutex.Init();
+       assert(m_sector_mutex.IsInitialized());*/
 }
 
 Map::~Map()
@@ -103,7 +104,7 @@ MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
 
 MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
 {
-       JMutexAutoLock lock(m_sector_mutex);
+       //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
 
        return getSectorNoGenerateNoExNoLock(p);
 }
@@ -606,13 +607,13 @@ s16 Map::propagateSunlight(v3s16 start,
                }
                else
                {
-                       // Turn mud into grass
+                       /*// Turn mud into grass
                        if(n.d == CONTENT_MUD)
                        {
                                n.d = CONTENT_GRASS;
                                block->setNode(relpos, n);
                                modified_blocks.insert(blockpos, block);
-                       }
+                       }*/
 
                        // Sunlight goes no further
                        break;
@@ -837,9 +838,6 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
 }
 
 /*
-       This is called after changing a node from transparent to opaque.
-       The lighting value of the node should be left as-is after changing
-       other values. This sets the lighting value to 0.
 */
 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                core::map<v3s16, MapBlock*> &modified_blocks)
@@ -876,12 +874,12 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
        catch(InvalidPositionException &e)
        {
        }
-       
+
+#if 1
        /*
-               If the new node doesn't propagate sunlight and there is
-               grass below, change it to mud
+               If the new node is solid and there is grass below, change it to mud
        */
-       if(content_features(n.d).sunlight_propagates == false)
+       if(content_features(n.d).walkable == true)
        {
                try{
                        MapNode bottomnode = getNode(bottompos);
@@ -897,7 +895,9 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
                {
                }
        }
+#endif
 
+#if 0
        /*
                If the new node is mud and it is under sunlight, change it
                to grass
@@ -906,6 +906,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
        {
                n.d = CONTENT_GRASS;
        }
+#endif
 
        /*
                Remove all light that has come out of this node
@@ -1346,7 +1347,7 @@ bool Map::dayNightDiffed(v3s16 blockpos)
 */
 void Map::timerUpdate(float dtime)
 {
-       JMutexAutoLock lock(m_sector_mutex);
+       //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
 
        core::map<v2s16, MapSector*>::Iterator si;
 
@@ -1396,7 +1397,7 @@ void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
                core::list<v3s16> *deleted_blocks)
 {
-       JMutexAutoLock lock(m_sector_mutex);
+       //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
 
        core::list<v2s16> sector_deletion_queue;
        core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
@@ -1791,8 +1792,10 @@ void Map::nodeMetadataStep(float dtime,
 
 ServerMap::ServerMap(std::string savedir):
        Map(dout_server),
-       m_seed(0)
+       m_seed(0),
+       m_map_metadata_changed(true)
 {
+       dstream<<__FUNCTION_NAME<<std::endl;
        
        //m_chunksize = 64;
        //m_chunksize = 16; // Too slow
@@ -1800,7 +1803,6 @@ ServerMap::ServerMap(std::string savedir):
        //m_chunksize = 4;
        //m_chunksize = 2;
        
-       // TODO: Save to and load from a file
        m_seed = (((u64)(myrand()%0xffff)<<0)
                        + ((u64)(myrand()%0xffff)<<16)
                        + ((u64)(myrand()%0xffff)<<32)
@@ -1834,11 +1836,19 @@ ServerMap::ServerMap(std::string savedir):
                        }
                        else
                        {
-                               // Load map metadata (seed, chunksize)
-                               loadMapMeta();
-                               
-                               // Load chunk metadata
-                               loadChunkMeta();
+                               try{
+                                       // Load map metadata (seed, chunksize)
+                                       loadMapMeta();
+
+                                       // Load chunk metadata
+                                       loadChunkMeta();
+                               }
+                               catch(FileNotGoodException &e){
+                                       dstream<<DTIME<<"WARNING: Server: Could not load "
+                                                       <<"metafile(s). Disabling chunk-based "
+                                                       <<"generation."<<std::endl;
+                                       m_chunksize = 0;
+                               }
                        
                                /*// Load sector (0,0) and throw and exception on fail
                                if(loadSectorFull(v2s16(0,0)) == false)
@@ -1883,6 +1893,8 @@ ServerMap::ServerMap(std::string savedir):
 
 ServerMap::~ServerMap()
 {
+       dstream<<__FUNCTION_NAME<<std::endl;
+       
        try
        {
                if(m_map_saving_enabled)
@@ -2033,6 +2045,20 @@ void make_tree(VoxelManipulator &vmanip, v3s16 p0)
        }
 }
 
+void make_cactus(VoxelManipulator &vmanip, v3s16 p0)
+{
+       MapNode cactusnode(CONTENT_CACTUS);
+
+       s16 trunk_h = 3;
+       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)] = cactusnode;
+               p1.Y++;
+       }
+}
+
 /*
        Noise functions. Make sure seed is mangled differently in each one.
 */
@@ -2056,7 +2082,7 @@ double base_rock_level_2d(u64 seed, v2s16 p)
 {
        // The base ground level
        double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT
-                       + 25. * noise2d_perlin(
+                       + 20. * noise2d_perlin(
                        0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
                        (seed>>32)+654879876, 6, 0.6);
        
@@ -2068,7 +2094,7 @@ double base_rock_level_2d(u64 seed, v2s16 p)
                base = base2;*/
 #if 1
        // Higher ground level
-       double higher = (double)WATER_LEVEL + 25. + 45. * noise2d_perlin(
+       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
@@ -2109,6 +2135,13 @@ double base_rock_level_2d(u64 seed, v2s16 p)
        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));
+}
+
 /*
        Adds random objects to block, depending on the content of the block
 */
@@ -2155,6 +2188,18 @@ void addRandomObjects(MapBlock *block)
                                                        block->m_static_objects.insert(0, s_obj);
                                                        delete obj;
                                                }
+                                               if(myrand() % 300 == 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;
+                                               }
                                        }
                                }
                        }
@@ -2170,25 +2215,11 @@ void addRandomObjects(MapBlock *block)
        This is the main map generation method
 */
 
-struct ChunkMakeData
-{
-       ManualMapVoxelManipulator vmanip;
-       u64 seed;
-       s16 y_blocks_min;
-       s16 y_blocks_max;
-       v2s16 sectorpos_base;
-       s16 sectorpos_base_size;
-       v2s16 sectorpos_bigbase;
-       s16 sectorpos_bigbase_size;
-       s16 max_spread_amount;
-
-       ChunkMakeData():
-               vmanip(NULL)
-       {}
-};
-
 void makeChunk(ChunkMakeData *data)
 {
+       if(data->no_op)
+               return;
+       
        s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
        s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
        s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
@@ -2221,10 +2252,41 @@ void makeChunk(ChunkMakeData *data)
        /*
                Generate general ground level to full area
        */
-       
        {
        // 22ms @cs=8
-       //TimeTaker timer1("ground level");
+       TimeTaker timer1("Generating ground level");
+
+#if 0
+       NoiseBuffer noisebuf1;
+       //NoiseBuffer noisebuf2;
+       {
+               v3f minpos_f(
+                       data->sectorpos_bigbase.X*MAP_BLOCKSIZE,
+                       y_nodes_min,
+                       data->sectorpos_bigbase.Y*MAP_BLOCKSIZE
+               );
+               v3f maxpos_f = minpos_f + v3f(
+                       data->sectorpos_bigbase_size*MAP_BLOCKSIZE,
+                       y_nodes_max-y_nodes_min,
+                       data->sectorpos_bigbase_size*MAP_BLOCKSIZE
+               );
+               v3f samplelength_f = v3f(4.0, 4.0, 4.0);
+
+               TimeTaker timer("noisebuf.create");
+               
+               noisebuf1.create(data->seed+25104, 6, 0.60, 200.0,
+                               minpos_f.X, minpos_f.Y, minpos_f.Z,
+                               maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
+                               samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
+               /*noisebuf1.create(data->seed+25104, 3, 0.60, 25.0,
+                               minpos_f.X, minpos_f.Y, minpos_f.Z,
+                               maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
+                               samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
+               noisebuf2.create(data->seed+25105, 4, 0.50, 200.0,
+                               minpos_f.X, minpos_f.Y, minpos_f.Z,
+                               maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
+                               samplelength_f.X, samplelength_f.Y, samplelength_f.Z);*/
+       }
 
        for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
        for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
@@ -2232,14 +2294,71 @@ void makeChunk(ChunkMakeData *data)
                // Node position
                v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
                
+               // Ground height at this point
+               float surface_y_f = 0.0;
+
+               // Use perlin noise for ground height
+               surface_y_f = base_rock_level_2d(data->seed, p2d);
+               //surface_y_f = base_rock_level_2d(data->seed, p2d);
+               
+               // Convert to integer
+               s16 surface_y = (s16)surface_y_f;
+               
+               // Log it
+               if(surface_y > stone_surface_max_y)
+                       stone_surface_max_y = surface_y;
+
                /*
-                       Skip of already generated
+                       Fill ground with stone
                */
                {
+                       // Use fast index incrementing
+                       v3s16 em = data->vmanip.m_area.getExtent();
+                       u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
+                       for(s16 y=y_nodes_min; y<=y_nodes_max; y++)
+                       {
+                               // Skip if already generated.
+                               // This is done here because there might be a cave at
+                               // any point in ground, which could look like it
+                               // wasn't generated.
+                               if(data->vmanip.m_data[i].d != CONTENT_AIR)
+                                       break;
+
+                               /*s16 noiseval = 50.0 * noise3d_perlin(
+                                               0.5+(float)p2d.X/100.0,
+                                               0.5+(float)y/100.0,
+                                               0.5+(float)p2d.Y/100.0,
+                                               data->seed+123, 5, 0.5);*/
+                               double noiseval = 64.0 * noisebuf1.get(p2d.X, y, p2d.Y);
+                               /*double noiseval = 30.0 * noisebuf1.get(p2d.X, y, p2d.Y);
+                               noiseval *= MYMAX(0, -0.2 + noisebuf2.get(p2d.X, y, p2d.Y));*/
+                               
+                               //if(y < surface_y + noiseval)
+                               if(noiseval > 0)
+                               //if(noiseval > y)
+                                       data->vmanip.m_data[i].d = CONTENT_STONE;
+
+                               data->vmanip.m_area.add_y(em, i, 1);
+                       }
+               }
+       }
+#endif
+       
+#if 1
+       for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
+       for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
+       {
+               // Node position
+               v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
+               
+               /*
+                       Skip of already generated
+               */
+               /*{
                        v3s16 p(p2d.X, y_nodes_min, p2d.Y);
                        if(data->vmanip.m_data[data->vmanip.m_area.index(p)].d != CONTENT_AIR)
                                continue;
-               }
+               }*/
 
                // Ground height at this point
                float surface_y_f = 0.0;
@@ -2270,12 +2389,20 @@ void makeChunk(ChunkMakeData *data)
                        u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
                        for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
                        {
+                               // Skip if already generated.
+                               // This is done here because there might be a cave at
+                               // any point in ground, which could look like it
+                               // wasn't generated.
+                               if(data->vmanip.m_data[i].d != CONTENT_AIR)
+                                       break;
+
                                data->vmanip.m_data[i].d = CONTENT_STONE;
 
                                data->vmanip.m_area.add_y(em, i, 1);
                        }
                }
        }
+#endif
        
        }//timer1
 
@@ -2296,13 +2423,14 @@ void makeChunk(ChunkMakeData *data)
        /*
                Loop this part, it will make stuff look older and newer nicely
        */
-       //for(u32 i_age=0; i_age<1; i_age++)
-       for(u32 i_age=0; i_age<2; i_age++)
+       const u32 age_loops = 2;
+       for(u32 i_age=0; i_age<age_loops; i_age++)
        { // Aging loop
        /******************************
                BEGINNING OF AGING LOOP
        ******************************/
 
+#if 1
        {
        // 24ms @cs=8
        //TimeTaker timer1("caves");
@@ -2541,6 +2669,9 @@ void makeChunk(ChunkMakeData *data)
        }
 
        }//timer1
+#endif
+
+#if 1
        {
        // 46ms @cs=8
        //TimeTaker timer1("ore veins");
@@ -2674,9 +2805,12 @@ void makeChunk(ChunkMakeData *data)
        }
 
        }//timer1
+#endif
+
+#if 1
        {
        // 15ms @cs=8
-       //TimeTaker timer1("add mud");
+       TimeTaker timer1("add mud");
 
        /*
                Add mud to the central chunk
@@ -2689,9 +2823,7 @@ void makeChunk(ChunkMakeData *data)
                v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
                
                // Randomize mud amount
-               s16 mud_add_amount = (s16)(2.5 + 2.0 * noise2d_perlin(
-                               0.5+(float)p2d.X/200, 0.5+(float)p2d.Y/200,
-                               data->seed+1, 3, 0.55));
+               s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0;
 
                // Find ground level
                s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
@@ -2705,7 +2837,8 @@ void makeChunk(ChunkMakeData *data)
                        u32 i = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
                        MapNode *n = &data->vmanip.m_data[i];
                        if(n->d == CONTENT_GRASS)
-                               n->d = CONTENT_MUD;
+                               *n = MapNode(CONTENT_MUD);
+                               //n->d = CONTENT_MUD;
                }
 
                /*
@@ -2722,7 +2855,8 @@ void makeChunk(ChunkMakeData *data)
                                        break;
                                        
                                MapNode &n = data->vmanip.m_data[i];
-                               n.d = CONTENT_MUD;
+                               n = MapNode(CONTENT_MUD);
+                               //n.d = CONTENT_MUD;
                                mudcount++;
 
                                data->vmanip.m_area.add_y(em, i, 1);
@@ -2732,6 +2866,9 @@ void makeChunk(ChunkMakeData *data)
        }
 
        }//timer1
+#endif
+
+#if 1
        {
        // 340ms @cs=8
        TimeTaker timer1("flow mud");
@@ -2886,9 +3023,12 @@ void makeChunk(ChunkMakeData *data)
        }
 
        }//timer1
+#endif
+
+#if 1
        {
        // 50ms @cs=8
-       //TimeTaker timer1("add water");
+       TimeTaker timer1("add water");
 
        /*
                Add water to the central chunk (and a bit more)
@@ -2947,8 +3087,8 @@ void makeChunk(ChunkMakeData *data)
 
                                        // Add to transforming liquid queue (in case it'd
                                        // start flowing)
-                                       /*v3s16 p = v3s16(p2d.X, y, p2d.Y);
-                                       m_transforming_liquid.push_back(p);*/
+                                       v3s16 p = v3s16(p2d.X, y, p2d.Y);
+                                       data->transforming_liquid.push_back(p);
                                }
                                
                                // Next one
@@ -2961,12 +3101,14 @@ void makeChunk(ChunkMakeData *data)
        }
 
        }//timer1
+#endif
        
        } // Aging loop
        /***********************
                END OF AGING LOOP
        ************************/
 
+#if 1
        {
        //TimeTaker timer1("convert mud to sand");
 
@@ -2999,6 +3141,13 @@ void makeChunk(ChunkMakeData *data)
                if(have_sand == false)
                        continue;
 
+               // Determine whether to have clay in the sand here
+               double claynoise = noise2d_perlin(
+                               0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
+                               data->seed+4321, 8, 0.95);
+
+               bool have_clay = have_sand && (claynoise > 0.95);
+
                // Find ground level
                s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
                
@@ -3015,7 +3164,10 @@ void makeChunk(ChunkMakeData *data)
                                MapNode *n = &data->vmanip.m_data[i];
                                if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
                                {
-                                       n->d = CONTENT_SAND;
+                                       if(have_clay && (surface_y == WATER_LEVEL))
+                                               n->d = CONTENT_CLAY;
+                                       else
+                                               n->d = CONTENT_SAND;
                                }
                                else
                                {
@@ -3031,6 +3183,9 @@ void makeChunk(ChunkMakeData *data)
        }
 
        }//timer1
+#endif
+
+#if 1
        {
        // 1ms @cs=8
        //TimeTaker timer1("generate trees");
@@ -3076,18 +3231,24 @@ void makeChunk(ChunkMakeData *data)
                                if(y > y_nodes_max - 6)
                                        continue;
                                v3s16 p(x,y,z);
-                               /*
-                                       Trees grow only on mud and grass
-                               */
                                {
                                        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)
+                                       if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS && n->d != CONTENT_SAND)
                                                continue;
+                                       // Trees grow only on mud and grass
+                                       if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
+                                       {
+                                               p.Y++;
+                                               make_tree(data->vmanip, p);
+                                       }
+                                       // Cactii grow only on sand
+                                       if(n->d == CONTENT_SAND)
+                                       {
+                                               p.Y++;
+                                               make_cactus(data->vmanip, p);
+                                       }
                                }
-                               p.Y++;
-                               // Make a tree
-                               make_tree(data->vmanip, p);
                        }
                }
                /*u32 tree_max = relative_area / 60;
@@ -3109,7 +3270,9 @@ void makeChunk(ChunkMakeData *data)
        }
 
        }//timer1
+#endif
 
+#if 1
        {
        // 19ms @cs=8
        //TimeTaker timer1("grow grass");
@@ -3163,6 +3326,7 @@ void makeChunk(ChunkMakeData *data)
        }
 
        }//timer1
+#endif
 
        /*
                Initial lighting (sunlight)
@@ -3174,6 +3338,7 @@ void makeChunk(ChunkMakeData *data)
        // 750ms @cs=8, can't optimize more
        TimeTaker timer1("initial lighting");
 
+       // NOTE: This is no used... umm... for some reason!
 #if 0
        /*
                Go through the edges and add all nodes that have light to light_sources
@@ -3426,33 +3591,16 @@ void makeChunk(ChunkMakeData *data)
 //###################################################################
 //###################################################################
 
-MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
-               core::map<v3s16, MapBlock*> &changed_blocks,
-               bool force)
+void ServerMap::initChunkMake(ChunkMakeData &data, v2s16 chunkpos)
 {
-       DSTACK(__FUNCTION_NAME);
-
-       /*
-               Don't generate if already fully generated
-       */
-       if(force == false)
+       if(m_chunksize == 0)
        {
-               MapChunk *chunk = getChunk(chunkpos);
-               if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
-               {
-                       dstream<<"generateChunkRaw(): Chunk "
-                                       <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
-                                       <<" already generated"<<std::endl;
-                       return chunk;
-               }
+               data.no_op = true;
+               return;
        }
 
-       dstream<<"generateChunkRaw(): Generating chunk "
-                       <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
-                       <<std::endl;
-       
-       TimeTaker timer("generateChunkRaw()");
-       
+       data.no_op = false;
+
        // The distance how far into the neighbors the generator is allowed to go.
        s16 max_spread_amount_sectors = 2;
        assert(max_spread_amount_sectors <= m_chunksize);
@@ -3468,9 +3616,20 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
                        sectorpos_base - v2s16(1,1) * max_spread_amount_sectors;
        s16 sectorpos_bigbase_size =
                        sectorpos_base_size + 2 * max_spread_amount_sectors;
-                       
-       ChunkMakeData data;
+       
+       // Check limits
+       const s16 limit = MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
+       if(sectorpos_bigbase.X < -limit
+       || sectorpos_bigbase.X + sectorpos_bigbase_size >= limit
+       || sectorpos_bigbase.Y < -limit
+       || sectorpos_bigbase.Y + sectorpos_bigbase_size >= limit)
+       {
+               data.no_op = true;
+               return;
+       }
+
        data.seed = m_seed;
+       data.chunkpos = chunkpos;
        data.y_blocks_min = y_blocks_min;
        data.y_blocks_max = y_blocks_max;
        data.sectorpos_base = sectorpos_base;
@@ -3483,7 +3642,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
                Create the whole area of this and the neighboring chunks
        */
        {
-               TimeTaker timer("generateChunkRaw() create area");
+               TimeTaker timer("initChunkMake() create area");
                
                for(s16 x=0; x<sectorpos_bigbase_size; x++)
                for(s16 z=0; z<sectorpos_bigbase_size; z++)
@@ -3537,13 +3696,18 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
        data.vmanip.setMap(this);
        // Add the area
        {
-               TimeTaker timer("generateChunkRaw() initialEmerge");
+               TimeTaker timer("initChunkMake() initialEmerge");
                data.vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max);
        }
        
-       // Generate stuff
-       makeChunk(&data);
+}
 
+MapChunk* ServerMap::finishChunkMake(ChunkMakeData &data,
+               core::map<v3s16, MapBlock*> &changed_blocks)
+{
+       if(data.no_op)
+               return NULL;
+       
        /*
                Blit generated stuff to map
        */
@@ -3565,18 +3729,27 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
                }
        }
 
+       /*
+               Copy transforming liquid information
+       */
+       while(data.transforming_liquid.size() > 0)
+       {
+               v3s16 p = data.transforming_liquid.pop_front();
+               m_transforming_liquid.push_back(p);
+       }
+
        /*
                Add random objects to blocks
        */
        {
-               for(s16 x=0; x<sectorpos_base_size; x++)
-               for(s16 z=0; z<sectorpos_base_size; z++)
+               for(s16 x=0; x<data.sectorpos_base_size; x++)
+               for(s16 z=0; z<data.sectorpos_base_size; z++)
                {
-                       v2s16 sectorpos = sectorpos_base + v2s16(x,z);
+                       v2s16 sectorpos = data.sectorpos_base + v2s16(x,z);
                        ServerMapSector *sector = createSector(sectorpos);
                        assert(sector);
 
-                       for(s16 y=y_blocks_min; y<=y_blocks_max; y++)
+                       for(s16 y=data.y_blocks_min; y<=data.y_blocks_max; y++)
                        {
                                v3s16 blockpos(sectorpos.X, y, sectorpos.Y);
                                MapBlock *block = createBlock(blockpos);
@@ -3592,7 +3765,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
        for(s16 x=-1; x<=1; x++)
        for(s16 y=-1; y<=1; y++)
        {
-               v2s16 chunkpos0 = chunkpos + v2s16(x,y);
+               v2s16 chunkpos0 = data.chunkpos + v2s16(x,y);
                // Add chunk meta information
                MapChunk *chunk = getChunk(chunkpos0);
                if(chunk == NULL)
@@ -3608,7 +3781,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
        /*
                Set central chunk non-volatile
        */
-       MapChunk *chunk = getChunk(chunkpos);
+       MapChunk *chunk = getChunk(data.chunkpos);
        assert(chunk);
        // Set non-volatile
        //chunk->setIsVolatile(false);
@@ -3618,6 +3791,49 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
                Save changed parts of map
        */
        save(true);
+       
+       return chunk;
+}
+
+#if 0
+// NOTE: Deprecated
+MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
+               core::map<v3s16, MapBlock*> &changed_blocks,
+               bool force)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       /*
+               Don't generate if already fully generated
+       */
+       if(force == false)
+       {
+               MapChunk *chunk = getChunk(chunkpos);
+               if(chunk != NULL && chunk->getGenLevel() == GENERATED_FULLY)
+               {
+                       dstream<<"generateChunkRaw(): Chunk "
+                                       <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
+                                       <<" already generated"<<std::endl;
+                       return chunk;
+               }
+       }
+
+       dstream<<"generateChunkRaw(): Generating chunk "
+                       <<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
+                       <<std::endl;
+       
+       TimeTaker timer("generateChunkRaw()");
+
+       ChunkMakeData data;
+       
+       // Initialize generation
+       initChunkMake(data, chunkpos);
+       
+       // Generate stuff
+       makeChunk(&data);
+
+       // Finalize generation
+       MapChunk *chunk = finishChunkMake(data, changed_blocks);
 
        /*
                Return central chunk (which was requested)
@@ -3625,6 +3841,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos,
        return chunk;
 }
 
+// NOTE: Deprecated
 MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
                core::map<v3s16, MapBlock*> &changed_blocks)
 {
@@ -3650,10 +3867,11 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos1,
        MapChunk *chunk = getChunk(chunkpos1);
        return chunk;
 }
+#endif
 
 ServerMapSector * ServerMap::createSector(v2s16 p2d)
 {
-       DSTACK("%s: p2d=(%d,%d)",
+       DSTACKF("%s: p2d=(%d,%d)",
                        __FUNCTION_NAME,
                        p2d.X, p2d.Y);
        
@@ -3704,6 +3922,7 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d)
        return sector;
 }
 
+#if 0
 MapSector * ServerMap::emergeSector(v2s16 p2d,
                core::map<v3s16, MapBlock*> &changed_blocks)
 {
@@ -3790,6 +4009,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d,
        */
        //return generateSector();
 }
+#endif
 
 /*
        NOTE: This is not used for main map generation, only for blocks
@@ -3803,9 +4023,17 @@ MapBlock * ServerMap::generateBlock(
                core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
 )
 {
-       DSTACK("%s: p=(%d,%d,%d)",
+       DSTACKF("%s: p=(%d,%d,%d)",
                        __FUNCTION_NAME,
                        p.X, p.Y, p.Z);
+
+       // If chunks are disabled
+       /*if(m_chunksize == 0)
+       {
+               dstream<<"ServerMap::generateBlock(): Chunks disabled -> "
+                               <<"not generating."<<std::endl;
+               return NULL;
+       }*/
        
        /*dstream<<"generateBlock(): "
                        <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
@@ -3844,6 +4072,25 @@ MapBlock * ServerMap::generateBlock(
                block->unDummify();
        }
        
+#if 0
+       /*
+               Generate a completely empty block
+       */
+       for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
+       for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+       {
+               for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+               {
+                       MapNode n;
+                       n.d = CONTENT_AIR;
+                       block->setNode(v3s16(x0,y0,z0), n);
+               }
+       }
+#else
+       /*
+               Generate a proper block
+       */
+       
        u8 water_material = CONTENT_WATERSOURCE;
        
        s32 lowest_ground_y = 32767;
@@ -3856,8 +4103,13 @@ MapBlock * ServerMap::generateBlock(
 
                //s16 surface_y = 0;
 
+               s16 mud_add_amount = get_mud_add_amount(m_seed, p2d_nodes+v2s16(x0,z0));
+
                s16 surface_y = base_rock_level_2d(m_seed, p2d_nodes+v2s16(x0,z0))
-                               + AVERAGE_MUD_AMOUNT;
+                               + mud_add_amount;
+               // If chunks are disabled
+               if(m_chunksize == 0)
+                       surface_y = WATER_LEVEL + 1;
 
                if(surface_y < lowest_ground_y)
                        lowest_ground_y = surface_y;
@@ -4375,6 +4627,8 @@ MapBlock * ServerMap::generateBlock(
                        }
                }
        }
+
+#endif // end of proper block generation
        
        /*
                Add block to sector.
@@ -4405,7 +4659,7 @@ MapBlock * ServerMap::generateBlock(
 
 MapBlock * ServerMap::createBlock(v3s16 p)
 {
-       DSTACK("%s: p=(%d,%d,%d)",
+       DSTACKF("%s: p=(%d,%d,%d)",
                        __FUNCTION_NAME, p.X, p.Y, p.Z);
        
        /*
@@ -4469,7 +4723,7 @@ MapBlock * ServerMap::emergeBlock(
                core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
 )
 {
-       DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
+       DSTACKF("%s: p=(%d,%d,%d), only_from_disk=%d",
                        __FUNCTION_NAME,
                        p.X, p.Y, p.Z, only_from_disk);
        
@@ -4655,13 +4909,16 @@ s16 ServerMap::findGroundLevel(v2s16 p2d)
        /*
                Plan B: Get from map generator perlin noise function
        */
-       double level = base_rock_level_2d(m_seed, p2d);
+       // This won't work if proper generation is disabled
+       if(m_chunksize == 0)
+               return WATER_LEVEL+2;
+       double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
        return (s16)level;
 }
 
-void ServerMap::createDir(std::string path)
+void ServerMap::createDirs(std::string path)
 {
-       if(fs::CreateDir(path) == false)
+       if(fs::CreateAllDirs(path) == false)
        {
                m_dout<<DTIME<<"ServerMap: Failed to create directory "
                                <<"\""<<path<<"\""<<std::endl;
@@ -4669,29 +4926,52 @@ void ServerMap::createDir(std::string path)
        }
 }
 
-std::string ServerMap::getSectorSubDir(v2s16 pos)
+std::string ServerMap::getSectorDir(v2s16 pos, int layout)
 {
        char cc[9];
-       snprintf(cc, 9, "%.4x%.4x",
-                       (unsigned int)pos.X&0xffff,
-                       (unsigned int)pos.Y&0xffff);
+       switch(layout)
+       {
+               case 1:
+                       snprintf(cc, 9, "%.4x%.4x",
+                               (unsigned int)pos.X&0xffff,
+                               (unsigned int)pos.Y&0xffff);
 
-       return std::string(cc);
-}
+                       return m_savedir + "/sectors/" + cc;
+               case 2:
+                       snprintf(cc, 9, "%.3x/%.3x",
+                               (unsigned int)pos.X&0xfff,
+                               (unsigned int)pos.Y&0xfff);
 
-std::string ServerMap::getSectorDir(v2s16 pos)
-{
-       return m_savedir + "/sectors/" + getSectorSubDir(pos);
+                       return m_savedir + "/sectors2/" + cc;
+               default:
+                       assert(false);
+       }
 }
 
 v2s16 ServerMap::getSectorPos(std::string dirname)
 {
-       if(dirname.size() != 8)
-               throw InvalidFilenameException("Invalid sector directory name");
        unsigned int x, y;
-       int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
-       if(r != 2)
-               throw InvalidFilenameException("Invalid sector directory name");
+       int r;
+       size_t spos = dirname.rfind('/') + 1;
+       assert(spos != std::string::npos);
+       if(dirname.size() - spos == 8)
+       {
+               // Old layout
+               r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y);
+       }
+       else if(dirname.size() - spos == 3)
+       {
+               // New layout
+               r = sscanf(dirname.substr(spos-4).c_str(), "%3x/%3x", &x, &y);
+               // Sign-extend the 12 bit values up to 16 bits...
+               if(x&0x800) x|=0xF000;
+               if(y&0x800) y|=0xF000;
+       }
+       else
+       {
+               assert(false);
+       }
+       assert(r == 2);
        v2s16 pos((s16)x, (s16)y);
        return pos;
 }
@@ -4723,14 +5003,23 @@ void ServerMap::save(bool only_changed)
                dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
                                <<std::endl;
        
-       saveMapMeta();
-       saveChunkMeta();
+       if(only_changed == false || m_map_metadata_changed)
+       {
+               saveMapMeta();
+       }
+
+       // Disable saving chunk metadata if chunks are disabled
+       if(m_chunksize != 0)
+       {
+               if(only_changed == false || anyChunkModified())
+                       saveChunkMeta();
+       }
        
        u32 sector_meta_count = 0;
        u32 block_count = 0;
        
        { //sectorlock
-       JMutexAutoLock lock(m_sector_mutex);
+       //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
        
        core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
        for(; i.atEnd() == false; i++)
@@ -4778,6 +5067,8 @@ void ServerMap::save(bool only_changed)
        }
 }
 
+#if 0
+// NOTE: Doing this is insane. Deprecated and probably broken.
 void ServerMap::loadAll()
 {
        DSTACK(__FUNCTION_NAME);
@@ -4790,7 +5081,7 @@ void ServerMap::loadAll()
 
        dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
        
-       JMutexAutoLock lock(m_sector_mutex);
+       //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
        
        s32 counter = 0;
        s32 printed_counter = -100000;
@@ -4838,6 +5129,7 @@ void ServerMap::loadAll()
        }
        dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
 }
+#endif
 
 #if 0
 void ServerMap::saveMasterHeightmap()
@@ -4878,7 +5170,7 @@ void ServerMap::saveMapMeta()
                        <<"seed="<<m_seed<<", chunksize="<<m_chunksize
                        <<std::endl;
 
-       createDir(m_savedir);
+       createDirs(m_savedir);
        
        std::string fullpath = m_savedir + "/map_meta.txt";
        std::ofstream os(fullpath.c_str(), std::ios_base::binary);
@@ -4897,13 +5189,14 @@ void ServerMap::saveMapMeta()
 
        os<<"[end_of_params]\n";
        
+       m_map_metadata_changed = false;
 }
 
 void ServerMap::loadMapMeta()
 {
        DSTACK(__FUNCTION_NAME);
        
-       dstream<<"INFO: ServerMap::loadMapMeta(): Loading chunk metadata"
+       dstream<<"INFO: ServerMap::loadMapMeta(): Loading map metadata"
                        <<std::endl;
 
        std::string fullpath = m_savedir + "/map_meta.txt";
@@ -4912,7 +5205,7 @@ void ServerMap::loadMapMeta()
        {
                dstream<<"ERROR: ServerMap::loadMapMeta(): "
                                <<"could not open"<<fullpath<<std::endl;
-               throw FileNotGoodException("Cannot open chunk metadata");
+               throw FileNotGoodException("Cannot open map metadata");
        }
 
        Settings params;
@@ -4941,13 +5234,16 @@ void ServerMap::loadMapMeta()
 void ServerMap::saveChunkMeta()
 {
        DSTACK(__FUNCTION_NAME);
+
+       // This should not be called if chunks are disabled.
+       assert(m_chunksize != 0);
        
        u32 count = m_chunks.size();
 
        dstream<<"INFO: ServerMap::saveChunkMeta(): Saving metadata of "
                        <<count<<" chunks"<<std::endl;
 
-       createDir(m_savedir);
+       createDirs(m_savedir);
        
        std::string fullpath = m_savedir + "/chunk_meta";
        std::ofstream os(fullpath.c_str(), std::ios_base::binary);
@@ -4981,6 +5277,8 @@ void ServerMap::saveChunkMeta()
                // Write chunk data
                chunk->serialize(os, version);
        }
+
+       setChunksNonModified();
 }
 
 void ServerMap::loadChunkMeta()
@@ -5033,10 +5331,8 @@ void ServerMap::saveSectorMeta(ServerMapSector *sector)
        u8 version = SER_FMT_VER_HIGHEST;
        // Get destination
        v2s16 pos = sector->getPos();
-       createDir(m_savedir);
-       createDir(m_savedir+"/sectors");
        std::string dir = getSectorDir(pos);
-       createDir(dir);
+       createDirs(dir);
        
        std::string fullpath = dir + "/meta";
        std::ofstream o(fullpath.c_str(), std::ios_base::binary);
@@ -5048,20 +5344,41 @@ void ServerMap::saveSectorMeta(ServerMapSector *sector)
        sector->differs_from_disk = false;
 }
 
-MapSector* ServerMap::loadSectorMeta(std::string dirname)
+MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load)
 {
        DSTACK(__FUNCTION_NAME);
        // Get destination
-       v2s16 p2d = getSectorPos(dirname);
-       std::string dir = m_savedir + "/sectors/" + dirname;
-       
-       std::string fullpath = dir + "/meta";
+       v2s16 p2d = getSectorPos(sectordir);
+
+       ServerMapSector *sector = NULL;
+
+       std::string fullpath = sectordir + "/meta";
        std::ifstream is(fullpath.c_str(), std::ios_base::binary);
        if(is.good() == false)
-               throw FileNotGoodException("Cannot open sector metafile");
-
-       ServerMapSector *sector = ServerMapSector::deSerialize
-                       (is, this, p2d, m_sectors);
+       {
+               // If the directory exists anyway, it probably is in some old
+               // format. Just go ahead and create the sector.
+               if(fs::PathExists(sectordir))
+               {
+                       dstream<<"ServerMap::loadSectorMeta(): Sector metafile "
+                                       <<fullpath<<" doesn't exist but directory does."
+                                       <<" Continuing with a sector with no metadata."
+                                       <<std::endl;
+                       sector = new ServerMapSector(this, p2d);
+                       m_sectors.insert(p2d, sector);
+               }
+               else
+               {
+                       throw FileNotGoodException("Cannot open sector metafile");
+               }
+       }
+       else
+       {
+               sector = ServerMapSector::deSerialize
+                               (is, this, p2d, m_sectors);
+               if(save_after_load)
+                       saveSectorMeta(sector);
+       }
        
        sector->differs_from_disk = false;
 
@@ -5071,14 +5388,31 @@ MapSector* ServerMap::loadSectorMeta(std::string dirname)
 bool ServerMap::loadSectorFull(v2s16 p2d)
 {
        DSTACK(__FUNCTION_NAME);
-       std::string sectorsubdir = getSectorSubDir(p2d);
 
        MapSector *sector = NULL;
 
-       JMutexAutoLock lock(m_sector_mutex);
+       // The directory layout we're going to load from.
+       //  1 - original sectors/xxxxzzzz/
+       //  2 - new sectors2/xxx/zzz/
+       //  If we load from anything but the latest structure, we will
+       //  immediately save to the new one, and remove the old.
+       int loadlayout = 1;
+       std::string sectordir1 = getSectorDir(p2d, 1);
+       std::string sectordir;
+       if(fs::PathExists(sectordir1))
+       {
+               sectordir = sectordir1;
+       }
+       else
+       {
+               loadlayout = 2;
+               sectordir = getSectorDir(p2d, 2);
+       }
+
+       //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
 
        try{
-               sector = loadSectorMeta(sectorsubdir);
+               sector = loadSectorMeta(sectordir, loadlayout != 2);
        }
        catch(InvalidFilenameException &e)
        {
@@ -5097,7 +5431,7 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
                Load blocks
        */
        std::vector<fs::DirListNode> list2 = fs::GetDirListing
-                       (m_savedir+"/sectors/"+sectorsubdir);
+                       (sectordir);
        std::vector<fs::DirListNode>::iterator i2;
        for(i2=list2.begin(); i2!=list2.end(); i2++)
        {
@@ -5105,16 +5439,25 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
                if(i2->dir)
                        continue;
                try{
-                       loadBlock(sectorsubdir, i2->name, sector);
+                       loadBlock(sectordir, i2->name, sector, loadlayout != 2);
                }
                catch(InvalidFilenameException &e)
                {
                        // This catches unknown crap in directory
                }
        }
+
+       if(loadlayout != 2)
+       {
+               dstream<<"Sector converted to new layout - deleting "<<
+                       sectordir1<<std::endl;
+               fs::RecursiveDelete(sectordir1);
+       }
+
        return true;
 }
 
+
 void ServerMap::saveBlock(MapBlock *block)
 {
        DSTACK(__FUNCTION_NAME);
@@ -5134,12 +5477,9 @@ void ServerMap::saveBlock(MapBlock *block)
        // Get destination
        v3s16 p3d = block->getPos();
        v2s16 p2d(p3d.X, p3d.Z);
-       createDir(m_savedir);
-       createDir(m_savedir+"/sectors");
        std::string dir = getSectorDir(p2d);
-       createDir(dir);
+       createDirs(dir);
        
-       // Block file is map/sectors/xxxxxxxx/xxxx
        char cc[5];
        snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
        std::string fullpath = dir + "/" + cc;
@@ -5153,34 +5493,21 @@ void ServerMap::saveBlock(MapBlock *block)
        */
        o.write((char*)&version, 1);
        
+       // Write basic data
        block->serialize(o, version);
-
-       /*
-               Versions up from 9 have block objects.
-       */
-       if(version >= 9)
-       {
-               block->serializeObjects(o, version);
-       }
-       
-       /*
-               Versions up from 15 have static objects.
-       */
-       if(version >= 15)
-       {
-               block->m_static_objects.serialize(o);
-       }
        
-       // We just wrote it to the disk
+       // Write extra data stored on disk
+       block->serializeDiskExtra(o, version);
+
+       // We just wrote it to the disk so clear modified flag
        block->resetChangedFlag();
 }
 
-void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
+void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
 {
        DSTACK(__FUNCTION_NAME);
 
-       // Block file is map/sectors/xxxxxxxx/xxxx
-       std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
+       std::string fullpath = sectordir+"/"+blockfile;
        try{
 
                std::ifstream is(fullpath.c_str(), std::ios_base::binary);
@@ -5217,34 +5544,21 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
                        created_new = true;
                }
                
-               // deserialize block data
+               // Read basic data
                block->deSerialize(is, version);
-               
-               /*
-                       Versions up from 9 have block objects.
-               */
-               if(version >= 9)
-               {
-                       block->updateObjects(is, version, NULL, 0);
-               }
 
-               /*
-                       Versions up from 15 have static objects.
-               */
-               if(version >= 15)
-               {
-                       block->m_static_objects.deSerialize(is);
-               }
+               // Read extra data stored on disk
+               block->deSerializeDiskExtra(is, version);
                
+               // If it's a new block, insert it to the map
                if(created_new)
                        sector->insertBlock(block);
                
                /*
-                       Convert old formats to new and save
+                       Save blocks loaded in old format in new format
                */
 
-               // Save old format blocks in new format
-               if(version < SER_FMT_VER_HIGHEST)
+               if(version < SER_FMT_VER_HIGHEST || save_after_load)
                {
                        saveBlock(block);
                }
@@ -5322,7 +5636,7 @@ MapSector * ClientMap::emergeSector(v2s16 p2d)
        ClientMapSector *sector = new ClientMapSector(this, p2d);
        
        {
-               JMutexAutoLock lock(m_sector_mutex);
+               //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
                m_sectors.insert(p2d, sector);
        }
        
@@ -5334,7 +5648,7 @@ void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
        DSTACK(__FUNCTION_NAME);
        ClientMapSector *sector = NULL;
 
-       JMutexAutoLock lock(m_sector_mutex);
+       //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
        
        core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
 
@@ -5347,7 +5661,7 @@ void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
        {
                sector = new ClientMapSector(this, p2d);
                {
-                       JMutexAutoLock lock(m_sector_mutex);
+                       //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
                        m_sectors.insert(p2d, sector);
                }
        }
@@ -5945,7 +6259,8 @@ void MapVoxelManipulator::blitBack
 }
 
 ManualMapVoxelManipulator::ManualMapVoxelManipulator(Map *map):
-               MapVoxelManipulator(map)
+               MapVoxelManipulator(map),
+               m_create_area(false)
 {
 }