Advertisement
Guest User

Untitled

a guest
Jun 28th, 2025
7
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 33.57 KB | None | 0 0
  1. /**
  2. * The Forgotten Server - a free and open-source MMORPG server emulator
  3. * Copyright (C) 2019 Mark Samman <[email protected]>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 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 General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU 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 "otpch.h"
  21.  
  22. #include "iologindata.h"
  23. #include "configmanager.h"
  24. #include "game.h"
  25.  
  26. #include <fmt/format.h>
  27.  
  28. extern ConfigManager g_config;
  29. extern Game g_game;
  30.  
  31. Account IOLoginData::loadAccount(uint32_t accno)
  32. {
  33. Account account;
  34.  
  35. DBResult_ptr result = Database::getInstance().storeQuery(fmt::format("SELECT `id`, `name`, `password`, `type`, `premium_ends_at` FROM `accounts` WHERE `id` = {:d}", accno));
  36. if (!result) {
  37. return account;
  38. }
  39.  
  40. account.id = result->getNumber<uint32_t>("id");
  41. account.name = result->getString("name");
  42. account.accountType = static_cast<AccountType_t>(result->getNumber<int32_t>("type"));
  43. account.premiumEndsAt = result->getNumber<time_t>("premium_ends_at");
  44. return account;
  45. }
  46.  
  47. std::string decodeSecret(const std::string& secret)
  48. {
  49. // simple base32 decoding
  50. std::string key;
  51. key.reserve(10);
  52.  
  53. uint32_t buffer = 0, left = 0;
  54. for (const auto& ch : secret) {
  55. buffer <<= 5;
  56. if (ch >= 'A' && ch <= 'Z') {
  57. buffer |= (ch & 0x1F) - 1;
  58. } else if (ch >= '2' && ch <= '7') {
  59. buffer |= ch - 24;
  60. } else {
  61. // if a key is broken, return empty and the comparison
  62. // will always be false since the token must not be empty
  63. return {};
  64. }
  65.  
  66. left += 5;
  67. if (left >= 8) {
  68. left -= 8;
  69. key.push_back(static_cast<char>(buffer >> left));
  70. }
  71. }
  72.  
  73. return key;
  74. }
  75.  
  76. bool IOLoginData::loginserverAuthentication(const std::string& name, const std::string& password, Account& account)
  77. {
  78. Database& db = Database::getInstance();
  79.  
  80. DBResult_ptr result = db.storeQuery(fmt::format("SELECT `id`, `name`, `password`, `secret`, `type`, `premium_ends_at` FROM `accounts` WHERE `name` = {:s}", db.escapeString(name)));
  81. if (!result) {
  82. return false;
  83. }
  84.  
  85. if (transformToSHA1(password) != result->getString("password")) {
  86. return false;
  87. }
  88.  
  89. account.id = result->getNumber<uint32_t>("id");
  90. account.name = result->getString("name");
  91. account.key = decodeSecret(result->getString("secret"));
  92. account.accountType = static_cast<AccountType_t>(result->getNumber<int32_t>("type"));
  93. account.premiumEndsAt = result->getNumber<time_t>("premium_ends_at");
  94.  
  95. result = db.storeQuery(fmt::format("SELECT `name` FROM `players` WHERE `account_id` = {:d} AND `deletion` = 0 ORDER BY `name` ASC", account.id));
  96. if (result) {
  97. do {
  98. account.characters.push_back(result->getString("name"));
  99. } while (result->next());
  100. }
  101. return true;
  102. }
  103.  
  104. uint32_t IOLoginData::gameworldAuthentication(const std::string& accountName, const std::string& password, std::string& characterName)
  105. {
  106. Database& db = Database::getInstance();
  107.  
  108. DBResult_ptr result = db.storeQuery(fmt::format("SELECT `id`, `password` FROM `accounts` WHERE `name` = {:s}", db.escapeString(accountName)));
  109. if (!result) {
  110. return 0;
  111. }
  112.  
  113. if (transformToSHA1(password) != result->getString("password")) {
  114. return 0;
  115. }
  116.  
  117. uint32_t accountId = result->getNumber<uint32_t>("id");
  118.  
  119. result = db.storeQuery(fmt::format("SELECT `name` FROM `players` WHERE `name` = {:s} AND `account_id` = {:d} AND `deletion` = 0", db.escapeString(characterName), accountId));
  120. if (!result) {
  121. return 0;
  122. }
  123.  
  124. characterName = result->getString("name");
  125. return accountId;
  126. }
  127.  
  128. uint32_t IOLoginData::getAccountIdByPlayerName(const std::string& playerName)
  129. {
  130. Database& db = Database::getInstance();
  131.  
  132. DBResult_ptr result = db.storeQuery(fmt::format("SELECT `account_id` FROM `players` WHERE `name` = {:s}", db.escapeString(playerName)));
  133. if (!result) {
  134. return 0;
  135. }
  136. return result->getNumber<uint32_t>("account_id");
  137. }
  138.  
  139. uint32_t IOLoginData::getAccountIdByPlayerId(uint32_t playerId)
  140. {
  141. Database& db = Database::getInstance();
  142.  
  143. DBResult_ptr result = db.storeQuery(fmt::format("SELECT `account_id` FROM `players` WHERE `id` = {:d}", playerId));
  144. if (!result) {
  145. return 0;
  146. }
  147. return result->getNumber<uint32_t>("account_id");
  148. }
  149.  
  150. AccountType_t IOLoginData::getAccountType(uint32_t accountId)
  151. {
  152. DBResult_ptr result = Database::getInstance().storeQuery(fmt::format("SELECT `type` FROM `accounts` WHERE `id` = {:d}", accountId));
  153. if (!result) {
  154. return ACCOUNT_TYPE_NORMAL;
  155. }
  156. return static_cast<AccountType_t>(result->getNumber<uint16_t>("type"));
  157. }
  158.  
  159. void IOLoginData::setAccountType(uint32_t accountId, AccountType_t accountType)
  160. {
  161. Database::getInstance().executeQuery(fmt::format("UPDATE `accounts` SET `type` = {:d} WHERE `id` = {:d}", static_cast<uint16_t>(accountType), accountId));
  162. }
  163.  
  164. void IOLoginData::updateOnlineStatus(uint32_t guid, bool login)
  165. {
  166. if (g_config.getBoolean(ConfigManager::ALLOW_CLONES)) {
  167. return;
  168. }
  169.  
  170. if (login) {
  171. Database::getInstance().executeQuery(fmt::format("INSERT INTO `players_online` VALUES ({:d})", guid));
  172. } else {
  173. Database::getInstance().executeQuery(fmt::format("DELETE FROM `players_online` WHERE `player_id` = {:d}", guid));
  174. }
  175. }
  176.  
  177. bool IOLoginData::preloadPlayer(Player* player, const std::string& name)
  178. {
  179. Database& db = Database::getInstance();
  180.  
  181. DBResult_ptr result = db.storeQuery(fmt::format("SELECT `p`.`id`, `p`.`account_id`, `p`.`group_id`, `a`.`type`, `a`.`premium_ends_at` FROM `players` as `p` JOIN `accounts` as `a` ON `a`.`id` = `p`.`account_id` WHERE `p`.`name` = {:s} AND `p`.`deletion` = 0", db.escapeString(name)));
  182. if (!result) {
  183. return false;
  184. }
  185.  
  186. player->setGUID(result->getNumber<uint32_t>("id"));
  187. Group* group = g_game.groups.getGroup(result->getNumber<uint16_t>("group_id"));
  188. if (!group) {
  189. std::cout << "[Error - IOLoginData::preloadPlayer] " << player->name << " has Group ID " << result->getNumber<uint16_t>("group_id") << " which doesn't exist." << std::endl;
  190. return false;
  191. }
  192. player->setGroup(group);
  193. player->accountNumber = result->getNumber<uint32_t>("account_id");
  194. player->accountType = static_cast<AccountType_t>(result->getNumber<uint16_t>("type"));
  195. player->premiumEndsAt = result->getNumber<time_t>("premium_ends_at");
  196. return true;
  197. }
  198.  
  199. bool IOLoginData::loadPlayerById(Player* player, uint32_t id)
  200. {
  201. Database& db = Database::getInstance();
  202. return loadPlayer(player, db.storeQuery(fmt::format("SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `lookaura`, `lookshader`, `lookwings`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `direction` FROM `players` WHERE `id` = {:d}", id)));
  203. }
  204.  
  205. bool IOLoginData::loadPlayerByName(Player* player, const std::string& name)
  206. {
  207. Database& db = Database::getInstance();
  208. return loadPlayer(player, db.storeQuery(fmt::format("SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `lookaura`, `lookshader`, `lookwings`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `direction` FROM `players` WHERE `name` = {:s}", db.escapeString(name))));
  209. }
  210.  
  211. bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
  212. {
  213. if (!result) {
  214. return false;
  215. }
  216.  
  217. Database& db = Database::getInstance();
  218.  
  219. uint32_t accno = result->getNumber<uint32_t>("account_id");
  220. Account acc = loadAccount(accno);
  221.  
  222. player->setGUID(result->getNumber<uint32_t>("id"));
  223. player->name = result->getString("name");
  224. player->accountNumber = accno;
  225.  
  226. player->accountType = acc.accountType;
  227.  
  228. player->premiumEndsAt = acc.premiumEndsAt;
  229.  
  230. Group* group = g_game.groups.getGroup(result->getNumber<uint16_t>("group_id"));
  231. if (!group) {
  232. std::cout << "[Error - IOLoginData::loadPlayer] " << player->name << " has Group ID " << result->getNumber<uint16_t>("group_id") << " which doesn't exist" << std::endl;
  233. return false;
  234. }
  235. player->setGroup(group);
  236.  
  237. player->bankBalance = result->getNumber<uint64_t>("balance");
  238.  
  239. player->setSex(static_cast<PlayerSex_t>(result->getNumber<uint16_t>("sex")));
  240. player->level = std::max<uint32_t>(1, result->getNumber<uint32_t>("level"));
  241.  
  242. uint64_t experience = result->getNumber<uint64_t>("experience");
  243.  
  244. uint64_t currExpCount = Player::getExpForLevel(player->level);
  245. uint64_t nextExpCount = Player::getExpForLevel(player->level + 1);
  246. if (experience < currExpCount || experience > nextExpCount) {
  247. experience = currExpCount;
  248. }
  249.  
  250. player->experience = experience;
  251.  
  252. if (currExpCount < nextExpCount) {
  253. player->levelPercent = Player::getPercentLevel(player->experience - currExpCount, nextExpCount - currExpCount);
  254. } else {
  255. player->levelPercent = 0;
  256. }
  257.  
  258. player->soul = result->getNumber<uint16_t>("soul");
  259. player->capacity = result->getNumber<uint32_t>("cap") * 100;
  260. player->blessings = result->getNumber<uint16_t>("blessings");
  261.  
  262. unsigned long conditionsSize;
  263. const char* conditions = result->getStream("conditions", conditionsSize);
  264. PropStream propStream;
  265. propStream.init(conditions, conditionsSize);
  266.  
  267. Condition* condition = Condition::createCondition(propStream);
  268. while (condition) {
  269. if (condition->unserialize(propStream)) {
  270. player->storedConditionList.push_front(condition);
  271. } else {
  272. delete condition;
  273. }
  274. condition = Condition::createCondition(propStream);
  275. }
  276.  
  277. if (!player->setVocation(result->getNumber<uint16_t>("vocation"))) {
  278. std::cout << "[Error - IOLoginData::loadPlayer] " << player->name << " has Vocation ID " << result->getNumber<uint16_t>("vocation") << " which doesn't exist" << std::endl;
  279. return false;
  280. }
  281.  
  282. player->mana = result->getNumber<uint32_t>("mana");
  283. player->manaMax = result->getNumber<uint32_t>("manamax");
  284. player->magLevel = result->getNumber<uint32_t>("maglevel");
  285.  
  286. uint64_t nextManaCount = player->vocation->getReqMana(player->magLevel + 1);
  287. uint64_t manaSpent = result->getNumber<uint64_t>("manaspent");
  288. if (manaSpent > nextManaCount) {
  289. manaSpent = 0;
  290. }
  291.  
  292. player->manaSpent = manaSpent;
  293. player->magLevelPercent = Player::getPercentLevel(player->manaSpent, nextManaCount);
  294.  
  295. player->health = result->getNumber<int32_t>("health");
  296. player->healthMax = result->getNumber<int32_t>("healthmax");
  297.  
  298. player->defaultOutfit.lookType = result->getNumber<uint16_t>("looktype");
  299. player->defaultOutfit.lookHead = result->getNumber<uint16_t>("lookhead");
  300. player->defaultOutfit.lookBody = result->getNumber<uint16_t>("lookbody");
  301. player->defaultOutfit.lookLegs = result->getNumber<uint16_t>("looklegs");
  302. player->defaultOutfit.lookFeet = result->getNumber<uint16_t>("lookfeet");
  303. player->defaultOutfit.lookAddons = result->getNumber<uint16_t>("lookaddons");
  304. player->defaultOutfit.lookAura = result->getNumber<uint16_t>("lookaura");
  305. player->defaultOutfit.lookShader = result->getNumber<uint16_t>("lookshader");
  306. player->defaultOutfit.lookWings = result->getNumber<uint16_t>("lookwings");
  307. player->currentOutfit = player->defaultOutfit;
  308. player->direction = static_cast<Direction> (result->getNumber<uint16_t>("direction"));
  309.  
  310. if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
  311. const time_t skullSeconds = result->getNumber<time_t>("skulltime") - time(nullptr);
  312. if (skullSeconds > 0) {
  313. //ensure that we round up the number of ticks
  314. player->skullTicks = (skullSeconds + 2);
  315.  
  316. uint16_t skull = result->getNumber<uint16_t>("skull");
  317. if (skull == SKULL_RED) {
  318. player->skull = SKULL_RED;
  319. } else if (skull == SKULL_BLACK) {
  320. player->skull = SKULL_BLACK;
  321. }
  322. }
  323. }
  324.  
  325. player->loginPosition.x = result->getNumber<uint16_t>("posx");
  326. player->loginPosition.y = result->getNumber<uint16_t>("posy");
  327. player->loginPosition.z = result->getNumber<uint16_t>("posz");
  328.  
  329. player->lastLoginSaved = result->getNumber<time_t>("lastlogin");
  330. player->lastLogout = result->getNumber<time_t>("lastlogout");
  331.  
  332. Town* town = g_game.map.towns.getTown(result->getNumber<uint32_t>("town_id"));
  333. if (!town) {
  334. std::cout << "[Error - IOLoginData::loadPlayer] " << player->name << " has Town ID " << result->getNumber<uint32_t>("town_id") << " which doesn't exist" << std::endl;
  335. return false;
  336. }
  337.  
  338. player->town = town;
  339.  
  340. const Position& loginPos = player->loginPosition;
  341. if (loginPos.x == 0 && loginPos.y == 0 && loginPos.z == 0) {
  342. player->loginPosition = player->getTemplePosition();
  343. }
  344.  
  345. player->staminaMinutes = result->getNumber<uint16_t>("stamina");
  346.  
  347. static const std::string skillNames[] = {"skill_fist", "skill_club", "skill_sword", "skill_axe", "skill_dist", "skill_shielding", "skill_fishing"};
  348. static const std::string skillNameTries[] = {"skill_fist_tries", "skill_club_tries", "skill_sword_tries", "skill_axe_tries", "skill_dist_tries", "skill_shielding_tries", "skill_fishing_tries"};
  349. static constexpr size_t size = sizeof(skillNames) / sizeof(std::string);
  350. for (uint8_t i = 0; i < size; ++i) {
  351. uint16_t skillLevel = result->getNumber<uint16_t>(skillNames[i]);
  352. uint64_t skillTries = result->getNumber<uint64_t>(skillNameTries[i]);
  353. uint64_t nextSkillTries = player->vocation->getReqSkillTries(i, skillLevel + 1);
  354. if (skillTries > nextSkillTries) {
  355. skillTries = 0;
  356. }
  357.  
  358. player->skills[i].level = skillLevel;
  359. player->skills[i].tries = skillTries;
  360. player->skills[i].percent = Player::getPercentLevel(skillTries, nextSkillTries);
  361. }
  362.  
  363. if ((result = db.storeQuery(fmt::format("SELECT `guild_id`, `rank_id`, `nick` FROM `guild_membership` WHERE `player_id` = {:d}", player->getGUID())))) {
  364. uint32_t guildId = result->getNumber<uint32_t>("guild_id");
  365. uint32_t playerRankId = result->getNumber<uint32_t>("rank_id");
  366. player->guildNick = result->getString("nick");
  367.  
  368. Guild* guild = g_game.getGuild(guildId);
  369. if (!guild) {
  370. guild = IOGuild::loadGuild(guildId);
  371. if (guild) {
  372. g_game.addGuild(guild);
  373. } else {
  374. std::cout << "[Warning - IOLoginData::loadPlayer] " << player->name << " has Guild ID " << guildId << " which doesn't exist" << std::endl;
  375. }
  376. }
  377.  
  378. if (guild) {
  379. player->guild = guild;
  380. GuildRank_ptr rank = guild->getRankById(playerRankId);
  381. if (!rank) {
  382. if ((result = db.storeQuery(fmt::format("SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `id` = {:d}", playerRankId)))) {
  383. guild->addRank(result->getNumber<uint32_t>("id"), result->getString("name"), result->getNumber<uint16_t>("level"));
  384. }
  385.  
  386. rank = guild->getRankById(playerRankId);
  387. if (!rank) {
  388. player->guild = nullptr;
  389. }
  390. }
  391.  
  392. player->guildRank = rank;
  393.  
  394. IOGuild::getWarList(guildId, player->guildWarVector);
  395.  
  396. if ((result = db.storeQuery(fmt::format("SELECT COUNT(*) AS `members` FROM `guild_membership` WHERE `guild_id` = {:d}", guildId)))) {
  397. guild->setMemberCount(result->getNumber<uint32_t>("members"));
  398. }
  399. }
  400. }
  401.  
  402. if ((result = db.storeQuery(fmt::format("SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = {:d}", player->getGUID())))) {
  403. do {
  404. player->learnedInstantSpellList.emplace_front(result->getString("name"));
  405. } while (result->next());
  406. }
  407.  
  408. //load inventory items
  409. ItemMap itemMap;
  410.  
  411. if ((result = db.storeQuery(fmt::format("SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_items` WHERE `player_id` = {:d} ORDER BY `sid` DESC", player->getGUID())))) {
  412. loadItems(itemMap, result);
  413.  
  414. for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) {
  415. const std::pair<Item*, int32_t>& pair = it->second;
  416. Item* item = pair.first;
  417. int32_t pid = pair.second;
  418. if (pid >= CONST_SLOT_FIRST && pid <= CONST_SLOT_LAST) {
  419. player->internalAddThing(pid, item);
  420. } else {
  421. ItemMap::const_iterator it2 = itemMap.find(pid);
  422. if (it2 == itemMap.end()) {
  423. continue;
  424. }
  425.  
  426. Container* container = it2->second.first->getContainer();
  427. if (container) {
  428. container->internalAddThing(item);
  429. }
  430. }
  431. }
  432. }
  433.  
  434. //load depot locker items
  435. itemMap.clear();
  436.  
  437. if ((result = db.storeQuery(fmt::format("SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_depotlockeritems` WHERE `player_id` = {:d} ORDER BY `sid` DESC", player->getGUID())))) {
  438. loadItems(itemMap, result);
  439.  
  440. for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) {
  441. const std::pair<Item*, int32_t>& pair = it->second;
  442. Item* item = pair.first;
  443.  
  444. int32_t pid = pair.second;
  445. if (pid >= 0 && pid < 100) {
  446. DepotLocker* depotLocker = player->getDepotLocker(pid);
  447. if (depotLocker) {
  448. depotLocker->internalAddThing(item);
  449. }
  450. } else {
  451. ItemMap::const_iterator it2 = itemMap.find(pid);
  452. if (it2 == itemMap.end()) {
  453. continue;
  454. }
  455.  
  456. Container* container = it2->second.first->getContainer();
  457. if (container) {
  458. container->internalAddThing(item);
  459. }
  460. }
  461. }
  462. }
  463.  
  464. //load depot items
  465. itemMap.clear();
  466.  
  467. if ((result = db.storeQuery(fmt::format("SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_depotitems` WHERE `player_id` = {:d} ORDER BY `sid` DESC", player->getGUID())))) {
  468. loadItems(itemMap, result);
  469.  
  470. for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) {
  471. const std::pair<Item*, int32_t>& pair = it->second;
  472. Item* item = pair.first;
  473.  
  474. int32_t pid = pair.second;
  475. if (pid >= 0 && pid < 100) {
  476. DepotChest* depotChest = player->getDepotChest(pid, true);
  477. if (depotChest) {
  478. depotChest->internalAddThing(item);
  479. }
  480. } else {
  481. ItemMap::const_iterator it2 = itemMap.find(pid);
  482. if (it2 == itemMap.end()) {
  483. continue;
  484. }
  485.  
  486. Container* container = it2->second.first->getContainer();
  487. if (container) {
  488. container->internalAddThing(item);
  489. }
  490. }
  491. }
  492. }
  493.  
  494. //load storage map
  495. if ((result = db.storeQuery(fmt::format("SELECT `key`, `value` FROM `player_storage` WHERE `player_id` = {:d}", player->getGUID())))) {
  496. do {
  497. player->addStorageValue(result->getNumber<uint32_t>("key"), result->getNumber<int32_t>("value"), true);
  498. } while (result->next());
  499. }
  500.  
  501. //load vip list
  502. if ((result = db.storeQuery(fmt::format("SELECT `player_id` FROM `account_viplist` WHERE `account_id` = {:d}", player->getAccount())))) {
  503. do {
  504. player->addVIPInternal(result->getNumber<uint32_t>("player_id"));
  505. } while (result->next());
  506. }
  507.  
  508. player->updateBaseSpeed();
  509. player->updateInventoryWeight();
  510. player->updateItemsLight(true);
  511. return true;
  512. }
  513.  
  514. bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& propWriteStream)
  515. {
  516. using ContainerBlock = std::pair<Container*, int32_t>;
  517. std::vector<ContainerBlock> containers;
  518. containers.reserve(32);
  519.  
  520. int32_t runningId = 100;
  521.  
  522. Database& db = Database::getInstance();
  523. for (const auto& it : itemList) {
  524. int32_t pid = it.first;
  525. Item* item = it.second;
  526. ++runningId;
  527.  
  528. propWriteStream.clear();
  529. item->serializeAttr(propWriteStream);
  530.  
  531. size_t attributesSize;
  532. const char* attributes = propWriteStream.getStream(attributesSize);
  533.  
  534. if (!query_insert.addRow(fmt::format("{:d}, {:d}, {:d}, {:d}, {:d}, {:s}", player->getGUID(), pid, runningId, item->getID(), item->getSubType(), db.escapeBlob(attributes, attributesSize)))) {
  535. return false;
  536. }
  537.  
  538. if (Container* container = item->getContainer()) {
  539. containers.emplace_back(container, runningId);
  540. }
  541. }
  542.  
  543. for (size_t i = 0; i < containers.size(); i++) {
  544. const ContainerBlock& cb = containers[i];
  545. Container* container = cb.first;
  546. int32_t parentId = cb.second;
  547.  
  548. for (Item* item : container->getItemList()) {
  549. ++runningId;
  550.  
  551. Container* subContainer = item->getContainer();
  552. if (subContainer) {
  553. containers.emplace_back(subContainer, runningId);
  554. }
  555.  
  556. propWriteStream.clear();
  557. item->serializeAttr(propWriteStream);
  558.  
  559. size_t attributesSize;
  560. const char* attributes = propWriteStream.getStream(attributesSize);
  561.  
  562. if (!query_insert.addRow(fmt::format("{:d}, {:d}, {:d}, {:d}, {:d}, {:s}", player->getGUID(), parentId, runningId, item->getID(), item->getSubType(), db.escapeBlob(attributes, attributesSize)))) {
  563. return false;
  564. }
  565. }
  566. }
  567.  
  568. return query_insert.execute();
  569. }
  570.  
  571. bool IOLoginData::savePlayer(Player* player)
  572. {
  573. if (player->getHealth() <= 0) {
  574. player->changeHealth(1);
  575. }
  576.  
  577. Database& db = Database::getInstance();
  578.  
  579. DBResult_ptr result = db.storeQuery(fmt::format("SELECT `save` FROM `players` WHERE `id` = {:d}", player->getGUID()));
  580. if (!result) {
  581. return false;
  582. }
  583.  
  584. if (result->getNumber<uint16_t>("save") == 0) {
  585. return db.executeQuery(fmt::format("UPDATE `players` SET `lastlogin` = {:d}, `lastip` = {:d} WHERE `id` = {:d}", player->lastLoginSaved, player->lastIP, player->getGUID()));
  586. }
  587.  
  588. //serialize conditions
  589. PropWriteStream propWriteStream;
  590. for (Condition* condition : player->conditions) {
  591. if (condition->isPersistent()) {
  592. condition->serialize(propWriteStream);
  593. propWriteStream.write<uint8_t>(CONDITIONATTR_END);
  594. }
  595. }
  596.  
  597. size_t conditionsSize;
  598. const char* conditions = propWriteStream.getStream(conditionsSize);
  599.  
  600. //First, an UPDATE query to write the player itself
  601. std::ostringstream query;
  602. query << "UPDATE `players` SET ";
  603. query << "`level` = " << player->level << ',';
  604. query << "`group_id` = " << player->group->id << ',';
  605. query << "`vocation` = " << player->getVocationId() << ',';
  606. query << "`health` = " << player->health << ',';
  607. query << "`healthmax` = " << player->healthMax << ',';
  608. query << "`experience` = " << player->experience << ',';
  609. query << "`lookbody` = " << static_cast<uint32_t>(player->defaultOutfit.lookBody) << ',';
  610. query << "`lookfeet` = " << static_cast<uint32_t>(player->defaultOutfit.lookFeet) << ',';
  611. query << "`lookhead` = " << static_cast<uint32_t>(player->defaultOutfit.lookHead) << ',';
  612. query << "`looklegs` = " << static_cast<uint32_t>(player->defaultOutfit.lookLegs) << ',';
  613. query << "`looktype` = " << player->defaultOutfit.lookType << ',';
  614. query << "`lookaddons` = " << static_cast<uint32_t>(player->defaultOutfit.lookAddons) << ',';
  615. query << "`lookaura` = " << static_cast<uint32_t>(player->defaultOutfit.lookAura) << ',';
  616. query << "`lookshader` = " << static_cast<uint32_t>(player->defaultOutfit.lookShader) << ',';
  617. query << "`lookwings` = " << static_cast<uint32_t>(player->defaultOutfit.lookWings) << ',';
  618. query << "`maglevel` = " << player->magLevel << ',';
  619. query << "`mana` = " << player->mana << ',';
  620. query << "`manamax` = " << player->manaMax << ',';
  621. query << "`manaspent` = " << player->manaSpent << ',';
  622. query << "`soul` = " << static_cast<uint16_t>(player->soul) << ',';
  623. query << "`town_id` = " << player->town->getID() << ',';
  624.  
  625. const Position& loginPosition = player->getLoginPosition();
  626. query << "`posx` = " << loginPosition.getX() << ',';
  627. query << "`posy` = " << loginPosition.getY() << ',';
  628. query << "`posz` = " << loginPosition.getZ() << ',';
  629.  
  630. query << "`cap` = " << (player->capacity / 100) << ',';
  631. query << "`sex` = " << static_cast<uint16_t>(player->sex) << ',';
  632.  
  633. if (player->lastLoginSaved != 0) {
  634. query << "`lastlogin` = " << player->lastLoginSaved << ',';
  635. }
  636.  
  637. if (player->lastIP != 0) {
  638. query << "`lastip` = " << player->lastIP << ',';
  639. }
  640.  
  641. query << "`conditions` = " << db.escapeBlob(conditions, conditionsSize) << ',';
  642.  
  643. if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
  644. int64_t skullTime = 0;
  645.  
  646. if (player->skullTicks > 0) {
  647. skullTime = time(nullptr) + player->skullTicks;
  648. }
  649. query << "`skulltime` = " << skullTime << ',';
  650.  
  651. Skulls_t skull = SKULL_NONE;
  652. if (player->skull == SKULL_RED) {
  653. skull = SKULL_RED;
  654. } else if (player->skull == SKULL_BLACK) {
  655. skull = SKULL_BLACK;
  656. }
  657. query << "`skull` = " << static_cast<int64_t>(skull) << ',';
  658. }
  659.  
  660. query << "`lastlogout` = " << player->getLastLogout() << ',';
  661. query << "`balance` = " << player->bankBalance << ',';
  662. query << "`stamina` = " << player->getStaminaMinutes() << ',';
  663.  
  664. query << "`skill_fist` = " << player->skills[SKILL_FIST].level << ',';
  665. query << "`skill_fist_tries` = " << player->skills[SKILL_FIST].tries << ',';
  666. query << "`skill_club` = " << player->skills[SKILL_CLUB].level << ',';
  667. query << "`skill_club_tries` = " << player->skills[SKILL_CLUB].tries << ',';
  668. query << "`skill_sword` = " << player->skills[SKILL_SWORD].level << ',';
  669. query << "`skill_sword_tries` = " << player->skills[SKILL_SWORD].tries << ',';
  670. query << "`skill_axe` = " << player->skills[SKILL_AXE].level << ',';
  671. query << "`skill_axe_tries` = " << player->skills[SKILL_AXE].tries << ',';
  672. query << "`skill_dist` = " << player->skills[SKILL_DISTANCE].level << ',';
  673. query << "`skill_dist_tries` = " << player->skills[SKILL_DISTANCE].tries << ',';
  674. query << "`skill_shielding` = " << player->skills[SKILL_SHIELD].level << ',';
  675. query << "`skill_shielding_tries` = " << player->skills[SKILL_SHIELD].tries << ',';
  676. query << "`skill_fishing` = " << player->skills[SKILL_FISHING].level << ',';
  677. query << "`skill_fishing_tries` = " << player->skills[SKILL_FISHING].tries << ',';
  678. query << "`direction` = " << static_cast<uint16_t> (player->getDirection()) << ',';
  679.  
  680. if (!player->isOffline()) {
  681. query << "`onlinetime` = `onlinetime` + " << (time(nullptr) - player->lastLoginSaved) << ',';
  682. }
  683. query << "`blessings` = " << player->blessings.to_ulong();
  684. query << " WHERE `id` = " << player->getGUID();
  685.  
  686. DBTransaction transaction;
  687. if (!transaction.begin()) {
  688. return false;
  689. }
  690.  
  691. if (!db.executeQuery(query.str())) {
  692. return false;
  693. }
  694.  
  695. // learned spells
  696. if (!db.executeQuery(fmt::format("DELETE FROM `player_spells` WHERE `player_id` = {:d}", player->getGUID()))) {
  697. return false;
  698. }
  699.  
  700. DBInsert spellsQuery("INSERT INTO `player_spells` (`player_id`, `name` ) VALUES ");
  701. for (const std::string& spellName : player->learnedInstantSpellList) {
  702. if (!spellsQuery.addRow(fmt::format("{:d}, {:s}", player->getGUID(), db.escapeString(spellName)))) {
  703. return false;
  704. }
  705. }
  706.  
  707. if (!spellsQuery.execute()) {
  708. return false;
  709. }
  710.  
  711. //item saving
  712. if (!db.executeQuery(fmt::format("DELETE FROM `player_items` WHERE `player_id` = {:d}", player->getGUID()))) {
  713. return false;
  714. }
  715.  
  716. DBInsert itemsQuery("INSERT INTO `player_items` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES ");
  717.  
  718. ItemBlockList itemList;
  719. for (int32_t slotId = CONST_SLOT_FIRST; slotId <= CONST_SLOT_LAST; ++slotId) {
  720. Item* item = player->inventory[slotId];
  721. if (item) {
  722. itemList.emplace_back(slotId, item);
  723. }
  724. }
  725.  
  726. if (!saveItems(player, itemList, itemsQuery, propWriteStream)) {
  727. return false;
  728. }
  729.  
  730. //save depot locker items
  731. bool needsSave = false;
  732.  
  733. for (const auto& it : player->depotLockerMap) {
  734. if (it.second->needsSave()) {
  735. needsSave = true;
  736. break;
  737. }
  738. }
  739.  
  740. if (needsSave) {
  741. if (!db.executeQuery(fmt::format("DELETE FROM `player_depotlockeritems` WHERE `player_id` = {:d}", player->getGUID()))) {
  742. return false;
  743. }
  744.  
  745. DBInsert lockerQuery("INSERT INTO `player_depotlockeritems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES ");
  746. itemList.clear();
  747.  
  748. for (const auto& it : player->depotLockerMap) {
  749. for (Item* item : it.second->getItemList()) {
  750. if (item->getID() != ITEM_DEPOT) {
  751. itemList.emplace_back(it.first, item);
  752. }
  753. }
  754. }
  755.  
  756. if (!saveItems(player, itemList, lockerQuery, propWriteStream)) {
  757. return false;
  758. }
  759.  
  760. //save depot items
  761. if (needsSave) {
  762. if (!db.executeQuery(fmt::format("DELETE FROM `player_depotitems` WHERE `player_id` = {:d}", player->getGUID()))) {
  763. return false;
  764. }
  765.  
  766. DBInsert depotQuery("INSERT INTO `player_depotitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES ");
  767. itemList.clear();
  768.  
  769. for (const auto& it : player->depotChests) {
  770. for (Item* item : it.second->getItemList()) {
  771. itemList.emplace_back(it.first, item);
  772. }
  773. }
  774.  
  775. if (!saveItems(player, itemList, depotQuery, propWriteStream)) {
  776. return false;
  777. }
  778. }
  779. }
  780.  
  781. if (!db.executeQuery(fmt::format("DELETE FROM `player_storage` WHERE `player_id` = {:d}", player->getGUID()))) {
  782. return false;
  783. }
  784.  
  785. DBInsert storageQuery("INSERT INTO `player_storage` (`player_id`, `key`, `value`) VALUES ");
  786. player->genReservedStorageRange();
  787.  
  788. for (const auto& it : player->storageMap) {
  789. if (!storageQuery.addRow(fmt::format("{:d}, {:d}, {:d}", player->getGUID(), it.first, it.second))) {
  790. return false;
  791. }
  792. }
  793.  
  794. if (!storageQuery.execute()) {
  795. return false;
  796. }
  797.  
  798. //End the transaction
  799. return transaction.commit();
  800. }
  801.  
  802. std::string IOLoginData::getNameByGuid(uint32_t guid)
  803. {
  804. DBResult_ptr result = Database::getInstance().storeQuery(fmt::format("SELECT `name` FROM `players` WHERE `id` = {:d}", guid));
  805. if (!result) {
  806. return std::string();
  807. }
  808. return result->getString("name");
  809. }
  810.  
  811. uint32_t IOLoginData::getGuidByName(const std::string& name)
  812. {
  813. Database& db = Database::getInstance();
  814.  
  815. DBResult_ptr result = db.storeQuery(fmt::format("SELECT `id` FROM `players` WHERE `name` = {:s}", db.escapeString(name)));
  816. if (!result) {
  817. return 0;
  818. }
  819. return result->getNumber<uint32_t>("id");
  820. }
  821.  
  822. bool IOLoginData::getGuidByNameEx(uint32_t& guid, bool& specialVip, std::string& name)
  823. {
  824. Database& db = Database::getInstance();
  825.  
  826. DBResult_ptr result = db.storeQuery(fmt::format("SELECT `name`, `id`, `group_id`, `account_id` FROM `players` WHERE `name` = {:s}", db.escapeString(name)));
  827. if (!result) {
  828. return false;
  829. }
  830.  
  831. name = result->getString("name");
  832. guid = result->getNumber<uint32_t>("id");
  833. Group* group = g_game.groups.getGroup(result->getNumber<uint16_t>("group_id"));
  834.  
  835. uint64_t flags;
  836. if (group) {
  837. flags = group->flags;
  838. } else {
  839. flags = 0;
  840. }
  841.  
  842. specialVip = (flags & PlayerFlag_SpecialVIP) != 0;
  843. return true;
  844. }
  845.  
  846. bool IOLoginData::formatPlayerName(std::string& name)
  847. {
  848. Database& db = Database::getInstance();
  849.  
  850. DBResult_ptr result = db.storeQuery(fmt::format("SELECT `name` FROM `players` WHERE `name` = {:s}", db.escapeString(name)));
  851. if (!result) {
  852. return false;
  853. }
  854.  
  855. name = result->getString("name");
  856. return true;
  857. }
  858.  
  859. void IOLoginData::loadItems(ItemMap& itemMap, DBResult_ptr result)
  860. {
  861. do {
  862. uint32_t sid = result->getNumber<uint32_t>("sid");
  863. uint32_t pid = result->getNumber<uint32_t>("pid");
  864. uint16_t type = result->getNumber<uint16_t>("itemtype");
  865. uint16_t count = result->getNumber<uint16_t>("count");
  866.  
  867. unsigned long attrSize;
  868. const char* attr = result->getStream("attributes", attrSize);
  869.  
  870. PropStream propStream;
  871. propStream.init(attr, attrSize);
  872.  
  873. Item* item = Item::CreateItem(type, count);
  874. if (item) {
  875. if (!item->unserializeAttr(propStream)) {
  876. std::cout << "WARNING: Serialize error in IOLoginData::loadItems" << std::endl;
  877. }
  878.  
  879. std::pair<Item*, uint32_t> pair(item, pid);
  880. itemMap[sid] = pair;
  881. }
  882. } while (result->next());
  883. }
  884.  
  885. void IOLoginData::increaseBankBalance(uint32_t guid, uint64_t bankBalance)
  886. {
  887. Database::getInstance().executeQuery(fmt::format("UPDATE `players` SET `balance` = `balance` + {:d} WHERE `id` = {:d}", bankBalance, guid));
  888. }
  889.  
  890. bool IOLoginData::hasBiddedOnHouse(uint32_t guid)
  891. {
  892. Database& db = Database::getInstance();
  893. return db.storeQuery(fmt::format("SELECT `id` FROM `houses` WHERE `highest_bidder` = {:d} LIMIT 1", guid)).get() != nullptr;
  894. }
  895.  
  896. std::forward_list<VIPEntry> IOLoginData::getVIPEntries(uint32_t accountId)
  897. {
  898. std::forward_list<VIPEntry> entries;
  899.  
  900. DBResult_ptr result = Database::getInstance().storeQuery(fmt::format("SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name` FROM `account_viplist` WHERE `account_id` = {:d}", accountId));
  901. if (result) {
  902. do {
  903. entries.emplace_front(
  904. result->getNumber<uint32_t>("player_id"),
  905. result->getString("name")
  906. );
  907. } while (result->next());
  908. }
  909. return entries;
  910. }
  911.  
  912. void IOLoginData::addVIPEntry(uint32_t accountId, uint32_t guid)
  913. {
  914. Database& db = Database::getInstance();
  915. db.executeQuery(fmt::format("INSERT INTO `account_viplist` (`account_id`, `player_id`) VALUES ({:d}, {:d})", accountId, guid));
  916. }
  917.  
  918. void IOLoginData::removeVIPEntry(uint32_t accountId, uint32_t guid)
  919. {
  920. Database::getInstance().executeQuery(fmt::format("DELETE FROM `account_viplist` WHERE `account_id` = {:d} AND `player_id` = {:d}", accountId, guid));
  921. }
  922.  
  923. void IOLoginData::updatePremiumTime(uint32_t accountId, time_t endTime)
  924. {
  925. Database::getInstance().executeQuery(fmt::format("UPDATE `accounts` SET `premium_ends_at` = {:d} WHERE `id` = {:d}", endTime, accountId));
  926. }
  927.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement