]> git.lizzy.rs Git - dragonfireclient.git/blob - src/database-redis.cpp
Add ability to delete MapBlocks from map
[dragonfireclient.git] / src / database-redis.cpp
1 /*
2 Minetest
3 Copyright (C) 2014 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "config.h"
21
22 #if USE_REDIS
23 /*
24         Redis databases
25 */
26
27
28 #include "database-redis.h"
29 #include <hiredis.h>
30
31 #include "map.h"
32 #include "mapsector.h"
33 #include "mapblock.h"
34 #include "serialization.h"
35 #include "main.h"
36 #include "settings.h"
37 #include "log.h"
38 #include "filesys.h"
39
40
41 Database_Redis::Database_Redis(ServerMap *map, std::string savedir)
42 {
43         Settings conf;
44         conf.readConfigFile((std::string(savedir) + DIR_DELIM + "world.mt").c_str());
45         std::string tmp;
46         try {
47         tmp = conf.get("redis_address");
48         hash = conf.get("redis_hash");
49         } catch(SettingNotFoundException e) {
50                 throw SettingNotFoundException("Set redis_address and redis_hash in world.mt to use the redis backend");
51         }
52         const char *addr = tmp.c_str();
53         int port = conf.exists("redis_port") ? conf.getU16("redis_port") : 6379;
54         ctx = redisConnect(addr, port);
55         if(!ctx)
56                 throw FileNotGoodException("Cannot allocate redis context");
57         else if(ctx->err) {
58                 std::string err = std::string("Connection error: ") + ctx->errstr;
59                 redisFree(ctx);
60                 throw FileNotGoodException(err);
61         }
62         srvmap = map;
63 }
64
65 int Database_Redis::Initialized(void)
66 {
67         return 1;
68 }
69
70 void Database_Redis::beginSave() {
71         redisReply *reply;
72         reply = (redisReply*) redisCommand(ctx, "MULTI");
73         if(!reply)
74                 throw FileNotGoodException(std::string("redis command 'MULTI' failed: ") + ctx->errstr);
75         freeReplyObject(reply);
76 }
77
78 void Database_Redis::endSave() {
79         redisReply *reply;
80         reply = (redisReply*) redisCommand(ctx, "EXEC");
81         if(!reply)
82                 throw FileNotGoodException(std::string("redis command 'EXEC' failed: ") + ctx->errstr);
83         freeReplyObject(reply);
84 }
85
86 bool Database_Redis::saveBlock(v3s16 blockpos, std::string &data)
87 {
88         std::string tmp = i64tos(getBlockAsInteger(blockpos));
89
90         redisReply *reply = (redisReply *)redisCommand(ctx, "HSET %s %s %b",
91                         hash.c_str(), tmp.c_str(), data.c_str(), data.size());
92         if (!reply) {
93                 errorstream << "WARNING: saveBlock: redis command 'HSET' failed on "
94                         "block " << PP(blockpos) << ": " << ctx->errstr << std::endl;
95                 freeReplyObject(reply);
96                 return false;
97         }
98
99         if (reply->type == REDIS_REPLY_ERROR) {
100                 errorstream << "WARNING: saveBlock: saving block " << PP(blockpos)
101                         << "failed" << std::endl;
102                 freeReplyObject(reply);
103                 return false;
104         }
105
106         freeReplyObject(reply);
107         return true;
108 }
109
110 std::string Database_Redis::loadBlock(v3s16 blockpos)
111 {
112         std::string tmp = i64tos(getBlockAsInteger(blockpos));
113         redisReply *reply;
114         reply = (redisReply*) redisCommand(ctx, "HGET %s %s", hash.c_str(), tmp.c_str());
115
116         if(!reply)
117                 throw FileNotGoodException(std::string("redis command 'HGET %s %s' failed: ") + ctx->errstr);
118         if(reply->type != REDIS_REPLY_STRING)
119                 return "";
120
121         std::string str(reply->str, reply->len);
122         freeReplyObject(reply); // std::string copies the memory so this won't cause any problems
123         return str;
124 }
125
126 bool Database_Redis::deleteBlock(v3s16 blockpos)
127 {
128         std::string tmp = i64tos(getBlockAsInteger(blockpos));
129
130         redisReply *reply = (redisReply *)redisCommand(ctx, "HDEL %s %s",
131                 hash.c_str(), tmp.c_str());
132         if (!reply) {
133                 errorstream << "WARNING: deleteBlock: redis command 'HDEL' failed on "
134                         "block " << PP(blockpos) << ": " << ctx->errstr << std::endl;
135                 freeReplyObject(reply);
136                 return false;
137         }
138
139         if (reply->type == REDIS_REPLY_ERROR) {
140                 errorstream << "WARNING: deleteBlock: deleting block " << PP(blockpos)
141                         << "failed" << std::endl;
142                 freeReplyObject(reply);
143                 return false;
144         }
145
146         freeReplyObject(reply);
147         return true;
148 }
149
150 void Database_Redis::listAllLoadableBlocks(std::list<v3s16> &dst)
151 {
152         redisReply *reply;
153         reply = (redisReply*) redisCommand(ctx, "HKEYS %s", hash.c_str());
154         if(!reply)
155                 throw FileNotGoodException(std::string("redis command 'HKEYS %s' failed: ") + ctx->errstr);
156         if(reply->type != REDIS_REPLY_ARRAY)
157                 throw FileNotGoodException("Failed to get keys from database");
158         for(size_t i = 0; i < reply->elements; i++)
159         {
160                 assert(reply->element[i]->type == REDIS_REPLY_STRING);
161                 dst.push_back(getIntegerAsBlock(stoi64(reply->element[i]->str)));
162         }
163         freeReplyObject(reply);
164 }
165
166 Database_Redis::~Database_Redis()
167 {
168         redisFree(ctx);
169 }
170 #endif