Advertisement
Guest User

Untitled

a guest
Jul 18th, 2018
164
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 39.11 KB | None | 0 0
  1. /**
  2. * The Forgotten Server - a free and open-source MMORPG server emulator
  3. * Copyright (C) 2017 Mark Samman <mark.samman@gmail.com>
  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. extern ConfigManager g_config;
  27. extern Game g_game;
  28.  
  29. Account IOLoginData::loadAccount(uint32_t accno)
  30. {
  31. Account account;
  32.  
  33. std::ostringstream query;
  34. query << "SELECT `id`, `name`, `password`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `id` = " << accno;
  35. DBResult_ptr result = Database::getInstance().storeQuery(query.str());
  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.premiumDays = result->getNumber<uint16_t>("premdays");
  44. account.lastDay = result->getNumber<time_t>("lastday");
  45. return account;
  46. }
  47.  
  48. bool IOLoginData::saveAccount(const Account& acc)
  49. {
  50. std::ostringstream query;
  51. query << "UPDATE `accounts` SET `premdays` = " << acc.premiumDays << ", `lastday` = " << acc.lastDay << " WHERE `id` = " << acc.id;
  52. return Database::getInstance().executeQuery(query.str());
  53. }
  54.  
  55. std::string decodeSecret(const std::string& secret)
  56. {
  57. // simple base32 decoding
  58. std::string key;
  59. key.reserve(10);
  60.  
  61. uint32_t buffer = 0, left = 0;
  62. for (const auto& ch : secret) {
  63. buffer <<= 5;
  64. if (ch >= 'A' && ch <= 'Z') {
  65. buffer |= (ch & 0x1F) - 1;
  66. } else if (ch >= '2' && ch <= '7') {
  67. buffer |= ch - 24;
  68. } else {
  69. // if a key is broken, return empty and the comparison
  70. // will always be false since the token must not be empty
  71. return {};
  72. }
  73.  
  74. left += 5;
  75. if (left >= 8) {
  76. left -= 8;
  77. key.push_back(static_cast<char>(buffer >> left));
  78. }
  79. }
  80.  
  81. return key;
  82. }
  83.  
  84. bool IOLoginData::loginserverAuthentication(const std::string& name, const std::string& password, Account& account)
  85. {
  86. Database& db = Database::getInstance();
  87.  
  88. std::ostringstream query;
  89. query << "SELECT `id`, `name`, `password`, `secret`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `name` = " << db.escapeString(name);
  90. DBResult_ptr result = db.storeQuery(query.str());
  91. if (!result) {
  92. return false;
  93. }
  94.  
  95. if (transformToSHA1(password) != result->getString("password")) {
  96. return false;
  97. }
  98.  
  99. account.id = result->getNumber<uint32_t>("id");
  100. account.name = result->getString("name");
  101. account.accountType = static_cast<AccountType_t>(result->getNumber<int32_t>("type"));
  102. account.premiumDays = result->getNumber<uint16_t>("premdays");
  103. account.lastDay = result->getNumber<time_t>("lastday");
  104.  
  105. query.str(std::string());
  106. query << "SELECT `name`, `deletion` FROM `players` WHERE `account_id` = " << account.id;
  107. result = db.storeQuery(query.str());
  108. if (result) {
  109. do {
  110. if (result->getNumber<uint64_t>("deletion") == 0) {
  111. account.characters.push_back(result->getString("name"));
  112. }
  113. } while (result->next());
  114. std::sort(account.characters.begin(), account.characters.end());
  115. }
  116. return true;
  117. }
  118.  
  119. uint32_t IOLoginData::gameworldAuthentication(const std::string& accountName, const std::string& password, std::string& characterName, std::string& token, uint32_t tokenTime)
  120. {
  121. Database& db = Database::getInstance();
  122.  
  123. std::ostringstream query;
  124. query << "SELECT `id`, `password`, `secret` FROM `accounts` WHERE `name` = " << db.escapeString(accountName);
  125. DBResult_ptr result = db.storeQuery(query.str());
  126. if (!result) {
  127. return 0;
  128. }
  129.  
  130. std::string secret = decodeSecret(result->getString("secret"));
  131. if (!secret.empty()) {
  132. if (token.empty()) {
  133. return 0;
  134. }
  135.  
  136. bool tokenValid = token == generateToken(secret, tokenTime) || token == generateToken(secret, tokenTime - 1) || token == generateToken(secret, tokenTime + 1);
  137. if (!tokenValid) {
  138. return 0;
  139. }
  140. }
  141.  
  142. if (transformToSHA1(password) != result->getString("password")) {
  143. return 0;
  144. }
  145.  
  146. uint32_t accountId = result->getNumber<uint32_t>("id");
  147.  
  148. query.str(std::string());
  149. query << "SELECT `account_id`, `name`, `deletion` FROM `players` WHERE `name` = " << db.escapeString(characterName);
  150. result = db.storeQuery(query.str());
  151. if (!result) {
  152. return 0;
  153. }
  154.  
  155. if (result->getNumber<uint32_t>("account_id") != accountId || result->getNumber<uint64_t>("deletion") != 0) {
  156. return 0;
  157. }
  158. characterName = result->getString("name");
  159. return accountId;
  160. }
  161.  
  162. AccountType_t IOLoginData::getAccountType(uint32_t accountId)
  163. {
  164. std::ostringstream query;
  165. query << "SELECT `type` FROM `accounts` WHERE `id` = " << accountId;
  166. DBResult_ptr result = Database::getInstance().storeQuery(query.str());
  167. if (!result) {
  168. return ACCOUNT_TYPE_NORMAL;
  169. }
  170. return static_cast<AccountType_t>(result->getNumber<uint16_t>("type"));
  171. }
  172.  
  173. void IOLoginData::setAccountType(uint32_t accountId, AccountType_t accountType)
  174. {
  175. std::ostringstream query;
  176. query << "UPDATE `accounts` SET `type` = " << static_cast<uint16_t>(accountType) << " WHERE `id` = " << accountId;
  177. Database::getInstance().executeQuery(query.str());
  178. }
  179.  
  180. void IOLoginData::updateOnlineStatus(uint32_t guid, bool login)
  181. {
  182. if (g_config.getBoolean(ConfigManager::ALLOW_CLONES)) {
  183. return;
  184. }
  185.  
  186. std::ostringstream query;
  187. if (login) {
  188. query << "INSERT INTO `players_online` VALUES (" << guid << ')';
  189. } else {
  190. query << "DELETE FROM `players_online` WHERE `player_id` = " << guid;
  191. }
  192. Database::getInstance().executeQuery(query.str());
  193. }
  194.  
  195. bool IOLoginData::preloadPlayer(Player* player, const std::string& name)
  196. {
  197. Database& db = Database::getInstance();
  198.  
  199. std::ostringstream query;
  200. query << "SELECT `id`, `account_id`, `group_id`, `deletion`, (SELECT `type` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `account_type`";
  201. if (!g_config.getBoolean(ConfigManager::FREE_PREMIUM)) {
  202. query << ", (SELECT `premdays` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `premium_days`";
  203. }
  204. query << " FROM `players` WHERE `name` = " << db.escapeString(name);
  205. DBResult_ptr result = db.storeQuery(query.str());
  206. if (result) {
  207. if (result->getNumber<uint64_t>("deletion") != 0) {
  208. return false;
  209. }
  210.  
  211. player->setGUID(result->getNumber<uint32_t>("id"));
  212. Group* group = g_game.groups.getGroup(result->getNumber<uint16_t>("group_id"));
  213. if (!group) {
  214. std::cout << "[Error - IOLoginData::preloadPlayer] " << player->name << " has Group ID " << result->getNumber<uint16_t>("group_id") << " which doesn't exist." << std::endl;
  215. return false;
  216. }
  217. player->setGroup(group);
  218. player->accountNumber = result->getNumber<uint32_t>("account_id");
  219. player->accountType = static_cast<AccountType_t>(result->getNumber<uint16_t>("account_type"));
  220. if (!g_config.getBoolean(ConfigManager::FREE_PREMIUM)) {
  221. player->premiumDays = result->getNumber<uint16_t>("premium_days");
  222. } else {
  223. player->premiumDays = std::numeric_limits<uint16_t>::max();
  224. }
  225. }
  226.  
  227. query.str(std::string());
  228. query << "SELECT * FROM `account_vip` WHERE `id` = " << result->getNumber<uint32_t>("account_id");
  229. result = db.storeQuery(query.str());
  230. if (result) {
  231. player->vipTime = result->getNumber<uint64_t>("time");
  232. }
  233. return true;
  234. }
  235.  
  236. bool IOLoginData::loadPlayerById(Player* player, uint32_t id)
  237. {
  238. Database& db = Database::getInstance();
  239. std::ostringstream query;
  240. query << "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`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `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` FROM `players` WHERE `id` = " << id;
  241. return loadPlayer(player, db.storeQuery(query.str()));
  242. }
  243.  
  244. bool IOLoginData::loadPlayerByName(Player* player, const std::string& name)
  245. {
  246. Database& db = Database::getInstance();
  247. std::ostringstream query;
  248. query << "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`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `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` FROM `players` WHERE `name` = " << db.escapeString(name);
  249. return loadPlayer(player, db.storeQuery(query.str()));
  250. }
  251.  
  252. bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
  253. {
  254. if (!result) {
  255. return false;
  256. }
  257.  
  258. Database& db = Database::getInstance();
  259.  
  260. uint32_t accno = result->getNumber<uint32_t>("account_id");
  261. Account acc = loadAccount(accno);
  262.  
  263. player->setGUID(result->getNumber<uint32_t>("id"));
  264. player->name = result->getString("name");
  265. player->accountNumber = accno;
  266.  
  267. player->accountType = acc.accountType;
  268.  
  269. if (g_config.getBoolean(ConfigManager::FREE_PREMIUM)) {
  270. player->premiumDays = std::numeric_limits<uint16_t>::max();
  271. } else {
  272. player->premiumDays = acc.premiumDays;
  273. }
  274.  
  275. Group* group = g_game.groups.getGroup(result->getNumber<uint16_t>("group_id"));
  276. if (!group) {
  277. std::cout << "[Error - IOLoginData::loadPlayer] " << player->name << " has Group ID " << result->getNumber<uint16_t>("group_id") << " which doesn't exist" << std::endl;
  278. return false;
  279. }
  280. player->setGroup(group);
  281.  
  282. player->bankBalance = result->getNumber<uint64_t>("balance");
  283.  
  284. player->setSex(static_cast<PlayerSex_t>(result->getNumber<uint16_t>("sex")));
  285. player->level = std::max<uint32_t>(1, result->getNumber<uint32_t>("level"));
  286.  
  287. uint64_t experience = result->getNumber<uint64_t>("experience");
  288.  
  289. uint64_t currExpCount = Player::getExpForLevel(player->level);
  290. uint64_t nextExpCount = Player::getExpForLevel(player->level + 1);
  291. if (experience < currExpCount || experience > nextExpCount) {
  292. experience = currExpCount;
  293. }
  294.  
  295. player->experience = experience;
  296.  
  297. if (currExpCount < nextExpCount) {
  298. player->levelPercent = Player::getPercentLevel(player->experience - currExpCount, nextExpCount - currExpCount);
  299. } else {
  300. player->levelPercent = 0;
  301. }
  302.  
  303. player->soul = result->getNumber<uint16_t>("soul");
  304. player->capacity = result->getNumber<uint32_t>("cap") * 100;
  305. player->blessings = result->getNumber<uint16_t>("blessings");
  306.  
  307. unsigned long conditionsSize;
  308. const char* conditions = result->getStream("conditions", conditionsSize);
  309. PropStream propStream;
  310. propStream.init(conditions, conditionsSize);
  311.  
  312. Condition* condition = Condition::createCondition(propStream);
  313. while (condition) {
  314. if (condition->unserialize(propStream)) {
  315. player->storedConditionList.push_front(condition);
  316. } else {
  317. delete condition;
  318. }
  319. condition = Condition::createCondition(propStream);
  320. }
  321.  
  322. if (!player->setVocation(result->getNumber<uint16_t>("vocation"))) {
  323. std::cout << "[Error - IOLoginData::loadPlayer] " << player->name << " has Vocation ID " << result->getNumber<uint16_t>("vocation") << " which doesn't exist" << std::endl;
  324. return false;
  325. }
  326.  
  327. player->mana = result->getNumber<uint32_t>("mana");
  328. player->manaMax = result->getNumber<uint32_t>("manamax");
  329. player->magLevel = result->getNumber<uint32_t>("maglevel");
  330.  
  331. uint64_t nextManaCount = player->vocation->getReqMana(player->magLevel + 1);
  332. uint64_t manaSpent = result->getNumber<uint64_t>("manaspent");
  333. if (manaSpent > nextManaCount) {
  334. manaSpent = 0;
  335. }
  336.  
  337. player->manaSpent = manaSpent;
  338. player->magLevelPercent = Player::getPercentLevel(player->manaSpent, nextManaCount);
  339.  
  340. player->health = result->getNumber<int32_t>("health");
  341. player->healthMax = result->getNumber<int32_t>("healthmax");
  342.  
  343. player->defaultOutfit.lookType = result->getNumber<uint16_t>("looktype");
  344. player->defaultOutfit.lookHead = result->getNumber<uint16_t>("lookhead");
  345. player->defaultOutfit.lookBody = result->getNumber<uint16_t>("lookbody");
  346. player->defaultOutfit.lookLegs = result->getNumber<uint16_t>("looklegs");
  347. player->defaultOutfit.lookFeet = result->getNumber<uint16_t>("lookfeet");
  348. player->defaultOutfit.lookAddons = result->getNumber<uint16_t>("lookaddons");
  349. player->currentOutfit = player->defaultOutfit;
  350.  
  351. if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
  352. const time_t skullSeconds = result->getNumber<time_t>("skulltime") - time(nullptr);
  353. if (skullSeconds > 0) {
  354. //ensure that we round up the number of ticks
  355. player->skullTicks = (skullSeconds + 2) * 1000;
  356.  
  357. uint16_t skull = result->getNumber<uint16_t>("skull");
  358. if (skull == SKULL_RED) {
  359. player->skull = SKULL_RED;
  360. } else if (skull == SKULL_BLACK) {
  361. player->skull = SKULL_BLACK;
  362. }
  363. }
  364. }
  365.  
  366. player->loginPosition.x = result->getNumber<uint16_t>("posx");
  367. player->loginPosition.y = result->getNumber<uint16_t>("posy");
  368. player->loginPosition.z = result->getNumber<uint16_t>("posz");
  369.  
  370. player->lastLoginSaved = result->getNumber<time_t>("lastlogin");
  371. player->lastLogout = result->getNumber<time_t>("lastlogout");
  372.  
  373. player->offlineTrainingTime = result->getNumber<int32_t>("offlinetraining_time") * 1000;
  374. player->offlineTrainingSkill = result->getNumber<int32_t>("offlinetraining_skill");
  375.  
  376. Town* town = g_game.map.towns.getTown(result->getNumber<uint32_t>("town_id"));
  377. if (!town) {
  378. std::cout << "[Error - IOLoginData::loadPlayer] " << player->name << " has Town ID " << result->getNumber<uint32_t>("town_id") << " which doesn't exist" << std::endl;
  379. return false;
  380. }
  381.  
  382. player->town = town;
  383.  
  384. const Position& loginPos = player->loginPosition;
  385. if (loginPos.x == 0 && loginPos.y == 0 && loginPos.z == 0) {
  386. player->loginPosition = player->getTemplePosition();
  387. }
  388.  
  389. player->staminaMinutes = result->getNumber<uint16_t>("stamina");
  390.  
  391. static const std::string skillNames[] = {"skill_fist", "skill_club", "skill_sword", "skill_axe", "skill_dist", "skill_shielding", "skill_fishing"};
  392. 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"};
  393. static constexpr size_t size = sizeof(skillNames) / sizeof(std::string);
  394. for (uint8_t i = 0; i < size; ++i) {
  395. uint16_t skillLevel = result->getNumber<uint16_t>(skillNames[i]);
  396. uint64_t skillTries = result->getNumber<uint64_t>(skillNameTries[i]);
  397. uint64_t nextSkillTries = player->vocation->getReqSkillTries(i, skillLevel + 1);
  398. if (skillTries > nextSkillTries) {
  399. skillTries = 0;
  400. }
  401.  
  402. player->skills[i].level = skillLevel;
  403. player->skills[i].tries = skillTries;
  404. player->skills[i].percent = Player::getPercentLevel(skillTries, nextSkillTries);
  405. }
  406.  
  407. std::ostringstream query;
  408. query << "SELECT `guild_id`, `rank_id`, `nick` FROM `guild_membership` WHERE `player_id` = " << player->getGUID();
  409. if ((result = db.storeQuery(query.str()))) {
  410. uint32_t guildId = result->getNumber<uint32_t>("guild_id");
  411. uint32_t playerRankId = result->getNumber<uint32_t>("rank_id");
  412. player->guildNick = result->getString("nick");
  413.  
  414. Guild* guild = g_game.getGuild(guildId);
  415. if (!guild) {
  416. guild = IOGuild::loadGuild(guildId);
  417. g_game.addGuild(guild);
  418. }
  419.  
  420. if (guild) {
  421. player->guild = guild;
  422. const GuildRank* rank = guild->getRankById(playerRankId);
  423. if (!rank) {
  424. query.str(std::string());
  425. query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `id` = " << playerRankId;
  426.  
  427. if ((result = db.storeQuery(query.str()))) {
  428. guild->addRank(result->getNumber<uint32_t>("id"), result->getString("name"), result->getNumber<uint16_t>("level"));
  429. }
  430.  
  431. rank = guild->getRankById(playerRankId);
  432. if (!rank) {
  433. player->guild = nullptr;
  434. }
  435. }
  436.  
  437. player->guildRank = rank;
  438.  
  439. IOGuild::getWarList(guildId, player->guildWarVector);
  440.  
  441. query.str(std::string());
  442. query << "SELECT COUNT(*) AS `members` FROM `guild_membership` WHERE `guild_id` = " << guildId;
  443. if ((result = db.storeQuery(query.str()))) {
  444. guild->setMemberCount(result->getNumber<uint32_t>("members"));
  445. }
  446. }
  447. }
  448.  
  449. query.str(std::string());
  450. query << "SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = " << player->getGUID();
  451. if ((result = db.storeQuery(query.str()))) {
  452. do {
  453. player->learnedInstantSpellList.emplace_front(result->getString("name"));
  454. } while (result->next());
  455. }
  456.  
  457. //load inventory items
  458. ItemMap itemMap;
  459.  
  460. query.str(std::string());
  461. query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_items` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC";
  462. if ((result = db.storeQuery(query.str()))) {
  463. loadItems(itemMap, result);
  464.  
  465. for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) {
  466. const std::pair<Item*, int32_t>& pair = it->second;
  467. Item* item = pair.first;
  468. int32_t pid = pair.second;
  469. if (pid >= 1 && pid <= 11) {
  470. player->internalAddThing(pid, item);
  471. } else {
  472. ItemMap::const_iterator it2 = itemMap.find(pid);
  473. if (it2 == itemMap.end()) {
  474. continue;
  475. }
  476.  
  477. Container* container = it2->second.first->getContainer();
  478. if (container) {
  479. container->internalAddThing(item);
  480. }
  481. }
  482. }
  483. }
  484.  
  485. //store inbox
  486. if (!player->inventory[CONST_SLOT_STORE_INBOX]) {
  487. player->internalAddThing(CONST_SLOT_STORE_INBOX, Item::CreateItem(ITEM_STORE_INBOX));
  488. }
  489.  
  490. //load depot items
  491. itemMap.clear();
  492.  
  493. query.str(std::string());
  494. query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_depotitems` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC";
  495. if ((result = db.storeQuery(query.str()))) {
  496. loadItems(itemMap, result);
  497.  
  498. for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) {
  499. const std::pair<Item*, int32_t>& pair = it->second;
  500. Item* item = pair.first;
  501.  
  502. int32_t pid = pair.second;
  503. if (pid >= 0 && pid < 100) {
  504. DepotChest* depotChest = player->getDepotChest(pid, true);
  505. if (depotChest) {
  506. depotChest->internalAddThing(item);
  507. }
  508. } else {
  509. ItemMap::const_iterator it2 = itemMap.find(pid);
  510. if (it2 == itemMap.end()) {
  511. continue;
  512. }
  513.  
  514. Container* container = it2->second.first->getContainer();
  515. if (container) {
  516. container->internalAddThing(item);
  517. }
  518. }
  519. }
  520. }
  521.  
  522. //load inbox items
  523. itemMap.clear();
  524.  
  525. query.str(std::string());
  526. query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC";
  527. if ((result = db.storeQuery(query.str()))) {
  528. loadItems(itemMap, result);
  529.  
  530. for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) {
  531. const std::pair<Item*, int32_t>& pair = it->second;
  532. Item* item = pair.first;
  533. int32_t pid = pair.second;
  534.  
  535. if (pid >= 0 && pid < 100) {
  536. player->getInbox()->internalAddThing(item);
  537. } else {
  538. ItemMap::const_iterator it2 = itemMap.find(pid);
  539.  
  540. if (it2 == itemMap.end()) {
  541. continue;
  542. }
  543.  
  544. Container* container = it2->second.first->getContainer();
  545. if (container) {
  546. container->internalAddThing(item);
  547. }
  548. }
  549. }
  550. }
  551.  
  552. //load storage map
  553. query.str(std::string());
  554. query << "SELECT `key`, `value` FROM `player_storage` WHERE `player_id` = " << player->getGUID();
  555. if ((result = db.storeQuery(query.str()))) {
  556. do {
  557. player->addStorageValue(result->getNumber<uint32_t>("key"), result->getNumber<int32_t>("value"), true);
  558. } while (result->next());
  559. }
  560.  
  561. //load vip
  562. query.str(std::string());
  563. query << "SELECT `player_id` FROM `account_viplist` WHERE `account_id` = " << player->getAccount();
  564. if ((result = db.storeQuery(query.str()))) {
  565. do {
  566. player->addVIPInternal(result->getNumber<uint32_t>("player_id"));
  567. } while (result->next());
  568. }
  569.  
  570. //bestiary
  571. if (!player->hasFlag(PlayerFlag_CannotBeAttacked))
  572. {
  573. player->updateAllBestiary(true);
  574.  
  575. query.str(std::string());
  576. query << "SELECT `player_id`, `name`, `kills`, `items` FROM `player_bestiary` WHERE `player_id` = " << player->getGUID();
  577. if ((result = db.storeQuery(query.str()))) {
  578. do {
  579. player->addBestiaryPosting(result->getString("name"), result->getNumber<uint32_t>("kills"), result->getString("items"));
  580. } while (result->next());
  581. }
  582. }
  583.  
  584. //load auto loot items
  585. query.str(std::string());
  586. query << "SELECT * FROM `player_autolootlist` WHERE `player_id` = " << player->getGUID();
  587. if ((result = db.storeQuery(query.str()))) {
  588. unsigned long autoLootListSize;
  589. const char* autoLootList = result->getStream("items", autoLootListSize);
  590. propStream.init(autoLootList, autoLootListSize);
  591. std::string item;
  592. while (propStream.readString(item) && item != "0") {
  593. player->autoLootList.insert(item);
  594. }
  595. }
  596.  
  597. player->updateBaseSpeed();
  598. player->updateInventoryWeight();
  599. player->updateItemsLight(true);
  600. return true;
  601. }
  602.  
  603. bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& propWriteStream)
  604. {
  605. std::ostringstream ss;
  606.  
  607. using ContainerBlock = std::pair<Container*, int32_t>;
  608. std::list<ContainerBlock> queue;
  609.  
  610. int32_t runningId = 100;
  611.  
  612. Database& db = Database::getInstance();
  613. for (const auto& it : itemList) {
  614. int32_t pid = it.first;
  615. Item* item = it.second;
  616. ++runningId;
  617.  
  618. propWriteStream.clear();
  619. item->serializeAttr(propWriteStream);
  620.  
  621. size_t attributesSize;
  622. const char* attributes = propWriteStream.getStream(attributesSize);
  623.  
  624. ss << player->getGUID() << ',' << pid << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db.escapeBlob(attributes, attributesSize);
  625. if (!query_insert.addRow(ss)) {
  626. return false;
  627. }
  628.  
  629. if (Container* container = item->getContainer()) {
  630. queue.emplace_back(container, runningId);
  631. }
  632. }
  633.  
  634. while (!queue.empty()) {
  635. const ContainerBlock& cb = queue.front();
  636. Container* container = cb.first;
  637. int32_t parentId = cb.second;
  638. queue.pop_front();
  639.  
  640. for (Item* item : container->getItemList()) {
  641. ++runningId;
  642.  
  643. Container* subContainer = item->getContainer();
  644. if (subContainer) {
  645. queue.emplace_back(subContainer, runningId);
  646. }
  647.  
  648. propWriteStream.clear();
  649. item->serializeAttr(propWriteStream);
  650.  
  651. size_t attributesSize;
  652. const char* attributes = propWriteStream.getStream(attributesSize);
  653.  
  654. ss << player->getGUID() << ',' << parentId << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db.escapeBlob(attributes, attributesSize);
  655. if (!query_insert.addRow(ss)) {
  656. return false;
  657. }
  658. }
  659. }
  660. return query_insert.execute();
  661. }
  662.  
  663. bool IOLoginData::savePlayer(Player* player)
  664. {
  665. if (player->getHealth() <= 0) {
  666. player->changeHealth(1);
  667. }
  668.  
  669. Database& db = Database::getInstance();
  670.  
  671. std::ostringstream query;
  672. query << "SELECT `save` FROM `players` WHERE `id` = " << player->getGUID();
  673. DBResult_ptr result = db.storeQuery(query.str());
  674. if (!result) {
  675. return false;
  676. }
  677.  
  678. if (result->getNumber<uint16_t>("save") == 0) {
  679. query.str(std::string());
  680. query << "UPDATE `players` SET `lastlogin` = " << player->lastLoginSaved << ", `lastip` = " << player->lastIP << " WHERE `id` = " << player->getGUID();
  681. return db.executeQuery(query.str());
  682. }
  683.  
  684. //serialize conditions
  685. PropWriteStream propWriteStream;
  686. for (Condition* condition : player->conditions) {
  687. if (condition->isPersistent()) {
  688. condition->serialize(propWriteStream);
  689. propWriteStream.write<uint8_t>(CONDITIONATTR_END);
  690. }
  691. }
  692.  
  693. size_t conditionsSize;
  694. const char* conditions = propWriteStream.getStream(conditionsSize);
  695.  
  696. //First, an UPDATE query to write the player itself
  697. query.str(std::string());
  698. query << "UPDATE `players` SET ";
  699. query << "`level` = " << player->level << ',';
  700. query << "`group_id` = " << player->group->id << ',';
  701. query << "`vocation` = " << player->getVocationId() << ',';
  702. query << "`health` = " << player->health << ',';
  703. query << "`healthmax` = " << player->healthMax << ',';
  704. query << "`experience` = " << player->experience << ',';
  705. query << "`lookbody` = " << static_cast<uint32_t>(player->defaultOutfit.lookBody) << ',';
  706. query << "`lookfeet` = " << static_cast<uint32_t>(player->defaultOutfit.lookFeet) << ',';
  707. query << "`lookhead` = " << static_cast<uint32_t>(player->defaultOutfit.lookHead) << ',';
  708. query << "`looklegs` = " << static_cast<uint32_t>(player->defaultOutfit.lookLegs) << ',';
  709. query << "`looktype` = " << player->defaultOutfit.lookType << ',';
  710. query << "`lookaddons` = " << static_cast<uint32_t>(player->defaultOutfit.lookAddons) << ',';
  711. query << "`maglevel` = " << player->magLevel << ',';
  712. query << "`mana` = " << player->mana << ',';
  713. query << "`manamax` = " << player->manaMax << ',';
  714. query << "`manaspent` = " << player->manaSpent << ',';
  715. query << "`soul` = " << static_cast<uint16_t>(player->soul) << ',';
  716. query << "`town_id` = " << player->town->getID() << ',';
  717.  
  718. const Position& loginPosition = player->getLoginPosition();
  719. query << "`posx` = " << loginPosition.getX() << ',';
  720. query << "`posy` = " << loginPosition.getY() << ',';
  721. query << "`posz` = " << loginPosition.getZ() << ',';
  722.  
  723. query << "`cap` = " << (player->capacity / 100) << ',';
  724. query << "`sex` = " << player->sex << ',';
  725.  
  726. if (player->lastLoginSaved != 0) {
  727. query << "`lastlogin` = " << player->lastLoginSaved << ',';
  728. }
  729.  
  730. if (player->lastIP != 0) {
  731. query << "`lastip` = " << player->lastIP << ',';
  732. }
  733.  
  734. query << "`conditions` = " << db.escapeBlob(conditions, conditionsSize) << ',';
  735.  
  736. if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
  737. int32_t skullTime = 0;
  738.  
  739. if (player->skullTicks > 0) {
  740. skullTime = time(nullptr) + player->skullTicks / 1000;
  741. }
  742.  
  743. query << "`skulltime` = " << skullTime << ',';
  744.  
  745. Skulls_t skull = SKULL_NONE;
  746. if (player->skull == SKULL_RED) {
  747. skull = SKULL_RED;
  748. } else if (player->skull == SKULL_BLACK) {
  749. skull = SKULL_BLACK;
  750. }
  751. query << "`skull` = " << static_cast<uint32_t>(skull) << ',';
  752. }
  753.  
  754. query << "`lastlogout` = " << player->getLastLogout() << ',';
  755. query << "`balance` = " << player->bankBalance << ',';
  756. query << "`offlinetraining_time` = " << player->getOfflineTrainingTime() / 1000 << ',';
  757. query << "`offlinetraining_skill` = " << player->getOfflineTrainingSkill() << ',';
  758. query << "`stamina` = " << player->getStaminaMinutes() << ',';
  759.  
  760. query << "`skill_fist` = " << player->skills[SKILL_FIST].level << ',';
  761. query << "`skill_fist_tries` = " << player->skills[SKILL_FIST].tries << ',';
  762. query << "`skill_club` = " << player->skills[SKILL_CLUB].level << ',';
  763. query << "`skill_club_tries` = " << player->skills[SKILL_CLUB].tries << ',';
  764. query << "`skill_sword` = " << player->skills[SKILL_SWORD].level << ',';
  765. query << "`skill_sword_tries` = " << player->skills[SKILL_SWORD].tries << ',';
  766. query << "`skill_axe` = " << player->skills[SKILL_AXE].level << ',';
  767. query << "`skill_axe_tries` = " << player->skills[SKILL_AXE].tries << ',';
  768. query << "`skill_dist` = " << player->skills[SKILL_DISTANCE].level << ',';
  769. query << "`skill_dist_tries` = " << player->skills[SKILL_DISTANCE].tries << ',';
  770. query << "`skill_shielding` = " << player->skills[SKILL_SHIELD].level << ',';
  771. query << "`skill_shielding_tries` = " << player->skills[SKILL_SHIELD].tries << ',';
  772. query << "`skill_fishing` = " << player->skills[SKILL_FISHING].level << ',';
  773. query << "`skill_fishing_tries` = " << player->skills[SKILL_FISHING].tries << ',';
  774.  
  775. if (!player->isOffline()) {
  776. query << "`onlinetime` = `onlinetime` + " << (time(nullptr) - player->lastLoginSaved) << ',';
  777. }
  778. query << "`blessings` = " << static_cast<uint32_t>(player->blessings);
  779. query << " WHERE `id` = " << player->getGUID();
  780.  
  781. DBTransaction transaction;
  782. if (!transaction.begin()) {
  783. return false;
  784. }
  785.  
  786. if (!db.executeQuery(query.str())) {
  787. return false;
  788. }
  789.  
  790. // learned spells
  791. query.str(std::string());
  792. query << "DELETE FROM `player_spells` WHERE `player_id` = " << player->getGUID();
  793. if (!db.executeQuery(query.str())) {
  794. return false;
  795. }
  796.  
  797. query.str(std::string());
  798.  
  799. DBInsert spellsQuery("INSERT INTO `player_spells` (`player_id`, `name` ) VALUES ");
  800. for (const std::string& spellName : player->learnedInstantSpellList) {
  801. query << player->getGUID() << ',' << db.escapeString(spellName);
  802. if (!spellsQuery.addRow(query)) {
  803. return false;
  804. }
  805. }
  806.  
  807. if (!spellsQuery.execute()) {
  808. return false;
  809. }
  810.  
  811. //item saving
  812. query << "DELETE FROM `player_items` WHERE `player_id` = " << player->getGUID();
  813. if (!db.executeQuery(query.str())) {
  814. return false;
  815. }
  816.  
  817. DBInsert itemsQuery("INSERT INTO `player_items` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES ");
  818.  
  819. ItemBlockList itemList;
  820. for (int32_t slotId = 1; slotId <= 11; ++slotId) {
  821. Item* item = player->inventory[slotId];
  822. if (item) {
  823. itemList.emplace_back(slotId, item);
  824. }
  825. }
  826.  
  827. if (!saveItems(player, itemList, itemsQuery, propWriteStream)) {
  828. return false;
  829. }
  830.  
  831. if (player->lastDepotId != -1) {
  832. //save depot items
  833. query.str(std::string());
  834. query << "DELETE FROM `player_depotitems` WHERE `player_id` = " << player->getGUID();
  835.  
  836. if (!db.executeQuery(query.str())) {
  837. return false;
  838. }
  839.  
  840. DBInsert depotQuery("INSERT INTO `player_depotitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES ");
  841. itemList.clear();
  842.  
  843. for (const auto& it : player->depotChests) {
  844. DepotChest* depotChest = it.second;
  845. for (Item* item : depotChest->getItemList()) {
  846. itemList.emplace_back(it.first, item);
  847. }
  848. }
  849.  
  850. if (!saveItems(player, itemList, depotQuery, propWriteStream)) {
  851. return false;
  852. }
  853. }
  854.  
  855. //save auto loot items
  856. propWriteStream.clear();
  857. for (const auto& it : player->autoLootList) {
  858. propWriteStream.writeString(it);
  859. }
  860. propWriteStream.writeString("0");
  861.  
  862. size_t autoLootItemsSize;
  863. const char* autoLootItems = propWriteStream.getStream(autoLootItemsSize);
  864.  
  865. query.str(std::string());
  866. query << "DELETE FROM `player_autolootlist` WHERE `player_id` = " << player->getGUID();
  867. if (!db.executeQuery(query.str())) {
  868. return false;
  869. }
  870.  
  871. query.str(std::string());
  872. query << "INSERT INTO `player_autolootlist` (`player_id`, `items`) VALUES (" << player->getGUID() << ", " << db.escapeBlob(autoLootItems, autoLootItemsSize) << ')';
  873. if (!db.executeQuery(query.str())) {
  874. return false;
  875. }
  876.  
  877. //save inbox items
  878. query.str(std::string());
  879. query << "DELETE FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID();
  880. if (!db.executeQuery(query.str())) {
  881. return false;
  882. }
  883.  
  884. DBInsert inboxQuery("INSERT INTO `player_inboxitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES ");
  885. itemList.clear();
  886.  
  887. for (Item* item : player->getInbox()->getItemList()) {
  888. itemList.emplace_back(0, item);
  889. }
  890.  
  891. if (!saveItems(player, itemList, inboxQuery, propWriteStream)) {
  892. return false;
  893. }
  894.  
  895. //bestiary
  896. if (!player->hasFlag(PlayerFlag_CannotBeAttacked))
  897. {
  898. query.str(std::string());
  899. query << "DELETE FROM `player_bestiary` WHERE `player_id` = " << player->getGUID();
  900. if (!db.executeQuery(query.str())) {
  901. return false;
  902. }
  903.  
  904. BestiaryMap bestiaryMap = player->getBestiary();
  905. if (!bestiaryMap.empty())
  906. {
  907. query.str(std::string());
  908.  
  909. DBInsert bestiaryQuery("INSERT INTO `player_bestiary` (`player_id`, `name`, `kills`, `items`) VALUES ");
  910. for (auto it = bestiaryMap.begin(); it != bestiaryMap.end(); it++)
  911. {
  912. if (it->second.kills == 0)
  913. continue;
  914.  
  915. query << player->getGUID() << "," << db.escapeString(it->first).c_str() << "," << it->second.kills << ",";
  916. if (!it->second.lootItemList.empty())
  917. {
  918. std::stringstream s;
  919. for (LootItemList::const_iterator _it = it->second.lootItemList.begin(); _it != it->second.lootItemList.end(); ++_it)
  920. {
  921. if (_it->second != 0)
  922. s << (s.str().empty() ? "" : ";") << _it->first << "-" << _it->second;
  923. }
  924.  
  925. query << db.escapeString(s.str()).c_str();
  926. }
  927. else
  928. query << "''";
  929.  
  930. if (!bestiaryQuery.addRow(query)) {
  931. return false;
  932. }
  933. }
  934.  
  935. if (!bestiaryQuery.execute()) {
  936. return false;
  937. }
  938. }
  939. }
  940.  
  941. query.str(std::string());
  942. query << "DELETE FROM `player_storage` WHERE `player_id` = " << player->getGUID();
  943. if (!db.executeQuery(query.str())) {
  944. return false;
  945. }
  946.  
  947. query.str(std::string());
  948.  
  949. DBInsert storageQuery("INSERT INTO `player_storage` (`player_id`, `key`, `value`) VALUES ");
  950. player->genReservedStorageRange();
  951.  
  952. for (const auto& it : player->storageMap) {
  953. query << player->getGUID() << ',' << it.first << ',' << it.second;
  954. if (!storageQuery.addRow(query)) {
  955. return false;
  956. }
  957. }
  958.  
  959. if (!storageQuery.execute()) {
  960. return false;
  961. }
  962.  
  963. //End the transaction
  964. return transaction.commit();
  965. }
  966.  
  967. std::string IOLoginData::getNameByGuid(uint32_t guid)
  968. {
  969. std::ostringstream query;
  970. query << "SELECT `name` FROM `players` WHERE `id` = " << guid;
  971. DBResult_ptr result = Database::getInstance().storeQuery(query.str());
  972. if (!result) {
  973. return std::string();
  974. }
  975. return result->getString("name");
  976. }
  977.  
  978. uint32_t IOLoginData::getGuidByName(const std::string& name)
  979. {
  980. Database& db = Database::getInstance();
  981.  
  982. std::ostringstream query;
  983. query << "SELECT `id` FROM `players` WHERE `name` = " << db.escapeString(name);
  984. DBResult_ptr result = db.storeQuery(query.str());
  985. if (!result) {
  986. return 0;
  987. }
  988. return result->getNumber<uint32_t>("id");
  989. }
  990.  
  991. bool IOLoginData::getGuidByNameEx(uint32_t& guid, bool& specialVip, std::string& name)
  992. {
  993. Database& db = Database::getInstance();
  994.  
  995. std::ostringstream query;
  996. query << "SELECT `name`, `id`, `group_id`, `account_id` FROM `players` WHERE `name` = " << db.escapeString(name);
  997. DBResult_ptr result = db.storeQuery(query.str());
  998. if (!result) {
  999. return false;
  1000. }
  1001.  
  1002. name = result->getString("name");
  1003. guid = result->getNumber<uint32_t>("id");
  1004. Group* group = g_game.groups.getGroup(result->getNumber<uint16_t>("group_id"));
  1005.  
  1006. uint64_t flags;
  1007. if (group) {
  1008. flags = group->flags;
  1009. } else {
  1010. flags = 0;
  1011. }
  1012.  
  1013. specialVip = (flags & PlayerFlag_SpecialVIP) != 0;
  1014. return true;
  1015. }
  1016.  
  1017. bool IOLoginData::formatPlayerName(std::string& name)
  1018. {
  1019. Database& db = Database::getInstance();
  1020.  
  1021. std::ostringstream query;
  1022. query << "SELECT `name` FROM `players` WHERE `name` = " << db.escapeString(name);
  1023.  
  1024. DBResult_ptr result = db.storeQuery(query.str());
  1025. if (!result) {
  1026. return false;
  1027. }
  1028.  
  1029. name = result->getString("name");
  1030. return true;
  1031. }
  1032.  
  1033. void IOLoginData::loadItems(ItemMap& itemMap, DBResult_ptr result)
  1034. {
  1035. do {
  1036. uint32_t sid = result->getNumber<uint32_t>("sid");
  1037. uint32_t pid = result->getNumber<uint32_t>("pid");
  1038. uint16_t type = result->getNumber<uint16_t>("itemtype");
  1039. uint16_t count = result->getNumber<uint16_t>("count");
  1040.  
  1041. unsigned long attrSize;
  1042. const char* attr = result->getStream("attributes", attrSize);
  1043.  
  1044. PropStream propStream;
  1045. propStream.init(attr, attrSize);
  1046.  
  1047. Item* item = Item::CreateItem(type, count);
  1048. if (item) {
  1049. if (!item->unserializeAttr(propStream)) {
  1050. std::cout << "WARNING: Serialize error in IOLoginData::loadItems" << std::endl;
  1051. }
  1052.  
  1053. std::pair<Item*, uint32_t> pair(item, pid);
  1054. itemMap[sid] = pair;
  1055. }
  1056. } while (result->next());
  1057. }
  1058.  
  1059. void IOLoginData::increaseBankBalance(uint32_t guid, uint64_t bankBalance)
  1060. {
  1061. std::ostringstream query;
  1062. query << "UPDATE `players` SET `balance` = `balance` + " << bankBalance << " WHERE `id` = " << guid;
  1063. Database::getInstance().executeQuery(query.str());
  1064. }
  1065.  
  1066. bool IOLoginData::hasBiddedOnHouse(uint32_t guid)
  1067. {
  1068. Database& db = Database::getInstance();
  1069.  
  1070. std::ostringstream query;
  1071. query << "SELECT `id` FROM `houses` WHERE `highest_bidder` = " << guid << " LIMIT 1";
  1072. return db.storeQuery(query.str()).get() != nullptr;
  1073. }
  1074.  
  1075. std::forward_list<VIPEntry> IOLoginData::getVIPEntries(uint32_t accountId)
  1076. {
  1077. std::forward_list<VIPEntry> entries;
  1078.  
  1079. std::ostringstream query;
  1080. query << "SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name`, `description`, `icon`, `notify` FROM `account_viplist` WHERE `account_id` = " << accountId;
  1081.  
  1082. DBResult_ptr result = Database::getInstance().storeQuery(query.str());
  1083. if (result) {
  1084. do {
  1085. entries.emplace_front(
  1086. result->getNumber<uint32_t>("player_id"),
  1087. result->getString("name"),
  1088. result->getString("description"),
  1089. result->getNumber<uint32_t>("icon"),
  1090. result->getNumber<uint16_t>("notify") != 0
  1091. );
  1092. } while (result->next());
  1093. }
  1094. return entries;
  1095. }
  1096.  
  1097. void IOLoginData::addVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify)
  1098. {
  1099. Database& db = Database::getInstance();
  1100.  
  1101. std::ostringstream query;
  1102. query << "INSERT INTO `account_viplist` (`account_id`, `player_id`, `description`, `icon`, `notify`) VALUES (" << accountId << ',' << guid << ',' << db.escapeString(description) << ',' << icon << ',' << notify << ')';
  1103. db.executeQuery(query.str());
  1104. }
  1105.  
  1106. void IOLoginData::editVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify)
  1107. {
  1108. Database& db = Database::getInstance();
  1109.  
  1110. std::ostringstream query;
  1111. query << "UPDATE `account_viplist` SET `description` = " << db.escapeString(description) << ", `icon` = " << icon << ", `notify` = " << notify << " WHERE `account_id` = " << accountId << " AND `player_id` = " << guid;
  1112. db.executeQuery(query.str());
  1113. }
  1114.  
  1115. void IOLoginData::removeVIPEntry(uint32_t accountId, uint32_t guid)
  1116. {
  1117. std::ostringstream query;
  1118. query << "DELETE FROM `account_viplist` WHERE `account_id` = " << accountId << " AND `player_id` = " << guid;
  1119. Database::getInstance().executeQuery(query.str());
  1120. }
  1121.  
  1122. void IOLoginData::addPremiumDays(uint32_t accountId, int32_t addDays)
  1123. {
  1124. std::ostringstream query;
  1125. query << "UPDATE `accounts` SET `premdays` = `premdays` + " << addDays << " WHERE `id` = " << accountId;
  1126. Database::getInstance().executeQuery(query.str());
  1127. }
  1128.  
  1129. void IOLoginData::removePremiumDays(uint32_t accountId, int32_t removeDays)
  1130. {
  1131. std::ostringstream query;
  1132. query << "UPDATE `accounts` SET `premdays` = `premdays` - " << removeDays << " WHERE `id` = " << accountId;
  1133. Database::getInstance().executeQuery(query.str());
  1134. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement