X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fdatabase-redis.cpp;h=cc4e5badea7dd0a075b2cc270a748a6838400c9f;hb=0ba1cf82033a810dd2e6178e23794bb135ede46a;hp=ff54753e6055bd62c5fc2535e6976bc23f1f3039;hpb=94dba66c167453f3a40f7d62a303c2aea7ac5f8f;p=minetest.git diff --git a/src/database-redis.cpp b/src/database-redis.cpp index ff54753e6..cc4e5bade 100644 --- a/src/database-redis.cpp +++ b/src/database-redis.cpp @@ -20,206 +20,151 @@ 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 "exceptions.h" +#include "util/string.h" -Database_Redis::Database_Redis(ServerMap *map, std::string savedir) +#include +#include + + +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); } -void Database_Redis::saveBlock(MapBlock *block) +bool Database_Redis::saveBlock(const v3s16 &pos, const std::string &data) { - DSTACK(__FUNCTION_NAME); - /* - Dummy blocks are not written - */ - if(block->isDummy()) - { - return; + std::string tmp = i64tos(getBlockAsInteger(pos)); + + 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(pos) << ": " << ctx->errstr << std::endl; + freeReplyObject(reply); + return false; } - // Format used for writing - u8 version = SER_FMT_VER_HIGHEST_WRITE; - // Get destination - v3s16 p3d = block->getPos(); - - /* - [0] u8 serialization version - [1] data - */ - - 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 tmp1 = o.str(); - std::string tmp2 = i64tos(getBlockAsInteger(p3d)); - - redisReply *reply; - reply = (redisReply*) redisCommand(ctx, "HSET %s %s %b", hash.c_str(), tmp2.c_str(), tmp1.c_str(), tmp1.size()); - if(!reply) - throw FileNotGoodException(std::string("redis command 'HSET %s %s %b' failed: ") + ctx->errstr); - if(reply->type == REDIS_REPLY_ERROR) - throw FileNotGoodException("Failed to store block in Database"); - freeReplyObject(reply); + if (reply->type == REDIS_REPLY_ERROR) { + errorstream << "WARNING: saveBlock: saving block " << PP(pos) + << " failed: " << reply->str << std::endl; + freeReplyObject(reply); + return false; + } - // We just wrote it to the disk so clear modified flag - block->resetModified(); + freeReplyObject(reply); + return true; } -MapBlock* Database_Redis::loadBlock(v3s16 blockpos) +std::string Database_Redis::loadBlock(const v3s16 &pos) { - v2s16 p2d(blockpos.X, blockpos.Z); - - std::string tmp = i64tos(getBlockAsInteger(blockpos)); - redisReply *reply; - reply = (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); + std::string tmp = i64tos(getBlockAsInteger(pos)); + redisReply *reply = static_cast(redisCommand(ctx, + "HGET %s %s", hash.c_str(), tmp.c_str())); - if (reply->type == REDIS_REPLY_STRING && reply->len == 0) { + if (!reply) { + throw FileNotGoodException(std::string( + "Redis command 'HGET %s %s' failed: ") + ctx->errstr); + } + switch (reply->type) { + case REDIS_REPLY_STRING: { + std::string str(reply->str, reply->len); + // std::string copies the memory so this won't cause any problems freeReplyObject(reply); - errorstream << "Blank block data in database (reply->len == 0) (" - << blockpos.X << "," << blockpos.Y << "," << blockpos.Z << ")" << std::endl; - - if (g_settings->getBool("ignore_world_load_errors")) { - errorstream << "Ignoring block load error. Duck and cover! " - << "(ignore_world_load_errors)" << std::endl; - } else { - throw SerializationError("Blank block data in database"); - } - return NULL; + return str; } - - if (reply->type == REDIS_REPLY_STRING) { - /* - Make sure sector is loaded - */ - MapSector *sector = srvmap->createSector(p2d); - - try { - std::istringstream is(std::string(reply->str, reply->len), std::ios_base::binary); - freeReplyObject(reply); // std::string copies the memory so we can already do this here - u8 version = SER_FMT_VER_INVALID; - is.read((char *)&version, 1); - - if (is.fail()) - throw SerializationError("ServerMap::loadBlock(): Failed" - " to read MapBlock version"); - - MapBlock *block = NULL; - bool created_new = false; - block = sector->getBlockNoCreateNoEx(blockpos.Y); - if (block == NULL) - { - block = sector->createBlankBlockNoInsert(blockpos.Y); - created_new = true; - } - - // Read basic data - block->deSerialize(is, version, true); - - // If it's a new block, insert it to the map - if (created_new) - sector->insertBlock(block); - - // We just loaded it from, so it's up-to-date. - block->resetModified(); - } - catch (SerializationError &e) - { - errorstream << "Invalid block data in database" - << " (" << blockpos.X << "," << blockpos.Y << "," << blockpos.Z - << ") (SerializationError): " << e.what() << std::endl; - // TODO: Block should be marked as invalid in memory so that it is - // not touched but the game can run - - if (g_settings->getBool("ignore_world_load_errors")) { - errorstream << "Ignoring block load error. Duck and cover! " - << "(ignore_world_load_errors)" << std::endl; - } else { - throw SerializationError("Invalid block data in database"); - } - } - - return srvmap->getBlockNoCreateNoEx(blockpos); // should not be using this here + case REDIS_REPLY_ERROR: + errorstream << "WARNING: loadBlock: loading block " << PP(pos) + << " failed: " << reply->str << std::endl; } - return NULL; + freeReplyObject(reply); + return ""; } -void Database_Redis::listAllLoadableBlocks(std::list &dst) +bool Database_Redis::deleteBlock(const v3s16 &pos) { - 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) - throw FileNotGoodException("Failed to get keys from database"); - 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))); + std::string tmp = i64tos(getBlockAsInteger(pos)); + + redisReply *reply = static_cast(redisCommand(ctx, + "HDEL %s %s", hash.c_str(), tmp.c_str())); + if (!reply) { + 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: " << reply->str << std::endl; + freeReplyObject(reply); + return false; } + freeReplyObject(reply); + return true; } -Database_Redis::~Database_Redis() +void Database_Redis::listAllLoadableBlocks(std::vector &dst) { - redisFree(ctx); + redisReply *reply = static_cast(redisCommand(ctx, "HKEYS %s", hash.c_str())); + if (!reply) { + throw FileNotGoodException(std::string( + "Redis command 'HKEYS %s' failed: ") + ctx->errstr); + } + switch (reply->type) { + case REDIS_REPLY_ARRAY: + 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))); + } + case REDIS_REPLY_ERROR: + throw FileNotGoodException(std::string( + "Failed to get keys from database: ") + reply->str); + } + freeReplyObject(reply); } -#endif + +#endif // USE_REDIS +