From 708337dfc2b3871dc6de983e781e4a4a60a1881d Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Sun, 16 Nov 2014 15:31:57 -0500 Subject: [PATCH] Clean up database API and save the local map on an interval --- src/client.cpp | 60 ++--- src/client.h | 6 +- src/database-dummy.cpp | 50 +--- src/database-dummy.h | 19 +- src/database-leveldb.cpp | 52 ++-- src/database-leveldb.h | 25 +- src/database-redis.cpp | 126 +++++----- src/database-redis.h | 22 +- src/database-sqlite3.cpp | 339 +++++++++++--------------- src/database-sqlite3.h | 42 ++-- src/database.cpp | 4 +- src/database.h | 24 +- src/main.cpp | 59 ++--- src/map.cpp | 76 +++--- src/map.h | 2 +- src/network/packethandlers/client.cpp | 4 +- 16 files changed, 391 insertions(+), 519 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index a1ae1e6ab..00b79e92e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -47,10 +47,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "config.h" #include "version.h" #include "drawscene.h" -#include "subgame.h" -#include "server.h" -#include "database.h" #include "database-sqlite3.h" +#include "serialization.h" extern gui::IGUIEnvironment* guienv; @@ -266,16 +264,13 @@ Client::Client( m_time_of_day_update_timer(0), m_recommended_send_interval(0.1), m_removed_sounds_check_timer(0), - m_state(LC_Created) + m_state(LC_Created), + m_localdb(NULL) { - /* - Add local player - */ - { - Player *player = new LocalPlayer(this, playername); + // Add local player + m_env.addPlayer(new LocalPlayer(this, playername)); - m_env.addPlayer(player); - } + m_cache_save_interval = g_settings->getU16("server_map_save_interval"); m_cache_smooth_lighting = g_settings->getBool("smooth_lighting"); m_cache_enable_shaders = g_settings->getBool("enable_shaders"); @@ -285,10 +280,10 @@ void Client::Stop() { //request all client managed threads to stop m_mesh_update_thread.Stop(); - if (localdb != NULL) { - actionstream << "Local map saving ended" << std::endl; - localdb->endSave(); - delete localserver; + // Save local server map + if (m_localdb) { + infostream << "Local map saving ended." << std::endl; + m_localdb->endSave(); } } @@ -679,6 +674,13 @@ void Client::step(float dtime) Send(pkt); } } + + // Write server map + if (m_localdb && m_localdb_save_interval.step(dtime, + m_cache_save_interval)) { + m_localdb->endSave(); + m_localdb->beginSave(); + } } bool Client::loadMedia(const std::string &data, const std::string &filename) @@ -813,34 +815,19 @@ void Client::initLocalMapSaving(const Address &address, const std::string &hostname, bool is_local_server) { - localdb = NULL; - - if (!g_settings->getBool("enable_local_map_saving") || is_local_server) + if (!g_settings->getBool("enable_local_map_saving") || is_local_server) { return; + } const std::string world_path = porting::path_user + DIR_DELIM + "worlds" + DIR_DELIM + "server_" + hostname + "_" + to_string(address.getPort()); - SubgameSpec gamespec; + fs::CreateAllDirs(world_path); - if (!getWorldExists(world_path)) { - gamespec = findSubgame(g_settings->get("default_game")); - if (!gamespec.isValid()) - gamespec = findSubgame("minimal"); - } else { - gamespec = findWorldSubgame(world_path); - } - - if (!gamespec.isValid()) { - errorstream << "Couldn't find subgame for local map saving." << std::endl; - return; - } - - localserver = new Server(world_path, gamespec, false, false); - localdb = new Database_SQLite3(&(ServerMap&)localserver->getMap(), world_path); - localdb->beginSave(); + m_localdb = new Database_SQLite3(world_path); + m_localdb->beginSave(); actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl; } @@ -1663,7 +1650,8 @@ void Client::makeScreenshot(IrrlichtDevice *device) if (image) { raw_image->copyTo(image); irr::c8 filename[256]; - snprintf(filename, sizeof(filename), "%s" DIR_DELIM "screenshot_%u.png", + snprintf(filename, sizeof(filename), + (std::string("%s") + DIR_DELIM + "screenshot_%u.png").c_str(), g_settings->get("screenshot_path").c_str(), device->getTimer()->getRealTime()); std::ostringstream sstr; diff --git a/src/client.h b/src/client.h index c0146911d..a0add689a 100644 --- a/src/client.h +++ b/src/client.h @@ -48,7 +48,6 @@ struct MapDrawControl; class MtEventManager; struct PointedThing; class Database; -class Server; struct QueuedMeshUpdate { @@ -620,8 +619,9 @@ private: LocalClientState m_state; // Used for saving server map to disk client-side - Database *localdb; - Server *localserver; + Database *m_localdb; + IntervalLimiter m_localdb_save_interval; + u16 m_cache_save_interval; // TODO: Add callback to update these when g_settings changes bool m_cache_smooth_lighting; diff --git a/src/database-dummy.cpp b/src/database-dummy.cpp index 5b1a14341..71f60d81c 100644 --- a/src/database-dummy.cpp +++ b/src/database-dummy.cpp @@ -18,64 +18,38 @@ with this program; if not, write to the Free Software Foundation, Inc., */ /* -Dummy "database" class +Dummy database class */ - #include "database-dummy.h" -#include "map.h" -#include "mapsector.h" -#include "mapblock.h" -#include "serialization.h" -#include "main.h" -#include "settings.h" -#include "log.h" -Database_Dummy::Database_Dummy(ServerMap *map) +bool Database_Dummy::saveBlock(const v3s16 &pos, const std::string &data) { - srvmap = map; -} - -int Database_Dummy::Initialized(void) -{ - return 1; -} - -void Database_Dummy::beginSave() {} -void Database_Dummy::endSave() {} - -bool Database_Dummy::saveBlock(v3s16 blockpos, std::string &data) -{ - m_database[getBlockAsInteger(blockpos)] = data; + m_database[getBlockAsInteger(pos)] = data; return true; } -std::string Database_Dummy::loadBlock(v3s16 blockpos) +std::string Database_Dummy::loadBlock(const v3s16 &pos) { - if (m_database.count(getBlockAsInteger(blockpos))) - return m_database[getBlockAsInteger(blockpos)]; + s64 i = getBlockAsInteger(pos); + if (m_database.count(i)) + return m_database[i]; else return ""; } -bool Database_Dummy::deleteBlock(v3s16 blockpos) +bool Database_Dummy::deleteBlock(const v3s16 &pos) { - m_database.erase(getBlockAsInteger(blockpos)); + m_database.erase(getBlockAsInteger(pos)); return true; } void Database_Dummy::listAllLoadableBlocks(std::vector &dst) { - for(std::map::iterator x = m_database.begin(); x != m_database.end(); ++x) - { - v3s16 p = getIntegerAsBlock(x->first); - //dstream<<"block_i="< &dst); - virtual int Initialized(void); - ~Database_Dummy(); + private: - ServerMap *srvmap; - std::map m_database; + std::map m_database; }; + #endif + diff --git a/src/database-leveldb.cpp b/src/database-leveldb.cpp index b35e612b6..e895354a4 100644 --- a/src/database-leveldb.cpp +++ b/src/database-leveldb.cpp @@ -22,57 +22,54 @@ with this program; if not, write to the Free Software Foundation, Inc., #if USE_LEVELDB #include "database-leveldb.h" -#include "leveldb/db.h" -#include "map.h" -#include "mapsector.h" -#include "mapblock.h" -#include "serialization.h" -#include "main.h" -#include "settings.h" #include "log.h" #include "filesys.h" +#include "exceptions.h" +#include "util/string.h" + +#include "leveldb/db.h" + #define ENSURE_STATUS_OK(s) \ if (!(s).ok()) { \ - throw FileNotGoodException(std::string("LevelDB error: ") + (s).ToString()); \ + throw FileNotGoodException(std::string("LevelDB error: ") + \ + (s).ToString()); \ } -Database_LevelDB::Database_LevelDB(ServerMap *map, std::string savedir) + +Database_LevelDB::Database_LevelDB(const std::string &savedir) { leveldb::Options options; options.create_if_missing = true; - leveldb::Status status = leveldb::DB::Open(options, savedir + DIR_DELIM + "map.db", &m_database); + leveldb::Status status = leveldb::DB::Open(options, + savedir + DIR_DELIM + "map.db", &m_database); ENSURE_STATUS_OK(status); - srvmap = map; } -int Database_LevelDB::Initialized(void) +Database_LevelDB::~Database_LevelDB() { - return 1; + delete m_database; } -void Database_LevelDB::beginSave() {} -void Database_LevelDB::endSave() {} - -bool Database_LevelDB::saveBlock(v3s16 blockpos, std::string &data) +bool Database_LevelDB::saveBlock(const v3s16 &pos, const std::string &data) { leveldb::Status status = m_database->Put(leveldb::WriteOptions(), - i64tos(getBlockAsInteger(blockpos)), data); + i64tos(getBlockAsInteger(pos)), data); if (!status.ok()) { errorstream << "WARNING: saveBlock: LevelDB error saving block " - << PP(blockpos) << ": " << status.ToString() << std::endl; + << PP(pos) << ": " << status.ToString() << std::endl; return false; } return true; } -std::string Database_LevelDB::loadBlock(v3s16 blockpos) +std::string Database_LevelDB::loadBlock(const v3s16 &pos) { std::string datastr; leveldb::Status status = m_database->Get(leveldb::ReadOptions(), - i64tos(getBlockAsInteger(blockpos)), &datastr); + i64tos(getBlockAsInteger(pos)), &datastr); if(status.ok()) return datastr; @@ -80,13 +77,13 @@ std::string Database_LevelDB::loadBlock(v3s16 blockpos) return ""; } -bool Database_LevelDB::deleteBlock(v3s16 blockpos) +bool Database_LevelDB::deleteBlock(const v3s16 &pos) { leveldb::Status status = m_database->Delete(leveldb::WriteOptions(), - i64tos(getBlockAsInteger(blockpos))); + i64tos(getBlockAsInteger(pos))); if (!status.ok()) { errorstream << "WARNING: deleteBlock: LevelDB error deleting block " - << PP(blockpos) << ": " << status.ToString() << std::endl; + << PP(pos) << ": " << status.ToString() << std::endl; return false; } @@ -103,8 +100,5 @@ void Database_LevelDB::listAllLoadableBlocks(std::vector &dst) delete it; } -Database_LevelDB::~Database_LevelDB() -{ - delete m_database; -} -#endif +#endif // USE_LEVELDB + diff --git a/src/database-leveldb.h b/src/database-leveldb.h index 5d44ba3e7..4afe2fdc7 100644 --- a/src/database-leveldb.h +++ b/src/database-leveldb.h @@ -28,23 +28,22 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "leveldb/db.h" #include -class ServerMap; - class Database_LevelDB : public Database { public: - Database_LevelDB(ServerMap *map, std::string savedir); - virtual void beginSave(); - virtual void endSave(); - virtual bool saveBlock(v3s16 blockpos, std::string &data); - virtual std::string loadBlock(v3s16 blockpos); - virtual bool deleteBlock(v3s16 blockpos); - virtual void listAllLoadableBlocks(std::vector &dst); - virtual int Initialized(void); + Database_LevelDB(const std::string &savedir); ~Database_LevelDB(); + + virtual bool saveBlock(const v3s16 &pos, const std::string &data); + virtual std::string loadBlock(const v3s16 &pos); + virtual bool deleteBlock(const v3s16 &pos); + virtual void listAllLoadableBlocks(std::vector &dst); + private: - ServerMap *srvmap; - leveldb::DB* m_database; + leveldb::DB *m_database; }; + +#endif // USE_LEVELDB + #endif -#endif + diff --git a/src/database-redis.cpp b/src/database-redis.cpp index 0962e97ba..7b42274b9 100644 --- a/src/database-redis.cpp +++ b/src/database-redis.cpp @@ -20,84 +20,78 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "config.h" #if USE_REDIS -/* - Redis databases -*/ - #include "database-redis.h" -#include -#include "map.h" -#include "mapsector.h" -#include "mapblock.h" -#include "serialization.h" -#include "main.h" #include "settings.h" #include "log.h" -#include "filesys.h" +#include "exceptions.h" +#include "util/string.h" + +#include +#include -Database_Redis::Database_Redis(ServerMap *map, std::string savedir) +Database_Redis::Database_Redis(Settings &conf) { - Settings conf; - conf.readConfigFile((std::string(savedir) + DIR_DELIM + "world.mt").c_str()); std::string tmp; try { - tmp = conf.get("redis_address"); - hash = conf.get("redis_hash"); - } catch(SettingNotFoundException e) { - throw SettingNotFoundException("Set redis_address and redis_hash in world.mt to use the redis backend"); + tmp = conf.get("redis_address"); + hash = conf.get("redis_hash"); + } catch (SettingNotFoundException) { + throw SettingNotFoundException("Set redis_address and " + "redis_hash in world.mt to use the redis backend"); } const char *addr = tmp.c_str(); int port = conf.exists("redis_port") ? conf.getU16("redis_port") : 6379; ctx = redisConnect(addr, port); - if(!ctx) + if (!ctx) { throw FileNotGoodException("Cannot allocate redis context"); - else if(ctx->err) { + } else if(ctx->err) { std::string err = std::string("Connection error: ") + ctx->errstr; redisFree(ctx); throw FileNotGoodException(err); } - srvmap = map; } -int Database_Redis::Initialized(void) +Database_Redis::~Database_Redis() { - return 1; + redisFree(ctx); } void Database_Redis::beginSave() { - redisReply *reply; - reply = (redisReply*) redisCommand(ctx, "MULTI"); - if(!reply) - throw FileNotGoodException(std::string("redis command 'MULTI' failed: ") + ctx->errstr); + redisReply *reply = static_cast(redisCommand(ctx, "MULTI")); + if (!reply) { + throw FileNotGoodException(std::string( + "Redis command 'MULTI' failed: ") + ctx->errstr); + } freeReplyObject(reply); } void Database_Redis::endSave() { - redisReply *reply; - reply = (redisReply*) redisCommand(ctx, "EXEC"); - if(!reply) - throw FileNotGoodException(std::string("redis command 'EXEC' failed: ") + ctx->errstr); + redisReply *reply = static_cast(redisCommand(ctx, "EXEC")); + if (!reply) { + throw FileNotGoodException(std::string( + "Redis command 'EXEC' failed: ") + ctx->errstr); + } freeReplyObject(reply); } -bool Database_Redis::saveBlock(v3s16 blockpos, std::string &data) +bool Database_Redis::saveBlock(const v3s16 &pos, const std::string &data) { - std::string tmp = i64tos(getBlockAsInteger(blockpos)); + std::string tmp = i64tos(getBlockAsInteger(pos)); - redisReply *reply = (redisReply *)redisCommand(ctx, "HSET %s %s %b", - hash.c_str(), tmp.c_str(), data.c_str(), data.size()); + redisReply *reply = static_cast(redisCommand(ctx, "HSET %s %s %b", + hash.c_str(), tmp.c_str(), data.c_str(), data.size())); if (!reply) { errorstream << "WARNING: saveBlock: redis command 'HSET' failed on " - "block " << PP(blockpos) << ": " << ctx->errstr << std::endl; + "block " << PP(pos) << ": " << ctx->errstr << std::endl; freeReplyObject(reply); return false; } if (reply->type == REDIS_REPLY_ERROR) { - errorstream << "WARNING: saveBlock: saving block " << PP(blockpos) + errorstream << "WARNING: saveBlock: saving block " << PP(pos) << "failed" << std::endl; freeReplyObject(reply); return false; @@ -107,38 +101,36 @@ bool Database_Redis::saveBlock(v3s16 blockpos, std::string &data) return true; } -std::string Database_Redis::loadBlock(v3s16 blockpos) +std::string Database_Redis::loadBlock(const v3s16 &pos) { - std::string tmp = i64tos(getBlockAsInteger(blockpos)); - redisReply *reply; - reply = (redisReply*) redisCommand(ctx, "HGET %s %s", hash.c_str(), tmp.c_str()); + std::string tmp = i64tos(getBlockAsInteger(pos)); + redisReply *reply = static_cast(redisCommand(ctx, + "HGET %s %s", hash.c_str(), tmp.c_str())); - if(!reply) - throw FileNotGoodException(std::string("redis command 'HGET %s %s' failed: ") + ctx->errstr); - if(reply->type != REDIS_REPLY_STRING) + if (!reply) { + throw FileNotGoodException(std::string( + "Redis command 'HGET %s %s' failed: ") + ctx->errstr); + } else if (reply->type != REDIS_REPLY_STRING) { return ""; + } std::string str(reply->str, reply->len); freeReplyObject(reply); // std::string copies the memory so this won't cause any problems return str; } -bool Database_Redis::deleteBlock(v3s16 blockpos) +bool Database_Redis::deleteBlock(const v3s16 &pos) { - std::string tmp = i64tos(getBlockAsInteger(blockpos)); + std::string tmp = i64tos(getBlockAsInteger(pos)); - redisReply *reply = (redisReply *)redisCommand(ctx, "HDEL %s %s", - hash.c_str(), tmp.c_str()); + redisReply *reply = static_cast(redisCommand(ctx, + "HDEL %s %s", hash.c_str(), tmp.c_str())); if (!reply) { - errorstream << "WARNING: deleteBlock: redis command 'HDEL' failed on " - "block " << PP(blockpos) << ": " << ctx->errstr << std::endl; - freeReplyObject(reply); - return false; - } - - if (reply->type == REDIS_REPLY_ERROR) { - errorstream << "WARNING: deleteBlock: deleting block " << PP(blockpos) - << "failed" << std::endl; + throw FileNotGoodException(std::string( + "Redis command 'HDEL %s %s' failed: ") + ctx->errstr); + } else if (reply->type == REDIS_REPLY_ERROR) { + errorstream << "WARNING: deleteBlock: deleting block " << PP(pos) + << " failed" << std::endl; freeReplyObject(reply); return false; } @@ -149,21 +141,19 @@ bool Database_Redis::deleteBlock(v3s16 blockpos) void Database_Redis::listAllLoadableBlocks(std::vector &dst) { - redisReply *reply; - reply = (redisReply*) redisCommand(ctx, "HKEYS %s", hash.c_str()); - if(!reply) - throw FileNotGoodException(std::string("redis command 'HKEYS %s' failed: ") + ctx->errstr); - if(reply->type != REDIS_REPLY_ARRAY) + redisReply *reply = static_cast(redisCommand(ctx, "HKEYS %s", hash.c_str())); + if (!reply) { + throw FileNotGoodException(std::string( + "Redis command 'HKEYS %s' failed: ") + ctx->errstr); + } else if (reply->type != REDIS_REPLY_ARRAY) { throw FileNotGoodException("Failed to get keys from database"); - for(size_t i = 0; i < reply->elements; i++) { + } + for (size_t i = 0; i < reply->elements; i++) { assert(reply->element[i]->type == REDIS_REPLY_STRING); dst.push_back(getIntegerAsBlock(stoi64(reply->element[i]->str))); } freeReplyObject(reply); } -Database_Redis::~Database_Redis() -{ - redisFree(ctx); -} -#endif +#endif // USE_REDIS + diff --git a/src/database-redis.h b/src/database-redis.h index 3c4e2b6d8..45e702c83 100644 --- a/src/database-redis.h +++ b/src/database-redis.h @@ -28,24 +28,28 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include -class ServerMap; +class Settings; class Database_Redis : public Database { public: - Database_Redis(ServerMap *map, std::string savedir); + Database_Redis(Settings &conf); + ~Database_Redis(); + virtual void beginSave(); virtual void endSave(); - virtual bool saveBlock(v3s16 blockpos, std::string &data); - virtual std::string loadBlock(v3s16 blockpos); - virtual bool deleteBlock(v3s16 blockpos); + + virtual bool saveBlock(const v3s16 &pos, const std::string &data); + virtual std::string loadBlock(const v3s16 &pos); + virtual bool deleteBlock(const v3s16 &pos); virtual void listAllLoadableBlocks(std::vector &dst); - virtual int Initialized(void); - ~Database_Redis(); + private: - ServerMap *srvmap; redisContext *ctx; std::string hash; }; + +#endif // USE_REDIS + #endif -#endif + diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp index 2edae8be2..3480894c9 100644 --- a/src/database-sqlite3.cpp +++ b/src/database-sqlite3.cpp @@ -18,259 +18,205 @@ with this program; if not, write to the Free Software Foundation, Inc., */ /* - 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 +SQLite format specification: + blocks: + (PK) INT id + BLOB data */ #include "database-sqlite3.h" -#include "map.h" -#include "mapsector.h" -#include "mapblock.h" -#include "serialization.h" -#include "main.h" -#include "settings.h" #include "log.h" #include "filesys.h" +#include "exceptions.h" +#include "main.h" +#include "settings.h" +#include "util/string.h" -Database_SQLite3::Database_SQLite3(ServerMap *map, std::string savedir) -{ - m_database = NULL; - m_database_read = NULL; - m_database_write = NULL; - m_database_list = NULL; - m_database_delete = NULL; - m_savedir = savedir; - srvmap = map; -} +#include -int Database_SQLite3::Initialized(void) + +#define SQLRES(s, r) \ + if ((s) != (r)) { \ + throw FileNotGoodException(std::string(\ + "SQLite3 database error (" \ + __FILE__ ":" TOSTRING(__LINE__) \ + "): ") +\ + sqlite3_errmsg(m_database)); \ + } +#define SQLOK(s) SQLRES(s, SQLITE_OK) + +#define PREPARE_STATEMENT(name, query) \ + SQLOK(sqlite3_prepare_v2(m_database, query, -1, &m_stmt_##name, NULL)) + +#define FINALIZE_STATEMENT(statement) \ + if (sqlite3_finalize(statement) != SQLITE_OK) { \ + throw FileNotGoodException(std::string( \ + "SQLite3: Failed to finalize " #statement ": ") + \ + sqlite3_errmsg(m_database)); \ + } + + +Database_SQLite3::Database_SQLite3(const std::string &savedir) : + m_initialized(false), + m_savedir(savedir), + m_database(NULL), + m_stmt_read(NULL), + m_stmt_write(NULL), + m_stmt_list(NULL), + m_stmt_delete(NULL) { - return m_database ? 1 : 0; } void Database_SQLite3::beginSave() { verifyDatabase(); - if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK) - errorstream<<"WARNING: beginSave() failed, saving might be slow."; + SQLRES(sqlite3_step(m_stmt_begin), SQLITE_DONE); + sqlite3_reset(m_stmt_begin); } void Database_SQLite3::endSave() { verifyDatabase(); - if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK) - errorstream<<"WARNING: endSave() failed, map might not have saved."; + SQLRES(sqlite3_step(m_stmt_end), SQLITE_DONE); + sqlite3_reset(m_stmt_end); } -void Database_SQLite3::createDirs(std::string path) +void Database_SQLite3::openDatabase() { - if(fs::CreateAllDirs(path) == false) - { - infostream< &dst) { 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="< &dst); - virtual int Initialized(void); + virtual bool initialized() const { return m_initialized; } ~Database_SQLite3(); -private: - ServerMap *srvmap; - std::string m_savedir; - sqlite3 *m_database; - sqlite3_stmt *m_database_read; - sqlite3_stmt *m_database_write; - sqlite3_stmt *m_database_delete; - sqlite3_stmt *m_database_list; +private: + // Open the database + void openDatabase(); // Create the database structure void createDatabase(); - // Verify we can read/write to the database + // Open and initialize the database if needed void verifyDatabase(); - void createDirs(std::string path); + + void bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index=1); + + bool m_initialized; + + std::string m_savedir; + + sqlite3 *m_database; + sqlite3_stmt *m_stmt_read; + sqlite3_stmt *m_stmt_write; + sqlite3_stmt *m_stmt_list; + sqlite3_stmt *m_stmt_delete; + sqlite3_stmt *m_stmt_begin; + sqlite3_stmt *m_stmt_end; }; #endif + diff --git a/src/database.cpp b/src/database.cpp index 26f6992fc..262d475ec 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -48,7 +48,7 @@ static inline s64 pythonmodulo(s64 i, s16 mod) } -s64 Database::getBlockAsInteger(const v3s16 pos) const +s64 Database::getBlockAsInteger(const v3s16 &pos) { return (u64) pos.Z * 0x1000000 + (u64) pos.Y * 0x1000 + @@ -56,7 +56,7 @@ s64 Database::getBlockAsInteger(const v3s16 pos) const } -v3s16 Database::getIntegerAsBlock(s64 i) const +v3s16 Database::getIntegerAsBlock(s64 i) { v3s16 pos; pos.X = unsigned_to_signed(pythonmodulo(i, 4096), 2048); diff --git a/src/database.h b/src/database.h index f4a2a4e8c..cee7b6fd9 100644 --- a/src/database.h +++ b/src/database.h @@ -32,16 +32,22 @@ with this program; if not, write to the Free Software Foundation, Inc., class Database { public: - virtual void beginSave() = 0; - virtual void endSave() = 0; + virtual ~Database() {} + + virtual void beginSave() {} + virtual void endSave() {} + + virtual bool saveBlock(const v3s16 &pos, const std::string &data) = 0; + virtual std::string loadBlock(const v3s16 &pos) = 0; + virtual bool deleteBlock(const v3s16 &pos) = 0; + + static s64 getBlockAsInteger(const v3s16 &pos); + static v3s16 getIntegerAsBlock(s64 i); - virtual bool saveBlock(v3s16 blockpos, std::string &data) = 0; - virtual std::string loadBlock(v3s16 blockpos) = 0; - virtual bool deleteBlock(v3s16 blockpos) = 0; - s64 getBlockAsInteger(const v3s16 pos) const; - v3s16 getIntegerAsBlock(s64 i) const; virtual void listAllLoadableBlocks(std::vector &dst) = 0; - virtual int Initialized(void)=0; - virtual ~Database() {}; + + virtual bool initialized() const { return true; } }; + #endif + diff --git a/src/main.cpp b/src/main.cpp index 922effb1f..4e5bf51fd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -914,52 +914,46 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & static bool migrate_database(const GameParams &game_params, const Settings &cmd_args, Server *server) { + std::string migrate_to = cmd_args.get("migrate"); Settings world_mt; - bool success = world_mt.readConfigFile((game_params.world_path - + DIR_DELIM + "world.mt").c_str()); + std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt"; + bool success = world_mt.readConfigFile(world_mt_path.c_str()); if (!success) { errorstream << "Cannot read world.mt" << std::endl; - return false; + return 1; } - if (!world_mt.exists("backend")) { - errorstream << "Please specify your current backend in world.mt file:" - << std::endl << " backend = {sqlite3|leveldb|redis|dummy}" - << std::endl; - return false; + errorstream << "Please specify your current backend in world.mt:" + << std::endl; + errorstream << " backend = {sqlite3|leveldb|redis|dummy}" + << std::endl; + return 1; } - std::string backend = world_mt.get("backend"); Database *new_db; - std::string migrate_to = cmd_args.get("migrate"); - if (backend == migrate_to) { - errorstream << "Cannot migrate: new backend is same as the old one" - << std::endl; - return false; + errorstream << "Cannot migrate: new backend is same" + <<" as the old one" << std::endl; + return 1; } - if (migrate_to == "sqlite3") - new_db = new Database_SQLite3(&(ServerMap&)server->getMap(), - game_params.world_path); -#if USE_LEVELDB + new_db = new Database_SQLite3(game_params.world_path); + #if USE_LEVELDB else if (migrate_to == "leveldb") - new_db = new Database_LevelDB(&(ServerMap&)server->getMap(), - game_params.world_path); -#endif -#if USE_REDIS + new_db = new Database_LevelDB(game_params.world_path); + #endif + #if USE_REDIS else if (migrate_to == "redis") - new_db = new Database_Redis(&(ServerMap&)server->getMap(), - game_params.world_path); -#endif + new_db = new Database_Redis(world_mt); + #endif else { - errorstream << "Migration to " << migrate_to << " is not supported" - << std::endl; - return false; + errorstream << "Migration to " << migrate_to + << " is not supported" << std::endl; + return 1; } std::vector blocks; - ServerMap &old_map = ((ServerMap&)server->getMap()); + ServerMap &old_map = (ServerMap &) server->getMap(); old_map.listAllLoadableBlocks(blocks); int count = 0; new_db->beginSave(); @@ -975,16 +969,15 @@ static bool migrate_database(const GameParams &game_params, const Settings &cmd_ } ++count; if (count % 500 == 0) - actionstream << "Migrated " << count << " blocks " - << (100.0 * count / blocks.size()) << "% completed" << std::endl; + actionstream << "Migrated " << count << " blocks " + << (100.0 * count / blocks.size()) << "% completed" << std::endl; } new_db->endSave(); delete new_db; actionstream << "Successfully migrated " << count << " blocks" << std::endl; world_mt.set("backend", migrate_to); - if (!world_mt.updateConfigFile( - (game_params.world_path+ DIR_DELIM + "world.mt").c_str())) + if (!world_mt.updateConfigFile(world_mt_path.c_str())) errorstream << "Failed to update world.mt!" << std::endl; else actionstream << "world.mt updated" << std::endl; diff --git a/src/map.cpp b/src/map.cpp index 2c7028009..e58ae2cfe 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -53,22 +53,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" -/* - 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 -*/ /* Map @@ -2031,25 +2015,26 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emer 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 - #if USE_REDIS - else if (backend == "redis") - dbase = new Database_Redis(this, savedir); - #endif - else - throw BaseException("Unknown map backend"); } + std::string backend = conf.get("backend"); + if (backend == "dummy") + dbase = new Database_Dummy(); + else if (backend == "sqlite3") + dbase = new Database_SQLite3(savedir); + #if USE_LEVELDB + else if (backend == "leveldb") + dbase = new Database_LevelDB(savedir); + #endif + #if USE_REDIS + else if (backend == "redis") + dbase = new Database_Redis(conf); + #endif + else + throw BaseException("Unknown map backend"); + + if (!conf.updateConfigFile(conf_path.c_str())) + errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl; m_savedir = savedir; m_map_saving_enabled = false; @@ -2828,7 +2813,8 @@ plan_b: } bool ServerMap::loadFromFolders() { - if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ? + if(!dbase->initialized() && + !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) return true; return false; } @@ -2850,14 +2836,14 @@ std::string ServerMap::getSectorDir(v2s16 pos, int layout) { case 1: snprintf(cc, 9, "%.4x%.4x", - (unsigned int)pos.X&0xffff, - (unsigned int)pos.Y&0xffff); + (unsigned int) pos.X & 0xffff, + (unsigned int) pos.Y & 0xffff); return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc; case 2: - snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x", - (unsigned int)pos.X&0xfff, - (unsigned int)pos.Y&0xfff); + snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(), + (unsigned int) pos.X & 0xfff, + (unsigned int) pos.Y & 0xfff); return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc; default: @@ -2881,10 +2867,10 @@ v2s16 ServerMap::getSectorPos(std::string dirname) { // New layout fs::RemoveLastPathComponent(dirname, &component, 2); - r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y); + r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y); // Sign-extend the 12 bit values up to 16 bits... - if(x&0x800) x|=0xF000; - if(y&0x800) y|=0xF000; + if(x & 0x800) x |= 0xF000; + if(y & 0x800) y |= 0xF000; } else { @@ -3299,7 +3285,7 @@ bool ServerMap::saveBlock(MapBlock *block, Database *db) std::string data = o.str(); bool ret = db->saveBlock(p3d, data); - if(ret) { + if (ret) { // We just wrote it to the disk so clear modified flag block->resetModified(); } @@ -3311,7 +3297,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile, { DSTACK(__FUNCTION_NAME); - std::string fullpath = sectordir+DIR_DELIM+blockfile; + std::string fullpath = sectordir + DIR_DELIM + blockfile; try { std::ifstream is(fullpath.c_str(), std::ios_base::binary); @@ -3513,7 +3499,7 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos) */ std::string blockfilename = getBlockFilename(blockpos); - if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false) + if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false) return NULL; /* diff --git a/src/map.h b/src/map.h index ab5f45ab9..073f9314d 100644 --- a/src/map.h +++ b/src/map.h @@ -481,8 +481,8 @@ public: // Returns true if sector now resides in memory //bool deFlushSector(v2s16 p2d); - bool saveBlock(MapBlock *block, Database *db); bool saveBlock(MapBlock *block); + static bool saveBlock(MapBlock *block, Database *db); // This will generate a sector with getSector if not found. void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load=false); MapBlock* loadBlock(v3s16 p); diff --git a/src/network/packethandlers/client.cpp b/src/network/packethandlers/client.cpp index e1e57b3a6..82f8b61b7 100644 --- a/src/network/packethandlers/client.cpp +++ b/src/network/packethandlers/client.cpp @@ -168,8 +168,8 @@ void Client::handleCommand_BlockData(NetworkPacket* pkt) sector->insertBlock(block); } - if (localdb != NULL) { - ((ServerMap&) localserver->getMap()).saveBlock(block, localdb); + if (m_localdb) { + ServerMap::saveBlock(block, m_localdb); } /*