]> git.lizzy.rs Git - minetest.git/blobdiff - src/map.cpp
Initially add small and tight logging facility
[minetest.git] / src / map.cpp
index 6092538fe947a195143b39dccfeb2b200a5ccf42..a13c028fa22f895d396eb536869db6ea261399c3 100644 (file)
@@ -28,13 +28,27 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "porting.h"
 #include "mapgen.h"
 #include "nodemetadata.h"
+#include "content_mapnode.h"
+#ifndef SERVER
+#include <IMaterialRenderer.h>
+#endif
+#include "settings.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
 */
 
 /*
@@ -761,7 +775,7 @@ void Map::updateLighting(enum LightBank bank,
                generation for testing or whatever
        */
 #if 0
-       //if(g_settings.get(""))
+       //if(g_settings->get(""))
        {
                core::map<v3s16, MapBlock*>::Iterator i;
                i = blocks_to_update.getIterator();
@@ -881,7 +895,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=("
@@ -1005,6 +1019,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
        if(meta_proto)
        {
                NodeMetadata *meta = meta_proto->clone();
+               meta->setOwner(player_name);
                setNodeMetadata(p, meta);
        }
 
@@ -1281,7 +1296,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
@@ -1399,6 +1415,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++)
        {
@@ -1408,6 +1425,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++)
                {
@@ -1446,6 +1464,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);
@@ -1562,6 +1581,12 @@ 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
@@ -1629,9 +1654,15 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                                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)
+                                               if (nb.t == NEIGHBOR_LOWER) {
                                                        flowing_down = true;
+                                               }
                                        } else {
                                                neutrals[num_neutrals++] = nb;
                                        }
@@ -1666,6 +1697,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                 */
                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
@@ -1674,47 +1706,52 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                } else if (num_sources == 1 && sources[0].t != NEIGHBOR_LOWER) {
                        // liquid_kind is set properly, see above
                        new_node_content = liquid_kind;
-                       new_node_level = LIQUID_LEVEL_MAX;
+                       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 > new_node_level) {
-                                                       new_node_level = LIQUID_LEVEL_MAX;
+                                               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)
-                                                               new_node_level = nb_liquid_level + WATER_DROP_BOOST;
-                                               }
+                                                               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 > new_node_level) {
-                                                       new_node_level = nb_liquid_level - 1;
+                                                       nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level) {
+                                                       max_node_level = nb_liquid_level - 1;
                                                }
                                                break;
                                }
                        }
-                       // don't flow as far in open terrain - if there isn't at least one adjacent solid block,
-                       // substract another unit from the resulting water level.
-                       if (!flowing_down && new_node_level >= 1) {
-                               bool at_wall = false;
-                               for (u16 i = 0; i < num_neutrals; i++) {
-                                       if (neutrals[i].t == NEIGHBOR_SAME_LEVEL) {
-                                               at_wall = true;
-                                               break;
-                                       }
-                               }
-                               if (!at_wall)
-                                       new_node_level -= 1;
-                       }
+
+                       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;
+
                }
 
                /*
@@ -1742,14 +1779,19 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                setNode(p0, n0);
                v3s16 blockpos = getNodeBlockPos(p0);
                MapBlock *block = getBlockNoCreateNoEx(blockpos);
-               if(block != NULL)
+               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;
+               }
 
                /*
                        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)
@@ -1763,22 +1805,12 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
                                for (u16 i = 0; i < num_flows; i++)
                                        m_transforming_liquid.push_back(flows[i].p);
                                break;
-                       case LIQUID_FLOWING:
-                               for (u16 i = 0; i < num_flows; i++) {
-                                       u8 flow_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
-                                       // liquid_level is still the ORIGINAL level of this node.
-                                       if (flows[i].t != NEIGHBOR_UPPER && ((flow_level < liquid_level || flow_level < new_node_level) ||
-                                               flow_down_enabled))
-                                               m_transforming_liquid.push_back(flows[i].p);
-                               }
-                               for (u16 i = 0; i < num_airs; i++) {
-                                       if (airs[i].t != NEIGHBOR_UPPER && (airs[i].t == NEIGHBOR_LOWER || new_node_level > 0))
-                                               m_transforming_liquid.push_back(airs[i].p);
-                               }
-                               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)
@@ -1860,16 +1892,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
@@ -1981,6 +2023,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
@@ -1996,7 +2048,7 @@ ServerMap::~ServerMap()
 
 void ServerMap::initBlockMake(mapgen::BlockMakeData *data, v3s16 blockpos)
 {
-       bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+       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;
@@ -2091,7 +2143,7 @@ MapBlock* ServerMap::finishBlockMake(mapgen::BlockMakeData *data,
                return NULL;
        }
 
-       bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+       bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
 
        /*dstream<<"Resulting vmanip:"<<std::endl;
        data->vmanip.print(dstream);*/
@@ -2294,6 +2346,7 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d)
        /*
                Try to load metadata from disk
        */
+#if 0
        if(loadSectorMeta(p2d) == true)
        {
                ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
@@ -2304,7 +2357,7 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d)
                }
                return sector;
        }
-
+#endif
        /*
                Do not create over-limit
        */
@@ -2345,7 +2398,7 @@ MapBlock * ServerMap::generateBlock(
                        <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
                        <<std::endl;*/
        
-       bool enable_mapgen_debug_info = g_settings.getBool("enable_mapgen_debug_info");
+       bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
 
        TimeTaker timer("generateBlock");
        
@@ -2746,6 +2799,75 @@ s16 ServerMap::findGroundLevel(v2s16 p2d)
        //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)
@@ -2849,6 +2971,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++)
        {
@@ -2863,6 +2986,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;
@@ -2881,8 +3006,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
@@ -3141,6 +3268,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);
@@ -3160,6 +3299,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);
 
@@ -3169,11 +3310,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
@@ -3181,7 +3327,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();
 }
@@ -3242,6 +3404,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.
@@ -3262,12 +3427,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/
@@ -3318,9 +3582,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);
 }
 
@@ -3347,7 +3611,8 @@ ClientMap::ClientMap(
        m_client(client),
        m_control(control),
        m_camera_position(0,0,0),
-       m_camera_direction(0,0,1)
+       m_camera_direction(0,0,1),
+       m_camera_fov(PI)
 {
        m_camera_mutex.Init();
        assert(m_camera_mutex.IsInitialized());
@@ -3456,6 +3721,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
        m_camera_mutex.Lock();
        v3f camera_position = m_camera_position;
        v3f camera_direction = m_camera_direction;
+       f32 camera_fov = m_camera_fov;
        m_camera_mutex.Unlock();
 
        /*
@@ -3548,7 +3814,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        
                        float d = 0.0;
                        if(isBlockInSight(block->getPos(), camera_position,
-                                       camera_direction, range, &d) == false)
+                                       camera_direction, camera_fov,
+                                       range, &d) == false)
                        {
                                continue;
                        }
@@ -3668,6 +3935,35 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
                        <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
 }
 
+void ClientMap::renderPostFx()
+{
+       // Sadly ISceneManager has no "post effects" render pass, in that case we
+       // could just register for that and handle it in renderMap().
+
+       m_camera_mutex.Lock();
+       v3f camera_position = m_camera_position;
+       m_camera_mutex.Unlock();
+
+       MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
+
+       // - If the player is in a solid node, make everything black.
+       // - If the player is in liquid, draw a semi-transparent overlay.
+       ContentFeatures& features = content_features(n);
+       video::SColor post_effect_color = features.post_effect_color;
+       if(features.solidness == 2 && g_settings->getBool("free_move") == false)
+       {
+               post_effect_color = video::SColor(255, 0, 0, 0);
+       }
+       if (post_effect_color.getAlpha() != 0)
+       {
+               // Draw a full-screen rectangle
+               video::IVideoDriver* driver = SceneManager->getVideoDriver();
+               v2u32 ss = driver->getScreenSize();
+               core::rect<s32> rect(0,0, ss.X, ss.Y);
+               driver->draw2DRectangle(post_effect_color, rect);
+       }
+}
+
 bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
                core::map<v3s16, MapBlock*> *affected_blocks)
 {