mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 19:03:43 +00:00
The guild packets of the 1.10.64 client (docs/Guilds.md) as Serialize/Deserialize structs, laid out the way the client's packet handlers read them: GUILD_CREATE_RESPONSE, GUILD_INVITE, GUILD_INVITE_INITIAL_RESPONSE, _FINAL_RESPONSE, _CONFIRM, GUILD_ADD_PLAYER, GUILD_REMOVE_PLAYER, GUILD_LOGIN_LOGOUT and GUILD_DATA (ClientPackets), the world packet TMP_GUILD_CREATE the create box sends, the chat packets the client sends through its world (GUILD_INVITE, GUILD_INVITE_RESPONSE, GUILD_LEAVE, GUILD_GET_ALL), DLU's world <-> chat packets (GUILD_CREATE, GUILD_KICK, GUILD_GET_STATUS as the chat server's guild update to a world, and GUILD_SET_RANK and GUILD_DISBAND appended to MessageType::Chat after CREATE_TEAM) and the game message DisplayGuildCreateBox (626). Fixed-size names always keep a NUL, since the client reads them as C strings. Enums eGuildCreateResponse, eGuildInviteResponse, eGuildInviteFinalResponse, eGuildRank and eGuildLeaveReason. Nothing sends them yet. Check: GuildPacketsTests compares every packet with the bytes at the client's offsets. Nothing to check in game. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
523 lines
20 KiB
C++
523 lines
20 KiB
C++
/*
|
|
* Darkflame Universe
|
|
* Copyright 2018
|
|
*/
|
|
|
|
#ifndef CHATPACKETS_H
|
|
#define CHATPACKETS_H
|
|
|
|
struct SystemAddress;
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include "dCommonVars.h"
|
|
#include "MessageType/Chat.h"
|
|
#include "MessageType/Client.h"
|
|
#include "MessageType/Game.h"
|
|
#include "BitStreamUtils.h"
|
|
#include "eAddFriendResponseCode.h"
|
|
#include "eAddFriendResponseType.h"
|
|
#include "eAddIgnoreResponse.h"
|
|
#include "eChatChannel.h"
|
|
#include "eChatMessageResponseCode.h"
|
|
#include "eGameMasterLevel.h"
|
|
|
|
/**
|
|
* Packets of the chat service (docs/PacketArchitecture.md).
|
|
*
|
|
* Top level: packets between world servers and the chat server (MessageType::Chat). The ones a client sends go to its
|
|
* world as a ROUTE_PACKET; the world forwards them to chat with the player's object ID in front, so every such
|
|
* struct starts with `playerID`.
|
|
*
|
|
* ChatPackets::Client: the chat-service packets the client receives. What the chat server sends a client in other
|
|
* services (friends, ignore list and team responses, team game messages) is in ClientPackets.h. The chat server wraps
|
|
* them in a WorldRoutePacket (WorldRoutePacket.h) to the world the player is in, which passes the inner packet on to
|
|
* the client unchanged.
|
|
*/
|
|
namespace ChatPackets {
|
|
// World -> chat: a player finished loading into a world (also sent when they change worlds)
|
|
struct LoginSessionNotify : public LUBitStream {
|
|
static constexpr uint32_t MAX_NAME_LENGTH = 33;
|
|
|
|
LWOOBJID playerID{};
|
|
std::string playerName; // u32 length, then 1 byte per character
|
|
LWOZONEID zoneID{};
|
|
int64_t muteExpire{}; // time_t
|
|
eGameMasterLevel gmLevel{};
|
|
// Sent again to a new chat server after a live update restarted chat (CHAT_SERVER_READY): the player didn't
|
|
// log in or change zones. Written last, only when set, and read only when there.
|
|
bool resync{};
|
|
|
|
LoginSessionNotify() : LUBitStream(ServiceType::CHAT, MessageType::Chat::LOGIN_SESSION_NOTIFY) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat: a player left (or switched characters, or a character was deleted)
|
|
struct UnexpectedDisconnect : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
|
|
UnexpectedDisconnect() : LUBitStream(ServiceType::CHAT, MessageType::Chat::UNEXPECTED_DISCONNECT) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat: a player's GM level changed
|
|
struct GMLevelUpdate : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
eGameMasterLevel gmLevel{};
|
|
|
|
GMLevelUpdate() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GMLEVEL_UPDATE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat, and chat -> every world: a player was muted until `expire` (time_t; 1 means until reviewed)
|
|
struct GMMute : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
int64_t expire{};
|
|
|
|
GMMute() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GM_MUTE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat -> every other world: a GM's /announce
|
|
struct Announcement : public LUBitStream {
|
|
std::string title; // u32 length, then 1 byte per character
|
|
std::string message; // u32 length, then 1 byte per character
|
|
|
|
Announcement() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GM_ANNOUNCE) {};
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat (custom DLU message): make a team of the players starting an activity together
|
|
struct CreateTeam : public LUBitStream {
|
|
// More can't be a team; bigger requests are dropped
|
|
static constexpr uint64_t MAX_MEMBERS = 3;
|
|
|
|
LWOOBJID leaderID{};
|
|
std::vector<LWOOBJID> members; // u64 count
|
|
LWOZONEID zoneID{};
|
|
|
|
CreateTeam() : LUBitStream(ServiceType::CHAT, MessageType::Chat::CREATE_TEAM) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// Chat -> every world: a team changed (or was deleted), so worlds can share loot and credit
|
|
struct TeamUpdate : public LUBitStream {
|
|
LWOOBJID teamID{};
|
|
bool deleteTeam{}; // one bit; nothing follows when set
|
|
uint8_t lootFlag{};
|
|
std::vector<LWOOBJID> members; // u8 count
|
|
|
|
TeamUpdate() : LUBitStream(ServiceType::CHAT, MessageType::Chat::TEAM_GET_STATUS) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat -> the earner's online friends: a mission announcement (routed to the client as is)
|
|
struct AchievementNotify : public LUBitStream {
|
|
LUWString targetPlayerName{};
|
|
uint32_t missionEmailID{};
|
|
LWOOBJID earningPlayerID{};
|
|
LUWString earnerName{};
|
|
AchievementNotify() : LUBitStream(ServiceType::CHAT, MessageType::Chat::ACHIEVEMENT_NOTIFY) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat -> world: receiverID has new mail. DLU's own server packet: the chat server passes it on to the world
|
|
// the receiver is in, which sends them the new-mail notification. Characters that are offline are skipped.
|
|
struct MailNotify : public LUBitStream {
|
|
LWOOBJID receiverID{ LWOOBJID_EMPTY };
|
|
MailNotify() : LUBitStream(ServiceType::CHAT, MessageType::Chat::MAIL) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat: /showall
|
|
struct ShowAllRequest : public LUBitStream {
|
|
LWOOBJID requestor = LWOOBJID_EMPTY;
|
|
bool displayZoneData = true; // one bit
|
|
bool displayIndividualPlayers = true; // one bit
|
|
|
|
ShowAllRequest() : LUBitStream(ServiceType::CHAT, MessageType::Chat::SHOW_ALL) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat: /findplayer
|
|
struct FindPlayerRequest : public LUBitStream {
|
|
LWOOBJID requestor = LWOOBJID_EMPTY;
|
|
LUWString playerName;
|
|
|
|
FindPlayerRequest() : LUBitStream(ServiceType::CHAT, MessageType::Chat::WHO) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat. A chat packet the client sent through the world server (WorldPackets::RoutePacket): its id,
|
|
// the object ID of the player who sent it (the chat server has no other way to know), then the routed bytes.
|
|
struct RoutedFromClient : public LUBitStream {
|
|
LWOOBJID senderID{};
|
|
std::vector<uint8_t> data{};
|
|
|
|
RoutedFromClient(uint8_t routedMessageID = 0) : LUBitStream(ServiceType::CHAT, routedMessageID) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// Client -> world -> chat. Every packet below starts with the player's object ID (put there by the world).
|
|
// `unknown` is 4 bytes of the client's packet the server doesn't use.
|
|
|
|
// A player's friends list (sent when they load into a world)
|
|
struct GetFriendsList : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
|
|
GetFriendsList() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GET_FRIENDS_LIST) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
struct AddFriendRequest : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
LUWString friendName;
|
|
uint8_t isBestFriendRequest{};
|
|
|
|
AddFriendRequest() : LUBitStream(ServiceType::CHAT, MessageType::Chat::ADD_FRIEND_REQUEST) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// The answer of the player who was asked to be friends
|
|
struct AddFriendResponse : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
eAddFriendResponseCode responseCode{};
|
|
LUWString friendName;
|
|
|
|
AddFriendResponse() : LUBitStream(ServiceType::CHAT, MessageType::Chat::ADD_FRIEND_RESPONSE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
struct RemoveFriend : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
LUWString friendName;
|
|
|
|
RemoveFriend() : LUBitStream(ServiceType::CHAT, MessageType::Chat::REMOVE_FRIEND) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
struct GetIgnoreList : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
|
|
GetIgnoreList() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GET_IGNORE_LIST) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
struct AddIgnore : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
LUWString playerName;
|
|
|
|
AddIgnore() : LUBitStream(ServiceType::CHAT, MessageType::Chat::ADD_IGNORE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
struct RemoveIgnore : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
LUWString playerName;
|
|
|
|
RemoveIgnore() : LUBitStream(ServiceType::CHAT, MessageType::Chat::REMOVE_IGNORE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// Chat in a channel the chat server handles (team chat)
|
|
struct GeneralChatMessage : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
eChatChannel chatChannel{};
|
|
uint32_t messageLength{}; // characters in message; more than MAX_MESSAGE_LENGTH is refused
|
|
// What the client fills in about itself (the server uses its own records)
|
|
LUWString senderName;
|
|
LWOOBJID senderID{};
|
|
uint16_t sourceID{};
|
|
uint8_t senderGMLevel{};
|
|
LUWString message{ 0u }; // messageLength characters
|
|
|
|
GeneralChatMessage() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// A whisper
|
|
struct PrivateChatMessage : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
eChatChannel chatChannel{};
|
|
uint32_t messageLength{}; // characters in message; more than MAX_MESSAGE_LENGTH is refused
|
|
// What the client fills in about itself (the server uses its own records)
|
|
LUWString senderName;
|
|
LWOOBJID senderID{};
|
|
uint16_t sourceID{};
|
|
uint8_t senderGMLevel{};
|
|
LUWString receiverName;
|
|
uint8_t receiverGMLevel{};
|
|
uint8_t responseCode{};
|
|
LUWString message{ 0u }; // messageLength characters
|
|
|
|
PrivateChatMessage() : LUBitStream(ServiceType::CHAT, MessageType::Chat::PRIVATE_CHAT_MESSAGE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
struct TeamInvite : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
LUWString invitedPlayer;
|
|
|
|
TeamInvite() : LUBitStream(ServiceType::CHAT, MessageType::Chat::TEAM_INVITE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
struct TeamInviteResponse : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
uint8_t declined{};
|
|
LWOOBJID leaderID{};
|
|
|
|
TeamInviteResponse() : LUBitStream(ServiceType::CHAT, MessageType::Chat::TEAM_INVITE_RESPONSE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
struct TeamLeave : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
|
|
TeamLeave() : LUBitStream(ServiceType::CHAT, MessageType::Chat::TEAM_LEAVE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
struct TeamKick : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
LUWString kickedPlayer;
|
|
|
|
TeamKick() : LUBitStream(ServiceType::CHAT, MessageType::Chat::TEAM_KICK) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// Make another member the team's leader
|
|
struct TeamSetLeader : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
LUWString promotedPlayer;
|
|
|
|
TeamSetLeader() : LUBitStream(ServiceType::CHAT, MessageType::Chat::TEAM_SET_LEADER) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
struct TeamSetLoot : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
uint8_t lootFlag{};
|
|
|
|
TeamSetLoot() : LUBitStream(ServiceType::CHAT, MessageType::Chat::TEAM_SET_LOOT) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// A player's team, again (sent when they load into a world)
|
|
struct TeamGetStatus : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
|
|
TeamGetStatus() : LUBitStream(ServiceType::CHAT, MessageType::Chat::TEAM_GET_STATUS) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// The lowest chat mode among who would read the player's team chat (chatChannel 8 team, 10 local team)
|
|
struct RequestMinimumChatMode : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
uint8_t chatChannel{};
|
|
|
|
RequestMinimumChatMode() : LUBitStream(ServiceType::CHAT, MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// The same for a private message to recipientName (chatChannel 7)
|
|
struct RequestMinimumChatModePrivate : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
uint8_t chatChannel{};
|
|
LUWString recipientName;
|
|
|
|
RequestMinimumChatModePrivate() : LUBitStream(ServiceType::CHAT, MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE_PRIVATE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// Guilds (docs/Guilds.md). The client sends GUILD_INVITE, GUILD_INVITE_RESPONSE, GUILD_LEAVE and GUILD_GET_ALL
|
|
// (routed like the ones above); the others are DLU's world <-> chat packets.
|
|
|
|
// Invite a player (by name) to the sender's guild
|
|
struct GuildInvite : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
LUWString invitedPlayer{ 33 };
|
|
|
|
GuildInvite() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GUILD_INVITE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// The answer to the invite in the message box (button 1 accepts: declined 0)
|
|
struct GuildInviteResponse : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
uint8_t declined{};
|
|
|
|
GuildInviteResponse() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GUILD_INVITE_RESPONSE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// Leave the guild. The client sends 66 more bytes of whatever was on its stack; they are not read.
|
|
struct GuildLeave : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
|
|
GuildLeave() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GUILD_LEAVE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// The player's guild and its members (answered with ClientPackets::GuildData)
|
|
struct GuildGetAll : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
|
|
GuildGetAll() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GUILD_GET_ALL) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat: the name from the client's TMP_GUILD_CREATE
|
|
struct GuildCreate : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
LUWString guildName{ 31 };
|
|
|
|
GuildCreate() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GUILD_CREATE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat: /gkick (the client never sends GUILD_KICK; the layout is the team kick's)
|
|
struct GuildKick : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
uint32_t unknown{};
|
|
LUWString kickedPlayer{ 33 };
|
|
|
|
GuildKick() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GUILD_KICK) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat: change a member's rank (eGuildRank; LEADER hands the guild over)
|
|
struct GuildSetRank : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
LUWString targetPlayer{ 33 };
|
|
uint8_t rank{};
|
|
|
|
GuildSetRank() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GUILD_SET_RANK) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// World -> chat: the leader disbands the guild
|
|
struct GuildDisband : public LUBitStream {
|
|
LWOOBJID playerID{};
|
|
|
|
GuildDisband() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GUILD_DISBAND) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// Chat -> the character's world: its guild changed; the world puts it in the character component (guild 0: none).
|
|
// guildName is what other players may see (empty while the name waits for moderation).
|
|
struct GuildStatus : public LUBitStream {
|
|
LWOOBJID characterID{};
|
|
LWOOBJID guildID{};
|
|
LUWString guildName{ 31 };
|
|
|
|
GuildStatus() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GUILD_GET_STATUS) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
/**
|
|
* Chat-service packets the client receives, from the chat server (routed through the player's world) or from its world.
|
|
*/
|
|
namespace Client {
|
|
// Chat in a channel (world -> client; sent to everyone)
|
|
struct GeneralChatMessage : public LUBitStream {
|
|
uint64_t unknown{}; // always 0 from the server
|
|
uint8_t chatChannel{};
|
|
LUWString senderName;
|
|
LWOOBJID senderID{};
|
|
uint16_t sourceID{};
|
|
uint8_t senderGMLevel{}; // DLU always sends 0
|
|
std::u16string message; // u32 length (without the terminator), the characters, then a 0 terminator
|
|
|
|
GeneralChatMessage() : LUBitStream(ServiceType::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
|
|
// A whisper, or team chat, or why a whisper didn't go out
|
|
struct PrivateChatMessage : public LUBitStream {
|
|
LWOOBJID playerID{}; // the sender again
|
|
eChatChannel chatChannel{};
|
|
uint32_t messageLength{}; // DLU always sends 0
|
|
LUWString senderName;
|
|
LWOOBJID senderID{};
|
|
uint16_t sourceID{};
|
|
eGameMasterLevel senderGMLevel{};
|
|
LUWString receiverName;
|
|
eGameMasterLevel receiverGMLevel{};
|
|
eChatMessageResponseCode responseCode{};
|
|
// Everything that is left. The sender's message as it came in, with its padding.
|
|
LUWString message{ 0u };
|
|
|
|
PrivateChatMessage() : LUBitStream(ServiceType::CHAT, MessageType::Chat::PRIVATE_CHAT_MESSAGE) {}
|
|
void Serialize(RakNet::BitStream& bitStream) const override;
|
|
bool Deserialize(RakNet::BitStream& bitStream) override;
|
|
};
|
|
}
|
|
|
|
// A system message (chat channel 4) to one client, or everyone when sysAddr is UNASSIGNED_SYSTEM_ADDRESS.
|
|
// `broadcast` is not used: a message with an address only goes to that client.
|
|
void SendSystemMessage(const SystemAddress& sysAddr, const std::string& message, bool broadcast = false);
|
|
void SendSystemMessage(const SystemAddress& sysAddr, const std::u16string& message, bool broadcast = false);
|
|
};
|
|
|
|
#endif // CHATPACKETS_H
|