]> git.lizzy.rs Git - minetest.git/blob - src/database/database-files.cpp
RemotePlayer: Remove Settings writer to Files database
[minetest.git] / src / database / database-files.cpp
1 /*
2 Minetest
3 Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
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 <cassert>
21 #include <json/json.h>
22 #include "convert_json.h"
23 #include "database-files.h"
24 #include "remoteplayer.h"
25 #include "settings.h"
26 #include "porting.h"
27 #include "filesys.h"
28 #include "server/player_sao.h"
29 #include "util/string.h"
30
31 // !!! WARNING !!!
32 // This backend is intended to be used on Minetest 0.4.16 only for the transition backend
33 // for player files
34
35 PlayerDatabaseFiles::PlayerDatabaseFiles(const std::string &savedir) : m_savedir(savedir)
36 {
37         fs::CreateDir(m_savedir);
38 }
39
40 void PlayerDatabaseFiles::deSerialize(RemotePlayer *p, std::istream &is,
41          const std::string &playername, PlayerSAO *sao)
42 {
43         Settings args("PlayerArgsEnd");
44
45         if (!args.parseConfigLines(is)) {
46                 throw SerializationError("PlayerArgsEnd of player " + playername + " not found!");
47         }
48
49         p->m_dirty = true;
50         //args.getS32("version"); // Version field value not used
51         const std::string &name = args.get("name");
52         strlcpy(p->m_name, name.c_str(), PLAYERNAME_SIZE);
53
54         if (sao) {
55                 try {
56                         sao->setHPRaw(args.getU16("hp"));
57                 } catch(SettingNotFoundException &e) {
58                         sao->setHPRaw(PLAYER_MAX_HP_DEFAULT);
59                 }
60
61                 try {
62                         sao->setBasePosition(args.getV3F("position"));
63                 } catch (SettingNotFoundException &e) {}
64
65                 try {
66                         sao->setLookPitch(args.getFloat("pitch"));
67                 } catch (SettingNotFoundException &e) {}
68                 try {
69                         sao->setPlayerYaw(args.getFloat("yaw"));
70                 } catch (SettingNotFoundException &e) {}
71
72                 try {
73                         sao->setBreath(args.getU16("breath"), false);
74                 } catch (SettingNotFoundException &e) {}
75
76                 try {
77                         const std::string &extended_attributes = args.get("extended_attributes");
78                         std::istringstream iss(extended_attributes);
79                         Json::CharReaderBuilder builder;
80                         builder.settings_["collectComments"] = false;
81                         std::string errs;
82
83                         Json::Value attr_root;
84                         Json::parseFromStream(builder, iss, &attr_root, &errs);
85
86                         const Json::Value::Members attr_list = attr_root.getMemberNames();
87                         for (const auto &it : attr_list) {
88                                 Json::Value attr_value = attr_root[it];
89                                 sao->getMeta().setString(it, attr_value.asString());
90                         }
91                         sao->getMeta().setModified(false);
92                 } catch (SettingNotFoundException &e) {}
93         }
94
95         try {
96                 p->inventory.deSerialize(is);
97         } catch (SerializationError &e) {
98                 errorstream << "Failed to deserialize player inventory. player_name="
99                         << name << " " << e.what() << std::endl;
100         }
101
102         if (!p->inventory.getList("craftpreview") && p->inventory.getList("craftresult")) {
103                 // Convert players without craftpreview
104                 p->inventory.addList("craftpreview", 1);
105
106                 bool craftresult_is_preview = true;
107                 if(args.exists("craftresult_is_preview"))
108                         craftresult_is_preview = args.getBool("craftresult_is_preview");
109                 if(craftresult_is_preview)
110                 {
111                         // Clear craftresult
112                         p->inventory.getList("craftresult")->changeItem(0, ItemStack());
113                 }
114         }
115 }
116
117 void PlayerDatabaseFiles::serialize(RemotePlayer *p, std::ostream &os)
118 {
119         // Utilize a Settings object for storing values
120         Settings args("PlayerArgsEnd");
121         args.setS32("version", 1);
122         args.set("name", p->m_name);
123
124         // This should not happen
125         assert(m_sao);
126         args.setU16("hp", p->m_sao->getHP());
127         args.setV3F("position", p->m_sao->getBasePosition());
128         args.setFloat("pitch", p->m_sao->getLookPitch());
129         args.setFloat("yaw", p->m_sao->getRotation().Y);
130         args.setU16("breath", p->m_sao->getBreath());
131
132         std::string extended_attrs;
133         {
134                 // serializeExtraAttributes
135                 PlayerSAO *sao = p->getPlayerSAO();
136                 assert(sao);
137                 Json::Value json_root;
138
139                 const StringMap &attrs = sao->getMeta().getStrings();
140                 for (const auto &attr : attrs) {
141                         json_root[attr.first] = attr.second;
142                 }
143
144                 extended_attrs = fastWriteJson(json_root);
145         }
146         args.set("extended_attributes", extended_attrs);
147
148         args.writeLines(os);
149
150         p->inventory.serialize(os);
151 }
152
153 void PlayerDatabaseFiles::savePlayer(RemotePlayer *player)
154 {
155         fs::CreateDir(m_savedir);
156
157         std::string savedir = m_savedir + DIR_DELIM;
158         std::string path = savedir + player->getName();
159         bool path_found = false;
160         RemotePlayer testplayer("", NULL);
161
162         for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES && !path_found; i++) {
163                 if (!fs::PathExists(path)) {
164                         path_found = true;
165                         continue;
166                 }
167
168                 // Open and deserialize file to check player name
169                 std::ifstream is(path.c_str(), std::ios_base::binary);
170                 if (!is.good()) {
171                         errorstream << "Failed to open " << path << std::endl;
172                         return;
173                 }
174
175                 deSerialize(&testplayer, is, path, NULL);
176                 is.close();
177                 if (strcmp(testplayer.getName(), player->getName()) == 0) {
178                         path_found = true;
179                         continue;
180                 }
181
182                 path = savedir + player->getName() + itos(i);
183         }
184
185         if (!path_found) {
186                 errorstream << "Didn't find free file for player " << player->getName()
187                                 << std::endl;
188                 return;
189         }
190
191         // Open and serialize file
192         std::ostringstream ss(std::ios_base::binary);
193         serialize(&testplayer, ss);
194         if (!fs::safeWriteToFile(path, ss.str())) {
195                 infostream << "Failed to write " << path << std::endl;
196         }
197
198         player->onSuccessfulSave();
199 }
200
201 bool PlayerDatabaseFiles::removePlayer(const std::string &name)
202 {
203         std::string players_path = m_savedir + DIR_DELIM;
204         std::string path = players_path + name;
205
206         RemotePlayer temp_player("", NULL);
207         for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
208                 // Open file and deserialize
209                 std::ifstream is(path.c_str(), std::ios_base::binary);
210                 if (!is.good())
211                         continue;
212
213                 deSerialize(&temp_player, is, path, NULL);
214                 is.close();
215
216                 if (temp_player.getName() == name) {
217                         fs::DeleteSingleFileOrEmptyDirectory(path);
218                         return true;
219                 }
220
221                 path = players_path + name + itos(i);
222         }
223
224         return false;
225 }
226
227 bool PlayerDatabaseFiles::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
228 {
229         std::string players_path = m_savedir + DIR_DELIM;
230         std::string path = players_path + player->getName();
231
232         const std::string player_to_load = player->getName();
233         for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
234                 // Open file and deserialize
235                 std::ifstream is(path.c_str(), std::ios_base::binary);
236                 if (!is.good())
237                         continue;
238
239                 deSerialize(player, is, path, sao);
240                 is.close();
241
242                 if (player->getName() == player_to_load)
243                         return true;
244
245                 path = players_path + player_to_load + itos(i);
246         }
247
248         infostream << "Player file for player " << player_to_load << " not found" << std::endl;
249         return false;
250 }
251
252 void PlayerDatabaseFiles::listPlayers(std::vector<std::string> &res)
253 {
254         std::vector<fs::DirListNode> files = fs::GetDirListing(m_savedir);
255         // list files into players directory
256         for (std::vector<fs::DirListNode>::const_iterator it = files.begin(); it !=
257                 files.end(); ++it) {
258                 // Ignore directories
259                 if (it->dir)
260                         continue;
261
262                 const std::string &filename = it->name;
263                 std::string full_path = m_savedir + DIR_DELIM + filename;
264                 std::ifstream is(full_path.c_str(), std::ios_base::binary);
265                 if (!is.good())
266                         continue;
267
268                 RemotePlayer player(filename.c_str(), NULL);
269                 // Null env & dummy peer_id
270                 PlayerSAO playerSAO(NULL, &player, 15789, false);
271
272                 deSerialize(&player, is, "", &playerSAO);
273                 is.close();
274
275                 res.emplace_back(player.getName());
276         }
277 }
278
279 AuthDatabaseFiles::AuthDatabaseFiles(const std::string &savedir) : m_savedir(savedir)
280 {
281         readAuthFile();
282 }
283
284 bool AuthDatabaseFiles::getAuth(const std::string &name, AuthEntry &res)
285 {
286         const auto res_i = m_auth_list.find(name);
287         if (res_i == m_auth_list.end()) {
288                 return false;
289         }
290         res = res_i->second;
291         return true;
292 }
293
294 bool AuthDatabaseFiles::saveAuth(const AuthEntry &authEntry)
295 {
296         m_auth_list[authEntry.name] = authEntry;
297
298         // save entire file
299         return writeAuthFile();
300 }
301
302 bool AuthDatabaseFiles::createAuth(AuthEntry &authEntry)
303 {
304         m_auth_list[authEntry.name] = authEntry;
305
306         // save entire file
307         return writeAuthFile();
308 }
309
310 bool AuthDatabaseFiles::deleteAuth(const std::string &name)
311 {
312         if (!m_auth_list.erase(name)) {
313                 // did not delete anything -> hadn't existed
314                 return false;
315         }
316         return writeAuthFile();
317 }
318
319 void AuthDatabaseFiles::listNames(std::vector<std::string> &res)
320 {
321         res.clear();
322         res.reserve(m_auth_list.size());
323         for (const auto &res_pair : m_auth_list) {
324                 res.push_back(res_pair.first);
325         }
326 }
327
328 void AuthDatabaseFiles::reload()
329 {
330         readAuthFile();
331 }
332
333 bool AuthDatabaseFiles::readAuthFile()
334 {
335         std::string path = m_savedir + DIR_DELIM + "auth.txt";
336         std::ifstream file(path, std::ios::binary);
337         if (!file.good()) {
338                 return false;
339         }
340         m_auth_list.clear();
341         while (file.good()) {
342                 std::string line;
343                 std::getline(file, line);
344                 std::vector<std::string> parts = str_split(line, ':');
345                 if (parts.size() < 3) // also: empty line at end
346                         continue;
347                 const std::string &name = parts[0];
348                 const std::string &password = parts[1];
349                 std::vector<std::string> privileges = str_split(parts[2], ',');
350                 s64 last_login = parts.size() > 3 ? atol(parts[3].c_str()) : 0;
351
352                 m_auth_list[name] = {
353                                 1,
354                                 name,
355                                 password,
356                                 privileges,
357                                 last_login,
358                 };
359         }
360         return true;
361 }
362
363 bool AuthDatabaseFiles::writeAuthFile()
364 {
365         std::string path = m_savedir + DIR_DELIM + "auth.txt";
366         std::ostringstream output(std::ios_base::binary);
367         for (const auto &auth_i : m_auth_list) {
368                 const AuthEntry &authEntry = auth_i.second;
369                 output << authEntry.name << ":" << authEntry.password << ":";
370                 output << str_join(authEntry.privileges, ",");
371                 output << ":" << authEntry.last_login;
372                 output << std::endl;
373         }
374         if (!fs::safeWriteToFile(path, output.str())) {
375                 infostream << "Failed to write " << path << std::endl;
376                 return false;
377         }
378         return true;
379 }