database-files.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /*
  2. Minetest
  3. Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include <cassert>
  17. #include <json/json.h>
  18. #include "convert_json.h"
  19. #include "database-files.h"
  20. #include "remoteplayer.h"
  21. #include "settings.h"
  22. #include "porting.h"
  23. #include "filesys.h"
  24. #include "server/player_sao.h"
  25. #include "util/string.h"
  26. // !!! WARNING !!!
  27. // This backend is intended to be used on Minetest 0.4.16 only for the transition backend
  28. // for player files
  29. PlayerDatabaseFiles::PlayerDatabaseFiles(const std::string &savedir) : m_savedir(savedir)
  30. {
  31. fs::CreateDir(m_savedir);
  32. }
  33. void PlayerDatabaseFiles::deSerialize(RemotePlayer *p, std::istream &is,
  34. const std::string &playername, PlayerSAO *sao)
  35. {
  36. Settings args("PlayerArgsEnd");
  37. if (!args.parseConfigLines(is)) {
  38. throw SerializationError("PlayerArgsEnd of player " + playername + " not found!");
  39. }
  40. p->m_dirty = true;
  41. //args.getS32("version"); // Version field value not used
  42. const std::string &name = args.get("name");
  43. strlcpy(p->m_name, name.c_str(), PLAYERNAME_SIZE);
  44. if (sao) {
  45. try {
  46. sao->setHPRaw(args.getU16("hp"));
  47. } catch(SettingNotFoundException &e) {
  48. sao->setHPRaw(PLAYER_MAX_HP_DEFAULT);
  49. }
  50. try {
  51. sao->setBasePosition(args.getV3F("position"));
  52. } catch (SettingNotFoundException &e) {}
  53. try {
  54. sao->setLookPitch(args.getFloat("pitch"));
  55. } catch (SettingNotFoundException &e) {}
  56. try {
  57. sao->setPlayerYaw(args.getFloat("yaw"));
  58. } catch (SettingNotFoundException &e) {}
  59. try {
  60. sao->setBreath(args.getU16("breath"), false);
  61. } catch (SettingNotFoundException &e) {}
  62. try {
  63. const std::string &extended_attributes = args.get("extended_attributes");
  64. std::istringstream iss(extended_attributes);
  65. Json::CharReaderBuilder builder;
  66. builder.settings_["collectComments"] = false;
  67. std::string errs;
  68. Json::Value attr_root;
  69. Json::parseFromStream(builder, iss, &attr_root, &errs);
  70. const Json::Value::Members attr_list = attr_root.getMemberNames();
  71. for (const auto &it : attr_list) {
  72. Json::Value attr_value = attr_root[it];
  73. sao->getMeta().setString(it, attr_value.asString());
  74. }
  75. sao->getMeta().setModified(false);
  76. } catch (SettingNotFoundException &e) {}
  77. }
  78. try {
  79. p->inventory.deSerialize(is);
  80. } catch (SerializationError &e) {
  81. errorstream << "Failed to deserialize player inventory. player_name="
  82. << name << " " << e.what() << std::endl;
  83. }
  84. if (!p->inventory.getList("craftpreview") && p->inventory.getList("craftresult")) {
  85. // Convert players without craftpreview
  86. p->inventory.addList("craftpreview", 1);
  87. bool craftresult_is_preview = true;
  88. if(args.exists("craftresult_is_preview"))
  89. craftresult_is_preview = args.getBool("craftresult_is_preview");
  90. if(craftresult_is_preview)
  91. {
  92. // Clear craftresult
  93. p->inventory.getList("craftresult")->changeItem(0, ItemStack());
  94. }
  95. }
  96. }
  97. void PlayerDatabaseFiles::serialize(RemotePlayer *p, std::ostream &os)
  98. {
  99. // Utilize a Settings object for storing values
  100. Settings args("PlayerArgsEnd");
  101. args.setS32("version", 1);
  102. args.set("name", p->m_name);
  103. // This should not happen
  104. PlayerSAO *sao = p->getPlayerSAO();
  105. assert(sao);
  106. args.setU16("hp", sao->getHP());
  107. args.setV3F("position", sao->getBasePosition());
  108. args.setFloat("pitch", sao->getLookPitch());
  109. args.setFloat("yaw", sao->getRotation().Y);
  110. args.setU16("breath", sao->getBreath());
  111. std::string extended_attrs;
  112. {
  113. // serializeExtraAttributes
  114. Json::Value json_root;
  115. const StringMap &attrs = sao->getMeta().getStrings();
  116. for (const auto &attr : attrs) {
  117. json_root[attr.first] = attr.second;
  118. }
  119. extended_attrs = fastWriteJson(json_root);
  120. }
  121. args.set("extended_attributes", extended_attrs);
  122. args.writeLines(os);
  123. p->inventory.serialize(os);
  124. }
  125. void PlayerDatabaseFiles::savePlayer(RemotePlayer *player)
  126. {
  127. fs::CreateDir(m_savedir);
  128. std::string savedir = m_savedir + DIR_DELIM;
  129. std::string path = savedir + player->getName();
  130. bool path_found = false;
  131. RemotePlayer testplayer("", NULL);
  132. for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES && !path_found; i++) {
  133. if (!fs::PathExists(path)) {
  134. path_found = true;
  135. continue;
  136. }
  137. // Open and deserialize file to check player name
  138. std::ifstream is(path.c_str(), std::ios_base::binary);
  139. if (!is.good()) {
  140. errorstream << "Failed to open " << path << std::endl;
  141. return;
  142. }
  143. deSerialize(&testplayer, is, path, NULL);
  144. is.close();
  145. if (strcmp(testplayer.getName(), player->getName()) == 0) {
  146. path_found = true;
  147. continue;
  148. }
  149. path = savedir + player->getName() + itos(i);
  150. }
  151. if (!path_found) {
  152. errorstream << "Didn't find free file for player " << player->getName()
  153. << std::endl;
  154. return;
  155. }
  156. // Open and serialize file
  157. std::ostringstream ss(std::ios_base::binary);
  158. serialize(&testplayer, ss);
  159. if (!fs::safeWriteToFile(path, ss.str())) {
  160. infostream << "Failed to write " << path << std::endl;
  161. }
  162. player->onSuccessfulSave();
  163. }
  164. bool PlayerDatabaseFiles::removePlayer(const std::string &name)
  165. {
  166. std::string players_path = m_savedir + DIR_DELIM;
  167. std::string path = players_path + name;
  168. RemotePlayer temp_player("", NULL);
  169. for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
  170. // Open file and deserialize
  171. std::ifstream is(path.c_str(), std::ios_base::binary);
  172. if (!is.good())
  173. continue;
  174. deSerialize(&temp_player, is, path, NULL);
  175. is.close();
  176. if (temp_player.getName() == name) {
  177. fs::DeleteSingleFileOrEmptyDirectory(path);
  178. return true;
  179. }
  180. path = players_path + name + itos(i);
  181. }
  182. return false;
  183. }
  184. bool PlayerDatabaseFiles::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
  185. {
  186. std::string players_path = m_savedir + DIR_DELIM;
  187. std::string path = players_path + player->getName();
  188. const std::string player_to_load = player->getName();
  189. for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
  190. // Open file and deserialize
  191. std::ifstream is(path.c_str(), std::ios_base::binary);
  192. if (!is.good())
  193. continue;
  194. deSerialize(player, is, path, sao);
  195. is.close();
  196. if (player->getName() == player_to_load)
  197. return true;
  198. path = players_path + player_to_load + itos(i);
  199. }
  200. infostream << "Player file for player " << player_to_load << " not found" << std::endl;
  201. return false;
  202. }
  203. void PlayerDatabaseFiles::listPlayers(std::vector<std::string> &res)
  204. {
  205. std::vector<fs::DirListNode> files = fs::GetDirListing(m_savedir);
  206. // list files into players directory
  207. for (std::vector<fs::DirListNode>::const_iterator it = files.begin(); it !=
  208. files.end(); ++it) {
  209. // Ignore directories
  210. if (it->dir)
  211. continue;
  212. const std::string &filename = it->name;
  213. std::string full_path = m_savedir + DIR_DELIM + filename;
  214. std::ifstream is(full_path.c_str(), std::ios_base::binary);
  215. if (!is.good())
  216. continue;
  217. RemotePlayer player(filename.c_str(), NULL);
  218. // Null env & dummy peer_id
  219. PlayerSAO playerSAO(NULL, &player, 15789, false);
  220. deSerialize(&player, is, "", &playerSAO);
  221. is.close();
  222. res.emplace_back(player.getName());
  223. }
  224. }
  225. AuthDatabaseFiles::AuthDatabaseFiles(const std::string &savedir) : m_savedir(savedir)
  226. {
  227. readAuthFile();
  228. }
  229. bool AuthDatabaseFiles::getAuth(const std::string &name, AuthEntry &res)
  230. {
  231. const auto res_i = m_auth_list.find(name);
  232. if (res_i == m_auth_list.end()) {
  233. return false;
  234. }
  235. res = res_i->second;
  236. return true;
  237. }
  238. bool AuthDatabaseFiles::saveAuth(const AuthEntry &authEntry)
  239. {
  240. m_auth_list[authEntry.name] = authEntry;
  241. // save entire file
  242. return writeAuthFile();
  243. }
  244. bool AuthDatabaseFiles::createAuth(AuthEntry &authEntry)
  245. {
  246. m_auth_list[authEntry.name] = authEntry;
  247. // save entire file
  248. return writeAuthFile();
  249. }
  250. bool AuthDatabaseFiles::deleteAuth(const std::string &name)
  251. {
  252. if (!m_auth_list.erase(name)) {
  253. // did not delete anything -> hadn't existed
  254. return false;
  255. }
  256. return writeAuthFile();
  257. }
  258. void AuthDatabaseFiles::listNames(std::vector<std::string> &res)
  259. {
  260. res.clear();
  261. res.reserve(m_auth_list.size());
  262. for (const auto &res_pair : m_auth_list) {
  263. res.push_back(res_pair.first);
  264. }
  265. }
  266. void AuthDatabaseFiles::reload()
  267. {
  268. readAuthFile();
  269. }
  270. bool AuthDatabaseFiles::readAuthFile()
  271. {
  272. std::string path = m_savedir + DIR_DELIM + "auth.txt";
  273. std::ifstream file(path, std::ios::binary);
  274. if (!file.good()) {
  275. return false;
  276. }
  277. m_auth_list.clear();
  278. while (file.good()) {
  279. std::string line;
  280. std::getline(file, line);
  281. std::vector<std::string> parts = str_split(line, ':');
  282. if (parts.size() < 3) // also: empty line at end
  283. continue;
  284. const std::string &name = parts[0];
  285. const std::string &password = parts[1];
  286. std::vector<std::string> privileges = str_split(parts[2], ',');
  287. s64 last_login = parts.size() > 3 ? atol(parts[3].c_str()) : 0;
  288. m_auth_list[name] = {
  289. 1,
  290. name,
  291. password,
  292. privileges,
  293. last_login,
  294. };
  295. }
  296. return true;
  297. }
  298. bool AuthDatabaseFiles::writeAuthFile()
  299. {
  300. std::string path = m_savedir + DIR_DELIM + "auth.txt";
  301. std::ostringstream output(std::ios_base::binary);
  302. for (const auto &auth_i : m_auth_list) {
  303. const AuthEntry &authEntry = auth_i.second;
  304. output << authEntry.name << ":" << authEntry.password << ":";
  305. output << str_join(authEntry.privileges, ",");
  306. output << ":" << authEntry.last_login;
  307. output << std::endl;
  308. }
  309. if (!fs::safeWriteToFile(path, output.str())) {
  310. infostream << "Failed to write " << path << std::endl;
  311. return false;
  312. }
  313. return true;
  314. }