forked from Mirrorlandia_minetest/minetest
Clean up database API and save the local map on an interval
This commit is contained in:
parent
c7454d4732
commit
708337dfc2
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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<v3s16> &dst)
|
||||
{
|
||||
for(std::map<u64, std::string>::iterator x = m_database.begin(); x != m_database.end(); ++x)
|
||||
{
|
||||
v3s16 p = getIntegerAsBlock(x->first);
|
||||
//dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
|
||||
dst.push_back(p);
|
||||
for (std::map<s64, std::string>::const_iterator x = m_database.begin();
|
||||
x != m_database.end(); ++x) {
|
||||
dst.push_back(getIntegerAsBlock(x->first));
|
||||
}
|
||||
}
|
||||
|
||||
Database_Dummy::~Database_Dummy()
|
||||
{
|
||||
m_database.clear();
|
||||
}
|
||||
|
@ -25,22 +25,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "database.h"
|
||||
#include "irrlichttypes.h"
|
||||
|
||||
class ServerMap;
|
||||
|
||||
class Database_Dummy : public Database
|
||||
{
|
||||
public:
|
||||
Database_Dummy(ServerMap *map);
|
||||
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<v3s16> &dst);
|
||||
virtual int Initialized(void);
|
||||
~Database_Dummy();
|
||||
|
||||
private:
|
||||
ServerMap *srvmap;
|
||||
std::map<u64, std::string> m_database;
|
||||
std::map<s64, std::string> m_database;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -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<v3s16> &dst)
|
||||
delete it;
|
||||
}
|
||||
|
||||
Database_LevelDB::~Database_LevelDB()
|
||||
{
|
||||
delete m_database;
|
||||
}
|
||||
#endif
|
||||
#endif // USE_LEVELDB
|
||||
|
||||
|
@ -28,23 +28,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "leveldb/db.h"
|
||||
#include <string>
|
||||
|
||||
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<v3s16> &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<v3s16> &dst);
|
||||
|
||||
private:
|
||||
ServerMap *srvmap;
|
||||
leveldb::DB* m_database;
|
||||
leveldb::DB *m_database;
|
||||
};
|
||||
|
||||
#endif // USE_LEVELDB
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -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 <hiredis.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 <hiredis.h>
|
||||
#include <cassert>
|
||||
|
||||
|
||||
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<redisReply *>(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<redisReply *>(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<redisReply *>(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<redisReply *>(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<redisReply *>(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<v3s16> &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<redisReply *>(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
|
||||
|
||||
|
@ -28,24 +28,28 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <hiredis.h>
|
||||
#include <string>
|
||||
|
||||
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<v3s16> &dst);
|
||||
virtual int Initialized(void);
|
||||
~Database_Redis();
|
||||
|
||||
private:
|
||||
ServerMap *srvmap;
|
||||
redisContext *ctx;
|
||||
std::string hash;
|
||||
};
|
||||
|
||||
#endif // USE_REDIS
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -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 <cassert>
|
||||
|
||||
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<<DTIME<<"Database_SQLite3: Failed to create directory "
|
||||
<<"\""<<path<<"\""<<std::endl;
|
||||
throw BaseException("Database_SQLite3 failed to create directory");
|
||||
}
|
||||
}
|
||||
if (m_database) return;
|
||||
|
||||
void Database_SQLite3::verifyDatabase() {
|
||||
if(m_database)
|
||||
return;
|
||||
|
||||
std::string dbp = m_savedir + DIR_DELIM "map.sqlite";
|
||||
bool needs_create = false;
|
||||
int d;
|
||||
std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
|
||||
|
||||
// Open the database connection
|
||||
|
||||
createDirs(m_savedir); // ?
|
||||
if (!fs::CreateAllDirs(m_savedir)) {
|
||||
infostream << "Database_SQLite3: Failed to create directory \""
|
||||
<< m_savedir << "\"" << std::endl;
|
||||
throw FileNotGoodException("Failed to create database "
|
||||
"save directory");
|
||||
}
|
||||
|
||||
if(!fs::PathExists(dbp))
|
||||
needs_create = true;
|
||||
bool needs_create = !fs::PathExists(dbp);
|
||||
|
||||
d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
|
||||
if(d != SQLITE_OK) {
|
||||
errorstream<<"SQLite3 database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
|
||||
if (sqlite3_open_v2(dbp.c_str(), &m_database,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
|
||||
NULL) != SQLITE_OK) {
|
||||
errorstream << "SQLite3 database failed to open: "
|
||||
<< sqlite3_errmsg(m_database) << std::endl;
|
||||
throw FileNotGoodException("Cannot open database file");
|
||||
}
|
||||
|
||||
if(needs_create)
|
||||
if (needs_create) {
|
||||
createDatabase();
|
||||
}
|
||||
|
||||
std::string querystr = std::string("PRAGMA synchronous = ")
|
||||
std::string query_str = std::string("PRAGMA synchronous = ")
|
||||
+ itos(g_settings->getU16("sqlite_synchronous"));
|
||||
d = sqlite3_exec(m_database, querystr.c_str(), NULL, NULL, NULL);
|
||||
if(d != SQLITE_OK) {
|
||||
errorstream<<"Database pragma set failed: "
|
||||
<<sqlite3_errmsg(m_database)<<std::endl;
|
||||
throw FileNotGoodException("Cannot set pragma");
|
||||
}
|
||||
SQLOK(sqlite3_exec(m_database, query_str.c_str(), NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
|
||||
if(d != SQLITE_OK) {
|
||||
errorstream<<"SQLite3 read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
|
||||
throw FileNotGoodException("Cannot prepare read statement");
|
||||
}
|
||||
void Database_SQLite3::verifyDatabase()
|
||||
{
|
||||
if (m_initialized) return;
|
||||
|
||||
openDatabase();
|
||||
|
||||
PREPARE_STATEMENT(begin, "BEGIN");
|
||||
PREPARE_STATEMENT(end, "COMMIT");
|
||||
PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `pos` = ? LIMIT 1");
|
||||
#ifdef __ANDROID__
|
||||
d = sqlite3_prepare(m_database, "INSERT INTO `blocks` VALUES(?, ?);", -1, &m_database_write, NULL);
|
||||
PREPARE_STATEMENT(write, "INSERT INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
|
||||
#else
|
||||
d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?);", -1, &m_database_write, NULL);
|
||||
PREPARE_STATEMENT(write, "REPLACE INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
|
||||
#endif
|
||||
if(d != SQLITE_OK) {
|
||||
errorstream<<"SQLite3 write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
|
||||
throw FileNotGoodException("Cannot prepare write statement");
|
||||
}
|
||||
PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?");
|
||||
PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`");
|
||||
|
||||
d = sqlite3_prepare(m_database, "DELETE FROM `blocks` WHERE `pos`=?;", -1, &m_database_delete, NULL);
|
||||
if(d != SQLITE_OK) {
|
||||
infostream<<"WARNING: SQLite3 database delete statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
|
||||
throw FileNotGoodException("Cannot prepare delete statement");
|
||||
}
|
||||
m_initialized = true;
|
||||
|
||||
d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
|
||||
if(d != SQLITE_OK) {
|
||||
infostream<<"SQLite3 list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
|
||||
throw FileNotGoodException("Cannot prepare read statement");
|
||||
}
|
||||
|
||||
infostream<<"ServerMap: SQLite3 database opened"<<std::endl;
|
||||
verbosestream << "ServerMap: SQLite3 database opened." << std::endl;
|
||||
}
|
||||
|
||||
bool Database_SQLite3::deleteBlock(v3s16 blockpos)
|
||||
inline void Database_SQLite3::bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index)
|
||||
{
|
||||
SQLOK(sqlite3_bind_int64(stmt, index, getBlockAsInteger(pos)));
|
||||
}
|
||||
|
||||
bool Database_SQLite3::deleteBlock(const v3s16 &pos)
|
||||
{
|
||||
verifyDatabase();
|
||||
|
||||
if (sqlite3_bind_int64(m_database_delete, 1,
|
||||
getBlockAsInteger(blockpos)) != SQLITE_OK) {
|
||||
errorstream << "WARNING: Could not bind block position for delete: "
|
||||
<< sqlite3_errmsg(m_database) << std::endl;
|
||||
}
|
||||
bindPos(m_stmt_delete, pos);
|
||||
|
||||
if (sqlite3_step(m_database_delete) != SQLITE_DONE) {
|
||||
bool good = sqlite3_step(m_stmt_delete) == SQLITE_DONE;
|
||||
sqlite3_reset(m_stmt_delete);
|
||||
|
||||
if (!good) {
|
||||
errorstream << "WARNING: deleteBlock: Block failed to delete "
|
||||
<< PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
|
||||
sqlite3_reset(m_database_delete);
|
||||
return false;
|
||||
<< PP(pos) << ": " << sqlite3_errmsg(m_database) << std::endl;
|
||||
}
|
||||
|
||||
sqlite3_reset(m_database_delete);
|
||||
return true;
|
||||
return good;
|
||||
}
|
||||
|
||||
bool Database_SQLite3::saveBlock(v3s16 blockpos, std::string &data)
|
||||
bool Database_SQLite3::saveBlock(const v3s16 &pos, const std::string &data)
|
||||
{
|
||||
verifyDatabase();
|
||||
|
||||
s64 bkey = getBlockAsInteger(blockpos);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
/**
|
||||
* Note: For some unknown reason sqlite3 fails to REPLACE blocks on android,
|
||||
* deleting them and inserting first works.
|
||||
* Note: For some unknown reason SQLite3 fails to REPLACE blocks on Android,
|
||||
* deleting them and then inserting works.
|
||||
*/
|
||||
if (sqlite3_bind_int64(m_database_read, 1, bkey) != SQLITE_OK) {
|
||||
infostream << "WARNING: Could not bind block position for load: "
|
||||
<< sqlite3_errmsg(m_database)<<std::endl;
|
||||
}
|
||||
|
||||
int step_result = sqlite3_step(m_database_read);
|
||||
sqlite3_reset(m_database_read);
|
||||
|
||||
if (step_result == SQLITE_ROW) {
|
||||
if (sqlite3_bind_int64(m_database_delete, 1, bkey) != SQLITE_OK) {
|
||||
infostream << "WARNING: Could not bind block position for delete: "
|
||||
<< sqlite3_errmsg(m_database)<<std::endl;
|
||||
}
|
||||
|
||||
if (sqlite3_step(m_database_delete) != SQLITE_DONE) {
|
||||
errorstream << "WARNING: saveBlock: Block failed to delete "
|
||||
<< PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
|
||||
return false;
|
||||
}
|
||||
sqlite3_reset(m_database_delete);
|
||||
bindPos(m_stmt_read, pos);
|
||||
|
||||
if (sqlite3_step(m_stmt_read) == SQLITE_ROW) {
|
||||
deleteBlock(pos);
|
||||
}
|
||||
sqlite3_reset(m_stmt_read);
|
||||
#endif
|
||||
|
||||
if (sqlite3_bind_int64(m_database_write, 1, bkey) != SQLITE_OK) {
|
||||
errorstream << "WARNING: saveBlock: Block position failed to bind: "
|
||||
<< PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
|
||||
sqlite3_reset(m_database_write);
|
||||
return false;
|
||||
}
|
||||
bindPos(m_stmt_write, pos);
|
||||
SQLOK(sqlite3_bind_blob(m_stmt_write, 2, data.data(), data.size(), NULL));
|
||||
|
||||
if (sqlite3_bind_blob(m_database_write, 2, (void *)data.c_str(),
|
||||
data.size(), NULL) != SQLITE_OK) {
|
||||
errorstream << "WARNING: saveBlock: Block data failed to bind: "
|
||||
<< PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
|
||||
sqlite3_reset(m_database_write);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sqlite3_step(m_database_write) != SQLITE_DONE) {
|
||||
errorstream << "WARNING: saveBlock: Block failed to save "
|
||||
<< PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
|
||||
sqlite3_reset(m_database_write);
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_reset(m_database_write);
|
||||
SQLRES(sqlite3_step(m_stmt_write), SQLITE_DONE)
|
||||
sqlite3_reset(m_stmt_write);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Database_SQLite3::loadBlock(v3s16 blockpos)
|
||||
std::string Database_SQLite3::loadBlock(const v3s16 &pos)
|
||||
{
|
||||
verifyDatabase();
|
||||
|
||||
if (sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
|
||||
errorstream << "Could not bind block position for load: "
|
||||
<< sqlite3_errmsg(m_database)<<std::endl;
|
||||
bindPos(m_stmt_read, pos);
|
||||
|
||||
if (sqlite3_step(m_stmt_read) != SQLITE_ROW) {
|
||||
sqlite3_reset(m_stmt_read);
|
||||
return "";
|
||||
}
|
||||
const char *data = (const char *) sqlite3_column_blob(m_stmt_read, 0);
|
||||
size_t len = sqlite3_column_bytes(m_stmt_read, 0);
|
||||
|
||||
if (sqlite3_step(m_database_read) == SQLITE_ROW) {
|
||||
const char *data = (const char *) sqlite3_column_blob(m_database_read, 0);
|
||||
size_t len = sqlite3_column_bytes(m_database_read, 0);
|
||||
std::string s;
|
||||
if (data)
|
||||
s = std::string(data, len);
|
||||
|
||||
std::string s = "";
|
||||
if(data)
|
||||
s = std::string(data, len);
|
||||
sqlite3_step(m_stmt_read);
|
||||
// We should never get more than 1 row, so ok to reset
|
||||
sqlite3_reset(m_stmt_read);
|
||||
|
||||
sqlite3_step(m_database_read);
|
||||
// We should never get more than 1 row, so ok to reset
|
||||
sqlite3_reset(m_database_read);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
sqlite3_reset(m_database_read);
|
||||
return "";
|
||||
return s;
|
||||
}
|
||||
|
||||
void Database_SQLite3::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_OK)
|
||||
throw FileNotGoodException("Could not create sqlite3 database structure");
|
||||
else
|
||||
infostream<<"ServerMap: SQLite3 database structure was created";
|
||||
SQLOK(sqlite3_exec(m_database,
|
||||
"CREATE TABLE IF NOT EXISTS `blocks` (\n"
|
||||
" `pos` INT PRIMARY KEY,\n"
|
||||
" `data` BLOB\n"
|
||||
");\n",
|
||||
NULL, NULL, NULL));
|
||||
|
||||
}
|
||||
|
||||
@ -278,36 +224,25 @@ void Database_SQLite3::listAllLoadableBlocks(std::vector<v3s16> &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="<<block_i<<" p="<<PP(p)<<std::endl;
|
||||
dst.push_back(p);
|
||||
while (sqlite3_step(m_stmt_list) == SQLITE_ROW) {
|
||||
dst.push_back(getIntegerAsBlock(sqlite3_column_int64(m_stmt_list, 0)));
|
||||
}
|
||||
sqlite3_reset(m_stmt_list);
|
||||
}
|
||||
|
||||
|
||||
#define FINALIZE_STATEMENT(statement) \
|
||||
if ( statement ) \
|
||||
rc = sqlite3_finalize(statement); \
|
||||
if ( rc != SQLITE_OK ) \
|
||||
errorstream << "Database_SQLite3::~Database_SQLite3():" \
|
||||
<< "Failed to finalize: " << #statement << ": rc=" << rc << std::endl;
|
||||
|
||||
Database_SQLite3::~Database_SQLite3()
|
||||
{
|
||||
int rc = SQLITE_OK;
|
||||
FINALIZE_STATEMENT(m_stmt_read)
|
||||
FINALIZE_STATEMENT(m_stmt_write)
|
||||
FINALIZE_STATEMENT(m_stmt_list)
|
||||
FINALIZE_STATEMENT(m_stmt_begin)
|
||||
FINALIZE_STATEMENT(m_stmt_end)
|
||||
FINALIZE_STATEMENT(m_stmt_delete)
|
||||
|
||||
FINALIZE_STATEMENT(m_database_read)
|
||||
FINALIZE_STATEMENT(m_database_write)
|
||||
FINALIZE_STATEMENT(m_database_list)
|
||||
FINALIZE_STATEMENT(m_database_delete)
|
||||
|
||||
if(m_database)
|
||||
rc = sqlite3_close(m_database);
|
||||
|
||||
if (rc != SQLITE_OK) {
|
||||
if (sqlite3_close(m_database) != SQLITE_OK) {
|
||||
errorstream << "Database_SQLite3::~Database_SQLite3(): "
|
||||
<< "Failed to close database: rc=" << rc << std::endl;
|
||||
<< "Failed to close database: "
|
||||
<< sqlite3_errmsg(m_database) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,35 +27,43 @@ extern "C" {
|
||||
#include "sqlite3.h"
|
||||
}
|
||||
|
||||
class ServerMap;
|
||||
|
||||
class Database_SQLite3 : public Database
|
||||
{
|
||||
public:
|
||||
Database_SQLite3(ServerMap *map, std::string savedir);
|
||||
Database_SQLite3(const 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 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<v3s16> &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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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<v3s16> &dst) = 0;
|
||||
virtual int Initialized(void)=0;
|
||||
virtual ~Database() {};
|
||||
|
||||
virtual bool initialized() const { return true; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
59
src/main.cpp
59
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<v3s16> 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;
|
||||
|
76
src/map.cpp
76
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;
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user