mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 19:03:43 +00:00
With new binaries in place, master moves everything onto them while the server keeps running (the dashboard's Live update, /liveupdate or SIGUSR2 to master): - database migrations of the new build first; a failure stops there - UGC finishes the jobs it is running (queued rows stay pending), auth restarts, chat hands its teams to master for the next chat server; master starts the new processes and retries ones that don't come back - once the new chat server is up (CHAT_SERVER_READY) every world connects at once and sends its players again (LoginSessionNotify resync, no login logged) - every world instance is replaced with an instance migration: public worlds and private ones (same password) at once, properties after the old instance saved and froze the property (MIGRATE_PREPARE: no building, claiming or saving there any more), activity zones and character select once their players left or after a wait; empty instances just stop, zones in prestart_worlds get a new one first - the dashboard restarts last and picks the status up again Players land where they stood (position carried in CarriedPlayerState, also on properties and Moon Base). Draining instances get no new players (InstanceMigration::AcceptsNewPlayers) and show as "Moving players" in the world list. The order lives in LiveUpdateMachine.h without master state and is unit tested; master's glue is LiveUpdateCoordinator. Master itself is not replaced. Message IDs are appended only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
516 lines
17 KiB
C++
516 lines
17 KiB
C++
/*
|
|
* Darkflame Universe
|
|
* Copyright 2018
|
|
*/
|
|
|
|
#include "ChatPackets.h"
|
|
#include "RakNetTypes.h"
|
|
#include "BitStream.h"
|
|
#include "Game.h"
|
|
#include "BitStreamUtils.h"
|
|
#include "dServer.h"
|
|
#include "ServiceType.h"
|
|
#include "MessageType/Chat.h"
|
|
|
|
namespace {
|
|
// A zone ID as three fields: map (u16), instance (u16), clone (u32)
|
|
void WriteZone(RakNet::BitStream& bitStream, const LWOZONEID& zoneID) {
|
|
bitStream.Write(zoneID.GetMapID());
|
|
bitStream.Write(zoneID.GetInstanceID());
|
|
bitStream.Write(zoneID.GetCloneID());
|
|
}
|
|
|
|
bool ReadZone(RakNet::BitStream& bitStream, LWOZONEID& zoneID) {
|
|
LWOMAPID mapID{};
|
|
LWOINSTANCEID instanceID{};
|
|
LWOCLONEID cloneID{};
|
|
VALIDATE_READ(bitStream.Read(mapID));
|
|
VALIDATE_READ(bitStream.Read(instanceID));
|
|
VALIDATE_READ(bitStream.Read(cloneID));
|
|
zoneID = LWOZONEID(mapID, instanceID, cloneID);
|
|
return true;
|
|
}
|
|
|
|
// The LUWString fills the rest of the stream (a message whose length isn't in the packet)
|
|
bool ReadRemainingWString(RakNet::BitStream& bitStream, LUWString& value) {
|
|
value.size = static_cast<uint32_t>(BITS_TO_BYTES(bitStream.GetNumberOfUnreadBits()) / sizeof(char16_t));
|
|
return value.size == 0 || bitStream.Read(value);
|
|
}
|
|
|
|
// Player and team packets from the client all start with the player's object ID and 4 unused bytes
|
|
bool ReadPlayerHeader(RakNet::BitStream& bitStream, LWOOBJID& playerID, uint32_t& unknown) {
|
|
VALIDATE_READ(bitStream.Read(playerID));
|
|
VALIDATE_READ(bitStream.Read(unknown));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
namespace ChatPackets {
|
|
void LoginSessionNotify::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
BitStreamUtils::WriteLengthPrefixed<uint32_t>(bitStream, playerName);
|
|
WriteZone(bitStream, zoneID);
|
|
bitStream.Write(muteExpire);
|
|
bitStream.Write(gmLevel);
|
|
// Only on a resync, so every other one is byte for byte what it always was
|
|
if (resync) bitStream.Write<uint8_t>(1);
|
|
}
|
|
|
|
bool LoginSessionNotify::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(playerID));
|
|
VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed<uint32_t>(bitStream, playerName, MAX_NAME_LENGTH));
|
|
VALIDATE_READ(ReadZone(bitStream, zoneID));
|
|
VALIDATE_READ(bitStream.Read(muteExpire));
|
|
VALIDATE_READ(bitStream.Read(gmLevel));
|
|
uint8_t resyncValue{};
|
|
resync = bitStream.GetNumberOfUnreadBits() >= 8 && bitStream.Read(resyncValue) && resyncValue != 0;
|
|
return true;
|
|
}
|
|
|
|
void UnexpectedDisconnect::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
}
|
|
|
|
bool UnexpectedDisconnect::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(playerID));
|
|
return true;
|
|
}
|
|
|
|
void GMLevelUpdate::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(gmLevel);
|
|
}
|
|
|
|
bool GMLevelUpdate::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(playerID));
|
|
VALIDATE_READ(bitStream.Read(gmLevel));
|
|
return true;
|
|
}
|
|
|
|
void GMMute::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(expire);
|
|
}
|
|
|
|
bool GMMute::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(playerID));
|
|
VALIDATE_READ(bitStream.Read(expire));
|
|
return true;
|
|
}
|
|
|
|
void Announcement::Serialize(RakNet::BitStream& bitStream) const {
|
|
BitStreamUtils::WriteLengthPrefixed<uint32_t>(bitStream, title);
|
|
BitStreamUtils::WriteLengthPrefixed<uint32_t>(bitStream, message);
|
|
}
|
|
|
|
bool Announcement::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed<uint32_t>(bitStream, title));
|
|
VALIDATE_READ(BitStreamUtils::ReadLengthPrefixed<uint32_t>(bitStream, message));
|
|
return true;
|
|
}
|
|
|
|
void CreateTeam::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(leaderID);
|
|
bitStream.Write<uint64_t>(members.size());
|
|
for (const auto member : members) bitStream.Write(member);
|
|
WriteZone(bitStream, zoneID);
|
|
}
|
|
|
|
bool CreateTeam::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(leaderID));
|
|
uint64_t count{};
|
|
VALIDATE_READ(bitStream.Read(count));
|
|
if (count > MAX_MEMBERS) return false;
|
|
members.resize(count);
|
|
for (auto& member : members) VALIDATE_READ(bitStream.Read(member));
|
|
VALIDATE_READ(ReadZone(bitStream, zoneID));
|
|
return true;
|
|
}
|
|
|
|
void TeamUpdate::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(teamID);
|
|
bitStream.Write(deleteTeam);
|
|
if (deleteTeam) return;
|
|
bitStream.Write(lootFlag);
|
|
bitStream.Write<uint8_t>(members.size());
|
|
for (const auto member : members) bitStream.Write(member);
|
|
}
|
|
|
|
bool TeamUpdate::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(teamID));
|
|
VALIDATE_READ(bitStream.Read(deleteTeam));
|
|
if (deleteTeam) return true;
|
|
VALIDATE_READ(bitStream.Read(lootFlag));
|
|
uint8_t count{};
|
|
VALIDATE_READ(bitStream.Read(count));
|
|
members.resize(count);
|
|
for (auto& member : members) VALIDATE_READ(bitStream.Read(member));
|
|
return true;
|
|
}
|
|
|
|
void AchievementNotify::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write<uint64_t>(0); // Packing
|
|
bitStream.Write<uint32_t>(0); // Packing
|
|
bitStream.Write<uint8_t>(0); // Packing
|
|
bitStream.Write(earnerName);
|
|
bitStream.Write<uint64_t>(0); // Packing / No way to know meaning because of not enough data.
|
|
bitStream.Write<uint32_t>(0); // Packing / No way to know meaning because of not enough data.
|
|
bitStream.Write<uint16_t>(0); // Packing / No way to know meaning because of not enough data.
|
|
bitStream.Write<uint8_t>(0); // Packing / No way to know meaning because of not enough data.
|
|
bitStream.Write(missionEmailID);
|
|
bitStream.Write(earningPlayerID);
|
|
bitStream.Write(targetPlayerName);
|
|
}
|
|
|
|
bool AchievementNotify::Deserialize(RakNet::BitStream& bitStream) {
|
|
bitStream.IgnoreBytes(13);
|
|
VALIDATE_READ(bitStream.Read(earnerName));
|
|
bitStream.IgnoreBytes(15);
|
|
VALIDATE_READ(bitStream.Read(missionEmailID));
|
|
VALIDATE_READ(bitStream.Read(earningPlayerID));
|
|
VALIDATE_READ(bitStream.Read(targetPlayerName));
|
|
return true;
|
|
}
|
|
|
|
void MailNotify::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(receiverID);
|
|
}
|
|
|
|
bool MailNotify::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(receiverID));
|
|
return true;
|
|
}
|
|
|
|
void ShowAllRequest::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(requestor);
|
|
bitStream.Write(displayZoneData);
|
|
bitStream.Write(displayIndividualPlayers);
|
|
}
|
|
|
|
bool ShowAllRequest::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(requestor));
|
|
VALIDATE_READ(bitStream.Read(displayZoneData));
|
|
VALIDATE_READ(bitStream.Read(displayIndividualPlayers));
|
|
return true;
|
|
}
|
|
|
|
void FindPlayerRequest::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(requestor);
|
|
bitStream.Write(playerName);
|
|
}
|
|
|
|
bool FindPlayerRequest::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(requestor));
|
|
VALIDATE_READ(bitStream.Read(playerName));
|
|
return true;
|
|
}
|
|
|
|
void GetFriendsList::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
}
|
|
|
|
bool GetFriendsList::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(playerID));
|
|
return true;
|
|
}
|
|
|
|
void AddFriendRequest::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(friendName);
|
|
bitStream.Write(isBestFriendRequest);
|
|
}
|
|
|
|
bool AddFriendRequest::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(ReadPlayerHeader(bitStream, playerID, unknown));
|
|
VALIDATE_READ(bitStream.Read(friendName));
|
|
VALIDATE_READ(bitStream.Read(isBestFriendRequest));
|
|
return true;
|
|
}
|
|
|
|
void AddFriendResponse::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(responseCode);
|
|
bitStream.Write(friendName);
|
|
}
|
|
|
|
bool AddFriendResponse::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(ReadPlayerHeader(bitStream, playerID, unknown));
|
|
VALIDATE_READ(bitStream.Read(responseCode));
|
|
VALIDATE_READ(bitStream.Read(friendName));
|
|
return true;
|
|
}
|
|
|
|
void RemoveFriend::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(friendName);
|
|
}
|
|
|
|
bool RemoveFriend::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(ReadPlayerHeader(bitStream, playerID, unknown));
|
|
VALIDATE_READ(bitStream.Read(friendName));
|
|
return true;
|
|
}
|
|
|
|
void GetIgnoreList::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
}
|
|
|
|
bool GetIgnoreList::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(playerID));
|
|
return true;
|
|
}
|
|
|
|
void AddIgnore::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(playerName);
|
|
}
|
|
|
|
bool AddIgnore::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(ReadPlayerHeader(bitStream, playerID, unknown));
|
|
VALIDATE_READ(bitStream.Read(playerName));
|
|
return true;
|
|
}
|
|
|
|
void RemoveIgnore::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(playerName);
|
|
}
|
|
|
|
bool RemoveIgnore::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(ReadPlayerHeader(bitStream, playerID, unknown));
|
|
VALIDATE_READ(bitStream.Read(playerName));
|
|
return true;
|
|
}
|
|
|
|
void GeneralChatMessage::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(chatChannel);
|
|
bitStream.Write(messageLength);
|
|
bitStream.Write(senderName);
|
|
bitStream.Write(senderID);
|
|
bitStream.Write(sourceID);
|
|
bitStream.Write(senderGMLevel);
|
|
bitStream.Write(message);
|
|
}
|
|
|
|
bool GeneralChatMessage::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(ReadPlayerHeader(bitStream, playerID, unknown));
|
|
VALIDATE_READ(bitStream.Read(chatChannel));
|
|
VALIDATE_READ(bitStream.Read(messageLength));
|
|
if (messageLength > MAX_MESSAGE_LENGTH) return false;
|
|
VALIDATE_READ(bitStream.Read(senderName));
|
|
VALIDATE_READ(bitStream.Read(senderID));
|
|
VALIDATE_READ(bitStream.Read(sourceID));
|
|
VALIDATE_READ(bitStream.Read(senderGMLevel));
|
|
message = LUWString(messageLength);
|
|
if (messageLength != 0) VALIDATE_READ(bitStream.Read(message));
|
|
return true;
|
|
}
|
|
|
|
void PrivateChatMessage::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(chatChannel);
|
|
bitStream.Write(messageLength);
|
|
bitStream.Write(senderName);
|
|
bitStream.Write(senderID);
|
|
bitStream.Write(sourceID);
|
|
bitStream.Write(senderGMLevel);
|
|
bitStream.Write(receiverName);
|
|
bitStream.Write(receiverGMLevel);
|
|
bitStream.Write(responseCode);
|
|
bitStream.Write(message);
|
|
}
|
|
|
|
bool PrivateChatMessage::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(ReadPlayerHeader(bitStream, playerID, unknown));
|
|
VALIDATE_READ(bitStream.Read(chatChannel));
|
|
VALIDATE_READ(bitStream.Read(messageLength));
|
|
if (messageLength > MAX_MESSAGE_LENGTH) return false;
|
|
VALIDATE_READ(bitStream.Read(senderName));
|
|
VALIDATE_READ(bitStream.Read(senderID));
|
|
VALIDATE_READ(bitStream.Read(sourceID));
|
|
VALIDATE_READ(bitStream.Read(senderGMLevel));
|
|
VALIDATE_READ(bitStream.Read(receiverName));
|
|
VALIDATE_READ(bitStream.Read(receiverGMLevel));
|
|
VALIDATE_READ(bitStream.Read(responseCode));
|
|
message = LUWString(messageLength);
|
|
if (messageLength != 0) VALIDATE_READ(bitStream.Read(message));
|
|
return true;
|
|
}
|
|
|
|
void TeamInvite::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(invitedPlayer);
|
|
}
|
|
|
|
bool TeamInvite::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(ReadPlayerHeader(bitStream, playerID, unknown));
|
|
VALIDATE_READ(bitStream.Read(invitedPlayer));
|
|
return true;
|
|
}
|
|
|
|
void TeamInviteResponse::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(declined);
|
|
bitStream.Write(leaderID);
|
|
}
|
|
|
|
bool TeamInviteResponse::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(ReadPlayerHeader(bitStream, playerID, unknown));
|
|
VALIDATE_READ(bitStream.Read(declined));
|
|
VALIDATE_READ(bitStream.Read(leaderID));
|
|
return true;
|
|
}
|
|
|
|
void TeamLeave::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
}
|
|
|
|
bool TeamLeave::Deserialize(RakNet::BitStream& bitStream) {
|
|
return ReadPlayerHeader(bitStream, playerID, unknown);
|
|
}
|
|
|
|
void TeamKick::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(kickedPlayer);
|
|
}
|
|
|
|
bool TeamKick::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(ReadPlayerHeader(bitStream, playerID, unknown));
|
|
VALIDATE_READ(bitStream.Read(kickedPlayer));
|
|
return true;
|
|
}
|
|
|
|
void TeamSetLeader::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(promotedPlayer);
|
|
}
|
|
|
|
bool TeamSetLeader::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(ReadPlayerHeader(bitStream, playerID, unknown));
|
|
VALIDATE_READ(bitStream.Read(promotedPlayer));
|
|
return true;
|
|
}
|
|
|
|
void TeamSetLoot::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(lootFlag);
|
|
}
|
|
|
|
bool TeamSetLoot::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(ReadPlayerHeader(bitStream, playerID, unknown));
|
|
VALIDATE_READ(bitStream.Read(lootFlag));
|
|
return true;
|
|
}
|
|
|
|
void TeamGetStatus::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
}
|
|
|
|
bool TeamGetStatus::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(playerID));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
namespace ChatPackets::Client {
|
|
void GeneralChatMessage::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(unknown);
|
|
bitStream.Write(chatChannel);
|
|
bitStream.Write<uint32_t>(message.size());
|
|
bitStream.Write(senderName);
|
|
bitStream.Write(senderID);
|
|
bitStream.Write(sourceID);
|
|
bitStream.Write(senderGMLevel);
|
|
bitStream.Write(message);
|
|
bitStream.Write<uint16_t>(0); // terminator
|
|
}
|
|
|
|
bool GeneralChatMessage::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(unknown));
|
|
VALIDATE_READ(bitStream.Read(chatChannel));
|
|
uint32_t length{};
|
|
VALIDATE_READ(bitStream.Read(length));
|
|
if (length > MAX_MESSAGE_LENGTH) return false;
|
|
VALIDATE_READ(bitStream.Read(senderName));
|
|
VALIDATE_READ(bitStream.Read(senderID));
|
|
VALIDATE_READ(bitStream.Read(sourceID));
|
|
VALIDATE_READ(bitStream.Read(senderGMLevel));
|
|
message.resize(length);
|
|
if (length != 0) VALIDATE_READ(bitStream.ReadBits(reinterpret_cast<unsigned char*>(message.data()), BYTES_TO_BITS(length * sizeof(char16_t)), true));
|
|
uint16_t terminator{};
|
|
VALIDATE_READ(bitStream.Read(terminator));
|
|
return true;
|
|
}
|
|
|
|
void PrivateChatMessage::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(playerID);
|
|
bitStream.Write(chatChannel);
|
|
bitStream.Write(messageLength);
|
|
bitStream.Write(senderName);
|
|
bitStream.Write(senderID);
|
|
bitStream.Write(sourceID);
|
|
bitStream.Write(senderGMLevel);
|
|
bitStream.Write(receiverName);
|
|
bitStream.Write(receiverGMLevel);
|
|
bitStream.Write(responseCode);
|
|
bitStream.Write(message);
|
|
}
|
|
|
|
bool PrivateChatMessage::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(playerID));
|
|
VALIDATE_READ(bitStream.Read(chatChannel));
|
|
VALIDATE_READ(bitStream.Read(messageLength));
|
|
VALIDATE_READ(bitStream.Read(senderName));
|
|
VALIDATE_READ(bitStream.Read(senderID));
|
|
VALIDATE_READ(bitStream.Read(sourceID));
|
|
VALIDATE_READ(bitStream.Read(senderGMLevel));
|
|
VALIDATE_READ(bitStream.Read(receiverName));
|
|
VALIDATE_READ(bitStream.Read(receiverGMLevel));
|
|
VALIDATE_READ(bitStream.Read(responseCode));
|
|
VALIDATE_READ(ReadRemainingWString(bitStream, message));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void ChatPackets::SendSystemMessage(const SystemAddress& sysAddr, const std::string& message, const bool broadcast) {
|
|
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(message), broadcast);
|
|
}
|
|
|
|
void ChatPackets::SendSystemMessage(const SystemAddress& sysAddr, const std::u16string& message, const bool broadcast) {
|
|
Client::GeneralChatMessage chatMessage;
|
|
chatMessage.chatChannel = 4;
|
|
chatMessage.senderName = LUWString("", 33);
|
|
chatMessage.message = message;
|
|
|
|
RakNet::BitStream bitStream;
|
|
chatMessage.WritePacket(bitStream);
|
|
// A message with an address goes only to that client (so Wincent's announcement works)
|
|
Game::server->Send(bitStream, sysAddr, sysAddr == UNASSIGNED_SYSTEM_ADDRESS);
|
|
}
|
|
|
|
void ChatPackets::RoutedFromClient::Serialize(RakNet::BitStream& bitStream) const {
|
|
bitStream.Write(senderID);
|
|
for (const auto byte : data) bitStream.Write(byte);
|
|
}
|
|
|
|
bool ChatPackets::RoutedFromClient::Deserialize(RakNet::BitStream& bitStream) {
|
|
VALIDATE_READ(bitStream.Read(senderID));
|
|
data.resize(BITS_TO_BYTES(bitStream.GetNumberOfUnreadBits()));
|
|
if (!data.empty()) VALIDATE_READ(bitStream.ReadBits(data.data(), BYTES_TO_BITS(data.size()), true));
|
|
return true;
|
|
}
|