remove content_* things from map.cpp
[oweals/minetest.git] / src / map.cpp
index 10e1302b12573fbde38f9aaff886163837480bfc..32675f08d96523c5b2f640a2912ffcb0279a79ca 100644 (file)
@@ -29,12 +29,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "mapgen.h"
 #include "nodemetadata.h"
 
-extern "C" {
-       #include "sqlite3.h"
-}
 /*
        SQLite format specification:
        - Initially only replaces sectors/ and sectors2/
+       
+       If map.sqlite does not exist in the save dir
+       or the block was not found in the database
+       the map will try to load from sectors folder.
+       In either case, map.sqlite will be created
+       and all future saves will save there.
+       
+       Structure of map.sqlite:
+       Tables:
+               blocks
+                       (PK) INT pos
+                       BLOB data
 */
 
 /*
@@ -630,9 +639,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);
                        }*/
@@ -881,7 +890,7 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
 /*
 */
 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
-               core::map<v3s16, MapBlock*> &modified_blocks)
+               core::map<v3s16, MapBlock*> &modified_blocks, std::string &player_name)
 {
        /*PrintInfo(m_dout);
        m_dout<<DTIME<<"Map::addNodeAndUpdate(): p=("
@@ -920,15 +929,15 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
        /*
                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);
                        }
                }
@@ -943,9 +952,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
 
@@ -986,7 +995,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);
        }
@@ -1001,10 +1010,11 @@ 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();
+               meta->setOwner(player_name);
                setNodeMetadata(p, meta);
        }
 
@@ -1015,7 +1025,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--){
@@ -1086,7 +1096,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()) || n2.getContent() == CONTENT_AIR)
                {
                        m_transforming_liquid.push_back(p2);
                }
@@ -1111,7 +1121,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,
@@ -1158,7 +1168,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++)
@@ -1240,25 +1250,19 @@ void Map::removeNodeAndUpdate(v3s16 p,
        }
 
        /*
-               Add neighboring liquid nodes to transform queue.
-
-               Also add horizontal neighbors of node on top of removed node
-               because they could be affected of the water on top flowing
-               down instead of into them.
+               Add neighboring liquid nodes and this node to transform queue.
+               (it's vital for the node itself to get updated last.)
        */
-       v3s16 dirs[10] = {
+       v3s16 dirs[7] = {
                v3s16(0,0,1), // back
                v3s16(0,1,0), // top
-               v3s16(1,1,0), // topright
-               v3s16(-1,1,0), // topleft
-               v3s16(0,1,1), // topback
-               v3s16(0,1,-1), // topfront
                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<10; i++)
+       for(u16 i=0; i<7; i++)
        {
                try
                {
@@ -1266,7 +1270,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()) || n2.getContent() == CONTENT_AIR)
                {
                        m_transforming_liquid.push_back(p2);
                }
@@ -1287,7 +1291,8 @@ bool Map::addNodeWithEvent(v3s16 p, MapNode n)
        bool succeeded = true;
        try{
                core::map<v3s16, MapBlock*> modified_blocks;
-               addNodeAndUpdate(p, n, modified_blocks);
+               std::string st = std::string("");
+               addNodeAndUpdate(p, n, modified_blocks, st);
 
                // Copy modified_blocks to event
                for(core::map<v3s16, MapBlock*>::Iterator
@@ -1405,6 +1410,7 @@ void Map::timerUpdate(float dtime, float unload_timeout,
 
        core::map<v2s16, MapSector*>::Iterator si;
 
+       beginSave();
        si = m_sectors.getIterator();
        for(; si.atEnd() == false; si++)
        {
@@ -1414,6 +1420,7 @@ void Map::timerUpdate(float dtime, float unload_timeout,
 
                core::list<MapBlock*> blocks;
                sector->getBlocks(blocks);
+               
                for(core::list<MapBlock*>::Iterator i = blocks.begin();
                                i != blocks.end(); i++)
                {
@@ -1452,6 +1459,7 @@ void Map::timerUpdate(float dtime, float unload_timeout,
                        sector_deletion_queue.push_back(si.getNode()->getKey());
                }
        }
+       endSave();
        
        // Finally delete the empty sectors
        deleteSectors(sector_deletion_queue);
@@ -1546,6 +1554,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);
@@ -1557,8 +1576,19 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
        /*if(initial_size != 0)
                dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
 
+       // list of nodes that due to viscosity have not reached their max level height
+       UniqueQueue<v3s16> must_reflow;
+       
+       // List of MapBlocks that will require a lighting update (due to lava)
+       core::map<v3s16, MapBlock*> lighting_modified_blocks;
+
        while(m_transforming_liquid.size() != 0)
        {
+               // This should be done here so that it is done when continue is used
+               if(loopcount >= initial_size * 3)
+                       break;
+               loopcount++;
+
                /*
                        Get a queued transforming liquid node
                */
@@ -1566,265 +1596,216 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
 
                MapNode n0 = getNodeNoEx(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);
-
-               // Counts surrounding liquid source blocks
-               u8 surrounding_sources = 0;
-
                /*
-                       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++)
-                       {
-                               bool from_top = (i==0);
-
-                               v3s16 p2 = p0 + dirs_from[i];
-                               MapNode n2 = getNodeNoEx(p2);
+                       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 = LIQUID_LEVEL_SOURCE;
+                               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;
+               }
 
-                               if(content_liquid(n2.d))
-                               {
-                                       u8 n2_nonsource_c = make_liquid_flowing(n2.d);
-                                       // Check that the liquids are the same type
-                                       if(n2_nonsource_c != nonsource_c)
-                                       {
-                                               dstream<<"WARNING: Not handling: different liquids"
-                                                               " collide"<<std::endl;
-                                               continue;
-                                       }
-                                       bool n2_is_source = !content_flowing_liquid(n2.d);
-                                       s8 n2_liquid_level = 8;
-                                       if(n2_is_source)
-                                               surrounding_sources++;
-                                       else
-                                               n2_liquid_level = n2.param2 & 0x07;
-
-                                       s8 new_liquid_level = -1;
-                                       if(from_top)
-                                       {
-                                               //new_liquid_level = 7;
-                                               if(n2_liquid_level >= 7 - WATER_DROP_BOOST)
-                                                       new_liquid_level = 7;
-                                               else
-                                                       new_liquid_level = n2_liquid_level + WATER_DROP_BOOST;
-                                       }
-                                       else if(n2_liquid_level > 0)
-                                       {
-                                               // If the neighbor node isn't a source and flows downwards,
-                                               // it doesn't flow into this node
-                                               if (n2_is_source)
-                                               {
-                                                       new_liquid_level = n2_liquid_level - 1;
-                                               }
-                                               else
-                                               {
-                                                       // Node below n2
-                                                       MapNode n3 = getNodeNoEx(p2 + v3s16(0,-1,0));
-                                                       // NOTE: collision of different liquids not yet handled here.
-                                                       if (content_features(n3.d).liquid_type != LIQUID_FLOWING)
-                                                       new_liquid_level = n2_liquid_level - 1;
+               /*
+                       Collect information about the environment
+                */
+               const v3s16 *dirs = g_6dirs;
+               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 1:
+                                       nt = NEIGHBOR_UPPER;
+                                       break;
+                               case 4:
+                                       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 is a water source the neighbor
+                                               // should be enqueded for transformation regardless of whether the
+                                               // current node changes or not.
+                                               if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
+                                                       m_transforming_liquid.push_back(npos);
+                                               // 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;
                                        }
-
-                                       if(new_liquid_level > new_liquid_level_max)
-                                               new_liquid_level_max = new_liquid_level;
-                               }
-                       } //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 || (!is_source && surrounding_sources >= 2))
-                       {
-                               if (surrounding_sources >= 2)
-                               {
-                                       n0.d = content_features(n0.d).liquid_alternative_source;
-                                       setNode(p0,n0);
-                               }
-                               else 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;
-                                       liquid_level = new_liquid_level_max;
-                                       setNode(p0, n0);
-                               }
-
-                               // Block has been modified
-                               {
-                                       v3s16 blockpos = getNodeBlockPos(p0);
-                                       MapBlock *block = getBlockNoCreateNoEx(blockpos);
-                                       if(block != NULL)
-                                               modified_blocks.insert(blockpos, block);
-                               }
-
-                               /*
-                                       Add neighboring non-source liquid nodes to transform queue.
-                               */
-                               v3s16 dirs[6] = {
-                                       v3s16(0,0,1), // back
-                                       v3s16(0,1,0), // top
-                                       v3s16(1,0,0), // right
-                                       v3s16(0,0,-1), // front
-                                       v3s16(0,-1,0), // bottom
-                                       v3s16(-1,0,0), // left
-                               };
-                               for(u16 i=0; i<6; i++)
-                               {
-                                       v3s16 p2 = p0 + dirs[i];
-
-                                       MapNode n2 = getNodeNoEx(p2);
-                                       if(content_flowing_liquid(n2.d))
-                                       {
-                                               m_transforming_liquid.push_back(p2);
+                                       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;
                                        }
-                               }
+                                       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;
+                                       }
+                                       break;
                        }
                }
 
-               // Get a new one from queue if the node has turned into non-water
-               if(content_liquid(n0.d) == false)
-                       continue;
-
                /*
-                       Flow water from this node
-               */
-               v3s16 dirs_to[5] = {
-                       v3s16(0,-1,0), // bottom
-                       v3s16(0,0,1), // back
-                       v3s16(1,0,0), // right
-                       v3s16(0,0,-1), // front
-                       v3s16(-1,0,0), // left
-               };
-               for(u16 i=0; i<5; i++)
-               {
-                       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 = getNodeNoEx(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;
-                               }
-
-                               if(n2_is_source)
-                               {
-                                       // Just flow into the source, nothing changes.
-                                       // n2_changed is not set because destination didn't change
-                                       flowed = true;
-                               }
-                               else
-                               {
-                                       if(liquid_next_level > n2_liquid_level)
-                                       {
-                                               n2.param2 = liquid_next_level;
-                                               setNode(p2, n2);
-
-                                               n2_changed = true;
-                                               flowed = true;
-                                       }
+                       decide on the type (and possibly level) of the current node
+                */
+               content_t new_node_content;
+               s8 new_node_level = -1;
+               s8 max_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;
+                       max_node_level = new_node_level = LIQUID_LEVEL_MAX;
+               } 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 > max_node_level) {
+                                                       max_node_level = LIQUID_LEVEL_MAX;
+                                                       if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
+                                                               max_node_level = nb_liquid_level + WATER_DROP_BOOST;
+                                               } else if (nb_liquid_level > max_node_level)
+                                                       max_node_level = nb_liquid_level;
+                                               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 > max_node_level) {
+                                                       max_node_level = nb_liquid_level - 1;
+                                               }
+                                               break;
                                }
                        }
-                       else if(n2.d == CONTENT_AIR)
-                       {
-                               n2.d = nonsource_c;
-                               n2.param2 = liquid_next_level;
-                               setNode(p2, n2);
 
-                               n2_changed = true;
-                               flowed = true;
-                       }
+                       u8 viscosity = content_features(liquid_kind).liquid_viscosity;
+                       if (viscosity > 1 && max_node_level != liquid_level) {
+                               // amount to gain, limited by viscosity
+                               // must be at least 1 in absolute value
+                               s8 level_inc = max_node_level - liquid_level;
+                               if (level_inc < -viscosity || level_inc > viscosity)
+                                       new_node_level = liquid_level + level_inc/viscosity;
+                               else if (level_inc < 0)
+                                       new_node_level = liquid_level - 1;
+                               else if (level_inc > 0)
+                                       new_node_level = liquid_level + 1;
+                               if (new_node_level != max_node_level)
+                                       must_reflow.push_back(p0);
+                       } else
+                               new_node_level = max_node_level;
+
+                       if (new_node_level >= 0)
+                               new_node_content = liquid_kind;
+                       else
+                               new_node_content = CONTENT_AIR;
 
-                       //dstream<<"[2] n2.param="<<(int)n2.param<<std::endl;
+               }
 
-                       if(n2_changed)
-                       {
-                               m_transforming_liquid.push_back(p2);
+               /*
+                       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;
 
-                               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;
+               /*
+                       update the current node
+                */
+               bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
+               if (content_features(new_node_content).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 {
+                       // set the liquid level and flow bit to 0
+                       n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
+               }
+               n0.setContent(new_node_content);
+               setNode(p0, n0);
+               v3s16 blockpos = getNodeBlockPos(p0);
+               MapBlock *block = getBlockNoCreateNoEx(blockpos);
+               if(block != NULL) {
+                       modified_blocks.insert(blockpos, block);
+                       // If node emits light, MapBlock requires lighting update
+                       if(content_features(n0).light_source != 0)
+                               lighting_modified_blocks[block->getPos()] = block;
                }
 
-               loopcount++;
-               //if(loopcount >= 100000)
-               if(loopcount >= initial_size * 1)
-                       break;
+               /*
+                       enqueue neighbors for update if neccessary
+                */
+               switch (content_features(n0.getContent()).liquid_type) {
+                       case LIQUID_SOURCE:
+                       case LIQUID_FLOWING:
+                               // 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;
+               }
        }
        //dstream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
+       while (must_reflow.size() > 0)
+               m_transforming_liquid.push_back(must_reflow.pop_front());
+       updateLighting(lighting_modified_blocks, modified_blocks);
 }
 
 NodeMetadata* Map::getNodeMetadata(v3s16 p)
@@ -1906,16 +1887,26 @@ void Map::nodeMetadataStep(float dtime,
 ServerMap::ServerMap(std::string savedir):
        Map(dout_server),
        m_seed(0),
-       m_map_metadata_changed(true)
+       m_map_metadata_changed(true),
+       m_database(NULL),
+       m_database_read(NULL),
+       m_database_write(NULL)
 {
        dstream<<__FUNCTION_NAME<<std::endl;
 
        //m_chunksize = 8; // Takes a few seconds
 
-       m_seed = (((u64)(myrand()%0xffff)<<0)
-                       + ((u64)(myrand()%0xffff)<<16)
-                       + ((u64)(myrand()%0xffff)<<32)
-                       + ((u64)(myrand()%0xffff)<<48));
+       if (g_settings.get("fixed_map_seed").empty())
+       {
+               m_seed = (((u64)(myrand()%0xffff)<<0)
+                               + ((u64)(myrand()%0xffff)<<16)
+                               + ((u64)(myrand()%0xffff)<<32)
+                               + ((u64)(myrand()%0xffff)<<48));
+       }
+       else
+       {
+               m_seed = g_settings.getU64("fixed_map_seed");
+       }
 
        /*
                Experimental and debug stuff
@@ -2027,6 +2018,16 @@ ServerMap::~ServerMap()
                                <<", exception: "<<e.what()<<std::endl;
        }
 
+       /*
+               Close database if it was opened
+       */
+       if(m_database_read)
+               sqlite3_finalize(m_database_read);
+       if(m_database_write)
+               sqlite3_finalize(m_database_write);
+       if(m_database)
+               sqlite3_close(m_database);
+
 #if 0
        /*
                Free all MapChunks
@@ -2042,9 +2043,19 @@ ServerMap::~ServerMap()
 
 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;
@@ -2065,20 +2076,28 @@ void ServerMap::initBlockMake(mapgen::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);
                        }
                }
        }
@@ -2090,6 +2109,7 @@ void ServerMap::initBlockMake(mapgen::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);
        
@@ -2114,7 +2134,7 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
 
        if(data->no_op)
        {
-               dstream<<"finishBlockMake(): no-op"<<std::endl;
+               //dstream<<"finishBlockMake(): no-op"<<std::endl;
                return NULL;
        }
 
@@ -2153,10 +2173,14 @@ MapBlock* ServerMap::finishBlockMake(mapgen::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));
 
-       block->setIsUnderground(mapgen::block_is_underground(data->seed, blockpos));
 
        /*
                Add sunlight to central block.
@@ -2184,10 +2208,25 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
                TimeTaker t("finishBlockMake lighting update");
 
                core::map<v3s16, MapBlock*> lighting_update_blocks;
+#if 1
                // Center block
                lighting_update_blocks.insert(block->getPos(), block);
-       #if 0
+
+               /*{
+                       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
+               // 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++)
@@ -2195,8 +2234,28 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
                        lighting_update_blocks.insert(i.getNode()->getKey(),
                                        i.getNode()->getValue());
                }
-       #endif
+               /*// 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);
+               
+               /*
+                       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
@@ -2238,7 +2297,26 @@ MapBlock* ServerMap::finishBlockMake(mapgen::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;
 }
 
@@ -2263,6 +2341,7 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d)
        /*
                Try to load metadata from disk
        */
+#if 0
        if(loadSectorMeta(p2d) == true)
        {
                ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
@@ -2273,7 +2352,7 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d)
                }
                return sector;
        }
-
+#endif
        /*
                Do not create over-limit
        */
@@ -2358,49 +2437,54 @@ 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
@@ -2482,7 +2566,7 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
        
        {
                MapBlock *block = getBlockNoCreateNoEx(p);
-               if(block)
+               if(block && block->isDummy() == false)
                        return block;
        }
 
@@ -2679,19 +2763,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;
        }
 
@@ -2710,6 +2794,75 @@ plan_b:
        //return (s16)level;
 }
 
+void ServerMap::createDatabase() {
+       int e;
+       assert(m_database);
+       e = sqlite3_exec(m_database,
+               "CREATE TABLE IF NOT EXISTS `blocks` ("
+                       "`pos` INT NOT NULL PRIMARY KEY,"
+                       "`data` BLOB"
+               ");"
+       , NULL, NULL, NULL);
+       if(e == SQLITE_ABORT)
+               throw FileNotGoodException("Could not create database structure");
+       else
+               dstream<<"Server: Database structure was created";
+}
+
+void ServerMap::verifyDatabase() {
+       if(m_database)
+               return;
+       
+       {
+               std::string dbp = m_savedir + "/map.sqlite";
+               bool needs_create = false;
+               int d;
+               
+               /*
+                       Open the database connection
+               */
+       
+               createDirs(m_savedir);
+       
+               if(!fs::PathExists(dbp))
+                       needs_create = true;
+       
+               d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
+               if(d != SQLITE_OK) {
+                       dstream<<"WARNING: Database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
+                       throw FileNotGoodException("Cannot open database file");
+               }
+               
+               if(needs_create)
+                       createDatabase();
+       
+               d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
+               if(d != SQLITE_OK) {
+                       dstream<<"WARNING: Database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
+                       throw FileNotGoodException("Cannot prepare read statement");
+               }
+               
+               d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
+               if(d != SQLITE_OK) {
+                       dstream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
+                       throw FileNotGoodException("Cannot prepare write statement");
+               }
+               
+               dstream<<"Server: Database opened"<<std::endl;
+       }
+}
+
+bool ServerMap::loadFromFolders() {
+       if(!m_database && !fs::PathExists(m_savedir + "/map.sqlite"))
+               return true;
+       return false;
+}
+
+sqlite3_int64 ServerMap::getBlockAsInteger(const v3s16 pos) {
+       return (sqlite3_int64)pos.Z*16777216 +
+               (sqlite3_int64)pos.Y*4096 + (sqlite3_int64)pos.X;
+}
+
 void ServerMap::createDirs(std::string path)
 {
        if(fs::CreateAllDirs(path) == false)
@@ -2813,6 +2966,7 @@ void ServerMap::save(bool only_changed)
        u32 block_count = 0;
        u32 block_count_all = 0; // Number of blocks in memory
        
+       beginSave();
        core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
        for(; i.atEnd() == false; i++)
        {
@@ -2827,6 +2981,8 @@ void ServerMap::save(bool only_changed)
                core::list<MapBlock*> blocks;
                sector->getBlocks(blocks);
                core::list<MapBlock*>::Iterator j;
+               
+               //sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL);
                for(j=blocks.begin(); j!=blocks.end(); j++)
                {
                        MapBlock *block = *j;
@@ -2845,8 +3001,10 @@ void ServerMap::save(bool only_changed)
                                                <<block->getPos().Z<<")"
                                                <<std::endl;*/
                        }
+               //sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL);
                }
        }
+       endSave();
 
        /*
                Only print if something happened or saved whole map
@@ -3105,6 +3263,18 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
 }
 #endif
 
+void ServerMap::beginSave() {
+       verifyDatabase();
+       if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
+               dstream<<"WARNING: beginSave() failed, saving might be slow.";
+}
+
+void ServerMap::endSave() {
+       verifyDatabase();
+       if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
+               dstream<<"WARNING: endSave() failed, map might not have saved.";
+}
+
 void ServerMap::saveBlock(MapBlock *block)
 {
        DSTACK(__FUNCTION_NAME);
@@ -3124,6 +3294,8 @@ void ServerMap::saveBlock(MapBlock *block)
        // Get destination
        v3s16 p3d = block->getPos();
        
+       
+#if 0
        v2s16 p2d(p3d.X, p3d.Z);
        std::string sectordir = getSectorDir(p2d);
 
@@ -3133,11 +3305,16 @@ void ServerMap::saveBlock(MapBlock *block)
        std::ofstream o(fullpath.c_str(), std::ios_base::binary);
        if(o.good() == false)
                throw FileNotGoodException("Cannot open block data");
-
+#endif
        /*
                [0] u8 serialization version
                [1] data
        */
+       
+       verifyDatabase();
+       
+       std::ostringstream o(std::ios_base::binary);
+       
        o.write((char*)&version, 1);
        
        // Write basic data
@@ -3145,7 +3322,23 @@ void ServerMap::saveBlock(MapBlock *block)
        
        // Write extra data stored on disk
        block->serializeDiskExtra(o, version);
-
+       
+       // Write block to database
+       
+       std::string tmp = o.str();
+       const char *bytes = tmp.c_str();
+       
+       if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
+               dstream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
+       if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
+               dstream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
+       int written = sqlite3_step(m_database_write);
+       if(written != SQLITE_DONE)
+               dstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
+               <<sqlite3_errmsg(m_database)<<std::endl;
+       // Make ready for later reuse
+       sqlite3_reset(m_database_write);
+       
        // We just wrote it to the disk so clear modified flag
        block->resetModified();
 }
@@ -3206,6 +3399,9 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
                if(version < SER_FMT_VER_HIGHEST || save_after_load)
                {
                        saveBlock(block);
+                       
+                       // Should be in database now, so delete the old file
+                       fs::RecursiveDelete(fullpath);
                }
                
                // We just loaded it from the disk, so it's up-to-date.
@@ -3226,12 +3422,111 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
        }
 }
 
+void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
+{
+       DSTACK(__FUNCTION_NAME);
+
+       try {
+               std::istringstream is(*blob, std::ios_base::binary);
+               
+               u8 version = SER_FMT_VER_INVALID;
+               is.read((char*)&version, 1);
+
+               if(is.fail())
+                       throw SerializationError("ServerMap::loadBlock(): Failed"
+                                       " to read MapBlock version");
+
+               /*u32 block_size = MapBlock::serializedLength(version);
+               SharedBuffer<u8> data(block_size);
+               is.read((char*)*data, block_size);*/
+
+               // This will always return a sector because we're the server
+               //MapSector *sector = emergeSector(p2d);
+
+               MapBlock *block = NULL;
+               bool created_new = false;
+               block = sector->getBlockNoCreateNoEx(p3d.Y);
+               if(block == NULL)
+               {
+                       block = sector->createBlankBlockNoInsert(p3d.Y);
+                       created_new = true;
+               }
+               
+               // Read basic data
+               block->deSerialize(is, version);
+
+               // 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);
+               
+               /*
+                       Save blocks loaded in old format in new format
+               */
+
+               if(version < SER_FMT_VER_HIGHEST || save_after_load)
+               {
+                       saveBlock(block);
+               }
+               
+               // We just loaded it from, so it's up-to-date.
+               block->resetModified();
+
+       }
+       catch(SerializationError &e)
+       {
+               dstream<<"WARNING: Invalid block data in database "
+                               <<" (SerializationError). "
+                               <<"what()="<<e.what()
+                               <<std::endl;
+                               //" Ignoring. A new one will be generated.
+               assert(0);
+
+               // TODO: Copy to a backup database.
+       }
+}
+
 MapBlock* ServerMap::loadBlock(v3s16 blockpos)
 {
        DSTACK(__FUNCTION_NAME);
 
        v2s16 p2d(blockpos.X, blockpos.Z);
 
+       if(!loadFromFolders()) {
+               verifyDatabase();
+               
+               if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
+                       dstream<<"WARNING: Could not bind block position for load: "
+                               <<sqlite3_errmsg(m_database)<<std::endl;
+               if(sqlite3_step(m_database_read) == SQLITE_ROW) {
+                       /*
+                               Make sure sector is loaded
+                       */
+                       MapSector *sector = createSector(p2d);
+                       
+                       /*
+                               Load block
+                       */
+                       const char * data = (const char *)sqlite3_column_blob(m_database_read, 0);
+                       size_t len = sqlite3_column_bytes(m_database_read, 0);
+                       
+                       std::string datastr(data, len);
+                       
+                       loadBlock(&datastr, blockpos, sector, false);
+
+                       sqlite3_step(m_database_read);
+                       // We should never get more than 1 row, so ok to reset
+                       sqlite3_reset(m_database_read);
+
+                       return getBlockNoCreateNoEx(blockpos);
+               }
+               sqlite3_reset(m_database_read);
+               
+               // Not found in database, try the files
+       }
+
        // The directory layout we're going to load from.
        //  1 - original sectors/xxxxzzzz/
        //  2 - new sectors2/xxx/zzz/
@@ -3282,9 +3577,9 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
                return NULL;
 
        /*
-               Load block
+               Load block and save it to the database
        */
-       loadBlock(sectordir, blockfilename, sector, loadlayout != 2);
+       loadBlock(sectordir, blockfilename, sector, true);
        return getBlockNoCreateNoEx(blockpos);
 }
 
@@ -4084,10 +4379,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)
                {