Advertisement
Guest User

protocolgame.cpp

a guest
Aug 24th, 2017
500
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 82.66 KB | None | 0 0
  1. /**
  2. * The Forgotten Server - a free and open-source MMORPG server emulator
  3. * Copyright (C) 2016 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 <boost/range/adaptor/reversed.hpp>
  23.  
  24. #include "protocolgame.h"
  25.  
  26. #include "outputmessage.h"
  27.  
  28. #include "player.h"
  29.  
  30. #include "configmanager.h"
  31. #include "actions.h"
  32. #include "game.h"
  33. #include "iologindata.h"
  34. #include "iomarket.h"
  35. #include "waitlist.h"
  36. #include "ban.h"
  37. #include "scheduler.h"
  38. #include "databasetasks.h"
  39. #include "modules.h"
  40. #include "store.h"
  41.  
  42. extern Game g_game;
  43. extern ConfigManager g_config;
  44. extern Actions actions;
  45. extern CreatureEvents* g_creatureEvents;
  46. extern Chat* g_chat;
  47. extern Imbuements g_imbuements;
  48. extern Store* g_store;
  49. extern Modules* g_modules;
  50.  
  51. ProtocolGame::LiveCastsMap ProtocolGame::liveCasts;
  52. void ProtocolGame::release()
  53. {
  54. //dispatcher thread
  55. stopLiveCast();
  56. if (player && player->client == shared_from_this()) {
  57. player->client.reset();
  58. player->decrementReferenceCounter();
  59. player = nullptr;
  60. }
  61.  
  62. OutputMessagePool::getInstance().removeProtocolFromAutosend(shared_from_this());
  63. Protocol::release();
  64. }
  65.  
  66. void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem)
  67. {
  68. //dispatcher thread
  69. Player* _player = g_game.getPlayerByName(name);
  70. if (!_player || g_config.getBoolean(ConfigManager::ALLOW_CLONES)) {
  71. player = new Player(getThis());
  72. player->setName(name);
  73.  
  74. player->incrementReferenceCounter();
  75. player->setID();
  76.  
  77. if (!IOLoginData::preloadPlayer(player, name)) {
  78. disconnectClient("Your character could not be loaded.");
  79. return;
  80. }
  81.  
  82. if (IOBan::isPlayerNamelocked(player->getGUID())) {
  83. disconnectClient("Your character has been namelocked.");
  84. return;
  85. }
  86.  
  87. if (g_game.getGameState() == GAME_STATE_CLOSING && !player->hasFlag(PlayerFlag_CanAlwaysLogin)) {
  88. disconnectClient("The game is just going down.\nPlease try again later.");
  89. return;
  90. }
  91.  
  92. if (g_game.getGameState() == GAME_STATE_CLOSED && !player->hasFlag(PlayerFlag_CanAlwaysLogin)) {
  93. disconnectClient("Server is currently closed.\nPlease try again later.");
  94. return;
  95. }
  96.  
  97. if (g_config.getBoolean(ConfigManager::ONE_PLAYER_ON_ACCOUNT) && player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER && g_game.getPlayerByAccount(player->getAccount())) {
  98. disconnectClient("You may only login with one character\nof your account at the same time.");
  99. return;
  100. }
  101.  
  102. if (!player->hasFlag(PlayerFlag_CannotBeBanned)) {
  103. BanInfo banInfo;
  104. if (IOBan::isAccountBanned(accountId, banInfo)) {
  105. if (banInfo.reason.empty()) {
  106. banInfo.reason = "(none)";
  107. }
  108.  
  109. std::ostringstream ss;
  110. if (banInfo.expiresAt > 0) {
  111. ss << "Your account has been banned until " << formatDateShort(banInfo.expiresAt) << " by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason;
  112. } else {
  113. ss << "Your account has been permanently banned by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason;
  114. }
  115. disconnectClient(ss.str());
  116. return;
  117. }
  118. }
  119.  
  120. if (!WaitingList::getInstance()->clientLogin(player)) {
  121. uint32_t currentSlot = WaitingList::getInstance()->getClientSlot(player);
  122. uint32_t retryTime = WaitingList::getTime(currentSlot);
  123. std::ostringstream ss;
  124.  
  125. ss << "Too many players online.\nYou are at place "
  126. << currentSlot << " on the waiting list.";
  127.  
  128. auto output = OutputMessagePool::getOutputMessage();
  129. output->addByte(0x16);
  130. output->addString(ss.str());
  131. output->addByte(retryTime);
  132. send(output);
  133. disconnect();
  134. return;
  135. }
  136.  
  137. if (!IOLoginData::loadPlayerByName(player, name)) {
  138. disconnectClient("Your character could not be loaded.");
  139. return;
  140. }
  141.  
  142. player->setOperatingSystem(operatingSystem);
  143.  
  144. if (!g_game.placeCreature(player, player->getLoginPosition())) {
  145. if (!g_game.placeCreature(player, player->getTemplePosition(), false, true)) {
  146. disconnectClient("Temple position is wrong. Contact the administrator.");
  147. return;
  148. }
  149. }
  150.  
  151. if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
  152. player->registerCreatureEvent("ExtendedOpcode");
  153. }
  154.  
  155. player->lastIP = player->getIP();
  156. player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1);
  157. m_acceptPackets = true;
  158. } else {
  159. if (eventConnect != 0 || !g_config.getBoolean(ConfigManager::REPLACE_KICK_ON_LOGIN)) {
  160. //Already trying to connect
  161. disconnectClient("You are already logged in.");
  162. return;
  163. }
  164.  
  165. if (_player->client) {
  166. _player->disconnect();
  167. _player->isConnecting = true;
  168.  
  169. eventConnect = g_scheduler.addEvent(createSchedulerTask(1000, std::bind(&ProtocolGame::connect, getThis(), _player->getID(), operatingSystem)));
  170. } else {
  171. connect(_player->getID(), operatingSystem);
  172. }
  173. }
  174. OutputMessagePool::getInstance().addProtocolToAutosend(shared_from_this());
  175. }
  176.  
  177. void ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem)
  178. {
  179. eventConnect = 0;
  180.  
  181. Player* _player = g_game.getPlayerByID(playerId);
  182. if (!_player || _player->client) {
  183. disconnectClient("You are already logged in.");
  184. return;
  185. }
  186.  
  187. if (isConnectionExpired()) {
  188. //ProtocolGame::release() has been called at this point and the Connection object
  189. //no longer exists, so we return to prevent leakage of the Player.
  190. return;
  191. }
  192.  
  193. player = _player;
  194. player->incrementReferenceCounter();
  195.  
  196. g_chat->removeUserFromAllChannels(*player);
  197. player->clearModalWindows();
  198. player->setOperatingSystem(operatingSystem);
  199. player->isConnecting = false;
  200.  
  201. player->client = getThis();
  202. sendAddCreature(player, player->getPosition(), 0, false);
  203. player->lastIP = player->getIP();
  204. player->lastLoginSaved = std::max<time_t>(time(nullptr), player->lastLoginSaved + 1);
  205. m_acceptPackets = true;
  206. }
  207.  
  208. void ProtocolGame::logout(bool displayEffect, bool forced)
  209. {
  210. //dispatcher thread
  211. if (!player) {
  212. return;
  213. }
  214.  
  215. if (!player->isRemoved()) {
  216. if (!forced) {
  217. if (!player->isAccessPlayer()) {
  218. if (player->getTile()->hasFlag(TILESTATE_NOLOGOUT)) {
  219. player->sendCancelMessage(RETURNVALUE_YOUCANNOTLOGOUTHERE);
  220. return;
  221. }
  222.  
  223. if (!player->getTile()->hasFlag(TILESTATE_PROTECTIONZONE) && player->hasCondition(CONDITION_INFIGHT)) {
  224. player->sendCancelMessage(RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT);
  225. return;
  226. }
  227. }
  228.  
  229. //scripting event - onLogout
  230. if (!g_creatureEvents->playerLogout(player)) {
  231. //Let the script handle the error message
  232. return;
  233. }
  234. }
  235.  
  236. if (displayEffect && player->getHealth() > 0) {
  237. g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF);
  238. }
  239. }
  240.  
  241. stopLiveCast();
  242. disconnect();
  243.  
  244. g_game.removeCreature(player);
  245. }
  246.  
  247. bool ProtocolGame::startLiveCast(const std::string& password /*= ""*/)
  248. {
  249. auto connection = getConnection();
  250. if (!g_config.getBoolean(ConfigManager::ENABLE_LIVE_CASTING) || isLiveCaster() || !player || player->isRemoved() || !connection) {
  251. return false;
  252. }
  253.  
  254. auto limit = liveCasts.size();
  255. limit = g_config.getNumber(ConfigManager::CAST_LIMIT);
  256. if (liveCasts.size() >= limit)
  257. {
  258. //player->sendCancelMessage("Cast is full right now, please try it later.");
  259. return false;
  260. }
  261.  
  262.  
  263. // TIREI ESSA PORRA player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  264.  
  265.  
  266. {
  267. std::lock_guard<decltype(liveCastLock)> lock {liveCastLock};
  268. //DO NOT do any send operations here
  269. liveCastName = player->getName();
  270. liveCastPassword = password;
  271. isCaster.store(true, std::memory_order_relaxed);
  272. spectatorsCount = 0;
  273. }
  274.  
  275. liveCasts.insert(std::make_pair(player, getThis()));
  276.  
  277. registerLiveCast();
  278. //Send a "dummy" channel
  279. sendChannel(CHANNEL_CAST, LIVE_CAST_CHAT_NAME, nullptr, nullptr);
  280. return true;
  281. }
  282.  
  283. bool ProtocolGame::stopLiveCast()
  284. {
  285. //dispatcher
  286. if (!isLiveCaster()) {
  287. return false;
  288. }
  289.  
  290. CastSpectatorVec spectators;
  291.  
  292. {
  293. std::lock_guard<decltype(liveCastLock)> lock {liveCastLock};
  294. //DO NOT do any send operations here
  295. std::swap(this->spectators, spectators);
  296. isCaster.store(false, std::memory_order_relaxed);
  297. }
  298.  
  299. liveCasts.erase(player);
  300. for (auto& spectator : spectators) {
  301. spectator->onLiveCastStop();
  302. }
  303. unregisterLiveCast();
  304.  
  305. return true;
  306. }
  307.  
  308. void ProtocolGame::clearLiveCastInfo()
  309. {
  310. static std::once_flag flag;
  311. std::call_once(flag, []() {
  312. assert(g_game.getGameState() == GAME_STATE_INIT);
  313. std::ostringstream query;
  314. query << "TRUNCATE TABLE `live_casts`;";
  315. g_databaseTasks.addTask(query.str());
  316. });
  317. }
  318.  
  319. void ProtocolGame::registerLiveCast()
  320. {
  321. std::ostringstream query;
  322. std::string escapedName = Database::getInstance()->escapeString(getLiveCastName());
  323. query << "INSERT into `live_casts` (`player_id`, `cast_name`, `password`) VALUES (" << player->getGUID() << ", "
  324. << escapedName << ", " << isPasswordProtected() << ");";
  325. g_databaseTasks.addTask(query.str());
  326. }
  327.  
  328. void ProtocolGame::unregisterLiveCast()
  329. {
  330. std::ostringstream query;
  331. query << "DELETE FROM `live_casts` WHERE `player_id`=" << player->getGUID() << ";";
  332. g_databaseTasks.addTask(query.str());
  333. }
  334.  
  335. void ProtocolGame::updateLiveCastInfo()
  336. {
  337. std::ostringstream query;
  338. std::string escapedName = Database::getInstance()->escapeString(getLiveCastName());
  339. query << "UPDATE `live_casts` SET `cast_name`=" << escapedName << ", `password`="
  340. << isPasswordProtected() << ", `spectators`=" << getSpectatorCount()
  341. << " WHERE `player_id`=" << player->getGUID() << ";";
  342. g_databaseTasks.addTask(query.str());
  343. }
  344.  
  345. void ProtocolGame::addSpectator(ProtocolSpectator_ptr spectatorClient)
  346. {
  347. std::lock_guard<decltype(liveCastLock)> lock(liveCastLock);
  348. //DO NOT do any send operations here
  349. spectators.emplace_back(spectatorClient);
  350.  
  351.  
  352.  
  353. /*spectatorsCount++;
  354. std::stringstream ss;
  355. ss << "Spectator(" << spectatorsCount << ")";*/
  356.  
  357.  
  358. updateLiveCastInfo();
  359. }
  360.  
  361. void ProtocolGame::removeSpectator(ProtocolSpectator_ptr spectatorClient)
  362. {
  363. std::lock_guard<decltype(liveCastLock)> lock(liveCastLock);
  364. //DO NOT do any send operations here
  365. auto it = std::find(spectators.begin(), spectators.end(), spectatorClient);
  366. if (it != spectators.end()) {
  367. spectators.erase(it);
  368. updateLiveCastInfo();
  369. }
  370. }
  371.  
  372.  
  373. void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)
  374. {
  375. if (g_game.getGameState() == GAME_STATE_SHUTDOWN) {
  376. disconnect();
  377. return;
  378. }
  379.  
  380. OperatingSystem_t operatingSystem = static_cast<OperatingSystem_t>(msg.get<uint16_t>());
  381. version = msg.get<uint16_t>();
  382.  
  383. msg.skipBytes(7); // U32 client version, U8 client type, U16 dat revision
  384.  
  385. if (!Protocol::RSA_decrypt(msg)) {
  386. disconnect();
  387. return;
  388. }
  389.  
  390. uint32_t key[4];
  391. key[0] = msg.get<uint32_t>();
  392. key[1] = msg.get<uint32_t>();
  393. key[2] = msg.get<uint32_t>();
  394. key[3] = msg.get<uint32_t>();
  395. enableXTEAEncryption();
  396. setXTEAKey(key);
  397.  
  398. if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) {
  399. NetworkMessage opcodeMessage;
  400. opcodeMessage.addByte(0x32);
  401. opcodeMessage.addByte(0x00);
  402. opcodeMessage.add<uint16_t>(0x00);
  403. writeToOutputBuffer(opcodeMessage);
  404. }
  405.  
  406. msg.skipBytes(1); // gamemaster flag
  407.  
  408. std::string sessionKey = msg.getString();
  409.  
  410. size_t pos = sessionKey.find('\n');
  411. if (pos == std::string::npos) {
  412. disconnectClient("You must enter your account name.");
  413. return;
  414. }
  415.  
  416. std::string accountName = sessionKey.substr(0, pos);
  417. if (accountName.empty()) {
  418. disconnectClient("You must enter your account name.");
  419. return;
  420. }
  421.  
  422. std::string password = sessionKey.substr(pos + 1);
  423.  
  424. std::string characterName = msg.getString();
  425.  
  426. uint32_t timeStamp = msg.get<uint32_t>();
  427. uint8_t randNumber = msg.getByte();
  428. if (m_challengeTimestamp != timeStamp || m_challengeRandom != randNumber) {
  429. disconnect();
  430. return;
  431. }
  432.  
  433. if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) {
  434. disconnectClient("Only clients with protocol " CLIENT_VERSION_STR " allowed!");
  435. return;
  436. }
  437.  
  438. if (g_game.getGameState() == GAME_STATE_STARTUP) {
  439. disconnectClient("Gameworld is starting up. Please wait.");
  440. return;
  441. }
  442.  
  443. if (g_game.getGameState() == GAME_STATE_MAINTAIN) {
  444. disconnectClient("Gameworld is under maintenance. Please re-connect in a while.");
  445. return;
  446. }
  447.  
  448. BanInfo banInfo;
  449. if (IOBan::isIpBanned(getIP(), banInfo)) {
  450. if (banInfo.reason.empty()) {
  451. banInfo.reason = "(none)";
  452. }
  453.  
  454. std::ostringstream ss;
  455. ss << "Your IP has been banned until " << formatDateShort(banInfo.expiresAt) << " by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason;
  456. disconnectClient(ss.str());
  457. return;
  458. }
  459.  
  460. uint32_t accountId = IOLoginData::gameworldAuthentication(accountName, password, characterName);
  461. if (accountId == 0) {
  462. disconnectClient("Account name or password is not correct.");
  463. return;
  464. }
  465.  
  466. g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem)));
  467. }
  468.  
  469. void ProtocolGame::disconnectClient(const std::string& message) const
  470. {
  471. auto output = OutputMessagePool::getOutputMessage();
  472. output->addByte(0x14);
  473. output->addString(message);
  474. send(output);
  475. disconnect();
  476. }
  477.  
  478. void ProtocolGame::writeToOutputBuffer(const NetworkMessage& msg, bool broadcast /*= true*/)
  479. {
  480. if (!broadcast && isLiveCaster()) {
  481. //We're casting and we need to send a packet that's not supposed to be broadcast so we need a new messasge.
  482. //This shouldn't impact performance by a huge amount as most packets can be broadcast.
  483. auto out = OutputMessagePool::getOutputMessage();
  484. out->append(msg);
  485. send(std::move(out));
  486. } else {
  487. auto out = getOutputBuffer(msg.getLength());
  488. if (isLiveCaster()) {
  489. out->setBroadcastMsg(true);
  490. }
  491. out->append(msg);
  492. }
  493. }
  494.  
  495. void ProtocolGame::parsePacket(NetworkMessage& msg)
  496. {
  497. if (!m_acceptPackets || g_game.getGameState() == GAME_STATE_SHUTDOWN || msg.getLength() <= 0) {
  498. return;
  499. }
  500.  
  501. uint8_t recvbyte = msg.getByte();
  502.  
  503. //a dead player can not perform actions
  504. if (!player || player->isRemoved() || player->getHealth() <= 0) {
  505. auto this_ptr = getThis();
  506. g_dispatcher.addTask(createTask([this_ptr]() {
  507. this_ptr->stopLiveCast();
  508. }));
  509. if (recvbyte == 0x0F) {
  510. disconnect();
  511. return;
  512. }
  513.  
  514. if (recvbyte != 0x14) {
  515. return;
  516. }
  517. }
  518.  
  519. switch (recvbyte) {
  520. case 0x14: g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::logout, getThis(), true, false))); break;
  521. case 0x1D: addGameTask(&Game::playerReceivePingBack, player->getID()); break;
  522. case 0x1E: addGameTask(&Game::playerReceivePing, player->getID()); break;
  523. case 0x32: parseExtendedOpcode(msg); break; //otclient extended opcode
  524. case 0x64: parseAutoWalk(msg); break;
  525. case 0x65: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTH); break;
  526. case 0x66: addGameTask(&Game::playerMove, player->getID(), DIRECTION_EAST); break;
  527. case 0x67: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTH); break;
  528. case 0x68: addGameTask(&Game::playerMove, player->getID(), DIRECTION_WEST); break;
  529. case 0x69: addGameTask(&Game::playerStopAutoWalk, player->getID()); break;
  530. case 0x6A: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHEAST); break;
  531. case 0x6B: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHEAST); break;
  532. case 0x6C: addGameTask(&Game::playerMove, player->getID(), DIRECTION_SOUTHWEST); break;
  533. case 0x6D: addGameTask(&Game::playerMove, player->getID(), DIRECTION_NORTHWEST); break;
  534. case 0x6F: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_NORTH); break;
  535. case 0x70: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_EAST); break;
  536. case 0x71: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_SOUTH); break;
  537. case 0x72: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_WEST); break;
  538. case 0x77: parseEquipObject(msg); break;
  539. case 0x78: parseThrow(msg); break;
  540. case 0x79: parseLookInShop(msg); break;
  541. case 0x7A: parsePlayerPurchase(msg); break;
  542. case 0x7B: parsePlayerSale(msg); break;
  543. case 0x7C: addGameTask(&Game::playerCloseShop, player->getID()); break;
  544. case 0x7D: parseRequestTrade(msg); break;
  545. case 0x7E: parseLookInTrade(msg); break;
  546. case 0x7F: addGameTask(&Game::playerAcceptTrade, player->getID()); break;
  547. case 0x80: addGameTask(&Game::playerCloseTrade, player->getID()); break;
  548. case 0x82: parseUseItem(msg); break;
  549. case 0x83: parseUseItemEx(msg); break;
  550. case 0x84: parseUseWithCreature(msg); break;
  551. case 0x85: parseRotateItem(msg); break;
  552. case 0x87: parseCloseContainer(msg); break;
  553. case 0x88: parseUpArrowContainer(msg); break;
  554. case 0x89: parseTextWindow(msg); break;
  555. case 0x8A: parseHouseWindow(msg); break;
  556. case 0x8B: parseWrapItem(msg); break;
  557. case 0x8C: parseLookAt(msg); break;
  558. case 0x8D: parseLookInBattleList(msg); break;
  559. case 0x8E: /* join aggression */ break;
  560. case 0x96: parseSay(msg); break;
  561. case 0x97: addGameTask(&Game::playerRequestChannels, player->getID()); break;
  562. case 0x98: parseOpenChannel(msg); break;
  563. case 0x99: parseCloseChannel(msg); break;
  564. case 0x9A: parseOpenPrivateChannel(msg); break;
  565. case 0x9E: addGameTask(&Game::playerCloseNpcChannel, player->getID()); break;
  566. case 0xA0: parseFightModes(msg); break;
  567. case 0xA1: parseAttack(msg); break;
  568. case 0xA2: parseFollow(msg); break;
  569. case 0xA3: parseInviteToParty(msg); break;
  570. case 0xA4: parseJoinParty(msg); break;
  571. case 0xA5: parseRevokePartyInvite(msg); break;
  572. case 0xA6: parsePassPartyLeadership(msg); break;
  573. case 0xA7: addGameTask(&Game::playerLeaveParty, player->getID()); break;
  574. case 0xA8: parseEnableSharedPartyExperience(msg); break;
  575. case 0xAA: addGameTask(&Game::playerCreatePrivateChannel, player->getID()); break;
  576. case 0xAB: parseChannelInvite(msg); break;
  577. case 0xAC: parseChannelExclude(msg); break;
  578. case 0xBE: addGameTask(&Game::playerCancelAttackAndFollow, player->getID()); break;
  579. case 0xC9: /* update tile */ break;
  580. case 0xCA: parseUpdateContainer(msg); break;
  581. case 0xCB: parseBrowseField(msg); break;
  582. case 0xCC: parseSeekInContainer(msg); break;
  583. case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break;
  584. case 0xD3: parseSetOutfit(msg); break;
  585. case 0xD4: parseToggleMount(msg); break;
  586. case 0xDC: parseAddVip(msg); break;
  587. case 0xDD: parseRemoveVip(msg); break;
  588. case 0xDE: parseEditVip(msg); break;
  589. case 0xD5: parseApplyImbuent(msg); break;
  590. case 0xD6: parseApplyClearingCharm(msg); break;
  591. case 0xD7: parseCloseImbuingDialog(msg); break;
  592. case 0xE6: parseBugReport(msg); break;
  593. case 0xE7: /* thank you */ break;
  594. case 0xE8: parseDebugAssert(msg); break;
  595. case 0xEB: parsePreyAction(msg); break; // prey
  596. case 0xEF: parseTransferCoins(msg); break;
  597. case 0xF0: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerShowQuestLog, player->getID()); break;
  598. case 0xF1: parseQuestLine(msg); break;
  599. case 0xF2: /* rule violation report */ break;
  600. case 0xF3: /* get object info */ break;
  601. case 0xF4: parseMarketLeave(); break;
  602. case 0xF5: parseMarketBrowse(msg); break;
  603. case 0xF6: parseMarketCreateOffer(msg); break;
  604. case 0xF7: parseMarketCancelOffer(msg); break;
  605. case 0xF8: parseMarketAcceptOffer(msg); break;
  606. case 0xF9: parseModalWindowAnswer(msg); break;
  607. case 0xFA: parseStoreOpen(); break;
  608. case 0xFB: parseStoreSelectCategory(msg); break;
  609. case 0xFC: parseStoreBuyOffer(msg); break;
  610. case 0xFD: parseStoreOpenHistory(msg); break;
  611. case 0xFE: parseStoreRequestHistory(msg); break;
  612.  
  613. default:
  614. // std::cout << "Player: " << player->getName() << " sent an unknown packet header: 0x" << std::hex << static_cast<uint16_t>(recvbyte) << std::dec << "!" << std::endl;
  615. break;
  616. }
  617.  
  618. if (msg.isOverrun()) {
  619. disconnect();
  620. }
  621. }
  622.  
  623. // Parse methods
  624. void ProtocolGame::parseChannelInvite(NetworkMessage& msg)
  625. {
  626. const std::string name = msg.getString();
  627. addGameTask(&Game::playerChannelInvite, player->getID(), name);
  628. }
  629.  
  630. void ProtocolGame::parseChannelExclude(NetworkMessage& msg)
  631. {
  632. const std::string name = msg.getString();
  633. addGameTask(&Game::playerChannelExclude, player->getID(), name);
  634. }
  635.  
  636. void ProtocolGame::parseOpenChannel(NetworkMessage& msg)
  637. {
  638. uint16_t channelId = msg.get<uint16_t>();
  639. addGameTask(&Game::playerOpenChannel, player->getID(), channelId);
  640. }
  641.  
  642. void ProtocolGame::parseCloseChannel(NetworkMessage& msg)
  643. {
  644. uint16_t channelId = msg.get<uint16_t>();
  645. if (channelId == CHANNEL_CAST) {
  646. stopLiveCast();
  647. }
  648. addGameTask(&Game::playerCloseChannel, player->getID(), channelId);
  649. }
  650.  
  651. void ProtocolGame::parseOpenPrivateChannel(NetworkMessage& msg)
  652. {
  653. const std::string receiver = msg.getString();
  654. addGameTask(&Game::playerOpenPrivateChannel, player->getID(), receiver);
  655. }
  656.  
  657. void ProtocolGame::parseAutoWalk(NetworkMessage& msg)
  658. {
  659. uint8_t numdirs = msg.getByte();
  660. if (numdirs == 0 || (msg.getBufferPosition() + numdirs) != (msg.getLength() + 8)) {
  661. return;
  662. }
  663.  
  664. msg.skipBytes(numdirs);
  665.  
  666. std::forward_list<Direction> path;
  667. for (uint8_t i = 0; i < numdirs; ++i) {
  668. uint8_t rawdir = msg.getPreviousByte();
  669. switch (rawdir) {
  670. case 1: path.push_front(DIRECTION_EAST); break;
  671. case 2: path.push_front(DIRECTION_NORTHEAST); break;
  672. case 3: path.push_front(DIRECTION_NORTH); break;
  673. case 4: path.push_front(DIRECTION_NORTHWEST); break;
  674. case 5: path.push_front(DIRECTION_WEST); break;
  675. case 6: path.push_front(DIRECTION_SOUTHWEST); break;
  676. case 7: path.push_front(DIRECTION_SOUTH); break;
  677. case 8: path.push_front(DIRECTION_SOUTHEAST); break;
  678. default: break;
  679. }
  680. }
  681.  
  682. if (path.empty()) {
  683. return;
  684. }
  685.  
  686. addGameTask(&Game::playerAutoWalk, player->getID(), path);
  687. }
  688.  
  689. void ProtocolGame::parseSetOutfit(NetworkMessage& msg)
  690. {
  691. Outfit_t newOutfit;
  692. newOutfit.lookType = msg.get<uint16_t>();
  693. newOutfit.lookHead = msg.getByte();
  694. newOutfit.lookBody = msg.getByte();
  695. newOutfit.lookLegs = msg.getByte();
  696. newOutfit.lookFeet = msg.getByte();
  697. newOutfit.lookAddons = msg.getByte();
  698. newOutfit.lookMount = msg.get<uint16_t>();
  699. addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit);
  700. }
  701.  
  702. void ProtocolGame::parseToggleMount(NetworkMessage& msg)
  703. {
  704. bool mount = msg.getByte() != 0;
  705. addGameTask(&Game::playerToggleMount, player->getID(), mount);
  706. }
  707.  
  708. void ProtocolGame::parseUseItem(NetworkMessage& msg)
  709. {
  710. Position pos = msg.getPosition();
  711. uint16_t spriteId = msg.get<uint16_t>();
  712. uint8_t stackpos = msg.getByte();
  713. uint8_t index = msg.getByte();
  714. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseItem, player->getID(), pos, stackpos, index, spriteId);
  715. }
  716.  
  717. void ProtocolGame::parseUseItemEx(NetworkMessage& msg)
  718. {
  719. Position fromPos = msg.getPosition();
  720. uint16_t fromSpriteId = msg.get<uint16_t>();
  721. uint8_t fromStackPos = msg.getByte();
  722. Position toPos = msg.getPosition();
  723. uint16_t toSpriteId = msg.get<uint16_t>();
  724. uint8_t toStackPos = msg.getByte();
  725. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseItemEx, player->getID(), fromPos, fromStackPos, fromSpriteId, toPos, toStackPos, toSpriteId);
  726. }
  727.  
  728. void ProtocolGame::parseUseWithCreature(NetworkMessage& msg)
  729. {
  730. Position fromPos = msg.getPosition();
  731. uint16_t spriteId = msg.get<uint16_t>();
  732. uint8_t fromStackPos = msg.getByte();
  733. uint32_t creatureId = msg.get<uint32_t>();
  734. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerUseWithCreature, player->getID(), fromPos, fromStackPos, creatureId, spriteId);
  735. }
  736.  
  737. void ProtocolGame::parseCloseContainer(NetworkMessage& msg)
  738. {
  739. uint8_t cid = msg.getByte();
  740. addGameTask(&Game::playerCloseContainer, player->getID(), cid);
  741. }
  742.  
  743. void ProtocolGame::parseUpArrowContainer(NetworkMessage& msg)
  744. {
  745. uint8_t cid = msg.getByte();
  746. addGameTask(&Game::playerMoveUpContainer, player->getID(), cid);
  747. }
  748.  
  749. void ProtocolGame::parseUpdateContainer(NetworkMessage& msg)
  750. {
  751. uint8_t cid = msg.getByte();
  752. addGameTask(&Game::playerUpdateContainer, player->getID(), cid);
  753. }
  754.  
  755. void ProtocolGame::parseThrow(NetworkMessage& msg)
  756. {
  757. Position fromPos = msg.getPosition();
  758. uint16_t spriteId = msg.get<uint16_t>();
  759. uint8_t fromStackpos = msg.getByte();
  760. Position toPos = msg.getPosition();
  761. uint8_t count = msg.getByte();
  762.  
  763. if (toPos != fromPos) {
  764. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerMoveThing, player->getID(), fromPos, spriteId, fromStackpos, toPos, count);
  765. }
  766. }
  767.  
  768. void ProtocolGame::parseLookAt(NetworkMessage& msg)
  769. {
  770. Position pos = msg.getPosition();
  771. msg.skipBytes(2); // spriteId
  772. uint8_t stackpos = msg.getByte();
  773. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookAt, player->getID(), pos, stackpos);
  774. }
  775.  
  776. void ProtocolGame::parseLookInBattleList(NetworkMessage& msg)
  777. {
  778. uint32_t creatureId = msg.get<uint32_t>();
  779. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInBattleList, player->getID(), creatureId);
  780. }
  781.  
  782. void ProtocolGame::parseSay(NetworkMessage& msg)
  783. {
  784. std::string receiver;
  785. uint16_t channelId;
  786.  
  787. SpeakClasses type = static_cast<SpeakClasses>(msg.getByte());
  788. switch (type) {
  789. case TALKTYPE_PRIVATE_TO:
  790. case TALKTYPE_PRIVATE_RED_TO:
  791. receiver = msg.getString();
  792. channelId = 0;
  793. break;
  794.  
  795. case TALKTYPE_CHANNEL_Y:
  796. case TALKTYPE_CHANNEL_R1:
  797. channelId = msg.get<uint16_t>();
  798. break;
  799.  
  800. default:
  801. channelId = 0;
  802. break;
  803. }
  804.  
  805. const std::string text = msg.getString();
  806. if (text.length() > 255) {
  807. return;
  808. }
  809.  
  810. addGameTask(&Game::playerSay, player->getID(), channelId, type, receiver, text);
  811. }
  812.  
  813. void ProtocolGame::parseFightModes(NetworkMessage& msg)
  814. {
  815. uint8_t rawFightMode = msg.getByte(); // 1 - offensive, 2 - balanced, 3 - defensive
  816. uint8_t rawChaseMode = msg.getByte(); // 0 - stand while fightning, 1 - chase opponent
  817. uint8_t rawSecureMode = msg.getByte(); // 0 - can't attack unmarked, 1 - can attack unmarked
  818. // uint8_t rawPvpMode = msg.getByte(); // pvp mode introduced in 10.0
  819.  
  820. chaseMode_t chaseMode;
  821. if (rawChaseMode == 1) {
  822. chaseMode = CHASEMODE_FOLLOW;
  823. } else {
  824. chaseMode = CHASEMODE_STANDSTILL;
  825. }
  826.  
  827. fightMode_t fightMode;
  828. if (rawFightMode == 1) {
  829. fightMode = FIGHTMODE_ATTACK;
  830. } else if (rawFightMode == 2) {
  831. fightMode = FIGHTMODE_BALANCED;
  832. } else {
  833. fightMode = FIGHTMODE_DEFENSE;
  834. }
  835.  
  836. addGameTask(&Game::playerSetFightModes, player->getID(), fightMode, chaseMode, rawSecureMode != 0);
  837. }
  838.  
  839. void ProtocolGame::parseAttack(NetworkMessage& msg)
  840. {
  841. uint32_t creatureId = msg.get<uint32_t>();
  842. // msg.get<uint32_t>(); creatureId (same as above)
  843. addGameTask(&Game::playerSetAttackedCreature, player->getID(), creatureId);
  844. }
  845.  
  846. void ProtocolGame::parseFollow(NetworkMessage& msg)
  847. {
  848. uint32_t creatureId = msg.get<uint32_t>();
  849. // msg.get<uint32_t>(); creatureId (same as above)
  850. addGameTask(&Game::playerFollowCreature, player->getID(), creatureId);
  851. }
  852.  
  853. void ProtocolGame::parseEquipObject(NetworkMessage& msg)
  854. {
  855. uint16_t spriteId = msg.get<uint16_t>();
  856. msg.get<uint8_t>();
  857. if (static_cast<int64_t>(OTSYS_TIME()) - static_cast<int64_t>(delayHotkeyEquip) <= static_cast<int64_t>(g_config.getNumber(ConfigManager::DELAY_HOTKEY_EQUIP) * 1000)) {
  858. std::ostringstream ss;
  859. ss << "You are exhausted.";
  860. player->sendCancelMessage(ss.str());
  861. return;
  862. }
  863.  
  864. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerEquipItem, player->getID(), spriteId);
  865. delayHotkeyEquip = OTSYS_TIME();
  866. }
  867.  
  868. void ProtocolGame::parseTextWindow(NetworkMessage& msg)
  869. {
  870. uint32_t windowTextId = msg.get<uint32_t>();
  871. const std::string newText = msg.getString();
  872. addGameTask(&Game::playerWriteItem, player->getID(), windowTextId, newText);
  873. }
  874.  
  875. void ProtocolGame::parseHouseWindow(NetworkMessage& msg)
  876. {
  877. uint8_t doorId = msg.getByte();
  878. uint32_t id = msg.get<uint32_t>();
  879. const std::string text = msg.getString();
  880. addGameTask(&Game::playerUpdateHouseWindow, player->getID(), doorId, id, text);
  881. }
  882.  
  883. void ProtocolGame::parseLookInShop(NetworkMessage& msg)
  884. {
  885. uint16_t id = msg.get<uint16_t>();
  886. uint8_t count = msg.getByte();
  887. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInShop, player->getID(), id, count);
  888. }
  889.  
  890. void ProtocolGame::parsePlayerPurchase(NetworkMessage& msg)
  891. {
  892. uint16_t id = msg.get<uint16_t>();
  893. uint8_t count = msg.getByte();
  894. uint8_t amount = msg.getByte();
  895. bool ignoreCap = msg.getByte() != 0;
  896. bool inBackpacks = msg.getByte() != 0;
  897. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerPurchaseItem, player->getID(), id, count, amount, ignoreCap, inBackpacks);
  898. }
  899.  
  900. void ProtocolGame::parsePlayerSale(NetworkMessage& msg)
  901. {
  902. uint16_t id = msg.get<uint16_t>();
  903. uint8_t count = msg.getByte();
  904. uint8_t amount = msg.getByte();
  905. bool ignoreEquipped = msg.getByte() != 0;
  906. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerSellItem, player->getID(), id, count, amount, ignoreEquipped);
  907. }
  908.  
  909. void ProtocolGame::parseRequestTrade(NetworkMessage& msg)
  910. {
  911. Position pos = msg.getPosition();
  912. uint16_t spriteId = msg.get<uint16_t>();
  913. uint8_t stackpos = msg.getByte();
  914. uint32_t playerId = msg.get<uint32_t>();
  915. addGameTask(&Game::playerRequestTrade, player->getID(), pos, stackpos, playerId, spriteId);
  916. }
  917.  
  918. void ProtocolGame::parseLookInTrade(NetworkMessage& msg)
  919. {
  920. bool counterOffer = (msg.getByte() == 0x01);
  921. uint8_t index = msg.getByte();
  922. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInTrade, player->getID(), counterOffer, index);
  923. }
  924.  
  925. void ProtocolGame::parseAddVip(NetworkMessage& msg)
  926. {
  927. const std::string name = msg.getString();
  928. addGameTask(&Game::playerRequestAddVip, player->getID(), name);
  929. }
  930.  
  931. void ProtocolGame::parseRemoveVip(NetworkMessage& msg)
  932. {
  933. uint32_t guid = msg.get<uint32_t>();
  934. addGameTask(&Game::playerRequestRemoveVip, player->getID(), guid);
  935. }
  936.  
  937. void ProtocolGame::parseApplyImbuent(NetworkMessage& msg)
  938. {
  939. uint8_t slot = msg.getByte();
  940. uint32_t imbuementId = msg.get<uint32_t>();
  941. bool protectionCharm = msg.getByte() != 0x00;
  942. addGameTask(&Game::playerApplyImbuent, player->getID(), slot, imbuementId, protectionCharm);
  943. }
  944. void ProtocolGame::parseApplyClearingCharm(NetworkMessage& msg)
  945. {
  946. uint8_t slot = msg.getByte();
  947. addGameTask(&Game::playerApplyClearingCharm, player->getID(), slot);
  948. }
  949. void ProtocolGame::parseCloseImbuingDialog(NetworkMessage&)
  950. {
  951. addGameTask(&Game::playerCloseImbuingDialog, player->getID());
  952. }
  953.  
  954. void ProtocolGame::parseEditVip(NetworkMessage& msg)
  955. {
  956. uint32_t guid = msg.get<uint32_t>();
  957. const std::string description = msg.getString();
  958. uint32_t icon = std::min<uint32_t>(10, msg.get<uint32_t>()); // 10 is max icon in 9.63
  959. bool notify = msg.getByte() != 0;
  960. addGameTask(&Game::playerRequestEditVip, player->getID(), guid, description, icon, notify);
  961. }
  962.  
  963. void ProtocolGame::parseRotateItem(NetworkMessage& msg)
  964. {
  965. Position pos = msg.getPosition();
  966. uint16_t spriteId = msg.get<uint16_t>();
  967. uint8_t stackpos = msg.getByte();
  968. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerRotateItem, player->getID(), pos, stackpos, spriteId);
  969. }
  970.  
  971. void ProtocolGame::parseWrapItem(NetworkMessage& msg)
  972. {
  973. Position pos = msg.getPosition();
  974. uint16_t spriteId = msg.get<uint16_t>();
  975. uint8_t stackpos = msg.getByte();
  976. addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerWrapItem, player->getID(), pos, stackpos, spriteId);
  977. }
  978.  
  979. void ProtocolGame::parseBugReport(NetworkMessage& msg)
  980. {
  981. uint8_t category = msg.getByte();
  982. std::string message = msg.getString();
  983.  
  984. Position position;
  985. if (category == BUG_CATEGORY_MAP) {
  986. position = msg.getPosition();
  987. }
  988.  
  989. addGameTask(&Game::playerReportBug, player->getID(), message, position, category);
  990. }
  991.  
  992. void ProtocolGame::parseDebugAssert(NetworkMessage& msg)
  993. {
  994. if (m_debugAssertSent) {
  995. return;
  996. }
  997.  
  998. m_debugAssertSent = true;
  999.  
  1000. std::string assertLine = msg.getString();
  1001. std::string date = msg.getString();
  1002. std::string description = msg.getString();
  1003. std::string comment = msg.getString();
  1004. addGameTask(&Game::playerDebugAssert, player->getID(), assertLine, date, description, comment);
  1005. }
  1006.  
  1007. // prey start
  1008. void ProtocolGame::parsePreyAction(NetworkMessage& msg)
  1009. {
  1010. PreySlotNum_t preySlot = static_cast<PreySlotNum_t>(msg.getByte());
  1011. PreyAction_t preyAction = static_cast<PreyAction_t>(msg.getByte());
  1012. uint8_t index;
  1013. if (preyAction == PREY_ACTION_MONSTERSELECTION) {
  1014. index = msg.getByte();
  1015. }
  1016. addGameTask(&Game::playerPerformPreyAction, player->getID(), preySlot, preyAction, index);
  1017. }
  1018.  
  1019. // prey end
  1020.  
  1021. void ProtocolGame::parseInviteToParty(NetworkMessage& msg)
  1022. {
  1023. uint32_t targetId = msg.get<uint32_t>();
  1024. addGameTask(&Game::playerInviteToParty, player->getID(), targetId);
  1025. }
  1026.  
  1027. void ProtocolGame::parseJoinParty(NetworkMessage& msg)
  1028. {
  1029. uint32_t targetId = msg.get<uint32_t>();
  1030. addGameTask(&Game::playerJoinParty, player->getID(), targetId);
  1031. }
  1032.  
  1033. void ProtocolGame::parseRevokePartyInvite(NetworkMessage& msg)
  1034. {
  1035. uint32_t targetId = msg.get<uint32_t>();
  1036. addGameTask(&Game::playerRevokePartyInvitation, player->getID(), targetId);
  1037. }
  1038.  
  1039. void ProtocolGame::parsePassPartyLeadership(NetworkMessage& msg)
  1040. {
  1041. uint32_t targetId = msg.get<uint32_t>();
  1042. addGameTask(&Game::playerPassPartyLeadership, player->getID(), targetId);
  1043. }
  1044.  
  1045. void ProtocolGame::parseEnableSharedPartyExperience(NetworkMessage& msg)
  1046. {
  1047. bool sharedExpActive = msg.getByte() == 1;
  1048. addGameTask(&Game::playerEnableSharedPartyExperience, player->getID(), sharedExpActive);
  1049. }
  1050.  
  1051. void ProtocolGame::parseQuestLine(NetworkMessage& msg)
  1052. {
  1053. uint16_t questId = msg.get<uint16_t>();
  1054. addGameTask(&Game::playerShowQuestLine, player->getID(), questId);
  1055. }
  1056.  
  1057. void ProtocolGame::parseMarketLeave()
  1058. {
  1059. addGameTask(&Game::playerLeaveMarket, player->getID());
  1060. }
  1061.  
  1062. void ProtocolGame::parseMarketBrowse(NetworkMessage& msg)
  1063. {
  1064. uint16_t browseId = msg.get<uint16_t>();
  1065.  
  1066. if (browseId == MARKETREQUEST_OWN_OFFERS) {
  1067. addGameTask(&Game::playerBrowseMarketOwnOffers, player->getID());
  1068. } else if (browseId == MARKETREQUEST_OWN_HISTORY) {
  1069. addGameTask(&Game::playerBrowseMarketOwnHistory, player->getID());
  1070. } else {
  1071. addGameTask(&Game::playerBrowseMarket, player->getID(), browseId);
  1072. }
  1073. }
  1074.  
  1075. void ProtocolGame::parseMarketCreateOffer(NetworkMessage& msg)
  1076. {
  1077. uint8_t type = msg.getByte();
  1078. uint16_t spriteId = msg.get<uint16_t>();
  1079. uint16_t amount = msg.get<uint16_t>();
  1080. uint32_t price = msg.get<uint32_t>();
  1081. bool anonymous = (msg.getByte() != 0);
  1082. addGameTask(&Game::playerCreateMarketOffer, player->getID(), type, spriteId, amount, price, anonymous);
  1083. }
  1084.  
  1085. void ProtocolGame::parseMarketCancelOffer(NetworkMessage& msg)
  1086. {
  1087. uint32_t timestamp = msg.get<uint32_t>();
  1088. uint16_t counter = msg.get<uint16_t>();
  1089. addGameTask(&Game::playerCancelMarketOffer, player->getID(), timestamp, counter);
  1090. }
  1091.  
  1092. void ProtocolGame::parseMarketAcceptOffer(NetworkMessage& msg)
  1093. {
  1094. uint32_t timestamp = msg.get<uint32_t>();
  1095. uint16_t counter = msg.get<uint16_t>();
  1096. uint16_t amount = msg.get<uint16_t>();
  1097. addGameTask(&Game::playerAcceptMarketOffer, player->getID(), timestamp, counter, amount);
  1098. }
  1099.  
  1100. void ProtocolGame::parseStoreOpen()
  1101. {
  1102. return sendStore();
  1103. }
  1104.  
  1105. void ProtocolGame::parseStoreSelectCategory(NetworkMessage& msg)
  1106. {
  1107. msg.getByte(); // ??
  1108. std::string categoryName = msg.getString();
  1109.  
  1110. for (auto& category : g_store->getCategories()) {
  1111. if (category.getName() == categoryName) {
  1112. return g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::sendStoreOffers, getThis(), category)));
  1113. }
  1114. }
  1115. }
  1116.  
  1117. void ProtocolGame::parseStoreBuyOffer(NetworkMessage& msg)
  1118. {
  1119. uint32_t offerId = msg.get<uint32_t>();
  1120. StoreOfferType_t offerType = static_cast<StoreOfferType_t>(msg.getByte());
  1121. std::string param;
  1122.  
  1123. if (offerType == STORE_OFFERTYPE_NAMECHANGE) {
  1124. param = msg.getString();
  1125. }
  1126.  
  1127. auto offer = g_store->getOfferById(offerId);
  1128. if (!offer) {
  1129. sendStoreError(STORE_ERROR_PURCHASE, "Offer not found. Please reopen the store and try again.");
  1130. return;
  1131. }
  1132.  
  1133. if ((strcasecmp(offer->getName().c_str(), "name change") == 0) && offerType != STORE_OFFERTYPE_NAMECHANGE) {
  1134. requestPurchaseData(offerId, STORE_OFFERTYPE_NAMECHANGE);
  1135. return;
  1136. }
  1137.  
  1138. addGameTask(&Game::playerPurchaseStoreOffer, player->getID(), offerId, param);
  1139. }
  1140.  
  1141. void ProtocolGame::parseStoreOpenHistory(NetworkMessage& msg)
  1142. {
  1143. storeHistoryEntriesPerPage = msg.getByte();
  1144.  
  1145. g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::sendStoreHistory, getThis(), currentPage, storeHistoryEntriesPerPage)));
  1146. }
  1147.  
  1148. void ProtocolGame::parseStoreRequestHistory(NetworkMessage& msg)
  1149. {
  1150. currentPage = msg.get<uint32_t>();
  1151.  
  1152. g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::sendStoreHistory, getThis(), currentPage, storeHistoryEntriesPerPage)));
  1153. }
  1154.  
  1155. void ProtocolGame::parseTransferCoins(NetworkMessage& msg)
  1156. {
  1157. std::string recipient = msg.getString();
  1158. uint16_t amount = msg.get<uint16_t>();
  1159.  
  1160. addGameTask(&Game::playerTransferCoins, player->getID(), recipient, amount);
  1161. }
  1162.  
  1163. void ProtocolGame::parseModalWindowAnswer(NetworkMessage& msg)
  1164. {
  1165. uint32_t id = msg.get<uint32_t>();
  1166. uint8_t button = msg.getByte();
  1167. uint8_t choice = msg.getByte();
  1168. addGameTask(&Game::playerAnswerModalWindow, player->getID(), id, button, choice);
  1169. }
  1170.  
  1171. void ProtocolGame::parseBrowseField(NetworkMessage& msg)
  1172. {
  1173. const Position& pos = msg.getPosition();
  1174. addGameTask(&Game::playerBrowseField, player->getID(), pos);
  1175. }
  1176.  
  1177. void ProtocolGame::parseSeekInContainer(NetworkMessage& msg)
  1178. {
  1179. uint8_t containerId = msg.getByte();
  1180. uint16_t index = msg.get<uint16_t>();
  1181. addGameTask(&Game::playerSeekInContainer, player->getID(), containerId, index);
  1182. }
  1183.  
  1184. // Send methods
  1185. void ProtocolGame::sendOpenPrivateChannel(const std::string& receiver)
  1186. {
  1187. NetworkMessage msg;
  1188. msg.addByte(0xAD);
  1189. msg.addString(receiver);
  1190. writeToOutputBuffer(msg);
  1191. }
  1192.  
  1193. void ProtocolGame::sendChannelEvent(uint16_t channelId, const std::string& playerName, ChannelEvent_t channelEvent)
  1194. {
  1195. NetworkMessage msg;
  1196. msg.addByte(0xF3);
  1197. msg.add<uint16_t>(channelId);
  1198. msg.addString(playerName);
  1199. msg.addByte(channelEvent);
  1200. writeToOutputBuffer(msg);
  1201. }
  1202.  
  1203. void ProtocolGame::sendCreatureOutfit(const Creature* creature, const Outfit_t& outfit)
  1204. {
  1205. if (!canSee(creature)) {
  1206. return;
  1207. }
  1208.  
  1209. NetworkMessage msg;
  1210. msg.addByte(0x8E);
  1211. msg.add<uint32_t>(creature->getID());
  1212. AddOutfit(msg, outfit);
  1213. writeToOutputBuffer(msg);
  1214. }
  1215.  
  1216. void ProtocolGame::sendCreatureWalkthrough(const Creature* creature, bool walkthrough)
  1217. {
  1218. if (!canSee(creature)) {
  1219. return;
  1220. }
  1221.  
  1222. NetworkMessage msg;
  1223. msg.addByte(0x92);
  1224. msg.add<uint32_t>(creature->getID());
  1225. msg.addByte(walkthrough ? 0x00 : 0x01);
  1226. writeToOutputBuffer(msg);
  1227. }
  1228.  
  1229. void ProtocolGame::sendCreatureShield(const Creature* creature)
  1230. {
  1231. if (!canSee(creature)) {
  1232. return;
  1233. }
  1234.  
  1235. NetworkMessage msg;
  1236. msg.addByte(0x91);
  1237. msg.add<uint32_t>(creature->getID());
  1238. msg.addByte(player->getPartyShield(creature->getPlayer()));
  1239. writeToOutputBuffer(msg);
  1240. }
  1241.  
  1242. void ProtocolGame::sendCreatureSkull(const Creature* creature)
  1243. {
  1244. if (g_game.getWorldType() != WORLD_TYPE_PVP) {
  1245. return;
  1246. }
  1247.  
  1248. if (!canSee(creature)) {
  1249. return;
  1250. }
  1251.  
  1252. NetworkMessage msg;
  1253. msg.addByte(0x90);
  1254. msg.add<uint32_t>(creature->getID());
  1255. msg.addByte(player->getSkullClient(creature));
  1256. writeToOutputBuffer(msg);
  1257. }
  1258.  
  1259. void ProtocolGame::sendCreatureType(uint32_t creatureId, uint8_t creatureType)
  1260. {
  1261. NetworkMessage msg;
  1262. msg.addByte(0x95);
  1263. msg.add<uint32_t>(creatureId);
  1264. msg.addByte(creatureType);
  1265. writeToOutputBuffer(msg);
  1266. }
  1267.  
  1268. void ProtocolGame::sendCreatureHelpers(uint32_t creatureId, uint16_t helpers)
  1269. {
  1270. NetworkMessage msg;
  1271. msg.addByte(0x94);
  1272. msg.add<uint32_t>(creatureId);
  1273. msg.add<uint16_t>(helpers);
  1274. writeToOutputBuffer(msg);
  1275. }
  1276.  
  1277. void ProtocolGame::sendCreatureSquare(const Creature* creature, SquareColor_t color)
  1278. {
  1279. if (!canSee(creature)) {
  1280. return;
  1281. }
  1282.  
  1283. NetworkMessage msg;
  1284. msg.addByte(0x93);
  1285. msg.add<uint32_t>(creature->getID());
  1286. msg.addByte(0x01);
  1287. msg.addByte(color);
  1288. writeToOutputBuffer(msg);
  1289. }
  1290.  
  1291. void ProtocolGame::sendTutorial(uint8_t tutorialId)
  1292. {
  1293. NetworkMessage msg;
  1294. msg.addByte(0xDC);
  1295. msg.addByte(tutorialId);
  1296. writeToOutputBuffer(msg);
  1297. }
  1298.  
  1299. void ProtocolGame::sendAddMarker(const Position& pos, uint8_t markType, const std::string& desc)
  1300. {
  1301. NetworkMessage msg;
  1302. msg.addByte(0xDD);
  1303. msg.addPosition(pos);
  1304. msg.addByte(markType);
  1305. msg.addString(desc);
  1306. writeToOutputBuffer(msg);
  1307. }
  1308.  
  1309. void ProtocolGame::sendReLoginWindow(uint8_t unfairFightReduction)
  1310. {
  1311. NetworkMessage msg;
  1312. msg.addByte(0x28);
  1313. msg.addByte(0x00);
  1314. msg.addByte(unfairFightReduction);
  1315. writeToOutputBuffer(msg);
  1316. }
  1317.  
  1318. void ProtocolGame::sendTextMessage(const TextMessage& message)
  1319. {
  1320. NetworkMessage msg;
  1321. msg.addByte(0xB4);
  1322. msg.addByte(message.type);
  1323. switch (message.type) {
  1324. case MESSAGE_DAMAGE_DEALT:
  1325. case MESSAGE_DAMAGE_RECEIVED:
  1326. case MESSAGE_DAMAGE_OTHERS: {
  1327. msg.addPosition(message.position);
  1328. msg.add<uint32_t>(message.primary.value);
  1329. msg.addByte(message.primary.color);
  1330. msg.add<uint32_t>(message.secondary.value);
  1331. msg.addByte(message.secondary.color);
  1332. break;
  1333. }
  1334. case MESSAGE_HEALED:
  1335. case MESSAGE_HEALED_OTHERS:
  1336. case MESSAGE_EXPERIENCE:
  1337. case MESSAGE_EXPERIENCE_OTHERS: {
  1338. msg.addPosition(message.position);
  1339. msg.add<uint32_t>(message.primary.value);
  1340. msg.addByte(message.primary.color);
  1341. break;
  1342. }
  1343.  
  1344. default: {
  1345. break;
  1346. }
  1347. }
  1348. msg.addString(message.text);
  1349. writeToOutputBuffer(msg);
  1350. }
  1351.  
  1352. void ProtocolGame::sendClosePrivate(uint16_t channelId)
  1353. {
  1354. NetworkMessage msg;
  1355. msg.addByte(0xB3);
  1356. msg.add<uint16_t>(channelId);
  1357. writeToOutputBuffer(msg);
  1358. }
  1359.  
  1360. void ProtocolGame::sendCreatePrivateChannel(uint16_t channelId, const std::string& channelName)
  1361. {
  1362. NetworkMessage msg;
  1363. msg.addByte(0xB2);
  1364. msg.add<uint16_t>(channelId);
  1365. msg.addString(channelName);
  1366. msg.add<uint16_t>(0x01);
  1367. msg.addString(player->getName());
  1368. msg.add<uint16_t>(0x00);
  1369. writeToOutputBuffer(msg);
  1370. }
  1371.  
  1372. void ProtocolGame::sendChannelsDialog()
  1373. {
  1374. NetworkMessage msg;
  1375. msg.addByte(0xAB);
  1376.  
  1377. const ChannelList& list = g_chat->getChannelList(*player);
  1378. msg.addByte(list.size());
  1379. for (ChatChannel* channel : list) {
  1380. msg.add<uint16_t>(channel->getId());
  1381. msg.addString(channel->getName());
  1382. }
  1383.  
  1384. writeToOutputBuffer(msg);
  1385. }
  1386.  
  1387. /*void ProtocolGame::sendChannelMessage(const std::string& author, const std::string& text, SpeakClasses type, uint16_t channel)
  1388. {
  1389. NetworkMessage msg;
  1390. msg.addByte(0xAA);
  1391. msg.add<uint32_t>(0x00);
  1392. msg.addString(author);
  1393. msg.add<uint16_t>(0x00);
  1394. msg.addByte(type);
  1395. msg.add<uint16_t>(channel);
  1396. msg.addString(text);
  1397. writeToOutputBuffer(msg);
  1398. }*/
  1399.  
  1400. void ProtocolGame::sendIcons(uint16_t icons)
  1401. {
  1402. NetworkMessage msg;
  1403. msg.addByte(0xA2);
  1404. msg.add<uint16_t>(icons);
  1405. writeToOutputBuffer(msg);
  1406. }
  1407.  
  1408. void ProtocolGame::sendMessageDialog(MessageDialog_t type, const std::string& message)
  1409. {
  1410. NetworkMessage msg;
  1411. msg.addByte(0xED);
  1412. msg.addByte(type);
  1413. msg.addString(message);
  1414. writeToOutputBuffer(msg);
  1415. }
  1416. void ProtocolGame::sendRefreshImbuingDialog(Item* item)
  1417. {
  1418. std::vector<Imbuement*> imbuements = g_imbuements.getImbuements(player, item);
  1419. if (imbuements.empty()) {
  1420. player->sendTextMessage(MESSAGE_EVENT_ADVANCE, "This item is not imbuable.");
  1421. return;
  1422. }
  1423. player->sendResourceData(RESOURCETYPE_BANK_GOLD, player->getBankBalance());
  1424. player->sendResourceData(RESOURCETYPE_INVENTORY_GOLD, player->getMoney());
  1425. player->setImbueItem(item);
  1426. NetworkMessage msg;
  1427. msg.addByte(0xEB);
  1428. msg.add<uint16_t>(item->getClientID());
  1429. int16_t slots;
  1430. g_imbuements.getItemSlotCount(item->getID(), slots);
  1431. msg.addByte(slots);
  1432. auto addImbuementData = [&](Imbuement* ib) {
  1433. msg.add<uint32_t>(ib->getId());
  1434. msg.addString(ib->getName());
  1435. msg.addString(ib->getDescription());
  1436. msg.addString(ib->getGroup());
  1437. //msg.add<uint16_t>(item->getClientID());
  1438. msg.add<uint16_t>(ib->imageId);
  1439. msg.add<uint32_t>(ib->getDuration());
  1440. msg.addByte(0x00); // premium only
  1441. const auto& sources = ib->getAstralSources();
  1442. msg.addByte(sources.size());
  1443. for (const auto& source : sources) {
  1444. const ItemType& it = Item::items[source.first];
  1445. msg.add<uint16_t>(it.clientId);
  1446. msg.addString(it.name);
  1447. msg.add<uint16_t>(source.second);
  1448. }
  1449. msg.add<uint32_t>(ib->getCost());
  1450. msg.addByte(ib->getSuccessRate());
  1451. msg.add<uint32_t>(ib->getProtectionCost());
  1452. };
  1453. for (int32_t offset = 0; offset < slots; offset++) {
  1454. uint32_t info = item->getIntAttr(static_cast<itemAttrTypes>(ITEM_ATTRIBUTE_IMBUEMENT_0 << offset));
  1455. int32_t duration = info >> 8;
  1456. uint16_t id = info & 0xFF;
  1457. if (duration) {
  1458. msg.addByte(0x01);
  1459. Imbuement* ib = g_imbuements.getImbuement(id);
  1460. addImbuementData(ib);
  1461. msg.add<uint32_t>(duration);
  1462. msg.add<uint32_t>(ib->getRemovalCost());
  1463. } else {
  1464. msg.addByte(0x00);
  1465. }
  1466. }
  1467. msg.add<uint16_t>(imbuements.size());
  1468. std::unordered_map<uint16_t, uint16_t> astralSources;
  1469. for (Imbuement* ib : imbuements) {
  1470. addImbuementData(ib);
  1471. const auto& sources = ib->getAstralSources();
  1472. for (const auto& source : sources) {
  1473. if (!astralSources.count(source.first)) {
  1474. astralSources[source.first] = player->getItemTypeCount(source.first);
  1475. }
  1476. }
  1477. }
  1478. msg.add<uint32_t>(astralSources.size());
  1479. for (const auto& source : astralSources) {
  1480. const ItemType& it = Item::items[source.first];
  1481. msg.add<uint16_t>(it.clientId);
  1482. msg.add<uint16_t>(source.second);
  1483. }
  1484. writeToOutputBuffer(msg);
  1485. }
  1486.  
  1487. void ProtocolGame::sendShop(Npc* npc, const ShopInfoList& itemList)
  1488. {
  1489. NetworkMessage msg;
  1490. msg.addByte(0x7A);
  1491. msg.addString(npc->getName());
  1492.  
  1493. uint16_t itemsToSend = std::min<size_t>(itemList.size(), std::numeric_limits<uint16_t>::max());
  1494. msg.add<uint16_t>(itemsToSend);
  1495.  
  1496. uint16_t i = 0;
  1497. for (ShopInfoList::const_iterator it = itemList.begin(); i < itemsToSend; ++it, ++i) {
  1498. AddShopItem(msg, *it);
  1499. }
  1500.  
  1501. writeToOutputBuffer(msg);
  1502. }
  1503.  
  1504. void ProtocolGame::sendCloseShop()
  1505. {
  1506. NetworkMessage msg;
  1507. msg.addByte(0x7C);
  1508. writeToOutputBuffer(msg);
  1509. }
  1510.  
  1511. void ProtocolGame::sendResourceData(ResourceType_t resourceType, int64_t amount)
  1512. {
  1513. NetworkMessage msg;
  1514. msg.addByte(0xEE);
  1515. msg.addByte(resourceType);
  1516. msg.add<int64_t>(amount);
  1517. writeToOutputBuffer(msg);
  1518. }
  1519.  
  1520. void ProtocolGame::sendSaleItemList(const std::list<ShopInfo>& shop)
  1521. {
  1522. player->sendResourceData(RESOURCETYPE_BANK_GOLD, player->getBankBalance());
  1523. player->sendResourceData(RESOURCETYPE_INVENTORY_GOLD, player->getMoney());
  1524. NetworkMessage msg;
  1525. msg.addByte(0x7B);
  1526.  
  1527. //msg.add<uint64_t>(player->getMoney() + player->getBankBalance());
  1528. //msg.add<int64_t>(player->getMoney() + player->getBankBalance());
  1529. msg.add<int64_t>(player->getMoney() + player->getBankBalance());
  1530.  
  1531. std::map<uint16_t, uint32_t> saleMap;
  1532.  
  1533. if (shop.size() <= 5) {
  1534. // For very small shops it's not worth it to create the complete map
  1535. for (const ShopInfo& shopInfo : shop) {
  1536. if (shopInfo.sellPrice == 0) {
  1537. continue;
  1538. }
  1539.  
  1540. int8_t subtype = -1;
  1541.  
  1542. const ItemType& itemType = Item::items[shopInfo.itemId];
  1543. if (itemType.hasSubType() && !itemType.stackable) {
  1544. subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType);
  1545. }
  1546.  
  1547. uint32_t count = player->getItemTypeCount(shopInfo.itemId, subtype);
  1548. if (count > 0) {
  1549. saleMap[shopInfo.itemId] = count;
  1550. }
  1551. }
  1552. } else {
  1553. // Large shop, it's better to get a cached map of all item counts and use it
  1554. // We need a temporary map since the finished map should only contain items
  1555. // available in the shop
  1556. std::map<uint32_t, uint32_t> tempSaleMap;
  1557. player->getAllItemTypeCount(tempSaleMap);
  1558.  
  1559. // We must still check manually for the special items that require subtype matches
  1560. // (That is, fluids such as potions etc., actually these items are very few since
  1561. // health potions now use their own ID)
  1562. for (const ShopInfo& shopInfo : shop) {
  1563. if (shopInfo.sellPrice == 0) {
  1564. continue;
  1565. }
  1566.  
  1567. int8_t subtype = -1;
  1568.  
  1569. const ItemType& itemType = Item::items[shopInfo.itemId];
  1570. if (itemType.hasSubType() && !itemType.stackable) {
  1571. subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType);
  1572. }
  1573.  
  1574. if (subtype != -1) {
  1575. uint32_t count;
  1576. if (!itemType.isFluidContainer() && !itemType.isSplash()) {
  1577. count = player->getItemTypeCount(shopInfo.itemId, subtype); // This shop item requires extra checks
  1578. } else {
  1579. count = subtype;
  1580. }
  1581.  
  1582. if (count > 0) {
  1583. saleMap[shopInfo.itemId] = count;
  1584. }
  1585. } else {
  1586. std::map<uint32_t, uint32_t>::const_iterator findIt = tempSaleMap.find(shopInfo.itemId);
  1587. if (findIt != tempSaleMap.end() && findIt->second > 0) {
  1588. saleMap[shopInfo.itemId] = findIt->second;
  1589. }
  1590. }
  1591. }
  1592. }
  1593.  
  1594. uint8_t itemsToSend = std::min<size_t>(saleMap.size(), std::numeric_limits<uint8_t>::max());
  1595. msg.addByte(itemsToSend);
  1596.  
  1597. uint8_t i = 0;
  1598. for (std::map<uint16_t, uint32_t>::const_iterator it = saleMap.begin(); i < itemsToSend; ++it, ++i) {
  1599. msg.addItemId(it->first);
  1600. msg.addByte(std::min<uint32_t>(it->second, std::numeric_limits<uint8_t>::max()));
  1601. }
  1602.  
  1603. writeToOutputBuffer(msg);
  1604. }
  1605.  
  1606. void ProtocolGame::sendMarketEnter(uint32_t depotId)
  1607. {
  1608. player->sendResourceData(RESOURCETYPE_BANK_GOLD, player->getBankBalance());
  1609. player->sendResourceData(RESOURCETYPE_INVENTORY_GOLD, player->getMoney());
  1610. NetworkMessage msg;
  1611. msg.addByte(0xF6);
  1612.  
  1613. msg.add<uint64_t>(player->getBankBalance());
  1614. msg.addByte(std::min<uint32_t>(IOMarket::getPlayerOfferCount(player->getGUID()), std::numeric_limits<uint8_t>::max()));
  1615.  
  1616. Container* depotChest = player->lastDepotChest;
  1617. if (!depotChest) {
  1618. msg.add<uint16_t>(0x00);
  1619. writeToOutputBuffer(msg);
  1620. return;
  1621. }
  1622.  
  1623. player->setInMarket(true);
  1624.  
  1625. std::map<uint16_t, uint32_t> depotItems;
  1626. std::forward_list<Container*> containerList { depotChest, player->getInbox() };
  1627.  
  1628. do {
  1629. Container* container = containerList.front();
  1630. containerList.pop_front();
  1631.  
  1632. for (Item* item : container->getItemList()) {
  1633. Container* c = item->getContainer();
  1634. if (c && !c->empty()) {
  1635. containerList.push_front(c);
  1636. continue;
  1637. }
  1638.  
  1639. const ItemType& itemType = Item::items[item->getID()];
  1640. if (itemType.wareId == 0) {
  1641. continue;
  1642. }
  1643.  
  1644. if (c && (!itemType.isContainer() || c->capacity() != itemType.maxItems)) {
  1645. continue;
  1646. }
  1647.  
  1648. if (!item->hasMarketAttributes()) {
  1649. continue;
  1650. }
  1651.  
  1652. depotItems[itemType.wareId] += Item::countByType(item, -1);
  1653. }
  1654. } while (!containerList.empty());
  1655.  
  1656. uint16_t itemsToSend = std::min<size_t>(depotItems.size(), std::numeric_limits<uint16_t>::max());
  1657. msg.add<uint16_t>(itemsToSend);
  1658.  
  1659. uint16_t i = 0;
  1660. for (std::map<uint16_t, uint32_t>::const_iterator it = depotItems.begin(); i < itemsToSend; ++it, ++i) {
  1661. msg.add<uint16_t>(it->first);
  1662. msg.add<uint16_t>(std::min<uint32_t>(0xFFFF, it->second));
  1663. }
  1664.  
  1665. writeToOutputBuffer(msg);
  1666.  
  1667. updateCoinBalance();
  1668. }
  1669.  
  1670. void ProtocolGame::sendMarketLeave()
  1671. {
  1672. NetworkMessage msg;
  1673. msg.addByte(0xF7);
  1674. writeToOutputBuffer(msg);
  1675. }
  1676.  
  1677. void ProtocolGame::sendMarketBrowseItem(uint16_t itemId, const MarketOfferList& buyOffers, const MarketOfferList& sellOffers)
  1678. {
  1679. NetworkMessage msg;
  1680.  
  1681. msg.addByte(0xF9);
  1682. msg.addItemId(itemId);
  1683.  
  1684. msg.add<uint32_t>(buyOffers.size());
  1685. for (const MarketOffer& offer : buyOffers) {
  1686. msg.add<uint32_t>(offer.timestamp);
  1687. msg.add<uint16_t>(offer.counter);
  1688. msg.add<uint16_t>(offer.amount);
  1689. msg.add<uint32_t>(offer.price);
  1690. msg.addString(offer.playerName);
  1691. }
  1692.  
  1693. msg.add<uint32_t>(sellOffers.size());
  1694. for (const MarketOffer& offer : sellOffers) {
  1695. msg.add<uint32_t>(offer.timestamp);
  1696. msg.add<uint16_t>(offer.counter);
  1697. msg.add<uint16_t>(offer.amount);
  1698. msg.add<uint32_t>(offer.price);
  1699. msg.addString(offer.playerName);
  1700. }
  1701.  
  1702. writeToOutputBuffer(msg);
  1703. }
  1704.  
  1705. void ProtocolGame::sendMarketAcceptOffer(const MarketOfferEx& offer)
  1706. {
  1707. NetworkMessage msg;
  1708. msg.addByte(0xF9);
  1709. msg.addItemId(offer.itemId);
  1710.  
  1711. if (offer.type == MARKETACTION_BUY) {
  1712. msg.add<uint32_t>(0x01);
  1713. msg.add<uint32_t>(offer.timestamp);
  1714. msg.add<uint16_t>(offer.counter);
  1715. msg.add<uint16_t>(offer.amount);
  1716. msg.add<uint32_t>(offer.price);
  1717. msg.addString(offer.playerName);
  1718. msg.add<uint32_t>(0x00);
  1719. } else {
  1720. msg.add<uint32_t>(0x00);
  1721. msg.add<uint32_t>(0x01);
  1722. msg.add<uint32_t>(offer.timestamp);
  1723. msg.add<uint16_t>(offer.counter);
  1724. msg.add<uint16_t>(offer.amount);
  1725. msg.add<uint32_t>(offer.price);
  1726. msg.addString(offer.playerName);
  1727. }
  1728.  
  1729. writeToOutputBuffer(msg);
  1730. }
  1731.  
  1732. void ProtocolGame::sendMarketBrowseOwnOffers(const MarketOfferList& buyOffers, const MarketOfferList& sellOffers)
  1733. {
  1734. NetworkMessage msg;
  1735. msg.addByte(0xF9);
  1736. msg.add<uint16_t>(MARKETREQUEST_OWN_OFFERS);
  1737.  
  1738. msg.add<uint32_t>(buyOffers.size());
  1739. for (const MarketOffer& offer : buyOffers) {
  1740. msg.add<uint32_t>(offer.timestamp);
  1741. msg.add<uint16_t>(offer.counter);
  1742. msg.addItemId(offer.itemId);
  1743. msg.add<uint16_t>(offer.amount);
  1744. msg.add<uint32_t>(offer.price);
  1745. }
  1746.  
  1747. msg.add<uint32_t>(sellOffers.size());
  1748. for (const MarketOffer& offer : sellOffers) {
  1749. msg.add<uint32_t>(offer.timestamp);
  1750. msg.add<uint16_t>(offer.counter);
  1751. msg.addItemId(offer.itemId);
  1752. msg.add<uint16_t>(offer.amount);
  1753. msg.add<uint32_t>(offer.price);
  1754. }
  1755.  
  1756. writeToOutputBuffer(msg);
  1757. }
  1758.  
  1759. void ProtocolGame::sendMarketCancelOffer(const MarketOfferEx& offer)
  1760. {
  1761. NetworkMessage msg;
  1762. msg.addByte(0xF9);
  1763. msg.add<uint16_t>(MARKETREQUEST_OWN_OFFERS);
  1764.  
  1765. if (offer.type == MARKETACTION_BUY) {
  1766. msg.add<uint32_t>(0x01);
  1767. msg.add<uint32_t>(offer.timestamp);
  1768. msg.add<uint16_t>(offer.counter);
  1769. msg.addItemId(offer.itemId);
  1770. msg.add<uint16_t>(offer.amount);
  1771. msg.add<uint32_t>(offer.price);
  1772. msg.add<uint32_t>(0x00);
  1773. } else {
  1774. msg.add<uint32_t>(0x00);
  1775. msg.add<uint32_t>(0x01);
  1776. msg.add<uint32_t>(offer.timestamp);
  1777. msg.add<uint16_t>(offer.counter);
  1778. msg.addItemId(offer.itemId);
  1779. msg.add<uint16_t>(offer.amount);
  1780. msg.add<uint32_t>(offer.price);
  1781. }
  1782.  
  1783. writeToOutputBuffer(msg);
  1784. }
  1785.  
  1786. void ProtocolGame::sendMarketBrowseOwnHistory(const HistoryMarketOfferList& buyOffers, const HistoryMarketOfferList& sellOffers)
  1787. {
  1788. uint32_t i = 0;
  1789. std::map<uint32_t, uint16_t> counterMap;
  1790. uint32_t buyOffersToSend = std::min<uint32_t>(buyOffers.size(), 810 + std::max<int32_t>(0, 810 - sellOffers.size()));
  1791. uint32_t sellOffersToSend = std::min<uint32_t>(sellOffers.size(), 810 + std::max<int32_t>(0, 810 - buyOffers.size()));
  1792.  
  1793. NetworkMessage msg;
  1794. msg.addByte(0xF9);
  1795. msg.add<uint16_t>(MARKETREQUEST_OWN_HISTORY);
  1796.  
  1797. msg.add<uint32_t>(buyOffersToSend);
  1798. for (HistoryMarketOfferList::const_iterator it = buyOffers.begin(); i < buyOffersToSend; ++it, ++i) {
  1799. msg.add<uint32_t>(it->timestamp);
  1800. msg.add<uint16_t>(counterMap[it->timestamp]++);
  1801. msg.addItemId(it->itemId);
  1802. msg.add<uint16_t>(it->amount);
  1803. msg.add<uint32_t>(it->price);
  1804. msg.addByte(it->state);
  1805. }
  1806.  
  1807. counterMap.clear();
  1808. i = 0;
  1809.  
  1810. msg.add<uint32_t>(sellOffersToSend);
  1811. for (HistoryMarketOfferList::const_iterator it = sellOffers.begin(); i < sellOffersToSend; ++it, ++i) {
  1812. msg.add<uint32_t>(it->timestamp);
  1813. msg.add<uint16_t>(counterMap[it->timestamp]++);
  1814. msg.addItemId(it->itemId);
  1815. msg.add<uint16_t>(it->amount);
  1816. msg.add<uint32_t>(it->price);
  1817. msg.addByte(it->state);
  1818. }
  1819.  
  1820. writeToOutputBuffer(msg);
  1821. }
  1822.  
  1823. void ProtocolGame::sendMarketDetail(uint16_t itemId)
  1824. {
  1825. NetworkMessage msg;
  1826. msg.addByte(0xF8);
  1827. msg.addItemId(itemId);
  1828.  
  1829. const ItemType& it = Item::items[itemId];
  1830. if (it.armor != 0) {
  1831. msg.addString(std::to_string(it.armor));
  1832. } else {
  1833. msg.add<uint16_t>(0x00);
  1834. }
  1835.  
  1836. if (it.attack != 0) {
  1837. // TODO: chance to hit, range
  1838. // example:
  1839. // "attack +x, chance to hit +y%, z fields"
  1840. if (it.abilities && it.abilities->elementType != COMBAT_NONE && it.abilities->elementDamage != 0) {
  1841. std::ostringstream ss;
  1842. ss << it.attack << " physical +" << it.abilities->elementDamage << ' ' << getCombatName(it.abilities->elementType);
  1843. msg.addString(ss.str());
  1844. } else {
  1845. msg.addString(std::to_string(it.attack));
  1846. }
  1847. } else {
  1848. msg.add<uint16_t>(0x00);
  1849. }
  1850.  
  1851. if (it.isContainer()) {
  1852. msg.addString(std::to_string(it.maxItems));
  1853. } else {
  1854. msg.add<uint16_t>(0x00);
  1855. }
  1856.  
  1857. if (it.defense != 0) {
  1858. if (it.extraDefense != 0) {
  1859. std::ostringstream ss;
  1860. ss << it.defense << ' ' << std::showpos << it.extraDefense << std::noshowpos;
  1861. msg.addString(ss.str());
  1862. } else {
  1863. msg.addString(std::to_string(it.defense));
  1864. }
  1865. } else {
  1866. msg.add<uint16_t>(0x00);
  1867. }
  1868.  
  1869. if (!it.description.empty()) {
  1870. const std::string& descr = it.description;
  1871. if (descr.back() == '.') {
  1872. msg.addString(std::string(descr, 0, descr.length() - 1));
  1873. } else {
  1874. msg.addString(descr);
  1875. }
  1876. } else {
  1877. msg.add<uint16_t>(0x00);
  1878. }
  1879.  
  1880. if (it.decayTime != 0) {
  1881. std::ostringstream ss;
  1882. ss << it.decayTime << " seconds";
  1883. msg.addString(ss.str());
  1884. } else {
  1885. msg.add<uint16_t>(0x00);
  1886. }
  1887.  
  1888. if (it.abilities) {
  1889. std::ostringstream ss;
  1890. bool separator = false;
  1891.  
  1892. for (size_t i = 0; i < COMBAT_COUNT; ++i) {
  1893. if (it.abilities->absorbPercent[i] == 0) {
  1894. continue;
  1895. }
  1896.  
  1897. if (separator) {
  1898. ss << ", ";
  1899. } else {
  1900. separator = true;
  1901. }
  1902.  
  1903. ss << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent[i] << std::noshowpos << '%';
  1904. }
  1905.  
  1906. msg.addString(ss.str());
  1907. } else {
  1908. msg.add<uint16_t>(0x00);
  1909. }
  1910.  
  1911. if (it.minReqLevel != 0) {
  1912. msg.addString(std::to_string(it.minReqLevel));
  1913. } else {
  1914. msg.add<uint16_t>(0x00);
  1915. }
  1916.  
  1917. if (it.minReqMagicLevel != 0) {
  1918. msg.addString(std::to_string(it.minReqMagicLevel));
  1919. } else {
  1920. msg.add<uint16_t>(0x00);
  1921. }
  1922.  
  1923. msg.addString(it.vocationString);
  1924.  
  1925. msg.addString(it.runeSpellName);
  1926.  
  1927. if (it.abilities) {
  1928. std::ostringstream ss;
  1929. bool separator = false;
  1930.  
  1931. for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; i++) {
  1932. if (!it.abilities->skills[i]) {
  1933. continue;
  1934. }
  1935.  
  1936. if (separator) {
  1937. ss << ", ";
  1938. } else {
  1939. separator = true;
  1940. }
  1941.  
  1942. ss << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << std::noshowpos;
  1943. }
  1944.  
  1945. if (it.abilities->stats[STAT_MAGICPOINTS] != 0) {
  1946. if (separator) {
  1947. ss << ", ";
  1948. } else {
  1949. separator = true;
  1950. }
  1951.  
  1952. ss << "magic level " << std::showpos << it.abilities->stats[STAT_MAGICPOINTS] << std::noshowpos;
  1953. }
  1954.  
  1955. if (it.abilities->speed != 0) {
  1956. if (separator) {
  1957. ss << ", ";
  1958. }
  1959.  
  1960. ss << "speed " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos;
  1961. }
  1962.  
  1963. msg.addString(ss.str());
  1964. } else {
  1965. msg.add<uint16_t>(0x00);
  1966. }
  1967.  
  1968. if (it.charges != 0) {
  1969. msg.addString(std::to_string(it.charges));
  1970. } else {
  1971. msg.add<uint16_t>(0x00);
  1972. }
  1973.  
  1974. std::string weaponName = getWeaponName(it.weaponType);
  1975.  
  1976. if (it.slotPosition & SLOTP_TWO_HAND) {
  1977. if (!weaponName.empty()) {
  1978. weaponName += ", two-handed";
  1979. } else {
  1980. weaponName = "two-handed";
  1981. }
  1982. }
  1983.  
  1984. msg.addString(weaponName);
  1985.  
  1986. if (it.weight != 0) {
  1987. std::ostringstream ss;
  1988. if (it.weight < 10) {
  1989. ss << "0.0" << it.weight;
  1990. } else if (it.weight < 100) {
  1991. ss << "0." << it.weight;
  1992. } else {
  1993. std::string weightString = std::to_string(it.weight);
  1994. weightString.insert(weightString.end() - 2, '.');
  1995. ss << weightString;
  1996. }
  1997. ss << " oz";
  1998. msg.addString(ss.str());
  1999. } else {
  2000. msg.add<uint16_t>(0x00);
  2001. }
  2002.  
  2003. int16_t slots;
  2004. if (g_imbuements.getItemSlotCount(itemId, slots) && slots > 0) {
  2005. msg.addString(std::to_string(slots));
  2006. } else {
  2007. msg.add<uint16_t>(0x00);
  2008. }
  2009.  
  2010. MarketStatistics* statistics = IOMarket::getInstance()->getPurchaseStatistics(itemId);
  2011. if (statistics) {
  2012. msg.addByte(0x01);
  2013. msg.add<uint32_t>(statistics->numTransactions);
  2014. msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), statistics->totalPrice));
  2015. msg.add<uint32_t>(statistics->highestPrice);
  2016. msg.add<uint32_t>(statistics->lowestPrice);
  2017. } else {
  2018. msg.addByte(0x00);
  2019. }
  2020.  
  2021. statistics = IOMarket::getInstance()->getSaleStatistics(itemId);
  2022. if (statistics) {
  2023. msg.addByte(0x01);
  2024. msg.add<uint32_t>(statistics->numTransactions);
  2025. msg.add<uint32_t>(std::min<uint64_t>(std::numeric_limits<uint32_t>::max(), statistics->totalPrice));
  2026. msg.add<uint32_t>(statistics->highestPrice);
  2027. msg.add<uint32_t>(statistics->lowestPrice);
  2028. } else {
  2029. msg.addByte(0x00);
  2030. }
  2031.  
  2032. writeToOutputBuffer(msg);
  2033. }
  2034.  
  2035. void ProtocolGame::sendStoreError(StoreError_t errorType, const std::string& message)
  2036. {
  2037. NetworkMessage msg;
  2038. msg.addByte(0xE0);
  2039.  
  2040. msg.addByte(errorType);
  2041. msg.addString(message);
  2042.  
  2043. writeToOutputBuffer(msg);
  2044. }
  2045.  
  2046. void ProtocolGame::requestPurchaseData(uint32_t offerId, StoreOfferType_t offerType)
  2047. {
  2048. NetworkMessage msg;
  2049. msg.addByte(0xE1);
  2050.  
  2051. msg.add<uint32_t>(offerId);
  2052. msg.addByte(offerType);
  2053.  
  2054. writeToOutputBuffer(msg);
  2055. }
  2056.  
  2057. void ProtocolGame::sendCoinBalance()
  2058. {
  2059. NetworkMessage msg;
  2060. msg.addByte(0xF2);
  2061. msg.addByte(0x01);
  2062.  
  2063. msg.addByte(0xDF);
  2064. msg.addByte(0x01);
  2065.  
  2066. msg.add<uint32_t>(player->coinBalance); //total coins
  2067. msg.add<uint32_t>(player->coinBalance); //transferable coins
  2068.  
  2069. writeToOutputBuffer(msg);
  2070. }
  2071.  
  2072. /*void ProtocolGame::updateCoinBalance()
  2073. {
  2074. NetworkMessage msg;
  2075. msg.addByte(0xF2);
  2076. msg.addByte(0x00);
  2077.  
  2078. writeToOutputBuffer(msg);
  2079.  
  2080. g_dispatcher.addTask(
  2081. createTask(std::bind([](ProtocolGame_ptr client) {
  2082. auto coinBalance = IOAccount::getCoinBalance(client->player->getAccount());
  2083. client->player->coinBalance = coinBalance;
  2084.  
  2085. client->sendCoinBalance();
  2086. }, getThis()))
  2087. );
  2088. }*/
  2089.  
  2090.  
  2091. // TAVA USANDO ESSE ABAIXO
  2092.  
  2093. /*void ProtocolGame::updateCoinBalance()
  2094. {
  2095. NetworkMessage msg;
  2096. msg.addByte(0xF2);
  2097. msg.addByte(0x00);
  2098.  
  2099. writeToOutputBuffer(msg);
  2100.  
  2101. g_dispatcher.addTask(
  2102. createTask(std::bind([](ProtocolGame* client) {
  2103. if (client) {
  2104. auto coinBalance = IOAccount::getCoinBalance(client->player->getAccount());
  2105. client->player->coinBalance = coinBalance;
  2106. client->sendCoinBalance();
  2107. }
  2108. }, this))
  2109. );
  2110. }*/
  2111.  
  2112.  
  2113. void ProtocolGame::updateCoinBalance()
  2114. {
  2115. NetworkMessage msg;
  2116. msg.addByte(0xF2);
  2117. msg.addByte(0x00);
  2118.  
  2119. writeToOutputBuffer(msg);
  2120.  
  2121. g_dispatcher.addTask(
  2122. createTask(std::bind([](ProtocolGame_ptr client) {
  2123. client->sendCoinBalance();
  2124. }, getThis()))
  2125. );
  2126. }
  2127.  
  2128. void ProtocolGame::sendStore()
  2129. {
  2130. NetworkMessage msg;
  2131. msg.addByte(0xFB);
  2132.  
  2133. msg.addByte(0x00);
  2134.  
  2135. msg.add<uint16_t>(g_store->getCategories().size());
  2136. for (auto& category : g_store->getCategories()) {
  2137. msg.addString(category.getName());
  2138. msg.addString(category.getDescription());
  2139. msg.addByte(category.getCategoryState());
  2140. /*msg.addByte(category.getState());*/
  2141.  
  2142. msg.addByte(category.getIcons().size());
  2143. for (const auto& icon : category.getIcons()) {
  2144. msg.addString(icon);
  2145. }
  2146.  
  2147. msg.addString(category.getParent());
  2148. }
  2149.  
  2150. writeToOutputBuffer(msg);
  2151.  
  2152. updateCoinBalance();
  2153. }
  2154.  
  2155. void ProtocolGame::sendStoreOffers(StoreCategory& category) {
  2156. NetworkMessage msg;
  2157. msg.addByte(0xFC);
  2158. msg.addString(category.getName());
  2159.  
  2160. msg.add<uint16_t>(category.getOffers().size());
  2161. for (auto& offer : category.getOffers()) {
  2162. msg.add<uint32_t>(offer.getId());
  2163. msg.addString(offer.getName());
  2164. msg.addString(offer.getDescription());
  2165.  
  2166. msg.add<uint32_t>(offer.getPrice());
  2167.  
  2168. msg.addByte(offer.getOfferState());
  2169.  
  2170. std::string reason = "";
  2171. bool enabled = g_store->executeOnRender(player, &offer, reason);
  2172. msg.addByte(!enabled);
  2173. if (!enabled) {
  2174. msg.addString(reason + "\n");
  2175. }
  2176.  
  2177. msg.addByte(offer.getIcons().size());
  2178. for (const auto& icon : offer.getIcons()) {
  2179. msg.addString(icon);
  2180. }
  2181.  
  2182. msg.add<uint16_t>(offer.getSubOffers().size());
  2183. for (auto& subOffer : offer.getSubOffers()) {
  2184. msg.addString(subOffer.getName());
  2185. msg.addString(subOffer.getDescription());
  2186.  
  2187. msg.addByte(subOffer.getIcons().size());
  2188. for (const auto& icon : subOffer.getIcons()) {
  2189. msg.addString(icon);
  2190. }
  2191. msg.addString(subOffer.getParent());
  2192. }
  2193. }
  2194.  
  2195. writeToOutputBuffer(msg);
  2196. }
  2197.  
  2198. void ProtocolGame::sendStoreHistory(uint32_t page, uint32_t entriesPerPage) {
  2199. // dispatcher thread
  2200. std::vector<StoreTransaction> storeHistory;
  2201. g_store->getTransactionHistory(player->getAccount(), page, entriesPerPage, storeHistory);
  2202.  
  2203. if (storeHistory.size() == 0) {
  2204. return sendStoreError(STORE_ERROR_HISTORY, "No entries yet.");
  2205. }
  2206.  
  2207. NetworkMessage msg;
  2208. msg.addByte(0xFD);
  2209. msg.add<uint32_t>(page);
  2210.  
  2211. int pages = storeHistory.size() / entriesPerPage + ((storeHistory.size() % entriesPerPage) > 0 ? 1 : 0);
  2212. msg.add<uint32_t>(pages);
  2213.  
  2214. msg.addByte((storeHistory.size() > entriesPerPage) ? entriesPerPage : storeHistory.size());
  2215.  
  2216. size_t count = 0;
  2217. for (const auto& entry : storeHistory) {
  2218. if (count++ >= entriesPerPage) {
  2219. break;
  2220. }
  2221.  
  2222. msg.add<uint32_t>(entry.timestamp);
  2223. msg.addByte(STORE_OFFERTYPE_OTHER); // offer type
  2224. msg.add<int32_t>(entry.premium_points);
  2225. msg.addString(entry.description);
  2226. }
  2227.  
  2228. writeToOutputBuffer(msg);
  2229. }
  2230.  
  2231. void ProtocolGame::sendStorePurchaseCompleted(const std::string& message)
  2232. {
  2233. NetworkMessage msg;
  2234. msg.addByte(0xFE);
  2235.  
  2236. msg.addByte(0x00);
  2237. msg.addString(message);
  2238. msg.add<uint32_t>(player->coinBalance);
  2239. msg.add<uint32_t>(player->coinBalance);
  2240.  
  2241. writeToOutputBuffer(msg);
  2242. }
  2243.  
  2244. void ProtocolGame::sendQuestLog()
  2245. {
  2246. NetworkMessage msg;
  2247. msg.addByte(0xF0);
  2248. msg.add<uint16_t>(g_game.quests.getQuestsCount(player));
  2249.  
  2250. for (const Quest& quest : g_game.quests.getQuests()) {
  2251. if (quest.isStarted(player)) {
  2252. msg.add<uint16_t>(quest.getID());
  2253. msg.addString(quest.getName());
  2254. msg.addByte(quest.isCompleted(player));
  2255. }
  2256. }
  2257.  
  2258. writeToOutputBuffer(msg);
  2259. }
  2260.  
  2261. void ProtocolGame::sendQuestLine(const Quest* quest)
  2262. {
  2263. NetworkMessage msg;
  2264. msg.addByte(0xF1);
  2265. msg.add<uint16_t>(quest->getID());
  2266. msg.addByte(quest->getMissionsCount(player));
  2267.  
  2268. for (const Mission& mission : quest->getMissions()) {
  2269. if (mission.isStarted(player)) {
  2270. msg.addString(mission.getName(player));
  2271. msg.addString(mission.getDescription(player));
  2272. }
  2273. }
  2274.  
  2275. writeToOutputBuffer(msg);
  2276. }
  2277.  
  2278. void ProtocolGame::sendTradeItemRequest(const std::string& traderName, const Item* item, bool ack)
  2279. {
  2280. NetworkMessage msg;
  2281.  
  2282. if (ack) {
  2283. msg.addByte(0x7D);
  2284. } else {
  2285. msg.addByte(0x7E);
  2286. }
  2287.  
  2288. msg.addString(traderName);
  2289.  
  2290. if (const Container* tradeContainer = item->getContainer()) {
  2291. std::list<const Container*> listContainer {tradeContainer};
  2292. std::list<const Item*> itemList {tradeContainer};
  2293. while (!listContainer.empty()) {
  2294. const Container* container = listContainer.front();
  2295. listContainer.pop_front();
  2296.  
  2297. for (Item* containerItem : container->getItemList()) {
  2298. Container* tmpContainer = containerItem->getContainer();
  2299. if (tmpContainer) {
  2300. listContainer.push_back(tmpContainer);
  2301. }
  2302. itemList.push_back(containerItem);
  2303. }
  2304. }
  2305.  
  2306. msg.addByte(itemList.size());
  2307. for (const Item* listItem : itemList) {
  2308. msg.addItem(listItem);
  2309. }
  2310. } else {
  2311. msg.addByte(0x01);
  2312. msg.addItem(item);
  2313. }
  2314. writeToOutputBuffer(msg);
  2315. }
  2316.  
  2317. void ProtocolGame::sendCloseTrade()
  2318. {
  2319. NetworkMessage msg;
  2320. msg.addByte(0x7F);
  2321. writeToOutputBuffer(msg);
  2322. }
  2323.  
  2324. void ProtocolGame::sendCloseContainer(uint8_t cid)
  2325. {
  2326. NetworkMessage msg;
  2327. msg.addByte(0x6F);
  2328. msg.addByte(cid);
  2329. writeToOutputBuffer(msg);
  2330. }
  2331.  
  2332. void ProtocolGame::sendCreatureTurn(const Creature* creature, uint32_t stackPos)
  2333. {
  2334. if (!canSee(creature)) {
  2335. return;
  2336. }
  2337.  
  2338. NetworkMessage msg;
  2339. msg.addByte(0x6B);
  2340. msg.addPosition(creature->getPosition());
  2341. msg.addByte(stackPos);
  2342. msg.add<uint16_t>(0x63);
  2343. msg.add<uint32_t>(creature->getID());
  2344. msg.addByte(creature->getDirection());
  2345. msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01);
  2346. writeToOutputBuffer(msg);
  2347. }
  2348.  
  2349. void ProtocolGame::sendCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, const Position* pos/* = nullptr*/)
  2350. {
  2351. NetworkMessage msg;
  2352. msg.addByte(0xAA);
  2353.  
  2354. static uint32_t statementId = 0;
  2355. msg.add<uint32_t>(++statementId);
  2356.  
  2357. msg.addString(creature->getName());
  2358.  
  2359. //Add level only for players
  2360. if (const Player* speaker = creature->getPlayer()) {
  2361. msg.add<uint16_t>(speaker->getLevel());
  2362. } else {
  2363. msg.add<uint16_t>(0x00);
  2364. }
  2365.  
  2366. msg.addByte(type);
  2367. if (pos) {
  2368. msg.addPosition(*pos);
  2369. } else {
  2370. msg.addPosition(creature->getPosition());
  2371. }
  2372.  
  2373. msg.addString(text);
  2374. writeToOutputBuffer(msg);
  2375. }
  2376.  
  2377. void ProtocolGame::sendToChannel(const Creature* creature, SpeakClasses type, const std::string& text, uint16_t channelId)
  2378. {
  2379. NetworkMessage msg;
  2380. msg.addByte(0xAA);
  2381.  
  2382. static uint32_t statementId = 0;
  2383. msg.add<uint32_t>(++statementId);
  2384. if (!creature) {
  2385. msg.add<uint32_t>(0x00);
  2386. } else if (type == TALKTYPE_CHANNEL_R2) {
  2387. msg.add<uint32_t>(0x00);
  2388. type = TALKTYPE_CHANNEL_R1;
  2389. } else {
  2390. msg.addString(creature->getName());
  2391. //Add level only for players
  2392. if (const Player* speaker = creature->getPlayer()) {
  2393. msg.add<uint16_t>(speaker->getLevel());
  2394. } else {
  2395. msg.add<uint16_t>(0x00);
  2396. }
  2397. }
  2398.  
  2399. msg.addByte(type);
  2400. msg.add<uint16_t>(channelId);
  2401. msg.addString(text);
  2402. writeToOutputBuffer(msg);
  2403. }
  2404.  
  2405. void ProtocolGame::sendPrivateMessage(const Player* speaker, SpeakClasses type, const std::string& text)
  2406. {
  2407. NetworkMessage msg;
  2408. msg.addByte(0xAA);
  2409. static uint32_t statementId = 0;
  2410. msg.add<uint32_t>(++statementId);
  2411. if (speaker) {
  2412. msg.addString(speaker->getName());
  2413. msg.add<uint16_t>(speaker->getLevel());
  2414. } else {
  2415. msg.add<uint32_t>(0x00);
  2416. }
  2417. msg.addByte(type);
  2418. msg.addString(text);
  2419. writeToOutputBuffer(msg);
  2420. }
  2421.  
  2422. void ProtocolGame::sendCancelTarget()
  2423. {
  2424. NetworkMessage msg;
  2425. msg.addByte(0xA3);
  2426. msg.add<uint32_t>(0x00);
  2427. writeToOutputBuffer(msg);
  2428. }
  2429.  
  2430. void ProtocolGame::sendChangeSpeed(const Creature* creature, uint32_t speed)
  2431. {
  2432. NetworkMessage msg;
  2433. msg.addByte(0x8F);
  2434. msg.add<uint32_t>(creature->getID());
  2435. msg.add<uint16_t>(creature->getBaseSpeed() / 2);
  2436. msg.add<uint16_t>(speed / 2);
  2437. writeToOutputBuffer(msg);
  2438. }
  2439.  
  2440. void ProtocolGame::sendDistanceShoot(const Position& from, const Position& to, uint8_t type)
  2441. {
  2442. NetworkMessage msg;
  2443. msg.addByte(0x85);
  2444. msg.addPosition(from);
  2445. msg.addPosition(to);
  2446. msg.addByte(type);
  2447. writeToOutputBuffer(msg);
  2448. }
  2449.  
  2450. void ProtocolGame::sendCreatureHealth(const Creature* creature)
  2451. {
  2452. NetworkMessage msg;
  2453. msg.addByte(0x8C);
  2454. msg.add<uint32_t>(creature->getID());
  2455.  
  2456. if (creature->isHealthHidden()) {
  2457. msg.addByte(0x00);
  2458. } else {
  2459. msg.addByte(std::ceil((static_cast<double>(creature->getHealth()) / std::max<int32_t>(creature->getMaxHealth(), 1)) * 100));
  2460. }
  2461. writeToOutputBuffer(msg);
  2462. }
  2463.  
  2464. void ProtocolGame::sendFYIBox(const std::string& message)
  2465. {
  2466. NetworkMessage msg;
  2467. msg.addByte(0x15);
  2468. msg.addString(message);
  2469. writeToOutputBuffer(msg);
  2470. }
  2471.  
  2472. //tile
  2473. void ProtocolGame::sendAddTileItem(const Position& pos, uint32_t stackpos, const Item* item)
  2474. {
  2475. if (!canSee(pos)) {
  2476. return;
  2477. }
  2478.  
  2479. NetworkMessage msg;
  2480. msg.addByte(0x6A);
  2481. msg.addPosition(pos);
  2482. msg.addByte(stackpos);
  2483. msg.addItem(item);
  2484. writeToOutputBuffer(msg);
  2485. }
  2486.  
  2487. void ProtocolGame::sendUpdateTileItem(const Position& pos, uint32_t stackpos, const Item* item)
  2488. {
  2489. if (!canSee(pos)) {
  2490. return;
  2491. }
  2492.  
  2493. NetworkMessage msg;
  2494. msg.addByte(0x6B);
  2495. msg.addPosition(pos);
  2496. msg.addByte(stackpos);
  2497. msg.addItem(item);
  2498. writeToOutputBuffer(msg);
  2499. }
  2500.  
  2501. void ProtocolGame::sendRemoveTileThing(const Position& pos, uint32_t stackpos)
  2502. {
  2503. if (!canSee(pos)) {
  2504. return;
  2505. }
  2506.  
  2507. NetworkMessage msg;
  2508. RemoveTileThing(msg, pos, stackpos);
  2509. writeToOutputBuffer(msg);
  2510. }
  2511.  
  2512. void ProtocolGame::sendFightModes()
  2513. {
  2514. NetworkMessage msg;
  2515. msg.addByte(0xA7);
  2516. msg.addByte(player->fightMode);
  2517. msg.addByte(player->chaseMode);
  2518. msg.addByte(player->secureMode);
  2519. msg.addByte(PVP_MODE_DOVE);
  2520. writeToOutputBuffer(msg);
  2521. }
  2522.  
  2523. void ProtocolGame::sendMoveCreature(const Creature* creature, const Position& newPos, int32_t newStackPos, const Position& oldPos, int32_t oldStackPos, bool teleport)
  2524. {
  2525. if (creature == player) {
  2526. if (oldStackPos >= 10) {
  2527. sendMapDescription(newPos);
  2528. } else if (teleport) {
  2529. NetworkMessage msg;
  2530. RemoveTileThing(msg, oldPos, oldStackPos);
  2531. writeToOutputBuffer(msg);
  2532. sendMapDescription(newPos);
  2533. } else {
  2534. NetworkMessage msg;
  2535. if (oldPos.z == 7 && newPos.z >= 8) {
  2536. RemoveTileThing(msg, oldPos, oldStackPos);
  2537. } else {
  2538. msg.addByte(0x6D);
  2539. msg.addPosition(oldPos);
  2540. msg.addByte(oldStackPos);
  2541. msg.addPosition(newPos);
  2542. }
  2543.  
  2544. if (newPos.z > oldPos.z) {
  2545. MoveDownCreature(msg, creature, newPos, oldPos);
  2546. } else if (newPos.z < oldPos.z) {
  2547. MoveUpCreature(msg, creature, newPos, oldPos);
  2548. }
  2549.  
  2550. if (oldPos.y > newPos.y) { // north, for old x
  2551. msg.addByte(0x65);
  2552. GetMapDescription(oldPos.x - 8, newPos.y - 6, newPos.z, 18, 1, msg);
  2553. } else if (oldPos.y < newPos.y) { // south, for old x
  2554. msg.addByte(0x67);
  2555. GetMapDescription(oldPos.x - 8, newPos.y + 7, newPos.z, 18, 1, msg);
  2556. }
  2557.  
  2558. if (oldPos.x < newPos.x) { // east, [with new y]
  2559. msg.addByte(0x66);
  2560. GetMapDescription(newPos.x + 9, newPos.y - 6, newPos.z, 1, 14, msg);
  2561. } else if (oldPos.x > newPos.x) { // west, [with new y]
  2562. msg.addByte(0x68);
  2563. GetMapDescription(newPos.x - 8, newPos.y - 6, newPos.z, 1, 14, msg);
  2564. }
  2565. writeToOutputBuffer(msg);
  2566. }
  2567. } else if (canSee(oldPos) && canSee(creature->getPosition())) {
  2568. if (teleport || (oldPos.z == 7 && newPos.z >= 8) || oldStackPos >= 10) {
  2569. sendRemoveTileThing(oldPos, oldStackPos);
  2570. sendAddCreature(creature, newPos, newStackPos, false);
  2571. } else {
  2572. NetworkMessage msg;
  2573. msg.addByte(0x6D);
  2574. msg.addPosition(oldPos);
  2575. msg.addByte(oldStackPos);
  2576. msg.addPosition(creature->getPosition());
  2577. writeToOutputBuffer(msg);
  2578. }
  2579. } else if (canSee(oldPos)) {
  2580. sendRemoveTileThing(oldPos, oldStackPos);
  2581. } else if (canSee(creature->getPosition())) {
  2582. sendAddCreature(creature, newPos, newStackPos, false);
  2583. }
  2584. }
  2585.  
  2586. void ProtocolGame::sendItems() {
  2587. NetworkMessage msg;
  2588. msg.addByte(0xF5);
  2589.  
  2590. const std::vector<uint16_t>& inventory = Item::items.getInventory();
  2591. msg.add<uint16_t>(inventory.size() + 11);
  2592. //for whatever reason, and I don't know why, real Tibia always sends 11 1-count objects at the beginning.
  2593. for (uint16_t i = 1; i <= 11; i++) {
  2594. msg.add<uint16_t>(i);
  2595. msg.addByte(0); //always 0
  2596. msg.add<uint16_t>(1); // always 1
  2597. }
  2598.  
  2599. for (int clientId : inventory) {
  2600. msg.add<uint16_t>(clientId);
  2601. msg.addByte(0); //always 0
  2602. msg.add<uint16_t>(1);
  2603. }
  2604.  
  2605. writeToOutputBuffer(msg);
  2606. }
  2607.  
  2608. void ProtocolGame::sendAddContainerItem(uint8_t cid, uint16_t slot, const Item* item)
  2609. {
  2610. NetworkMessage msg;
  2611. msg.addByte(0x70);
  2612. msg.addByte(cid);
  2613. msg.add<uint16_t>(slot);
  2614. msg.addItem(item);
  2615. writeToOutputBuffer(msg);
  2616. }
  2617.  
  2618. void ProtocolGame::sendUpdateContainerItem(uint8_t cid, uint16_t slot, const Item* item)
  2619. {
  2620. NetworkMessage msg;
  2621. msg.addByte(0x71);
  2622. msg.addByte(cid);
  2623. msg.add<uint16_t>(slot);
  2624. msg.addItem(item);
  2625. writeToOutputBuffer(msg);
  2626. }
  2627.  
  2628. void ProtocolGame::sendRemoveContainerItem(uint8_t cid, uint16_t slot, const Item* lastItem)
  2629. {
  2630. NetworkMessage msg;
  2631. msg.addByte(0x72);
  2632. msg.addByte(cid);
  2633. msg.add<uint16_t>(slot);
  2634. if (lastItem) {
  2635. msg.addItem(lastItem);
  2636. } else {
  2637. msg.add<uint16_t>(0x00);
  2638. }
  2639. writeToOutputBuffer(msg);
  2640. }
  2641.  
  2642. void ProtocolGame::sendTextWindow(uint32_t windowTextId, Item* item, uint16_t maxlen, bool canWrite)
  2643. {
  2644. NetworkMessage msg;
  2645. msg.addByte(0x96);
  2646. msg.add<uint32_t>(windowTextId);
  2647. msg.addItem(item);
  2648.  
  2649. if (canWrite) {
  2650. msg.add<uint16_t>(maxlen);
  2651. msg.addString(item->getText());
  2652. } else {
  2653. const std::string& text = item->getText();
  2654. msg.add<uint16_t>(text.size());
  2655. msg.addString(text);
  2656. }
  2657.  
  2658. const std::string& writer = item->getWriter();
  2659. if (!writer.empty()) {
  2660. msg.addString(writer);
  2661. } else {
  2662. msg.add<uint16_t>(0x00);
  2663. }
  2664.  
  2665. time_t writtenDate = item->getDate();
  2666. if (writtenDate != 0) {
  2667. msg.addString(formatDateShort(writtenDate));
  2668. } else {
  2669. msg.add<uint16_t>(0x00);
  2670. }
  2671.  
  2672. writeToOutputBuffer(msg);
  2673. }
  2674.  
  2675. void ProtocolGame::sendTextWindow(uint32_t windowTextId, uint32_t itemId, const std::string& text)
  2676. {
  2677. NetworkMessage msg;
  2678. msg.addByte(0x96);
  2679. msg.add<uint32_t>(windowTextId);
  2680. msg.addItem(itemId, 1);
  2681. msg.add<uint16_t>(text.size());
  2682. msg.addString(text);
  2683. msg.add<uint16_t>(0x00);
  2684. msg.add<uint16_t>(0x00);
  2685. writeToOutputBuffer(msg);
  2686. }
  2687.  
  2688. void ProtocolGame::sendHouseWindow(uint32_t windowTextId, const std::string& text)
  2689. {
  2690. NetworkMessage msg;
  2691. msg.addByte(0x97);
  2692. msg.addByte(0x00);
  2693. msg.add<uint32_t>(windowTextId);
  2694. msg.addString(text);
  2695. writeToOutputBuffer(msg);
  2696. }
  2697.  
  2698. void ProtocolGame::sendOutfitWindow()
  2699. {
  2700. NetworkMessage msg;
  2701. msg.addByte(0xC8);
  2702.  
  2703. Outfit_t currentOutfit = player->getDefaultOutfit();
  2704. Mount* currentMount = g_game.mounts.getMountByID(player->getCurrentMount());
  2705. if (currentMount) {
  2706. currentOutfit.lookMount = currentMount->clientId;
  2707. }
  2708.  
  2709. AddOutfit(msg, currentOutfit);
  2710.  
  2711. std::vector<ProtocolOutfit> protocolOutfits;
  2712. if (player->isAccessPlayer()) {
  2713. static const std::string gamemasterOutfitName = "Gamemaster";
  2714. protocolOutfits.emplace_back(
  2715. &gamemasterOutfitName,
  2716. 75,
  2717. 0
  2718. );
  2719. }
  2720.  
  2721. const auto& outfits = Outfits::getInstance()->getOutfits(player->getSex());
  2722. protocolOutfits.reserve(outfits.size());
  2723. for (const Outfit& outfit : outfits) {
  2724. uint8_t addons;
  2725. if (!player->getOutfitAddons(outfit, addons)) {
  2726. continue;
  2727. }
  2728.  
  2729. protocolOutfits.emplace_back(
  2730. &outfit.name,
  2731. outfit.lookType,
  2732. addons
  2733. );
  2734. if (protocolOutfits.size() == 150) { // Game client doesn't allow more than 50 outfits
  2735. break;
  2736. }
  2737. }
  2738.  
  2739. msg.addByte(protocolOutfits.size());
  2740. for (const ProtocolOutfit& outfit : protocolOutfits) {
  2741. msg.add<uint16_t>(outfit.lookType);
  2742. msg.addString(*outfit.name);
  2743. msg.addByte(outfit.addons);
  2744. }
  2745.  
  2746. std::vector<const Mount*> mounts;
  2747. for (const Mount& mount : g_game.mounts.getMounts()) {
  2748. if (player->hasMount(&mount)) {
  2749. mounts.push_back(&mount);
  2750. }
  2751. }
  2752.  
  2753. msg.addByte(mounts.size());
  2754. for (const Mount* mount : mounts) {
  2755. msg.add<uint16_t>(mount->clientId);
  2756. msg.addString(mount->name);
  2757. }
  2758.  
  2759. writeToOutputBuffer(msg);
  2760. }
  2761.  
  2762. void ProtocolGame::sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus)
  2763. {
  2764. NetworkMessage msg;
  2765. msg.addByte(0xD3);
  2766. msg.add<uint32_t>(guid);
  2767. msg.addByte(newStatus);
  2768. writeToOutputBuffer(msg);
  2769. }
  2770.  
  2771. void ProtocolGame::sendSpellCooldown(uint8_t spellId, uint32_t time)
  2772. {
  2773. NetworkMessage msg;
  2774. msg.addByte(0xA4);
  2775. msg.addByte(spellId);
  2776. msg.add<uint32_t>(time);
  2777. writeToOutputBuffer(msg);
  2778. }
  2779.  
  2780. void ProtocolGame::sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time)
  2781. {
  2782. NetworkMessage msg;
  2783. msg.addByte(0xA5);
  2784. msg.addByte(groupId);
  2785. msg.add<uint32_t>(time);
  2786. writeToOutputBuffer(msg);
  2787. }
  2788.  
  2789. void ProtocolGame::sendModalWindow(const ModalWindow& modalWindow)
  2790. {
  2791. NetworkMessage msg;
  2792. msg.addByte(0xFA);
  2793.  
  2794. msg.add<uint32_t>(modalWindow.id);
  2795. msg.addString(modalWindow.title);
  2796. msg.addString(modalWindow.message);
  2797.  
  2798. msg.addByte(modalWindow.buttons.size());
  2799. for (const auto& it : modalWindow.buttons) {
  2800. msg.addString(it.first);
  2801. msg.addByte(it.second);
  2802. }
  2803.  
  2804. msg.addByte(modalWindow.choices.size());
  2805. for (const auto& it : modalWindow.choices) {
  2806. msg.addString(it.first);
  2807. msg.addByte(it.second);
  2808. }
  2809.  
  2810. msg.addByte(modalWindow.defaultEscapeButton);
  2811. msg.addByte(modalWindow.defaultEnterButton);
  2812. msg.addByte(modalWindow.priority ? 0x01 : 0x00);
  2813.  
  2814. writeToOutputBuffer(msg);
  2815. }
  2816.  
  2817. ////////////// Add common messages
  2818. void ProtocolGame::MoveUpCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos, const Position& oldPos)
  2819. {
  2820. if (creature != player) {
  2821. return;
  2822. }
  2823.  
  2824. //floor change up
  2825. msg.addByte(0xBE);
  2826.  
  2827. //going to surface
  2828. if (newPos.z == 7) {
  2829. int32_t skip = -1;
  2830. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 5, 18, 14, 3, skip); //(floor 7 and 6 already set)
  2831. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 4, 18, 14, 4, skip);
  2832. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 3, 18, 14, 5, skip);
  2833. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 2, 18, 14, 6, skip);
  2834. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 1, 18, 14, 7, skip);
  2835. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, 0, 18, 14, 8, skip);
  2836.  
  2837. if (skip >= 0) {
  2838. msg.addByte(skip);
  2839. msg.addByte(0xFF);
  2840. }
  2841. }
  2842. //underground, going one floor up (still underground)
  2843. else if (newPos.z > 7) {
  2844. int32_t skip = -1;
  2845. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, oldPos.getZ() - 3, 18, 14, 3, skip);
  2846.  
  2847. if (skip >= 0) {
  2848. msg.addByte(skip);
  2849. msg.addByte(0xFF);
  2850. }
  2851. }
  2852.  
  2853. //moving up a floor up makes us out of sync
  2854. //west
  2855. msg.addByte(0x68);
  2856. GetMapDescription(oldPos.x - 8, oldPos.y - 5, newPos.z, 1, 14, msg);
  2857.  
  2858. //north
  2859. msg.addByte(0x65);
  2860. GetMapDescription(oldPos.x - 8, oldPos.y - 6, newPos.z, 18, 1, msg);
  2861. }
  2862.  
  2863. void ProtocolGame::MoveDownCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos, const Position& oldPos)
  2864. {
  2865. if (creature != player) {
  2866. return;
  2867. }
  2868.  
  2869. //floor change down
  2870. msg.addByte(0xBF);
  2871.  
  2872. //going from surface to underground
  2873. if (newPos.z == 8) {
  2874. int32_t skip = -1;
  2875.  
  2876. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z, 18, 14, -1, skip);
  2877. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 1, 18, 14, -2, skip);
  2878. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 2, 18, 14, -3, skip);
  2879.  
  2880. if (skip >= 0) {
  2881. msg.addByte(skip);
  2882. msg.addByte(0xFF);
  2883. }
  2884. }
  2885. //going further down
  2886. else if (newPos.z > oldPos.z && newPos.z > 8 && newPos.z < 14) {
  2887. int32_t skip = -1;
  2888. GetFloorDescription(msg, oldPos.x - 8, oldPos.y - 6, newPos.z + 2, 18, 14, -3, skip);
  2889.  
  2890. if (skip >= 0) {
  2891. msg.addByte(skip);
  2892. msg.addByte(0xFF);
  2893. }
  2894. }
  2895.  
  2896. //moving down a floor makes us out of sync
  2897. //east
  2898. msg.addByte(0x66);
  2899. GetMapDescription(oldPos.x + 9, oldPos.y - 7, newPos.z, 1, 14, msg);
  2900.  
  2901. //south
  2902. msg.addByte(0x67);
  2903. GetMapDescription(oldPos.x - 8, oldPos.y + 7, newPos.z, 18, 1, msg);
  2904. }
  2905.  
  2906. void ProtocolGame::AddShopItem(NetworkMessage& msg, const ShopInfo& item)
  2907. {
  2908. const ItemType& it = Item::items[item.itemId];
  2909. msg.add<uint16_t>(it.clientId);
  2910.  
  2911. if (it.isSplash() || it.isFluidContainer()) {
  2912. msg.addByte(serverFluidToClient(item.subType));
  2913. } else {
  2914. msg.addByte(0x00);
  2915. }
  2916.  
  2917. msg.addString(item.realName);
  2918. msg.add<uint32_t>(it.weight);
  2919. msg.add<uint32_t>(item.buyPrice);
  2920. msg.add<uint32_t>(item.sellPrice);
  2921. }
  2922.  
  2923. void ProtocolGame::parseExtendedOpcode(NetworkMessage& msg)
  2924. {
  2925. uint8_t opcode = msg.getByte();
  2926. const std::string& buffer = msg.getString();
  2927.  
  2928. // process additional opcodes via lua script event
  2929. addGameTask(&Game::parsePlayerExtendedOpcode, player->getID(), opcode, buffer);
  2930. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement