From 1693962643138ef6a7e84bacc4a334b061ac4773 Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Mon, 27 May 2024 11:42:12 -0300 Subject: [PATCH] fixed move Recent Kills and Death History functions from 'protocolgame.cpp' to 'player_cyclopedia.cpp'. --- src/creatures/creatures_definitions.hpp | 4 +- .../players/cyclopedia/player_cyclopedia.cpp | 151 +++++++++++------- .../players/cyclopedia/player_cyclopedia.hpp | 21 ++- src/creatures/players/player.cpp | 42 ++--- src/creatures/players/player.hpp | 8 +- src/game/game.cpp | 6 +- src/game/game.hpp | 4 +- src/server/network/protocol/protocolgame.cpp | 40 +---- src/server/network/protocol/protocolgame.hpp | 4 +- 9 files changed, 148 insertions(+), 132 deletions(-) diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index fe7dd856468..33f62dbd2aa 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -784,7 +784,9 @@ enum CombatType_t : uint8_t { }; enum PlayerAsyncOngoingTaskFlags : uint64_t { - PlayerAsyncTask_Highscore = 1 << 0 + PlayerAsyncTask_Highscore = 1 << 0, + PlayerAsyncTask_RecentDeaths = 1 << 1, + PlayerAsyncTask_RecentPvPKills = 1 << 2 }; enum PartyAnalyzer_t : uint8_t { diff --git a/src/creatures/players/cyclopedia/player_cyclopedia.cpp b/src/creatures/players/cyclopedia/player_cyclopedia.cpp index 567cf52b99c..0b437458418 100644 --- a/src/creatures/players/cyclopedia/player_cyclopedia.cpp +++ b/src/creatures/players/cyclopedia/player_cyclopedia.cpp @@ -9,9 +9,9 @@ #include "pch.hpp" -#include "player_cyclopedia.hpp" - +#include "database/databasetasks.hpp" #include "creatures/players/player.hpp" +#include "player_cyclopedia.hpp" #include "game/game.hpp" #include "kv/kv.hpp" @@ -19,12 +19,6 @@ PlayerCyclopedia::PlayerCyclopedia(Player &player) : m_player(player) { } void PlayerCyclopedia::loadSummaryData() { - loadSummary(); - loadRecentKills(); - loadDeathHistory(); -} - -void PlayerCyclopedia::loadSummary() { DBResult_ptr result = g_database().storeQuery(fmt::format("SELECT COUNT(*) as `count` FROM `player_hirelings` WHERE `player_id` = {}", m_player.getGUID())); auto kvScoped = m_player.kv()->scoped("summary")->scoped(g_game().getSummaryKeyByType(static_cast(Summary_t::HIRELINGS))); if (result && !kvScoped->get("amount").has_value()) { @@ -32,63 +26,108 @@ void PlayerCyclopedia::loadSummary() { } } -void PlayerCyclopedia::loadRecentKills() { +void PlayerCyclopedia::loadDeathHistory(uint16_t page, uint16_t entriesPerPage) { Benchmark bm_check; + uint32_t offset = static_cast(page - 1) * entriesPerPage; + auto query = fmt::format("SELECT `time`, `level`, `killed_by`, `mostdamage_by`, (select count(*) FROM `player_deaths` WHERE `player_id` = {}) as `entries` FROM `player_deaths` WHERE `player_id` = {} AND `time` >= DATE_SUB(NOW(), INTERVAL 30 DAY) ORDER BY `time` DESC LIMIT {}, {}", m_player.getGUID(), m_player.getGUID(), offset, entriesPerPage); + + uint32_t playerID = m_player.getID(); + std::function callback = [playerID, page, entriesPerPage](const DBResult_ptr &result, bool) { + std::shared_ptr player = g_game().getPlayerByID(playerID); + if (!player) { + return; + } - Database &db = g_database(); - const std::string &escapedName = db.escapeString(m_player.getName()); - DBResult_ptr result = db.storeQuery(fmt::format("SELECT `d`.`time`, `d`.`killed_by`, `d`.`mostdamage_by`, `d`.`unjustified`, `d`.`mostdamage_unjustified`, `p`.`name` FROM `player_deaths` AS `d` INNER JOIN `players` AS `p` ON `d`.`player_id` = `p`.`id` WHERE ((`d`.`killed_by` = {} AND `d`.`is_player` = 1) OR (`d`.`mostdamage_by` = {} AND `d`.`mostdamage_is_player` = 1))", escapedName, escapedName)); - if (result) { + player->resetAsyncOngoingTask(PlayerAsyncTask_RecentDeaths); + if (!result) { + player->sendCyclopediaCharacterRecentDeaths(0, 0, {}); + return; + } + + auto pages = result->getNumber("entries"); + pages += entriesPerPage - 1; + pages /= entriesPerPage; + + std::vector entries; + entries.reserve(result->countResults()); do { std::string cause1 = result->getString("killed_by"); std::string cause2 = result->getString("mostdamage_by"); - std::string name = result->getString("name"); - uint8_t status = CYCLOPEDIA_CHARACTERINFO_RECENTKILLSTATUS_JUSTIFIED; - if (m_player.getName() == cause1) { - if (result->getNumber("unjustified") == 1) { - status = CYCLOPEDIA_CHARACTERINFO_RECENTKILLSTATUS_UNJUSTIFIED; - } - } else if (m_player.getName() == cause2) { - if (result->getNumber("mostdamage_unjustified") == 1) { - status = CYCLOPEDIA_CHARACTERINFO_RECENTKILLSTATUS_UNJUSTIFIED; + std::ostringstream cause; + cause << "Died at Level " << result->getNumber("level") << " by"; + if (!cause1.empty()) { + cause << getArticle(cause1) << cause1; + } + + if (!cause2.empty()) { + if (!cause1.empty()) { + cause << " and"; } + cause << getArticle(cause2) << cause2; } + cause << '.'; - insertPvpKillOnHistory(fmt::format("Killed {}.", name), result->getNumber("time"), status); + entries.emplace_back(std::move(cause.str()), result->getNumber("time")); } while (result->next()); - } + player->sendCyclopediaCharacterRecentDeaths(page, static_cast(pages), entries); + }; + g_databaseTasks().store(query, callback); + m_player.addAsyncOngoingTask(PlayerAsyncTask_RecentDeaths); - g_logger().debug("Checking and updating recent kill of player {} took {} milliseconds.", m_player.getName(), bm_check.duration()); + g_logger().debug("Loading death history from the player {} took {} milliseconds.", m_player.getName(), bm_check.duration()); } -void PlayerCyclopedia::loadDeathHistory() { +void PlayerCyclopedia::loadRecentKills(uint16_t page, uint16_t entriesPerPage) { Benchmark bm_check; - DBResult_ptr result = g_database().storeQuery(fmt::format("SELECT `time`, `level`, `killed_by`, `mostdamage_by` FROM `player_deaths` WHERE `player_id` = {} ORDER BY `time` DESC", m_player.getGUID())); - if (result) { + const std::string &escapedName = g_database().escapeString(m_player.getName()); + uint32_t offset = static_cast(page - 1) * entriesPerPage; + auto query = fmt::format("SELECT `d`.`time`, `d`.`killed_by`, `d`.`mostdamage_by`, `d`.`unjustified`, `d`.`mostdamage_unjustified`, `p`.`name`, (select count(*) FROM `player_deaths` WHERE ((`killed_by` = {} AND `is_player` = 1) OR (`mostdamage_by` = {} AND `mostdamage_is_player` = 1))) as `entries` FROM `player_deaths` AS `d` INNER JOIN `players` AS `p` ON `d`.`player_id` = `p`.`id` WHERE ((`d`.`killed_by` = {} AND `d`.`is_player` = 1) OR (`d`.`mostdamage_by` = {} AND `d`.`mostdamage_is_player` = 1)) ORDER BY `time` DESC LIMIT {}, {}", escapedName, escapedName, escapedName, escapedName, offset, entriesPerPage); + + uint32_t playerID = m_player.getID(); + std::function callback = [playerID, page, entriesPerPage](const DBResult_ptr &result, bool) { + std::shared_ptr player = g_game().getPlayerByID(playerID); + if (!player) { + return; + } + + player->resetAsyncOngoingTask(PlayerAsyncTask_RecentPvPKills); + if (!result) { + player->sendCyclopediaCharacterRecentPvPKills(0, 0, {}); + return; + } + + auto pages = result->getNumber("entries"); + pages += entriesPerPage - 1; + pages /= entriesPerPage; + + std::vector entries; + entries.reserve(result->countResults()); do { std::string cause1 = result->getString("killed_by"); std::string cause2 = result->getString("mostdamage_by"); - std::ostringstream description; - description << "Died at Level " << result->getNumber("level") << " by"; - if (!cause1.empty()) { - description << getArticle(cause1) << cause1; - } + std::string name = result->getString("name"); - if (!cause2.empty()) { - if (!cause1.empty()) { - description << " and"; + uint8_t status = CYCLOPEDIA_CHARACTERINFO_RECENTKILLSTATUS_JUSTIFIED; + if (player->getName() == cause1) { + if (result->getNumber("unjustified") == 1) { + status = CYCLOPEDIA_CHARACTERINFO_RECENTKILLSTATUS_UNJUSTIFIED; + } + } else if (player->getName() == cause2) { + if (result->getNumber("mostdamage_unjustified") == 1) { + status = CYCLOPEDIA_CHARACTERINFO_RECENTKILLSTATUS_UNJUSTIFIED; } - description << getArticle(cause2) << cause2; } - description << '.'; - insertDeathOnHistory(std::move(description.str()), result->getNumber("time")); + entries.emplace_back(fmt::format("Killed {}.", name), result->getNumber("time"), status); } while (result->next()); - } + player->sendCyclopediaCharacterRecentPvPKills(page, static_cast(pages), entries); + }; + g_databaseTasks().store(query, callback); + m_player.addAsyncOngoingTask(PlayerAsyncTask_RecentPvPKills); - g_logger().debug("Checking and updating death history of player {} took {} milliseconds.", m_player.getName(), bm_check.duration()); + g_logger().debug("Loading recent kills from the player {} took {} milliseconds.", m_player.getName(), bm_check.duration()); } void PlayerCyclopedia::updateStoreSummary(uint8_t type, uint16_t amount, const std::string &id) { @@ -118,21 +157,21 @@ void PlayerCyclopedia::updateAmount(uint8_t type, uint16_t amount) { m_player.kv()->scoped("summary")->scoped(g_game().getSummaryKeyByType(type))->set("amount", oldAmount + amount); } -std::vector PlayerCyclopedia::getDeathHistory() const { - return m_deathHistory; -} - -void PlayerCyclopedia::insertDeathOnHistory(std::string cause, uint32_t timestamp) { - m_deathHistory.emplace_back(std::move(cause), timestamp); -} - -std::vector PlayerCyclopedia::getPvpKillsHistory() const { - return m_pvpKillsHistory; -} - -void PlayerCyclopedia::insertPvpKillOnHistory(std::string cause, uint32_t timestamp, uint8_t status) { - m_pvpKillsHistory.emplace_back(std::move(cause), timestamp, status); -} +// std::vector PlayerCyclopedia::getDeathHistory() const { +// return m_deathHistory; +// } + +// void PlayerCyclopedia::insertDeathOnHistory(std::string cause, uint32_t timestamp) { +// m_deathHistory.emplace_back(std::move(cause), timestamp); +// } + +// std::vector PlayerCyclopedia::getPvpKillsHistory() const { +// return m_pvpKillsHistory; +// } +// +// void PlayerCyclopedia::insertPvpKillOnHistory(std::string cause, uint32_t timestamp, uint8_t status) { +// m_pvpKillsHistory.emplace_back(std::move(cause), timestamp, status); +// } Summary PlayerCyclopedia::getSummary() { return { getAmount(Summary_t::BOOSTS), getAmount(Summary_t::PREY_CARDS), getAmount(Summary_t::INSTANT_REWARDS), getAmount(Summary_t::HIRELINGS) }; diff --git a/src/creatures/players/cyclopedia/player_cyclopedia.hpp b/src/creatures/players/cyclopedia/player_cyclopedia.hpp index e437c6b937d..9b3b30343d3 100644 --- a/src/creatures/players/cyclopedia/player_cyclopedia.hpp +++ b/src/creatures/players/cyclopedia/player_cyclopedia.hpp @@ -9,9 +9,9 @@ #pragma once -#include -#include -#include +#include "creatures/creatures_definitions.hpp" +#include "utils/utils_definitions.hpp" +#include "enums/player_cyclopedia.hpp" class Player; class KV; @@ -31,19 +31,18 @@ class PlayerCyclopedia { explicit PlayerCyclopedia(Player &player); void loadSummaryData(); - void loadSummary(); - void loadRecentKills(); - void loadDeathHistory(); + void loadDeathHistory(uint16_t page, uint16_t entriesPerPage); + void loadRecentKills(uint16_t page, uint16_t entriesPerPage); void updateStoreSummary(uint8_t type, uint16_t amount = 1, const std::string &id = ""); uint16_t getAmount(uint8_t type); void updateAmount(uint8_t type, uint16_t amount = 1); - [[nodiscard]] std::vector getDeathHistory() const; - void insertDeathOnHistory(std::string cause, uint32_t timestamp); + // [[nodiscard]] std::vector getDeathHistory() const; + // void insertDeathOnHistory(std::string cause, uint32_t timestamp); - [[nodiscard]] std::vector getPvpKillsHistory() const; - void insertPvpKillOnHistory(std::string cause, uint32_t timestamp, uint8_t status); + // [[nodiscard]] std::vector getPvpKillsHistory() const; + // void insertPvpKillOnHistory(std::string cause, uint32_t timestamp, uint8_t status); Summary getSummary(); @@ -51,7 +50,7 @@ class PlayerCyclopedia { void insertValue(uint8_t type, uint16_t amount = 1, const std::string &id = ""); private: - std::vector m_deathHistory; + // std::vector m_deathHistory; std::vector m_pvpKillsHistory; Player &m_player; }; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 0e784438df7..c6c163d9ce1 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -2859,27 +2859,27 @@ void Player::death(std::shared_ptr lastHitCreature) { } sendTextMessage(MESSAGE_EVENT_ADVANCE, blessOutput.str()); - // Pvp and pve death registration - std::ostringstream description; - if (pvpDeath) { - description << "Killed " << getName() << '.'; - CyclopediaCharacterInfo_RecentKillStatus_t status = unfairFightReduction != 100 ? CYCLOPEDIA_CHARACTERINFO_RECENTKILLSTATUS_UNJUSTIFIED : CYCLOPEDIA_CHARACTERINFO_RECENTKILLSTATUS_JUSTIFIED; - if (lastHitCreature && lastHitCreature->getPlayer()) { - lastHitCreature->getPlayer()->cyclopedia()->insertPvpKillOnHistory(std::move(description.str()), OTSYS_TIME() / 1000, status); - } else if (lastHitCreature && lastHitCreature->getMaster() && lastHitCreature->getMaster()->getPlayer()) { - lastHitCreature->getMaster()->getPlayer()->cyclopedia()->insertPvpKillOnHistory(std::move(description.str()), OTSYS_TIME() / 1000, status); - } - } else { - description << "Died at Level " << getLevel() << " by"; - if (lastHitCreature) { - description << getArticle(lastHitCreature->getName()) << lastHitCreature->getName(); - } else { - description << " a field item"; - } - description << '.'; - - cyclopedia()->insertDeathOnHistory(std::move(description.str()), OTSYS_TIME() / 1000); - } + // // Pvp and pve death registration + // std::ostringstream description; + // if (pvpDeath) { + // description << "Killed " << getName() << '.'; + // CyclopediaCharacterInfo_RecentKillStatus_t status = unfairFightReduction != 100 ? CYCLOPEDIA_CHARACTERINFO_RECENTKILLSTATUS_UNJUSTIFIED : CYCLOPEDIA_CHARACTERINFO_RECENTKILLSTATUS_JUSTIFIED; + // if (lastHitCreature && lastHitCreature->getPlayer()) { + // lastHitCreature->getPlayer()->cyclopedia()->insertPvpKillOnHistory(std::move(description.str()), OTSYS_TIME() / 1000, status); + // } else if (lastHitCreature && lastHitCreature->getMaster() && lastHitCreature->getMaster()->getPlayer()) { + // lastHitCreature->getMaster()->getPlayer()->cyclopedia()->insertPvpKillOnHistory(std::move(description.str()), OTSYS_TIME() / 1000, status); + // } + // } else { + // description << "Died at Level " << getLevel() << " by"; + // if (lastHitCreature) { + // description << getArticle(lastHitCreature->getName()) << lastHitCreature->getName(); + // } else { + // description << " a field item"; + // } + // description << '.'; + // + // cyclopedia()->insertDeathOnHistory(std::move(description.str()), OTSYS_TIME() / 1000); + // } sendStats(); sendSkills(); diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index edda885751c..d5796ac6786 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -1631,14 +1631,14 @@ class Player final : public Creature, public Cylinder, public Bankable { client->sendCyclopediaCharacterCombatStats(); } } - void sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t entriesPerPage) { + void sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t pages, const std::vector &entries) { if (client) { - client->sendCyclopediaCharacterRecentDeaths(page, entriesPerPage); + client->sendCyclopediaCharacterRecentDeaths(page, pages, entries); } } - void sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t entriesPerPage) { + void sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t pages, const std::vector &entries) { if (client) { - client->sendCyclopediaCharacterRecentPvPKills(page, entriesPerPage); + client->sendCyclopediaCharacterRecentPvPKills(page, pages, entries); } } void sendCyclopediaCharacterAchievements(uint16_t secretsUnlocked, std::vector> achievementsUnlocked); diff --git a/src/game/game.cpp b/src/game/game.cpp index 0e0034e9904..3e484302f33 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -8351,10 +8351,10 @@ void Game::playerCyclopediaCharacterInfo(std::shared_ptr player, uint32_ player->sendCyclopediaCharacterCombatStats(); break; case CYCLOPEDIA_CHARACTERINFO_RECENTDEATHS: - player->sendCyclopediaCharacterRecentDeaths(page, entriesPerPage); + player->cyclopedia()->loadDeathHistory(page, entriesPerPage); break; case CYCLOPEDIA_CHARACTERINFO_RECENTPVPKILLS: - player->sendCyclopediaCharacterRecentPvPKills(page, entriesPerPage); + player->cyclopedia()->loadRecentKills(page, entriesPerPage); break; case CYCLOPEDIA_CHARACTERINFO_ACHIEVEMENTS: player->achiev()->sendUnlockedSecretAchievements(); @@ -10677,7 +10677,7 @@ Title Game::getTitleByName(const std::string &name) { return {}; } -std::unordered_map Game::getBlessingNames() { +std::map Game::getBlessingNames() { return m_blessingNames; } diff --git a/src/game/game.hpp b/src/game/game.hpp index 584a652dc27..bcf59f883a8 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -735,7 +735,7 @@ class Game { return m_summaryCategories[type]; } - std::unordered_map getBlessingNames(); + std::map getBlessingNames(); std::unordered_map getHirelingSkills(); std::unordered_map getHirelingOutfits(); @@ -749,7 +749,7 @@ class Game { std::vector m_highscoreCategories; std::unordered_map m_highscoreCategoriesNames; - std::unordered_map m_blessingNames; + std::map m_blessingNames; std::unordered_map m_summaryCategories; std::unordered_map m_hirelingSkills; diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index d06a2d55ca0..724169efdd6 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -3653,7 +3653,7 @@ void ProtocolGame::sendCyclopediaCharacterCombatStats() { writeToOutputBuffer(msg); } -void ProtocolGame::sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t entriesPerPage) { +void ProtocolGame::sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t pages, const std::vector &entries) { if (!player || oldProtocol) { return; } @@ -3662,21 +3662,10 @@ void ProtocolGame::sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t e msg.addByte(0xDA); msg.addByte(CYCLOPEDIA_CHARACTERINFO_RECENTDEATHS); msg.addByte(0x00); // 0x00 Here means 'no error' - - auto entries = player->cyclopedia()->getDeathHistory(); - uint16_t pages = std::min(entriesPerPage, entries.size()); - - auto totalPages = static_cast(std::ceil(static_cast(entries.size()) / pages)); - uint16_t currentPage = std::min(page, totalPages); - - uint16_t firstObject = (currentPage - 1) * pages; - uint16_t finalObject = firstObject + pages; - - msg.add(currentPage); - msg.add(totalPages); + msg.add(page); msg.add(pages); - for (uint16_t i = firstObject; i < finalObject; i++) { - const RecentDeathEntry &entry = entries[i]; + msg.add(entries.size()); + for (const RecentDeathEntry &entry : entries) { msg.add(entry.timestamp); msg.addString(entry.cause, "ProtocolGame::sendCyclopediaCharacterRecentDeaths - entry.cause"); } @@ -3684,7 +3673,7 @@ void ProtocolGame::sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t e writeToOutputBuffer(msg); } -void ProtocolGame::sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t entriesPerPage) { +void ProtocolGame::sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t pages, const std::vector &entries) { if (!player || oldProtocol) { return; } @@ -3693,21 +3682,10 @@ void ProtocolGame::sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t msg.addByte(0xDA); msg.addByte(CYCLOPEDIA_CHARACTERINFO_RECENTPVPKILLS); msg.addByte(0x00); // 0x00 Here means 'no error' - - auto entries = player->cyclopedia()->getPvpKillsHistory(); - uint16_t pages = std::min(entriesPerPage, entries.size()); - - auto totalPages = static_cast(std::ceil(static_cast(entries.size()) / pages)); - uint16_t currentPage = std::min(page, totalPages); - - uint16_t firstObject = (currentPage - 1) * pages; - uint16_t finalObject = firstObject + pages; - - msg.add(currentPage); - msg.add(totalPages); + msg.add(page); msg.add(pages); - for (uint16_t i = firstObject; i < finalObject; i++) { - const RecentPvPKillEntry &entry = entries[i]; + msg.add(entries.size()); + for (const RecentPvPKillEntry &entry : entries) { msg.add(entry.timestamp); msg.addString(entry.description, "ProtocolGame::sendCyclopediaCharacterRecentPvPKills - entry.description"); msg.addByte(entry.status); @@ -3977,10 +3955,8 @@ void ProtocolGame::sendCyclopediaCharacterStoreSummary() { // getBlessingsObtained auto blessingNames = g_game().getBlessingNames(); - std::vector> orderedBlessings; msg.addByte(static_cast(blessingNames.size())); for (const auto &bless : blessingNames) { - g_logger().debug("bless: {} - {}", bless.first, bless.second); msg.addString(bless.second, "ProtocolGame::sendCyclopediaCharacterStoreSummary - blessing.name"); uint8_t blessingIndex = bless.first - 1; msg.addByte((blessingIndex < player->blessings.size()) ? static_cast(player->blessings[blessingIndex]) : 0); diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 194915b31b7..4b87751b86d 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -319,8 +319,8 @@ class ProtocolGame final : public Protocol { void sendCyclopediaCharacterBaseInformation(); void sendCyclopediaCharacterGeneralStats(); void sendCyclopediaCharacterCombatStats(); - void sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t entriesPerPage); - void sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t entriesPerPage); + void sendCyclopediaCharacterRecentDeaths(uint16_t page, uint16_t pages, const std::vector &entries); + void sendCyclopediaCharacterRecentPvPKills(uint16_t page, uint16_t pages, const std::vector &entries); void sendCyclopediaCharacterAchievements(uint16_t secretsUnlocked, std::vector> achievementsUnlocked); void sendCyclopediaCharacterItemSummary(const ItemsTierCountList &inventoryItems, const ItemsTierCountList &storeInboxItems, const StashItemList &supplyStashItems, const ItemsTierCountList &depotBoxItems, const ItemsTierCountList &inboxItems); void sendCyclopediaCharacterOutfitsMounts();