From 99e7349f6ccf02967eb0fbb7aaf57a3dc0f46934 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Wed, 17 Apr 2024 21:47:28 -0500 Subject: [PATCH] feat: slashcommands for showall, findplayer, get/openhttpmoninfo, and debug world packet (#1545) * feat: showall, findplayer, get/openhttpmoninfo http monitor info is planned to be used later, just putting in info that i've since reverse engineered and don't want lost Additionally add debug world packet for duture dev use Tested all new commands and variation of command arguments * fix missing newline at eofs * address most feedback * Compormise and use struct with (de)serialize * remove httpmoninfo commands --- dChatServer/ChatPacketHandler.cpp | 62 +++++++++++++++++++ dChatServer/ChatPacketHandler.h | 2 + dChatServer/ChatServer.cpp | 4 ++ dChatServer/PlayerContainer.cpp | 2 + dChatServer/PlayerContainer.h | 5 ++ dGame/dUtilities/SlashCommandHandler.cpp | 18 ++++++ .../dUtilities/SlashCommands/DEVGMCommands.h | 2 +- .../GMGreaterThanZeroCommands.cpp | 35 +++++++++++ .../SlashCommands/GMGreaterThanZeroCommands.h | 4 +- dNet/ChatPackets.cpp | 24 +++++++ dNet/ChatPackets.h | 15 +++++ dNet/WorldPackets.cpp | 24 +++++++ dNet/WorldPackets.h | 15 +++++ 13 files changed, 210 insertions(+), 2 deletions(-) diff --git a/dChatServer/ChatPacketHandler.cpp b/dChatServer/ChatPacketHandler.cpp index d37777b6..82cea018 100644 --- a/dChatServer/ChatPacketHandler.cpp +++ b/dChatServer/ChatPacketHandler.cpp @@ -18,6 +18,7 @@ #include "eGameMessageType.h" #include "StringifiedEnum.h" #include "eGameMasterLevel.h" +#include "ChatPackets.h" void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { //Get from the packet which player we want to do something with: @@ -354,6 +355,67 @@ void ChatPacketHandler::HandleGMLevelUpdate(Packet* packet) { inStream.Read(player.gmLevel); } + +void ChatPacketHandler::HandleWho(Packet* packet) { + CINSTREAM_SKIP_HEADER; + FindPlayerRequest request; + request.Deserialize(inStream); + + const auto& sender = Game::playerContainer.GetPlayerData(request.requestor); + if (!sender) return; + + const auto& player = Game::playerContainer.GetPlayerData(request.playerName.GetAsString()); + bool online = player; + + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); + bitStream.Write(request.requestor); + + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::WHO_RESPONSE); + bitStream.Write(online); + bitStream.Write(player.zoneID.GetMapID()); + bitStream.Write(player.zoneID.GetInstanceID()); + bitStream.Write(player.zoneID.GetCloneID()); + bitStream.Write(request.playerName); + + SystemAddress sysAddr = sender.sysAddr; + SEND_PACKET; +} + +void ChatPacketHandler::HandleShowAll(Packet* packet) { + CINSTREAM_SKIP_HEADER; + ShowAllRequest request; + request.Deserialize(inStream); + + const auto& sender = Game::playerContainer.GetPlayerData(request.requestor); + if (!sender) return; + + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); + bitStream.Write(request.requestor); + + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::SHOW_ALL_RESPONSE); + bitStream.Write(!request.displayZoneData && !request.displayIndividualPlayers); + bitStream.Write(Game::playerContainer.GetPlayerCount()); + bitStream.Write(Game::playerContainer.GetSimCount()); + bitStream.Write(request.displayIndividualPlayers); + bitStream.Write(request.displayZoneData); + if (request.displayZoneData || request.displayIndividualPlayers){ + for (auto& [playerID, playerData ]: Game::playerContainer.GetAllPlayers()){ + if (!playerData) continue; + bitStream.Write(0); // structure packing + if (request.displayIndividualPlayers) bitStream.Write(LUWString(playerData.playerName)); + if (request.displayZoneData) { + bitStream.Write(playerData.zoneID.GetMapID()); + bitStream.Write(playerData.zoneID.GetInstanceID()); + bitStream.Write(playerData.zoneID.GetCloneID()); + } + } + } + SystemAddress sysAddr = sender.sysAddr; + SEND_PACKET; +} + // the structure the client uses to send this packet is shared in many chat messages // that are sent to the server. Because of this, there are large gaps of unused data in chat messages void ChatPacketHandler::HandleChatMessage(Packet* packet) { diff --git a/dChatServer/ChatPacketHandler.h b/dChatServer/ChatPacketHandler.h index 847fc899..def9c9b9 100644 --- a/dChatServer/ChatPacketHandler.h +++ b/dChatServer/ChatPacketHandler.h @@ -50,6 +50,8 @@ namespace ChatPacketHandler { void HandleFriendResponse(Packet* packet); void HandleRemoveFriend(Packet* packet); void HandleGMLevelUpdate(Packet* packet); + void HandleWho(Packet* packet); + void HandleShowAll(Packet* packet); void HandleChatMessage(Packet* packet); void HandlePrivateChatMessage(Packet* packet); diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index cc938c3c..81b6ddef 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -289,7 +289,11 @@ void HandlePacket(Packet* packet) { Game::playerContainer.RemovePlayer(packet); break; case eChatMessageType::WHO: + ChatPacketHandler::HandleWho(packet); + break; case eChatMessageType::SHOW_ALL: + ChatPacketHandler::HandleShowAll(packet); + break; case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE: case eChatMessageType::WORLD_DISCONNECT_REQUEST: case eChatMessageType::WORLD_PROXIMITY_RESPONSE: diff --git a/dChatServer/PlayerContainer.cpp b/dChatServer/PlayerContainer.cpp index 57b3f233..17e2cd1a 100644 --- a/dChatServer/PlayerContainer.cpp +++ b/dChatServer/PlayerContainer.cpp @@ -49,6 +49,7 @@ void PlayerContainer::InsertPlayer(Packet* packet) { data.sysAddr = packet->systemAddress; m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName); + m_PlayerCount++; LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID()); @@ -87,6 +88,7 @@ void PlayerContainer::RemovePlayer(Packet* packet) { } } + m_PlayerCount--; LOG("Removed user: %llu", playerID); m_Players.erase(playerID); diff --git a/dChatServer/PlayerContainer.h b/dChatServer/PlayerContainer.h index 3f2d783a..9a17f927 100644 --- a/dChatServer/PlayerContainer.h +++ b/dChatServer/PlayerContainer.h @@ -71,6 +71,9 @@ public: const PlayerData& GetPlayerData(const std::string& playerName); PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID); PlayerData& GetPlayerDataMutable(const std::string& playerName); + uint32_t GetPlayerCount() { return m_PlayerCount; }; + uint32_t GetSimCount() { return m_SimCount; }; + const std::map& GetAllPlayers() { return m_Players; }; TeamData* CreateLocalTeam(std::vector members); TeamData* CreateTeam(LWOOBJID leader, bool local = false); @@ -93,5 +96,7 @@ private: std::unordered_map m_Names; uint32_t m_MaxNumberOfBestFriends = 5; uint32_t m_MaxNumberOfFriends = 50; + uint32_t m_PlayerCount = 0; + uint32_t m_SimCount = 0; }; diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index bd7d5f28..bf7e9eb4 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -878,8 +878,26 @@ void SlashCommandHandler::Startup() { }; RegisterCommand(TitleCommand); + Command ShowAllCommand{ + .help = "Show all online players across World Servers", + .info = "Usage: /showall (displayZoneData: Default 1) (displayIndividualPlayers: Default 1)", + .aliases = { "showall" }, + .handle = GMGreaterThanZeroCommands::ShowAll, + .requiredLevel = eGameMasterLevel::JUNIOR_MODERATOR + }; + RegisterCommand(ShowAllCommand); + + Command FindPlayerCommand{ + .help = "Find the World Server a player is in if they are online", + .info = "Find the World Server a player is in if they are online", + .aliases = { "findplayer" }, + .handle = GMGreaterThanZeroCommands::FindPlayer, + .requiredLevel = eGameMasterLevel::JUNIOR_MODERATOR + }; + RegisterCommand(FindPlayerCommand); // Register GM Zero Commands + Command HelpCommand{ .help = "Display command info", .info = "If a command is given, display detailed info on that command. Otherwise display a list of commands with short desctiptions.", diff --git a/dGame/dUtilities/SlashCommands/DEVGMCommands.h b/dGame/dUtilities/SlashCommands/DEVGMCommands.h index 149348a1..5e78ed80 100644 --- a/dGame/dUtilities/SlashCommands/DEVGMCommands.h +++ b/dGame/dUtilities/SlashCommands/DEVGMCommands.h @@ -70,4 +70,4 @@ namespace DEVGMCommands { void RollLoot(Entity* entity, const SystemAddress& sysAddr, const std::string args); void CastSkill(Entity* entity, const SystemAddress& sysAddr, const std::string args); void DeleteInven(Entity* entity, const SystemAddress& sysAddr, const std::string args); -} \ No newline at end of file +} diff --git a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp index d0aa66aa..ea33aa03 100644 --- a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp @@ -287,4 +287,39 @@ namespace GMGreaterThanZeroCommands { std::string name = entity->GetCharacter()->GetName() + " - " + args; GameMessages::SendSetName(entity->GetObjectID(), GeneralUtils::UTF8ToUTF16(name), UNASSIGNED_SYSTEM_ADDRESS); } + + void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + bool displayZoneData = true; + bool displayIndividualPlayers = true; + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + + if (!splitArgs.empty() && !splitArgs.at(0).empty()) displayZoneData = splitArgs.at(0) == "1"; + if (splitArgs.size() > 1) displayIndividualPlayers = splitArgs.at(1) == "1"; + + ShowAllRequest request { + .requestor = entity->GetObjectID(), + .displayZoneData = displayZoneData, + .displayIndividualPlayers = displayIndividualPlayers + }; + + CBITSTREAM; + request.Serialize(bitStream); + Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); + } + + void FindPlayer(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + if (args.empty()) { + GameMessages::SendSlashCommandFeedbackText(entity, u"No player Given"); + return; + } + + FindPlayerRequest request { + .requestor = entity->GetObjectID(), + .playerName = LUWString(args) + }; + + CBITSTREAM; + request.Serialize(bitStream); + Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); + } } diff --git a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.h b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.h index 4b03441f..e8d60383 100644 --- a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.h +++ b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.h @@ -10,4 +10,6 @@ namespace GMGreaterThanZeroCommands { void GmInvis(Entity* entity, const SystemAddress& sysAddr, const std::string args); void SetName(Entity* entity, const SystemAddress& sysAddr, const std::string args); void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args); -} \ No newline at end of file + void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void FindPlayer(Entity* entity, const SystemAddress& sysAddr, const std::string args); +} diff --git a/dNet/ChatPackets.cpp b/dNet/ChatPackets.cpp index d0354659..6c9b36b7 100644 --- a/dNet/ChatPackets.cpp +++ b/dNet/ChatPackets.cpp @@ -12,6 +12,30 @@ #include "eConnectionType.h" #include "eChatMessageType.h" +void ShowAllRequest::Serialize(RakNet::BitStream& bitStream) { + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::SHOW_ALL); + bitStream.Write(this->requestor); + bitStream.Write(this->displayZoneData); + bitStream.Write(this->displayIndividualPlayers); +} + +void ShowAllRequest::Deserialize(RakNet::BitStream& inStream) { + inStream.Read(this->requestor); + inStream.Read(this->displayZoneData); + inStream.Read(this->displayIndividualPlayers); +} + +void FindPlayerRequest::Serialize(RakNet::BitStream& bitStream) { + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WHO); + bitStream.Write(this->requestor); + bitStream.Write(this->playerName); +} + +void FindPlayerRequest::Deserialize(RakNet::BitStream& inStream) { + inStream.Read(this->requestor); + inStream.Read(this->playerName); +} + void ChatPackets::SendChatMessage(const SystemAddress& sysAddr, char chatChannel, const std::string& senderName, LWOOBJID playerObjectID, bool senderMythran, const std::u16string& message) { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GENERAL_CHAT_MESSAGE); diff --git a/dNet/ChatPackets.h b/dNet/ChatPackets.h index 8f6de175..c33d00dd 100644 --- a/dNet/ChatPackets.h +++ b/dNet/ChatPackets.h @@ -11,6 +11,21 @@ struct SystemAddress; #include #include "dCommonVars.h" +struct ShowAllRequest{ + LWOOBJID requestor = LWOOBJID_EMPTY; + bool displayZoneData = true; + bool displayIndividualPlayers = true; + void Serialize(RakNet::BitStream& bitStream); + void Deserialize(RakNet::BitStream& inStream); +}; + +struct FindPlayerRequest{ + LWOOBJID requestor = LWOOBJID_EMPTY; + LUWString playerName; + void Serialize(RakNet::BitStream& bitStream); + void Deserialize(RakNet::BitStream& inStream); +}; + namespace ChatPackets { void SendChatMessage(const SystemAddress& sysAddr, char chatChannel, const std::string& senderName, LWOOBJID playerObjectID, bool senderMythran, const std::u16string& message); void SendSystemMessage(const SystemAddress& sysAddr, const std::u16string& message, bool broadcast = false); diff --git a/dNet/WorldPackets.cpp b/dNet/WorldPackets.cpp index 1c2b8dec..44dd687e 100644 --- a/dNet/WorldPackets.cpp +++ b/dNet/WorldPackets.cpp @@ -12,6 +12,15 @@ #include +void HTTPMonitorInfo::Serialize(RakNet::BitStream &bitStream) const { + bitStream.Write(port); + bitStream.Write(openWeb); + bitStream.Write(supportsSum); + bitStream.Write(supportsDetail); + bitStream.Write(supportsWho); + bitStream.Write(supportsObjects); +} + void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone) { RakNet::BitStream bitStream; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::LOAD_STATIC_ZONE); @@ -160,3 +169,18 @@ void WorldPackets::SendGMLevelChange(const SystemAddress& sysAddr, bool success, SEND_PACKET; } + +void WorldPackets::SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPMonitorInfo& info) { + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::HTTP_MONITOR_INFO_RESPONSE); + info.Serialize(bitStream); + SEND_PACKET; +} + +void WorldPackets::SendDebugOuput(const SystemAddress& sysAddr, const std::string& data){ + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::DEBUG_OUTPUT); + bitStream.Write(data.size()); + bitStream.Write(data); + SEND_PACKET; +} diff --git a/dNet/WorldPackets.h b/dNet/WorldPackets.h index 0d5de079..0081623e 100644 --- a/dNet/WorldPackets.h +++ b/dNet/WorldPackets.h @@ -10,6 +10,19 @@ struct SystemAddress; enum class eGameMasterLevel : uint8_t; enum class eCharacterCreationResponse : uint8_t; enum class eRenameResponse : uint8_t; +namespace RakNet { + class BitStream; +}; + +struct HTTPMonitorInfo { + uint16_t port = 80; + bool openWeb = false; + bool supportsSum = false; + bool supportsDetail = false; + bool supportsWho = false; + bool supportsObjects = false; + void Serialize(RakNet::BitStream &bitstream) const; +}; namespace WorldPackets { void SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone); @@ -21,6 +34,8 @@ namespace WorldPackets { void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm); void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector> unacceptedItems); void SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel); + void SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPMonitorInfo& info); + void SendDebugOuput(const SystemAddress& sysAddr, const std::string& data); } #endif // WORLDPACKETS_H