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>
78 lines
2.9 KiB
C++
78 lines
2.9 KiB
C++
#ifndef PACKETDISPATCHER_H
|
|
#define PACKETDISPATCHER_H
|
|
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <type_traits>
|
|
|
|
#include "BitStream.h"
|
|
#include "BitStreamUtils.h"
|
|
#include "Logger.h"
|
|
#include "RakNetTypes.h"
|
|
#include "StringifiedEnum.h"
|
|
|
|
/**
|
|
* One dispatch map per service a server receives (see docs/PacketArchitecture.md, section 2.2).
|
|
*
|
|
* Each entry names the struct a packet ID is read into and the function that handles it, so the same struct can be
|
|
* handled differently by each server that receives it (WORLD_READY means one thing to master and another to the
|
|
* dashboard). Dispatch reads the packet into a fresh struct; a packet that fails to Deserialize is logged and
|
|
* dropped before its handler runs.
|
|
*
|
|
* PacketDispatcher<MessageType::Master> handlers;
|
|
* handlers.On<MasterPackets::PlayerAdded>(MessageType::Master::PLAYER_ADDED, &OnPlayerAdded);
|
|
* ...
|
|
* handlers.Dispatch(packet); // reads the header and routes by packet ID
|
|
*/
|
|
template<typename IdT>
|
|
class PacketDispatcher {
|
|
public:
|
|
template<typename Msg>
|
|
using Handler = std::function<void(Msg& msg, const SystemAddress& sysAddr)>;
|
|
|
|
// Registers the struct packets with this ID are read into, and what to do with them
|
|
template<typename Msg>
|
|
void On(const IdT id, Handler<Msg> handler) {
|
|
static_assert(std::is_base_of_v<LUBitStream, Msg>, "packets are LUBitStream structs");
|
|
m_Handlers[id] = [handler = std::move(handler)](RakNet::BitStream& bitStream, const SystemAddress& sysAddr) {
|
|
Msg msg;
|
|
if (!msg.Deserialize(bitStream)) return false;
|
|
handler(msg, sysAddr);
|
|
return true;
|
|
};
|
|
}
|
|
|
|
bool Has(const IdT id) const { return m_Handlers.contains(id); }
|
|
|
|
/**
|
|
* Routes a payload (the stream positioned right after the packet header) to its handler.
|
|
* Returns false if the ID has no handler. Packets that fail to Deserialize are logged and dropped.
|
|
*/
|
|
bool Dispatch(const IdT id, RakNet::BitStream& bitStream, const SystemAddress& sysAddr) const {
|
|
const auto it = m_Handlers.find(id);
|
|
if (it == m_Handlers.end()) return false;
|
|
if (!it->second(bitStream, sysAddr)) {
|
|
LOG("Dropped a %s packet (%u) from %s that failed to read", StringifiedEnum::ToString(id).data(), static_cast<uint32_t>(id), sysAddr.ToString());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Reads the packet's header and routes the rest to the handler for its ID. Returns false if the packet is not an
|
|
* LU packet of this dispatcher's service or has no handler.
|
|
*/
|
|
bool Dispatch(const Packet* packet, const ServiceType service) const {
|
|
if (!packet || packet->length < 1) return false;
|
|
RakNet::BitStream bitStream(packet->data, packet->length, false);
|
|
LUBitStream header;
|
|
if (!header.ReadHeader(bitStream) || header.connectionType != service) return false;
|
|
return Dispatch(static_cast<IdT>(header.internalPacketID), bitStream, packet->systemAddress);
|
|
}
|
|
|
|
private:
|
|
std::map<IdT, std::function<bool(RakNet::BitStream&, const SystemAddress&)>> m_Handlers;
|
|
};
|
|
|
|
#endif // PACKETDISPATCHER_H
|