mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 19:03:43 +00:00
Every packet of the chat service is now an LUBitStream struct in dNet/ChatPackets.h (docs/PacketArchitecture.md, PR 13), and nothing in the chat server or the chat side of the world server reads or writes a packet by hand any more. - World <-> chat: LoginSessionNotify, UnexpectedDisconnect, GMLevelUpdate, GMMute, Announcement (GM announce), CreateTeam, TeamUpdate (TEAM_GET_STATUS to worlds), AchievementNotify, ShowAllRequest, FindPlayerRequest. - Client -> world -> chat (friends, ignore list, teams, general and private chat): GetFriendsList, AddFriendRequest, AddFriendResponse, RemoveFriend, GetIgnoreList, AddIgnore, RemoveIgnore, GeneralChatMessage, PrivateChatMessage, TeamInvite, TeamInviteResponse, TeamLeave, TeamKick, TeamSetLeader, TeamSetLoot, TeamGetStatus. The 77 bytes the handlers skipped are named fields now (the sender block the client fills in); the 4 unused bytes after the player ID are kept as `unknown`. - What the client receives. Chat service (ChatPackets::Client): GeneralChatMessage (replaces SendChatMessage; SendSystemMessage stays as a helper built on it), PrivateChatMessage. Client service (ClientPackets, as structs go in the file of the ServiceType in their header): SendCannedText (replaces SendMessageFail), GetFriendsListResponse, AddFriendRequest, AddFriendResponse, RemoveFriendResponse, UpdateFriendNotify, WhoResponse, ShowAllResponse, Get/Add/RemoveIgnoreResponse, TeamInvite, TeamInviteInitialResponse, and the team game messages chat writes (TeamInviteConfirm, TeamGetStatusResponse, TeamSetLeader, TeamAddPlayer, TeamRemovePlayer, TeamSetOffWorldFlag) as TeamGameMsg structs, since chat doesn't link dGame's NetGameMsg. - WORLD_ROUTE_PACKET is ChatPackets::WorldRoutePacket (its own file, dNet/WorldRoutePacket.h, since it carries packets of other services): the target and the inner packet. ChatPacketHandler::SendRouted replaces the per-function route headers and SendRoutedMsg. Dispatch: dNet/PacketDispatcher.h is a dispatch map from packet ID to (struct, handler function); packets that fail to Deserialize are logged and dropped. The chat server's switch and the world's HandlePacketChat switch are now maps. The handlers take the structs; their logic is unchanged. World code sends to chat with ChatServerLink::Send (dGame) instead of writing to Game::chatServer by hand. eChatChannel, eChatMessageResponseCode and eAddIgnoreResponse moved to dCommon/dEnums so the structs can use them. Verified: tests/dGameTests/dNetTests/Legacy/ChatPacketsLegacy.h is a verbatim copy of the old senders and readers; ChatPacketsTests (25 tests) sends a grid of inputs through both and requires identical bits, bytes and destination, checks the old readers read what the structs write, round trips every struct, checks truncated packets are refused, and has hand written golden packets for general chat, canned text, GM mute and a routed team game message. Breaking one field width (UpdateFriendNotify) and the TeamAddPlayer zone flag made the tests fail. No wire bytes changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
101 lines
3.0 KiB
C++
101 lines
3.0 KiB
C++
#pragma once
|
|
#include <map>
|
|
#include "dCommonVars.h"
|
|
#include "RakString.h"
|
|
#include <vector>
|
|
#include "Game.h"
|
|
#include "dServer.h"
|
|
#include <unordered_map>
|
|
#include "ChatPackets.h"
|
|
|
|
enum class eGameMasterLevel : uint8_t;
|
|
|
|
struct TeamData;
|
|
|
|
struct IgnoreData {
|
|
IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {}
|
|
inline bool operator==(const std::string& other) const noexcept {
|
|
return playerName == other;
|
|
}
|
|
|
|
inline bool operator==(const LWOOBJID& other) const noexcept {
|
|
return playerId == other;
|
|
}
|
|
|
|
LWOOBJID playerId = LWOOBJID_EMPTY;
|
|
std::string playerName;
|
|
};
|
|
|
|
struct PlayerData {
|
|
operator bool() const noexcept {
|
|
return playerID != LWOOBJID_EMPTY;
|
|
}
|
|
|
|
bool operator==(const PlayerData& other) const noexcept {
|
|
return playerID == other.playerID;
|
|
}
|
|
|
|
bool GetIsMuted() const {
|
|
return muteExpire == 1 || muteExpire > time(NULL);
|
|
}
|
|
|
|
SystemAddress worldServerSysAddr{};
|
|
LWOZONEID zoneID{};
|
|
LWOOBJID playerID = LWOOBJID_EMPTY;
|
|
time_t muteExpire = 0;
|
|
uint8_t countOfBestFriends = 0;
|
|
std::string playerName;
|
|
std::vector<FriendData> friends;
|
|
std::vector<IgnoreData> ignoredPlayers;
|
|
eGameMasterLevel gmLevel = static_cast<eGameMasterLevel>(0); // CIVILLIAN
|
|
bool isFTP = false;
|
|
bool isLogin = false;
|
|
};
|
|
|
|
|
|
struct TeamData {
|
|
TeamData();
|
|
LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
|
|
LWOOBJID leaderID = LWOOBJID_EMPTY;
|
|
std::vector<LWOOBJID> memberIDs{};
|
|
uint8_t lootFlag = 0;
|
|
bool local = false;
|
|
LWOZONEID zoneId = {};
|
|
};
|
|
|
|
class PlayerContainer {
|
|
public:
|
|
void Initialize();
|
|
void InsertPlayer(const ChatPackets::LoginSessionNotify& notify, const SystemAddress& sysAddr);
|
|
void ScheduleRemovePlayer(const ChatPackets::UnexpectedDisconnect& notify, const SystemAddress& sysAddr);
|
|
void RemovePlayer(const LWOOBJID playerID);
|
|
void MuteUpdate(const ChatPackets::GMMute& mute, const SystemAddress& sysAddr);
|
|
void BroadcastMuteUpdate(LWOOBJID player, time_t time);
|
|
void Shutdown();
|
|
|
|
const PlayerData& GetPlayerData(const LWOOBJID& playerID);
|
|
const PlayerData& GetPlayerData(const std::string& playerName);
|
|
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
|
|
PlayerData& GetPlayerDataMutable(const std::string& playerName);
|
|
std::u16string GetName(LWOOBJID playerID);
|
|
LWOOBJID GetId(const std::u16string& playerName);
|
|
void Update(const float deltaTime);
|
|
|
|
uint32_t GetPlayerCount() { return m_PlayerCount; };
|
|
uint32_t GetSimCount() { return m_SimCount; };
|
|
const std::map<LWOOBJID, PlayerData>& GetAllPlayers() const { return m_Players; };
|
|
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
|
|
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
|
|
bool PlayerBeingRemoved(const LWOOBJID playerID) { return m_PlayersToRemove.contains(playerID); }
|
|
|
|
private:
|
|
std::map<LWOOBJID, PlayerData> m_Players;
|
|
std::unordered_map<LWOOBJID, std::u16string> m_Names;
|
|
std::map<LWOOBJID, float> m_PlayersToRemove;
|
|
uint32_t m_MaxNumberOfBestFriends = 5;
|
|
uint32_t m_MaxNumberOfFriends = 50;
|
|
uint32_t m_PlayerCount = 0;
|
|
uint32_t m_SimCount = 0;
|
|
};
|
|
|