3 Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
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.
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.
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.
21 #include <json/json.h>
22 #include "convert_json.h"
23 #include "database-files.h"
24 #include "remoteplayer.h"
28 #include "server/player_sao.h"
29 #include "util/string.h"
32 // This backend is intended to be used on Minetest 0.4.16 only for the transition backend
35 PlayerDatabaseFiles::PlayerDatabaseFiles(const std::string &savedir) : m_savedir(savedir)
37 fs::CreateDir(m_savedir);
40 void PlayerDatabaseFiles::deSerialize(RemotePlayer *p, std::istream &is,
41 const std::string &playername, PlayerSAO *sao)
43 Settings args("PlayerArgsEnd");
45 if (!args.parseConfigLines(is)) {
46 throw SerializationError("PlayerArgsEnd of player " + playername + " not found!");
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);
56 sao->setHPRaw(args.getU16("hp"));
57 } catch(SettingNotFoundException &e) {
58 sao->setHPRaw(PLAYER_MAX_HP_DEFAULT);
62 sao->setBasePosition(args.getV3F("position"));
63 } catch (SettingNotFoundException &e) {}
66 sao->setLookPitch(args.getFloat("pitch"));
67 } catch (SettingNotFoundException &e) {}
69 sao->setPlayerYaw(args.getFloat("yaw"));
70 } catch (SettingNotFoundException &e) {}
73 sao->setBreath(args.getU16("breath"), false);
74 } catch (SettingNotFoundException &e) {}
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;
83 Json::Value attr_root;
84 Json::parseFromStream(builder, iss, &attr_root, &errs);
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());
91 sao->getMeta().setModified(false);
92 } catch (SettingNotFoundException &e) {}
96 p->inventory.deSerialize(is);
97 } catch (SerializationError &e) {
98 errorstream << "Failed to deserialize player inventory. player_name="
99 << name << " " << e.what() << std::endl;
102 if (!p->inventory.getList("craftpreview") && p->inventory.getList("craftresult")) {
103 // Convert players without craftpreview
104 p->inventory.addList("craftpreview", 1);
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)
112 p->inventory.getList("craftresult")->changeItem(0, ItemStack());
117 void PlayerDatabaseFiles::serialize(RemotePlayer *p, std::ostream &os)
119 // Utilize a Settings object for storing values
120 Settings args("PlayerArgsEnd");
121 args.setS32("version", 1);
122 args.set("name", p->m_name);
124 // This should not happen
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());
132 std::string extended_attrs;
134 // serializeExtraAttributes
135 PlayerSAO *sao = p->getPlayerSAO();
137 Json::Value json_root;
139 const StringMap &attrs = sao->getMeta().getStrings();
140 for (const auto &attr : attrs) {
141 json_root[attr.first] = attr.second;
144 extended_attrs = fastWriteJson(json_root);
146 args.set("extended_attributes", extended_attrs);
150 p->inventory.serialize(os);
153 void PlayerDatabaseFiles::savePlayer(RemotePlayer *player)
155 fs::CreateDir(m_savedir);
157 std::string savedir = m_savedir + DIR_DELIM;
158 std::string path = savedir + player->getName();
159 bool path_found = false;
160 RemotePlayer testplayer("", NULL);
162 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES && !path_found; i++) {
163 if (!fs::PathExists(path)) {
168 // Open and deserialize file to check player name
169 std::ifstream is(path.c_str(), std::ios_base::binary);
171 errorstream << "Failed to open " << path << std::endl;
175 deSerialize(&testplayer, is, path, NULL);
177 if (strcmp(testplayer.getName(), player->getName()) == 0) {
182 path = savedir + player->getName() + itos(i);
186 errorstream << "Didn't find free file for player " << player->getName()
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;
198 player->onSuccessfulSave();
201 bool PlayerDatabaseFiles::removePlayer(const std::string &name)
203 std::string players_path = m_savedir + DIR_DELIM;
204 std::string path = players_path + name;
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);
213 deSerialize(&temp_player, is, path, NULL);
216 if (temp_player.getName() == name) {
217 fs::DeleteSingleFileOrEmptyDirectory(path);
221 path = players_path + name + itos(i);
227 bool PlayerDatabaseFiles::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
229 std::string players_path = m_savedir + DIR_DELIM;
230 std::string path = players_path + player->getName();
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);
239 deSerialize(player, is, path, sao);
242 if (player->getName() == player_to_load)
245 path = players_path + player_to_load + itos(i);
248 infostream << "Player file for player " << player_to_load << " not found" << std::endl;
252 void PlayerDatabaseFiles::listPlayers(std::vector<std::string> &res)
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 !=
258 // Ignore directories
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);
268 RemotePlayer player(filename.c_str(), NULL);
269 // Null env & dummy peer_id
270 PlayerSAO playerSAO(NULL, &player, 15789, false);
272 deSerialize(&player, is, "", &playerSAO);
275 res.emplace_back(player.getName());
279 AuthDatabaseFiles::AuthDatabaseFiles(const std::string &savedir) : m_savedir(savedir)
284 bool AuthDatabaseFiles::getAuth(const std::string &name, AuthEntry &res)
286 const auto res_i = m_auth_list.find(name);
287 if (res_i == m_auth_list.end()) {
294 bool AuthDatabaseFiles::saveAuth(const AuthEntry &authEntry)
296 m_auth_list[authEntry.name] = authEntry;
299 return writeAuthFile();
302 bool AuthDatabaseFiles::createAuth(AuthEntry &authEntry)
304 m_auth_list[authEntry.name] = authEntry;
307 return writeAuthFile();
310 bool AuthDatabaseFiles::deleteAuth(const std::string &name)
312 if (!m_auth_list.erase(name)) {
313 // did not delete anything -> hadn't existed
316 return writeAuthFile();
319 void AuthDatabaseFiles::listNames(std::vector<std::string> &res)
322 res.reserve(m_auth_list.size());
323 for (const auto &res_pair : m_auth_list) {
324 res.push_back(res_pair.first);
328 void AuthDatabaseFiles::reload()
333 bool AuthDatabaseFiles::readAuthFile()
335 std::string path = m_savedir + DIR_DELIM + "auth.txt";
336 std::ifstream file(path, std::ios::binary);
341 while (file.good()) {
343 std::getline(file, line);
344 std::vector<std::string> parts = str_split(line, ':');
345 if (parts.size() < 3) // also: empty line at end
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;
352 m_auth_list[name] = {
363 bool AuthDatabaseFiles::writeAuthFile()
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;
374 if (!fs::safeWriteToFile(path, output.str())) {
375 infostream << "Failed to write " << path << std::endl;