]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/map.cpp
Fix typo in lua_api.txt
[dragonfireclient.git] / src / map.cpp
index 9c658ba3f4bb3d1a73e74527e292eff0b51c4e0c..cea20b0d3b0dd8ca1c50dd63ab93ea02deb37e3e 100644 (file)
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "filesys.h"
 #include "voxel.h"
 #include "porting.h"
-#include "mapgen.h"
+#include "serialization.h"
 #include "nodemetadata.h"
 #include "settings.h"
 #include "log.h"
@@ -34,10 +34,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/directiontables.h"
 #include "util/mathconstants.h"
 #include "rollback_interface.h"
+#include "environment.h"
 #include "emerge.h"
 #include "mapgen_v6.h"
-#include "mapgen_indev.h"
 #include "biome.h"
+#include "config.h"
+#include "server.h"
+#include "database.h"
+#include "database-dummy.h"
+#include "database-sqlite3.h"
+#if USE_LEVELDB
+#include "database-leveldb.h"
+#endif
 
 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
 
@@ -2128,6 +2136,7 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
                content_t new_node_content;
                s8 new_node_level = -1;
                s8 max_node_level = -1;
+               u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
                if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || 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
@@ -2137,6 +2146,8 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
                        // liquid_kind is set properly, see above
                        new_node_content = liquid_kind;
                        max_node_level = new_node_level = LIQUID_LEVEL_MAX;
+                       if (new_node_level < (LIQUID_LEVEL_MAX+1-range))
+                               new_node_content = CONTENT_AIR;
                } else {
                        // no surrounding sources, so get the maximum level that can flow into this node
                        for (u16 i = 0; i < num_flows; i++) {
@@ -2177,8 +2188,7 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
                        } else
                                new_node_level = max_node_level;
 
-                       u8 range = rangelim(nodemgr->get(liquid_kind).liquid_range, 0, LIQUID_LEVEL_MAX+1);
-                       if (new_node_level >= (LIQUID_LEVEL_MAX+1-range))
+                       if (max_node_level >= (LIQUID_LEVEL_MAX+1-range))
                                new_node_content = liquid_kind;
                        else
                                new_node_content = CONTENT_AIR;
@@ -2401,10 +2411,7 @@ s16 Map::getHumidity(v3s16 p)
 ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emerge):
        Map(dout_server, gamedef),
        m_seed(0),
-       m_map_metadata_changed(true),
-       m_database(NULL),
-       m_database_read(NULL),
-       m_database_write(NULL)
+       m_map_metadata_changed(true)
 {
        verbosestream<<__FUNCTION_NAME<<std::endl;
 
@@ -2435,6 +2442,28 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emer
                Try to load map; if not found, create a new one.
        */
 
+       // Determine which database backend to use
+       std::string conf_path = savedir + DIR_DELIM + "world.mt";
+       Settings conf;
+       bool succeeded = conf.readConfigFile(conf_path.c_str());
+       if (!succeeded || !conf.exists("backend")) {
+               // fall back to sqlite3
+               dbase = new Database_SQLite3(this, savedir);
+               conf.set("backend", "sqlite3");
+       } else {
+               std::string backend = conf.get("backend");
+               if (backend == "dummy")
+                       dbase = new Database_Dummy(this);
+               else if (backend == "sqlite3")
+                       dbase = new Database_SQLite3(this, savedir);
+               #if USE_LEVELDB
+               else if (backend == "leveldb")
+                       dbase = new Database_LevelDB(this, savedir);
+               #endif
+               else
+                       throw BaseException("Unknown map backend");
+       }
+
        m_savedir = savedir;
        m_map_saving_enabled = false;
 
@@ -2526,12 +2555,7 @@ ServerMap::~ServerMap()
        /*
                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);
+       delete(dbase);
 
 #if 0
        /*
@@ -2803,6 +2827,38 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data,
        /*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
                        <<","<<blockpos_requested.Y<<","
                        <<blockpos_requested.Z<<")"<<std::endl;*/
+                       
+       /*
+               Update weather data in blocks
+       */
+       ServerEnvironment *senv = &((Server *)m_gamedef)->getEnv();
+       if (senv->m_use_weather) {
+               for(s16 x=blockpos_min.X-extra_borders.X;
+                       x<=blockpos_max.X+extra_borders.X; x++)
+               for(s16 z=blockpos_min.Z-extra_borders.Z;
+                       z<=blockpos_max.Z+extra_borders.Z; z++)
+               for(s16 y=blockpos_min.Y-extra_borders.Y;
+                       y<=blockpos_max.Y+extra_borders.Y; y++)
+               {
+                       v3s16 p(x, y, z);
+                       updateBlockHeat(senv, p * MAP_BLOCKSIZE, NULL);
+                       updateBlockHumidity(senv, p * MAP_BLOCKSIZE, NULL);
+               }
+       } else {
+               for(s16 x=blockpos_min.X-extra_borders.X;
+                       x<=blockpos_max.X+extra_borders.X; x++)
+               for(s16 z=blockpos_min.Z-extra_borders.Z;
+                       z<=blockpos_max.Z+extra_borders.Z; z++)
+               for(s16 y=blockpos_min.Y-extra_borders.Y;
+                       y<=blockpos_max.Y+extra_borders.Y; y++)
+               {
+                       MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
+                       block->heat     = HEAT_UNDEFINED;
+                       block->humidity = HUMIDITY_UNDEFINED;
+                       block->weather_update_time = 0;
+               }
+       }
+       
 #if 0
        if(enable_mapgen_debug_info)
        {
@@ -3167,81 +3223,12 @@ 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
-               infostream<<"ServerMap: Database structure was created";
-}
-
-void ServerMap::verifyDatabase() {
-       if(m_database)
-               return;
-
-       {
-               std::string dbp = m_savedir + DIR_DELIM + "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) {
-                       infostream<<"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) {
-                       infostream<<"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) {
-                       infostream<<"WARNING: Database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
-                       throw FileNotGoodException("Cannot prepare write statement");
-               }
-
-               d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
-               if(d != SQLITE_OK) {
-                       infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
-                       throw FileNotGoodException("Cannot prepare read statement");
-               }
-
-               infostream<<"ServerMap: Database opened"<<std::endl;
-       }
-}
-
 bool ServerMap::loadFromFolders() {
-       if(!m_database && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
+       if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "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)
@@ -3414,50 +3401,13 @@ void ServerMap::save(ModifiedState save_level)
        }
 }
 
-static s32 unsignedToSigned(s32 i, s32 max_positive)
-{
-       if(i < max_positive)
-               return i;
-       else
-               return i - 2*max_positive;
-}
-
-// modulo of a negative number does not work consistently in C
-static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
-{
-       if(i >= 0)
-               return i % mod;
-       return mod - ((-i) % mod);
-}
-
-v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
-{
-       s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
-       i = (i - x) / 4096;
-       s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
-       i = (i - y) / 4096;
-       s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
-       return v3s16(x,y,z);
-}
-
 void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
 {
        if(loadFromFolders()){
                errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
                                <<"all blocks that are stored in flat files"<<std::endl;
        }
-
-       {
-               verifyDatabase();
-
-               while(sqlite3_step(m_database_list) == SQLITE_ROW)
-               {
-                       sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
-                       v3s16 p = getIntegerAsBlock(block_i);
-                       //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
-                       dst.push_back(p);
-               }
-       }
+       dbase->listAllLoadableBlocks(dst);
 }
 
 void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
@@ -3491,20 +3441,21 @@ void ServerMap::saveMapMeta()
        createDirs(m_savedir);
 
        std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
-       std::ofstream os(fullpath.c_str(), std::ios_base::binary);
-       if(os.good() == false)
-       {
-               infostream<<"ERROR: ServerMap::saveMapMeta(): "
-                               <<"could not open"<<fullpath<<std::endl;
-               throw FileNotGoodException("Cannot open chunk metadata");
-       }
+       std::ostringstream ss(std::ios_base::binary);
 
        Settings params;
 
        m_emerge->setParamsToSettings(&params);
-       params.writeLines(os);
+       params.writeLines(ss);
 
-       os<<"[end_of_params]\n";
+       ss<<"[end_of_params]\n";
+
+       if(!fs::safeWriteToFile(fullpath, ss.str()))
+       {
+               infostream<<"ERROR: ServerMap::saveMapMeta(): "
+                               <<"could not write "<<fullpath<<std::endl;
+               throw FileNotGoodException("Cannot save chunk metadata");
+       }
 
        m_map_metadata_changed = false;
 }
@@ -3556,7 +3507,7 @@ void ServerMap::loadMapMeta()
                m_seed = mgparams->seed;
        } else {
                if (params.exists("seed")) {
-                       m_seed = params.getU64("seed");
+                       m_seed = read_seed(params.get("seed").c_str());
                        m_mgparams->seed = m_seed;
                }
        }
@@ -3568,18 +3519,19 @@ void ServerMap::saveSectorMeta(ServerMapSector *sector)
 {
        DSTACK(__FUNCTION_NAME);
        // Format used for writing
-       u8 version = SER_FMT_VER_HIGHEST;
+       u8 version = SER_FMT_VER_HIGHEST_WRITE;
        // Get destination
        v2s16 pos = sector->getPos();
        std::string dir = getSectorDir(pos);
        createDirs(dir);
 
        std::string fullpath = dir + DIR_DELIM + "meta";
-       std::ofstream o(fullpath.c_str(), std::ios_base::binary);
-       if(o.good() == false)
-               throw FileNotGoodException("Cannot open sector metafile");
+       std::ostringstream ss(std::ios_base::binary);
+
+       sector->serialize(ss, version);
 
-       sector->serialize(o, version);
+       if(!fs::safeWriteToFile(fullpath, ss.str()))
+               throw FileNotGoodException("Cannot write sector metafile");
 
        sector->differs_from_disk = false;
 }
@@ -3741,88 +3693,16 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
 #endif
 
 void ServerMap::beginSave() {
-       verifyDatabase();
-       if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
-               infostream<<"WARNING: beginSave() failed, saving might be slow.";
+       dbase->beginSave();
 }
 
 void ServerMap::endSave() {
-       verifyDatabase();
-       if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
-               infostream<<"WARNING: endSave() failed, map might not have saved.";
+       dbase->endSave();
 }
 
 void ServerMap::saveBlock(MapBlock *block)
 {
-       DSTACK(__FUNCTION_NAME);
-       /*
-               Dummy blocks are not written
-       */
-       if(block->isDummy())
-       {
-               /*v3s16 p = block->getPos();
-               infostream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
-                               <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
-               return;
-       }
-
-       // Format used for writing
-       u8 version = SER_FMT_VER_HIGHEST;
-       // Get destination
-       v3s16 p3d = block->getPos();
-
-
-#if 0
-       v2s16 p2d(p3d.X, p3d.Z);
-       std::string sectordir = getSectorDir(p2d);
-
-       createDirs(sectordir);
-
-       std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
-       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
-       block->serialize(o, version, true);
-
-       // Write block to database
-
-       std::string tmp = o.str();
-       const char *bytes = tmp.c_str();
-
-       bool success = true;
-       if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK) {
-               infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
-               success = false;
-       }
-       if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) { // TODO this mught not be the right length
-               infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
-               success = false;
-       }
-       int written = sqlite3_step(m_database_write);
-       if(written != SQLITE_DONE) {
-               errorstream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
-                               <<sqlite3_errmsg(m_database)<<std::endl;
-               success = false;
-       }
-       // Make ready for later reuse
-       sqlite3_reset(m_database_write);
-
-       // We just wrote it to the disk so clear modified flag
-       if (success)
-               block->resetModified();
+  dbase->saveBlock(block);
 }
 
 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load)
@@ -3875,7 +3755,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSecto
                        Save blocks loaded in old format in new format
                */
 
-               if(version < SER_FMT_VER_HIGHEST || save_after_load)
+               if(version < SER_FMT_VER_HIGHEST_WRITE || save_after_load)
                {
                        saveBlock(block);
 
@@ -3942,7 +3822,7 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool
                        Save blocks loaded in old format in new format
                */
 
-               //if(version < SER_FMT_VER_HIGHEST || save_after_load)
+               //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
                // Only save if asked to; no need to update version
                if(save_after_load)
                        saveBlock(block);
@@ -3976,38 +3856,11 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
 
        v2s16 p2d(blockpos.X, blockpos.Z);
 
-       if(!loadFromFolders()) {
-               verifyDatabase();
-
-               if(sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK)
-                       infostream<<"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);
+       MapBlock *ret;
 
-                       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
-       }
+       ret = dbase->loadBlock(blockpos);
+       if (ret) return (ret);
+       // Not found in database, try the files
 
        // The directory layout we're going to load from.
        //  1 - original sectors/xxxxzzzz/
@@ -4070,59 +3923,44 @@ void ServerMap::PrintInfo(std::ostream &out)
        out<<"ServerMap: ";
 }
 
-s16 ServerMap::getHeat(ServerEnvironment *env, v3s16 p, MapBlock *block)
+s16 ServerMap::updateBlockHeat(ServerEnvironment *env, v3s16 p, MapBlock *block)
 {
-       if(block == NULL)
-               block = getBlockNoCreateNoEx(getNodeBlockPos(p));
-       if(block != NULL) {
-               if (env->getGameTime() - block->heat_time < 10)
+       u32 gametime = env->getGameTime();
+       
+       if (block) {
+               if (gametime - block->weather_update_time < 10)
                        return block->heat;
+       } else {
+               block = getBlockNoCreateNoEx(getNodeBlockPos(p));
        }
 
-       //variant 1: full random
-       //f32 heat = NoisePerlin3D(m_emerge->biomedef->np_heat, p.X, env->getGameTime()/100, p.Z, m_emerge->params->seed);
-
-       //variant 2: season change based on default heat map
-       f32 heat = NoisePerlin2D(m_emerge->biomedef->np_heat, p.X, p.Z, m_emerge->params->seed);
-       heat += -30; // -30 - todo REMOVE after fixed NoiseParams nparams_biome_def_heat = {50, 50, -> 20, 50,
-       f32 base = (f32)env->getGameTime() * env->getTimeOfDaySpeed();
-       base /= ( 86400 * g_settings->getS16("year_days") );
-       base += (f32)p.X / 3000;
-       heat += 30 * sin(base * M_PI); // season
-
-       heat += p.Y / -333; // upper=colder, lower=hotter
+       f32 heat = m_emerge->biomedef->calcBlockHeat(p, m_seed,
+                       env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed());
 
-       // daily change, hotter at sun +4, colder at night -4
-       heat += 8 * (sin(cycle_shift(env->getTimeOfDayF(), -0.25) * M_PI) - 0.5); 
-
-       if(block != NULL) {
+       if(block) {
                block->heat = heat;
-               block->heat_time = env->getGameTime();
+               block->weather_update_time = gametime;
        }
        return heat;
 }
 
-s16 ServerMap::getHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block)
+s16 ServerMap::updateBlockHumidity(ServerEnvironment *env, v3s16 p, MapBlock *block)
 {
-       if(block == NULL)
-               block = getBlockNoCreateNoEx(getNodeBlockPos(p));
-       if(block != NULL) {
-               if (env->getGameTime() - block->humidity_time < 10)
+       u32 gametime = env->getGameTime();
+       
+       if (block) {
+               if (gametime - block->weather_update_time < 10)
                        return block->humidity;
+       } else {
+               block = getBlockNoCreateNoEx(getNodeBlockPos(p));
        }
 
-       f32 humidity = NoisePerlin3D(   m_emerge->biomedef->np_humidity,
-                                       p.X, env->getGameTime()/10, p.Z,
-                                       m_emerge->params->seed);
-       humidity += -12 * ( sin(cycle_shift(env->getTimeOfDayF(), -0.1) * M_PI) - 0.5);
-       //todo like heat//humidity += 20 * ( sin(((f32)p.Z / 300) * M_PI) - 0.5);
-
-       if (humidity > 100) humidity = 100;
-       if (humidity < 0) humidity = 0;
-
-       if(block != NULL) {
+       f32 humidity = m_emerge->biomedef->calcBlockHumidity(p, m_seed,
+                       env->getTimeOfDayF(), gametime * env->getTimeOfDaySpeed());
+                       
+       if(block) {
                block->humidity = humidity;
-               block->humidity_time = env->getGameTime();
+               block->weather_update_time = gametime;
        }
        return humidity;
 }