From 631236fa8a1dddfe4cd73c9c8ced735f65e2d8b0 Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Sat, 18 Apr 2020 18:22:31 +0200 Subject: [PATCH] Core/Packets: converted CMSG_WHO and SMSG_WHO to packet class --- src/server/game/Handlers/MiscHandler.cpp | 205 +++++++----------- src/server/game/Server/Packets/AllPackets.h | 1 + .../game/Server/Packets/QueryPackets.cpp | 30 +++ src/server/game/Server/Packets/QueryPackets.h | 14 ++ src/server/game/Server/Packets/WhoPackets.cpp | 87 ++++++++ src/server/game/Server/Packets/WhoPackets.h | 79 +++++++ src/server/game/Server/WorldSession.h | 7 +- 7 files changed, 301 insertions(+), 122 deletions(-) create mode 100644 src/server/game/Server/Packets/WhoPackets.cpp create mode 100644 src/server/game/Server/Packets/WhoPackets.h diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 21af795b853..d92e9c92852 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -48,10 +48,12 @@ #include "Opcodes.h" #include "OutdoorPvP.h" #include "Player.h" +#include "QueryPackets.h" #include "ScriptMgr.h" #include "Spell.h" #include "SpellInfo.h" #include "WhoListStorage.h" +#include "WhoPackets.h" #include "World.h" #include "WorldPacket.h" #include @@ -178,89 +180,62 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData) } } -void WorldSession::HandleWhoOpcode(WorldPacket& recvData) +void WorldSession::HandleWhoOpcode(WorldPackets::Who::WhoRequestPkt& whoRequest) { - TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_WHO Message"); + WorldPackets::Who::WhoRequest& request = whoRequest.Request; - uint32 matchCount = 0; - - uint32 levelMin, levelMax, racemask, classmask, zonesCount, strCount; - uint32 zoneids[10]; // 10 is client limit - std::string packetPlayerName, packetGuildName; - - recvData >> levelMin; // maximal player level, default 0 - recvData >> levelMax; // minimal player level, default 100 (MAX_LEVEL) - recvData >> packetPlayerName; // player name, case sensitive... - - recvData >> packetGuildName; // guild name, case sensitive... - - recvData >> racemask; // race mask - recvData >> classmask; // class mask - recvData >> zonesCount; // zones count, client limit = 10 (2.0.10) - - if (zonesCount > 10) - return; // can't be received from real client or broken packet - - for (uint32 i = 0; i < zonesCount; ++i) - { - uint32 temp; - recvData >> temp; // zone id, 0 if zone is unknown... - zoneids[i] = temp; - TC_LOG_DEBUG("network", "Zone %u: %u", i, zoneids[i]); - } - - recvData >> strCount; // user entered strings count, client limit=4 (checked on 2.0.10) - - if (strCount > 4) - return; // can't be received from real client or broken packet - - TC_LOG_DEBUG("network", "Minlvl %u, maxlvl %u, name %s, guild %s, racemask %u, classmask %u, zones %u, strings %u", levelMin, levelMax, packetPlayerName.c_str(), packetGuildName.c_str(), racemask, classmask, zonesCount, strCount); - - std::wstring str[4]; // 4 is client limit - for (uint32 i = 0; i < strCount; ++i) - { - std::string temp; - recvData >> temp; // user entered string, it used as universal search pattern(guild+player name)? - - if (!Utf8toWStr(temp, str[i])) - continue; - - wstrToLower(str[i]); - - TC_LOG_DEBUG("network", "String %u: %s", i, temp.c_str()); - } - - std::wstring wpacketPlayerName; - std::wstring wpacketGuildName; - if (!(Utf8toWStr(packetPlayerName, wpacketPlayerName) && Utf8toWStr(packetGuildName, wpacketGuildName))) + // zones count, client limit = 10 (2.0.10) + // can't be received from real client or broken packet + if (whoRequest.Request.Areas.size() > 10) return; - wstrToLower(wpacketPlayerName); - wstrToLower(wpacketGuildName); + // user entered strings count, client limit=4 (checked on 2.0.10) + // can't be received from real client or broken packet + if (request.Words.size() > 4) + return; + + std::vector wWords; + wWords.resize(request.Words.size()); + for (size_t i = 0; i < request.Words.size(); ++i) + { + TC_LOG_DEBUG("network", "WorldSession::HandleWhoOpcode: Word: %s", request.Words[i].Word.c_str()); + + // user entered string, it used as universal search pattern(guild+player name)? + if (!Utf8toWStr(request.Words[i].Word, wWords[i])) + continue; + + wstrToLower(wWords[i]); + } + + std::wstring wPlayerName; + std::wstring wGuildName; + + if (!(Utf8toWStr(request.Name, wPlayerName) && Utf8toWStr(request.Guild, wGuildName))) + return; + + wstrToLower(wPlayerName); + wstrToLower(wGuildName); // client send in case not set max level value 100 but Trinity supports 255 max level, // update it to show GMs with characters after 100 level - if (levelMax >= MAX_LEVEL) - levelMax = STRONG_MAX_LEVEL; + if (whoRequest.Request.MaxLevel >= MAX_LEVEL) + whoRequest.Request.MaxLevel = STRONG_MAX_LEVEL; uint32 team = _player->GetTeam(); - uint32 gmLevelInWhoList = sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST); - uint32 displayCount = 0; + uint32 gmLevelInWhoList = sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST); - WorldPacket data(SMSG_WHO, 500); // guess size - data << uint32(matchCount); // placeholder, count of players matching criteria - data << uint32(displayCount); // placeholder, count of players displayed + WorldPackets::Who::WhoResponsePkt response; WhoListInfoVector const& whoList = sWhoListStorageMgr->GetWhoList(); for (WhoListPlayerInfo const& target : whoList) { - // player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST + // player can see member of other team only if has RBAC_PERM_TWO_SIDE_WHO_LIST if (target.GetTeam() != team && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_WHO_LIST)) continue; - // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST - if (!HasPermission(rbac::RBAC_PERM_WHO_SEE_ALL_SEC_LEVELS) && target.GetSecurity() > AccountTypes(gmLevelInWhoList)) + // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if has RBAC_PERM_WHO_SEE_ALL_SEC_LEVELS + if (target.GetSecurity() > AccountTypes(gmLevelInWhoList) && !HasPermission(rbac::RBAC_PERM_WHO_SEE_ALL_SEC_LEVELS)) continue; // check if target is globally visible for player @@ -270,87 +245,75 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData) // check if target's level is in level range uint8 lvl = target.GetLevel(); - if (lvl < levelMin || lvl > levelMax) + if (lvl < request.MinLevel || lvl > request.MaxLevel) continue; // check if class matches classmask - uint8 class_ = target.GetClass(); - if (!(classmask & (1 << class_))) + if (request.ClassFilter >= 0 && !(request.ClassFilter & (1 << target.GetClass()))) continue; // check if race matches racemask - uint32 race = target.GetRace(); - if (!(racemask & (1 << race))) + if (request.RaceFilter >= 0 && (request.RaceFilter & (1 << target.GetRace()))) continue; - uint32 playerZoneId = target.GetZoneId(); - uint8 gender = target.GetGender(); - - bool showZones = true; - for (uint32 i = 0; i < zonesCount; ++i) + if (!whoRequest.Request.Areas.empty()) { - if (zoneids[i] == playerZoneId) - { - showZones = true; - break; - } - - showZones = false; + if (std::find(whoRequest.Request.Areas.begin(), whoRequest.Request.Areas.end(), target.GetZoneId()) == whoRequest.Request.Areas.end()) + continue; } - if (!showZones) + + std::wstring const& wTargetName = target.GetWidePlayerName(); + if (!(wPlayerName.empty() || wTargetName.find(wPlayerName) != std::wstring::npos)) continue; - std::wstring const& wideplayername = target.GetWidePlayerName(); - if (!(wpacketPlayerName.empty() || wideplayername.find(wpacketPlayerName) != std::wstring::npos)) + std::wstring const& wTargetGuildName = target.GetWideGuildName(); + + if (!wGuildName.empty() && wTargetGuildName.find(wGuildName) == std::wstring::npos) continue; - std::wstring const& wideguildname = target.GetWideGuildName(); - if (!(wpacketGuildName.empty() || wideguildname.find(wpacketGuildName) != std::wstring::npos)) - continue; - - std::string aname; - if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(playerZoneId)) - aname = areaEntry->AreaName[GetSessionDbcLocale()]; - - bool s_show = true; - for (uint32 i = 0; i < strCount; ++i) + if (!wWords.empty()) { - if (!str[i].empty()) + std::string aName; + if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(target.GetZoneId())) + aName = areaEntry->AreaName[GetSessionDbcLocale()]; + + bool show = false; + for (size_t i = 0; i < wWords.size(); ++i) { - if (wideguildname.find(str[i]) != std::wstring::npos || - wideplayername.find(str[i]) != std::wstring::npos || - Utf8FitTo(aname, str[i])) + if (!wWords[i].empty()) { - s_show = true; - break; + if (wTargetName.find(wWords[i]) != std::wstring::npos || + wTargetGuildName.find(wWords[i]) != std::wstring::npos || + Utf8FitTo(aName, wWords[i])) + { + show = true; + break; + } } - s_show = false; } + + if (!show) + continue; } - if (!s_show) + + WorldPackets::Who::WhoEntry whoEntry; + if (!whoEntry.PlayerData.Initialize(target.GetGuid(), nullptr)) continue; - // 49 is maximum player count sent to client - can be overridden + if (!target.GetGuildName().empty()) + whoEntry.GuildName = target.GetGuildName(); + + whoEntry.AreaID = target.GetZoneId(); + + response.Response.Entries.push_back(whoEntry); + + // 50 is maximum player count sent to client - can be overridden // through config, but is unstable - if ((matchCount++) >= sWorld->getIntConfig(CONFIG_MAX_WHO)) - continue; - - data << target.GetPlayerName(); // player name - data << target.GetGuildName(); // guild name - data << uint32(lvl); // player level - data << uint32(class_); // player class - data << uint32(race); // player race - data << uint8(gender); // player gender - data << uint32(playerZoneId); // player zone id - - ++displayCount; + if (response.Response.Entries.size() >= sWorld->getIntConfig(CONFIG_MAX_WHO)) + break; } - data.put(0, displayCount); // insert right count, count displayed - data.put(4, matchCount); // insert right count, count of matches - - SendPacket(&data); - TC_LOG_DEBUG("network", "WORLD: Send SMSG_WHO Message"); + SendPacket(response.Write()); } void WorldSession::HandleLogoutRequestOpcode(WorldPacket& /*recvData*/) diff --git a/src/server/game/Server/Packets/AllPackets.h b/src/server/game/Server/Packets/AllPackets.h index 2924ba51b4b..32b393e3159 100644 --- a/src/server/game/Server/Packets/AllPackets.h +++ b/src/server/game/Server/Packets/AllPackets.h @@ -28,6 +28,7 @@ #include "QuestPackets.h" #include "QueryPackets.h" #include "SystemPackets.h" +#include "WhoPackets.h" #include "WorldStatePackets.h" #endif // AllPackets_h__ diff --git a/src/server/game/Server/Packets/QueryPackets.cpp b/src/server/game/Server/Packets/QueryPackets.cpp index 76d5be73edc..d9c39e56b57 100644 --- a/src/server/game/Server/Packets/QueryPackets.cpp +++ b/src/server/game/Server/Packets/QueryPackets.cpp @@ -15,6 +15,8 @@ * with this program. If not, see . */ +#include "CharacterCache.h" +#include "Player.h" #include "QueryPackets.h" void WorldPackets::Query::DBQueryBulk::Read() @@ -75,3 +77,31 @@ WorldPacket const* WorldPackets::Query::HotfixNotifyBlob::Write() return &_worldPacket; } + +bool WorldPackets::Query::PlayerGuidLookupData::Initialize(ObjectGuid const& guid, Player const* player /*= nullptr*/) +{ + CharacterCacheEntry const* characterInfo = sCharacterCache->GetCharacterCacheByGuid(guid); + if (!characterInfo) + return false; + + if (player) + { + ASSERT(player->GetGUID() == guid); + + Name = player->GetName(); + Race = player->getRace(); + Sex = player->getGender(); + ClassID = player->getClass(); + Level = player->getLevel(); + } + else + { + Name = characterInfo->Name; + Race = characterInfo->Race; + Sex = characterInfo->Sex; + ClassID = characterInfo->Class; + Level = characterInfo->Level; + } + + return true; +} diff --git a/src/server/game/Server/Packets/QueryPackets.h b/src/server/game/Server/Packets/QueryPackets.h index ec29d9ecd5b..2f1f2edfb56 100644 --- a/src/server/game/Server/Packets/QueryPackets.h +++ b/src/server/game/Server/Packets/QueryPackets.h @@ -21,6 +21,9 @@ #include "Packet.h" #include "DB2Stores.h" #include "ObjectGuid.h" +#include "SharedDefines.h" + +class Player; namespace WorldPackets { @@ -65,6 +68,17 @@ namespace WorldPackets HotfixData const* Hotfixes = nullptr; }; + + struct PlayerGuidLookupData + { + bool Initialize(ObjectGuid const& guid, Player const* player = nullptr); + + std::string Name; + uint8 Race = RACE_NONE; + uint8 Sex = GENDER_NONE; + uint8 ClassID = CLASS_NONE; + uint8 Level = 0; + }; } } diff --git a/src/server/game/Server/Packets/WhoPackets.cpp b/src/server/game/Server/Packets/WhoPackets.cpp new file mode 100644 index 00000000000..8c3a2919775 --- /dev/null +++ b/src/server/game/Server/Packets/WhoPackets.cpp @@ -0,0 +1,87 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "WhoPackets.h" + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Who::WhoWord& word) +{ + data >> word.Word; + + return data; +} + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Who::WhoRequest& request) +{ + data >> request.MinLevel; + data >> request.MaxLevel; + data >> request.Guild; + data >> request.Name; + data >> request.RaceFilter; + data >> request.ClassFilter; + + uint32 areas = 0; + data >> areas; + + request.Areas.resize(areas); + for (size_t i = 0; i < request.Areas.size(); ++i) + data >> request.Areas[i]; + + uint32 words = 0; + data >> words; + + request.Words.resize(words); + for (size_t i = 0; i < request.Words.size(); ++i) + data >> request.Words[i]; + + return data; +} + +void WorldPackets::Who::WhoRequestPkt::Read() +{ + _worldPacket >> Request; +} + +ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Who::WhoEntry const& entry) +{ + data << entry.PlayerData.Name; + data << entry.GuildName; + data << uint32(entry.PlayerData.Level); + data << uint32(entry.PlayerData.ClassID); + data << uint32(entry.PlayerData.Race); + data << uint8(entry.PlayerData.Sex); + data << uint32(entry.AreaID); + + return data; +} + +ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Who::WhoResponse const& response) +{ + data << uint32(response.Entries.size()); // Number of displayed characters + data << uint32(response.Entries.size()); // Number of matching characters (when using filters) TODO + + for (WorldPackets::Who::WhoEntry const& whoEntry : response.Entries) + data << whoEntry; + + return data; +} + +WorldPacket const* WorldPackets::Who::WhoResponsePkt::Write() +{ + _worldPacket << Response; + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/WhoPackets.h b/src/server/game/Server/Packets/WhoPackets.h new file mode 100644 index 00000000000..f7cac16995f --- /dev/null +++ b/src/server/game/Server/Packets/WhoPackets.h @@ -0,0 +1,79 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef WhoPackets_h__ +#define WhoPackets_h__ + +#include "Packet.h" +#include "QueryPackets.h" + +namespace WorldPackets +{ + namespace Who + { + struct WhoWord + { + std::string Word; + }; + + struct WhoRequest + { + int32 MinLevel = 0; + int32 MaxLevel = 0; + std::string Name; + std::string Guild; + int32 RaceFilter = -1; + int32 ClassFilter = -1; + std::vector Words; + std::vector Areas; + }; + + class WhoRequestPkt final : public ClientPacket + { + public: + WhoRequestPkt(WorldPacket&& packet) : ClientPacket(CMSG_WHO, std::move(packet)) { } + + void Read() override; + + WhoRequest Request; + }; + + struct WhoEntry + { + Query::PlayerGuidLookupData PlayerData; + std::string GuildName; + int32 AreaID = 0; + }; + + struct WhoResponse + { + std::vector Entries; + }; + + class WhoResponsePkt final : public ServerPacket + { + public: + WhoResponsePkt() : ServerPacket(SMSG_WHO, 1) { } + + WorldPacket const* Write() override; + + WhoResponse Response; + }; + } +} + +#endif // WhoPackets_h__ diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 5c29dc40c05..10e2f33658a 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -171,6 +171,11 @@ namespace WorldPackets { class DBQueryBulk; } + + namespace Who + { + class WhoRequestPkt; + } } enum AccountDataType @@ -656,7 +661,7 @@ class TC_GAME_API WorldSession void HandleLootOpcode(WorldPacket& recvPacket); void HandleLootReleaseOpcode(WorldPacket& recvPacket); void HandleLootMasterGiveOpcode(WorldPacket& recvPacket); - void HandleWhoOpcode(WorldPacket& recvPacket); + void HandleWhoOpcode(WorldPackets::Who::WhoRequestPkt& whoRequest); void HandleLogoutRequestOpcode(WorldPacket& recvPacket); void HandlePlayerLogoutOpcode(WorldPacket& recvPacket); void HandleLogoutCancelOpcode(WorldPacket& recvPacket);