refactor: master packets as structs

Every MASTER service packet is now an LUBitStream struct (docs/PacketArchitecture.md,
PR 14) and the master server's switch is a dispatch map (PacketDispatcher), as are the
master handlers of the world, chat and dashboard servers.

- dNet/MasterPackets.h: RequestZoneTransfer, RequestZoneTransferResponse, ServerInfo,
  RequestSessionKey, SetSessionKey, SessionKeyResponse, NewSessionAlert, PlayerAdded /
  PlayerRemoved, CreatePrivateZone, RequestPrivateZone (passwords still cut to 50
  characters when read), WorldReady, WorldReadyInfo (WORLD_READY to the dashboard),
  PrepZone, Shutdown, ShutdownResponse, WorldShutDown (SHUTDOWN_RESPONSE to the
  dashboard), ShutdownUniverse, AffirmTransferRequest/Response, RequestServerList,
  ServerListResponse, DashboardShutdown, ConfigReload, InstanceShutdown. The Send*
  functions are gone; MasterPackets::SendToMaster(msg) and SendTo(sysAddr, msg) send a
  struct.
- The dashboard and instance migration structs (PlayerAction, DataChanged, Dashboard
  messages, MessageCapture, InstanceMigration) are LUBitStreams of the MASTER service now
  and moved to dNet/master/, included by MasterPackets.h. Their payloads are unchanged;
  master forwards them by re-serializing the struct instead of copying raw bytes.
- InstanceManager, ZoneInstanceManager, MigrationCoordinator, dServer (server info, zone
  transfer response), auth (SET_SESSION_KEY), the world (session keys, player added and
  removed, world ready, shutdown response, affirmations, prep zone, shutdown universe) and
  the dashboard (server list, instance shutdown, config reload, announcements, player
  actions, message capture) send and read structs.
- The login stamps are a `stamps` field of RequestZoneTransfer and
  RequestZoneTransferResponse (read leniently as before: a message without them reads as
  empty); master adds its stamps in the REQUEST_ZONE_TRANSFER handler and when it answers,
  as it did.
- InstanceManager::GetInstanceBySysAddr takes a const address.

Verified: tests/dGameTests/dNetTests/Legacy/MasterPacketsLegacy.h is a verbatim copy of
the old writers and readers; MasterPacketsTests requires identical bytes for a grid of
inputs, checks the old readers read what the structs write, round trips and truncation,
hand written golden packets, that zone transfers without stamps still read, that the dashboard/migration structs write what
"header + Serialize" wrote, and that the dispatcher drops truncated packets. No wire
bytes changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-26 23:34:47 -05:00
parent 313f821203
commit d8e2a3ae42
54 changed files with 2275 additions and 1248 deletions

View File

@@ -1,4 +1,5 @@
#include "AuthPackets.h"
#include "MasterPackets.h"
#include <ctime>
#include "BitStreamUtils.h"
@@ -294,12 +295,11 @@ void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAdd
//Inform the master server that we've created a session for this user, before the client can reach the world server:
if (responseCode == eLoginResponse::SUCCESS) {
stamps.Add(eStamps::PASSPORT_AUTH_IM_COMMUNICATION_START);
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::SET_SESSION_KEY);
bitStream.Write(sessionKey);
bitStream.Write(LUString(username));
MasterPackets::SetSessionKey setSessionKey;
setSessionKey.sessionKey = sessionKey;
setSessionKey.username = LUString(username);
stamps.Add(eStamps::PASSPORT_AUTH_IM_LOGIN_START);
server->SendToMaster(bitStream);
MasterPackets::SendToMaster(setSessionKey, server);
stamps.Add(eStamps::PASSPORT_AUTH_IM_COMMUNICATION_END, 1);
LOG("Set session key for user %s", username.c_str());

View File

@@ -2,109 +2,274 @@
#include "BitStream.h"
#include "dCommonVars.h"
#include "dServer.h"
#include "Game.h"
#include "ServiceType.h"
#include "MessageType/Master.h"
#include "BitStreamUtils.h"
#include <algorithm>
#include <string>
void MasterPackets::SendZoneTransferRequest(dServer* server, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t cloneID, const Stamps& stamps) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::REQUEST_ZONE_TRANSFER);
bitStream.Write(requestID);
bitStream.Write<uint8_t>(mythranShift);
bitStream.Write(zoneID);
bitStream.Write(cloneID);
stamps.Serialize(bitStream);
server->SendToMaster(bitStream);
namespace {
// u32 length and 1 byte per character. A longer string is cut to maxLength when read (the rest is skipped).
bool ReadCappedString(RakNet::BitStream& bitStream, std::string& value, const uint32_t maxLength) {
uint32_t length{};
VALIDATE_READ(bitStream.Read(length));
value.resize(std::min(length, maxLength));
return value.empty() || bitStream.ReadBits(reinterpret_cast<unsigned char*>(value.data()), BYTES_TO_BITS(value.size()), true);
}
}
void MasterPackets::SendZoneCreatePrivate(dServer* server, uint32_t zoneID, uint32_t cloneID, const std::string& password) {
void MasterPackets::SendToMaster(const LUBitStream& msg, dServer* server) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::CREATE_PRIVATE_ZONE);
msg.WritePacket(bitStream);
(server ? server : Game::server)->SendToMaster(bitStream);
}
bitStream.Write(zoneID);
bitStream.Write(cloneID);
void MasterPackets::SendTo(const SystemAddress& sysAddr, const LUBitStream& msg) {
RakNet::BitStream bitStream;
msg.WritePacket(bitStream);
Game::server->Send(bitStream, sysAddr, false);
}
bitStream.Write<uint32_t>(password.size());
for (auto character : password) {
bitStream.Write<char>(character);
namespace MasterPackets {
void RequestZoneTransfer::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(requestID);
bitStream.Write(mythranShift);
bitStream.Write(zoneID);
bitStream.Write(cloneID);
stamps.Serialize(bitStream);
}
server->SendToMaster(bitStream);
}
void MasterPackets::SendZoneRequestPrivate(dServer* server, uint64_t requestID, bool mythranShift, const std::string& password) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::REQUEST_PRIVATE_ZONE);
bitStream.Write(requestID);
bitStream.Write<uint8_t>(mythranShift);
bitStream.Write<uint32_t>(password.size());
for (auto character : password) {
bitStream.Write<char>(character);
bool RequestZoneTransfer::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(requestID));
VALIDATE_READ(bitStream.Read(mythranShift));
VALIDATE_READ(bitStream.Read(zoneID));
VALIDATE_READ(bitStream.Read(cloneID));
if (!stamps.Deserialize(bitStream)) stamps = {};
return true;
}
server->SendToMaster(bitStream);
}
void MasterPackets::SendWorldReady(dServer* server, LWOMAPID zoneId, LWOINSTANCEID instanceId) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::WORLD_READY);
bitStream.Write(zoneId);
bitStream.Write(instanceId);
server->SendToMaster(bitStream);
}
void MasterPackets::SendZoneTransferResponse(dServer* server, const SystemAddress& sysAddr, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, const std::string& serverIP, uint32_t serverPort, const Stamps& stamps) {
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::REQUEST_ZONE_TRANSFER_RESPONSE);
bitStream.Write(requestID);
bitStream.Write<uint8_t>(mythranShift);
bitStream.Write(zoneID);
bitStream.Write(zoneInstance);
bitStream.Write(zoneClone);
bitStream.Write<uint16_t>(serverPort);
bitStream.Write(LUString(serverIP, 255));
stamps.Serialize(bitStream);
server->Send(bitStream, sysAddr, false);
}
void MasterPackets::HandleServerInfo(Packet* packet) {
RakNet::BitStream inStream(packet->data, packet->length, false);
uint64_t header{};
inStream.Read(header);
uint32_t theirPort = 0;
uint32_t theirZoneID = 0;
uint32_t theirInstanceID = 0;
LUString theirIP;
inStream.Read(theirPort);
inStream.Read(theirZoneID);
inStream.Read(theirInstanceID);
inStream.Read(theirIP);
//TODO: Actually mark this server as an available server in the manager
}
void MasterPackets::SendServerInfo(dServer* server, Packet* packet) {
LOG("SendServerInfo called for server type %i", static_cast<int>(server->GetServerType()));
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::SERVER_INFO);
bitStream.Write(server->GetPort());
bitStream.Write(server->GetZoneID());
bitStream.Write(server->GetInstanceID());
bitStream.Write(server->GetServerType());
bitStream.Write(LUString(server->GetIP()));
server->SendToMaster(bitStream);
void RequestZoneTransferResponse::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(requestID);
bitStream.Write(mythranShift);
bitStream.Write(zoneID);
bitStream.Write(zoneInstance);
bitStream.Write(zoneClone);
bitStream.Write(serverPort);
bitStream.Write(serverIP);
stamps.Serialize(bitStream);
}
bool RequestZoneTransferResponse::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(requestID));
VALIDATE_READ(bitStream.Read(mythranShift));
VALIDATE_READ(bitStream.Read(zoneID));
VALIDATE_READ(bitStream.Read(zoneInstance));
VALIDATE_READ(bitStream.Read(zoneClone));
VALIDATE_READ(bitStream.Read(serverPort));
VALIDATE_READ(bitStream.Read(serverIP));
if (!stamps.Deserialize(bitStream)) stamps = {};
return true;
}
void ServerInfo::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(port);
bitStream.Write(zoneID);
bitStream.Write(instanceID);
bitStream.Write(serverType);
bitStream.Write(ip);
}
bool ServerInfo::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(port));
VALIDATE_READ(bitStream.Read(zoneID));
VALIDATE_READ(bitStream.Read(instanceID));
VALIDATE_READ(bitStream.Read(serverType));
VALIDATE_READ(bitStream.Read(ip));
return true;
}
void RequestSessionKey::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(username);
}
bool RequestSessionKey::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(username));
return true;
}
void SetSessionKey::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(sessionKey);
bitStream.Write(username);
}
bool SetSessionKey::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(sessionKey));
VALIDATE_READ(bitStream.Read(username));
return true;
}
void SessionKeyResponse::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(sessionKey);
bitStream.Write(username);
}
bool SessionKeyResponse::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(sessionKey));
VALIDATE_READ(bitStream.Read(username));
return true;
}
void NewSessionAlert::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(sessionKey);
bitStream.Write(username);
}
bool NewSessionAlert::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(sessionKey));
VALIDATE_READ(bitStream.Read(username));
return true;
}
void PlayerCountChange::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(zoneID);
bitStream.Write(instanceID);
}
bool PlayerCountChange::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(zoneID));
VALIDATE_READ(bitStream.Read(instanceID));
return true;
}
void CreatePrivateZone::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(zoneID);
bitStream.Write(cloneID);
BitStreamUtils::WriteLengthPrefixed<uint32_t>(bitStream, password);
}
bool CreatePrivateZone::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(zoneID));
VALIDATE_READ(bitStream.Read(cloneID));
VALIDATE_READ(ReadCappedString(bitStream, password, MAX_PASSWORD_LENGTH));
return true;
}
void RequestPrivateZone::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(requestID);
bitStream.Write(mythranShift);
BitStreamUtils::WriteLengthPrefixed<uint32_t>(bitStream, password);
}
bool RequestPrivateZone::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(requestID));
VALIDATE_READ(bitStream.Read(mythranShift));
VALIDATE_READ(ReadCappedString(bitStream, password, MAX_PASSWORD_LENGTH));
return true;
}
void WorldReady::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(zoneID);
bitStream.Write(instanceID);
}
bool WorldReady::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(zoneID));
VALIDATE_READ(bitStream.Read(instanceID));
return true;
}
void WorldReadyInfo::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(zoneID);
bitStream.Write(instanceID);
bitStream.Write(cloneID);
bitStream.Write(ip);
bitStream.Write(port);
bitStream.Write(isPrivate);
}
bool WorldReadyInfo::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(zoneID));
VALIDATE_READ(bitStream.Read(instanceID));
VALIDATE_READ(bitStream.Read(cloneID));
VALIDATE_READ(bitStream.Read(ip));
VALIDATE_READ(bitStream.Read(port));
VALIDATE_READ(bitStream.Read(isPrivate));
return true;
}
void PrepZone::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(zoneID);
}
bool PrepZone::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(zoneID));
return true;
}
void WorldShutDown::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(zoneID);
bitStream.Write(instanceID);
}
bool WorldShutDown::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(zoneID));
VALIDATE_READ(bitStream.Read(instanceID));
return true;
}
void AffirmTransfer::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(requestID);
}
bool AffirmTransfer::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(requestID));
return true;
}
void ServerListResponse::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(authOnline);
bitStream.Write(chatOnline);
bitStream.Write<uint32_t>(instances.size());
for (const auto& instance : instances) {
bitStream.Write(instance.mapID);
bitStream.Write(instance.instanceID);
bitStream.Write(instance.cloneID);
bitStream.Write(instance.players);
bitStream.Write(instance.ip);
bitStream.Write(instance.port);
bitStream.Write(instance.isPrivate);
}
}
bool ServerListResponse::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(authOnline));
VALIDATE_READ(bitStream.Read(chatOnline));
uint32_t count{};
VALIDATE_READ(bitStream.Read(count));
if (count > MAX_INSTANCES) return false;
instances.resize(count);
for (auto& instance : instances) {
VALIDATE_READ(bitStream.Read(instance.mapID));
VALIDATE_READ(bitStream.Read(instance.instanceID));
VALIDATE_READ(bitStream.Read(instance.cloneID));
VALIDATE_READ(bitStream.Read(instance.players));
VALIDATE_READ(bitStream.Read(instance.ip));
VALIDATE_READ(bitStream.Read(instance.port));
VALIDATE_READ(bitStream.Read(instance.isPrivate));
}
return true;
}
void InstanceShutdown::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(zoneID);
bitStream.Write(instanceID);
}
bool InstanceShutdown::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(zoneID));
VALIDATE_READ(bitStream.Read(instanceID));
return true;
}
}

View File

@@ -3,24 +3,290 @@
#include <cstdint>
#include <string>
#include <vector>
#include "RakNetTypes.h"
#include "dCommonVars.h"
#include "BitStreamUtils.h"
#include "MessageType/Master.h"
#include "ServiceType.h"
#include "Stamps.h"
#include "master/DashboardMessages.h"
#include "master/DataChanged.h"
#include "master/InstanceMigration.h"
#include "master/MessageCapture.h"
#include "master/PlayerAction.h"
class dServer;
/**
* Packets between master and the other servers (MessageType::Master). None of them reach a client.
*
* The dashboard's messages and instance migration are MASTER packets too; they live in per-topic headers under
* dNet/master/ (PlayerAction.h, DataChanged.h, DashboardMessages.h, MessageCapture.h, InstanceMigration.h), which
* this header includes, so including MasterPackets.h gives every MASTER struct.
*/
namespace MasterPackets {
// stamps: the login stamps travelling with the request (empty unless auth asks for a world during a login)
void SendZoneTransferRequest(dServer* server, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t cloneID, const Stamps& stamps = {});
void SendZoneTransferResponse(dServer* server, const SystemAddress& sysAddr, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, const std::string& serverIP, uint32_t serverPort, const Stamps& stamps = {});
// Any server -> master, over server's connection to master (Game::server's when null)
void SendToMaster(const LUBitStream& msg, dServer* server = nullptr);
void HandleServerInfo(Packet* packet);
void SendServerInfo(dServer* server, Packet* packet);
// Master -> one server. Never broadcasts: nothing is sent to UNASSIGNED_SYSTEM_ADDRESS (a server that hasn't
// connected yet), unlike LUBitStream::Send.
void SendTo(const SystemAddress& sysAddr, const LUBitStream& msg);
void SendZoneCreatePrivate(dServer* server, uint32_t zoneID, uint32_t cloneID, const std::string& password);
// World -> master: a player wants to go to zoneID (clone cloneID)
struct RequestZoneTransfer : public LUBitStream {
uint64_t requestID{};
uint8_t mythranShift{};
uint32_t zoneID{};
uint32_t cloneID{};
// The login stamps travelling with the request (see Stamps.h); empty unless auth asks for a world during a
// login. Read leniently: a request without them reads as empty.
Stamps stamps{};
void SendZoneRequestPrivate(dServer* server, uint64_t requestID, bool mythranShift, const std::string& password);
RequestZoneTransfer() : LUBitStream(ServiceType::MASTER, MessageType::Master::REQUEST_ZONE_TRANSFER) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
void SendWorldReady(dServer* server, LWOMAPID zoneId, LWOINSTANCEID instanceId);
// Master -> world: where to send the player of requestID
struct RequestZoneTransferResponse : public LUBitStream {
uint64_t requestID{};
uint8_t mythranShift{};
uint32_t zoneID{};
uint32_t zoneInstance{};
uint32_t zoneClone{};
uint16_t serverPort{};
LUString serverIP{ 255u };
// The request's login stamps with master's own added (see Stamps.h). Read leniently like the request's.
Stamps stamps{};
RequestZoneTransferResponse() : LUBitStream(ServiceType::MASTER, MessageType::Master::REQUEST_ZONE_TRANSFER_RESPONSE) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// Any server -> master, once connected; master -> dashboard for every other server (ip "offline" when one is gone)
struct ServerInfo : public LUBitStream {
uint32_t port{};
uint32_t zoneID{};
uint32_t instanceID{};
ServiceType serverType{};
LUString ip{};
ServerInfo() : LUBitStream(ServiceType::MASTER, MessageType::Master::SERVER_INFO) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// World -> master: the session key of the account logging in
struct RequestSessionKey : public LUBitStream {
LUWString username{};
RequestSessionKey() : LUBitStream(ServiceType::MASTER, MessageType::Master::REQUEST_SESSION_KEY) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// Auth -> master: an account logged in with this session key
struct SetSessionKey : public LUBitStream {
uint32_t sessionKey{};
LUString username{};
SetSessionKey() : LUBitStream(ServiceType::MASTER, MessageType::Master::SET_SESSION_KEY) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// Master -> world: answer to RequestSessionKey
struct SessionKeyResponse : public LUBitStream {
uint32_t sessionKey{};
LUWString username{};
SessionKeyResponse() : LUBitStream(ServiceType::MASTER, MessageType::Master::SESSION_KEY_RESPONSE) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// Master -> every server: the account logged in again with a new session key
struct NewSessionAlert : public LUBitStream {
uint32_t sessionKey{};
LUString username{};
NewSessionAlert() : LUBitStream(ServiceType::MASTER, MessageType::Master::NEW_SESSION_ALERT) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// World -> master -> dashboard: a player came into (PLAYER_ADDED) or left (PLAYER_REMOVED) a world
struct PlayerCountChange : public LUBitStream {
LWOMAPID zoneID{};
LWOINSTANCEID instanceID{};
PlayerCountChange(MessageType::Master id) : LUBitStream(ServiceType::MASTER, id) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
struct PlayerAdded : public PlayerCountChange {
PlayerAdded() : PlayerCountChange(MessageType::Master::PLAYER_ADDED) {}
};
struct PlayerRemoved : public PlayerCountChange {
PlayerRemoved() : PlayerCountChange(MessageType::Master::PLAYER_REMOVED) {}
};
// World -> master: start a private instance of zoneID that needs `password` to join
struct CreatePrivateZone : public LUBitStream {
// Longer passwords are cut to this when read
static constexpr uint32_t MAX_PASSWORD_LENGTH = 50;
uint32_t zoneID{};
uint32_t cloneID{};
std::string password; // u32 length, then 1 byte per character
CreatePrivateZone() : LUBitStream(ServiceType::MASTER, MessageType::Master::CREATE_PRIVATE_ZONE) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// World -> master: send the player of requestID to the private instance with this password
struct RequestPrivateZone : public LUBitStream {
// Longer passwords are cut to this when read
static constexpr uint32_t MAX_PASSWORD_LENGTH = 50;
uint64_t requestID{};
uint8_t mythranShift{};
std::string password; // u32 length, then 1 byte per character
RequestPrivateZone() : LUBitStream(ServiceType::MASTER, MessageType::Master::REQUEST_PRIVATE_ZONE) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// World -> master: the world finished loading and takes players
struct WorldReady : public LUBitStream {
LWOMAPID zoneID{};
LWOINSTANCEID instanceID{};
WorldReady() : LUBitStream(ServiceType::MASTER, MessageType::Master::WORLD_READY) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// Master -> dashboard (WORLD_READY): a world is ready, and where it is
struct WorldReadyInfo : public LUBitStream {
LWOMAPID zoneID{};
LWOINSTANCEID instanceID{};
LWOCLONEID cloneID{};
LUString ip{};
uint32_t port{};
uint8_t isPrivate{};
WorldReadyInfo() : LUBitStream(ServiceType::MASTER, MessageType::Master::WORLD_READY) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// World -> master: start an instance of zoneID now, players are about to ask for it
struct PrepZone : public LUBitStream {
int32_t zoneID{};
PrepZone() : LUBitStream(ServiceType::MASTER, MessageType::Master::PREP_ZONE) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// Master -> any server: shut down
struct Shutdown : public LUBitStream {
Shutdown() : LUBitStream(ServiceType::MASTER, MessageType::Master::SHUTDOWN) {}
};
// World -> master: shutting down
struct ShutdownResponse : public LUBitStream {
ShutdownResponse() : LUBitStream(ServiceType::MASTER, MessageType::Master::SHUTDOWN_RESPONSE) {}
};
// Master -> dashboard (SHUTDOWN_RESPONSE): a world is gone
struct WorldShutDown : public LUBitStream {
LWOMAPID zoneID{};
LWOINSTANCEID instanceID{};
WorldShutDown() : LUBitStream(ServiceType::MASTER, MessageType::Master::SHUTDOWN_RESPONSE) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// World -> master: a GM asked to shut every server down in 10 minutes
struct ShutdownUniverse : public LUBitStream {
ShutdownUniverse() : LUBitStream(ServiceType::MASTER, MessageType::Master::SHUTDOWN_UNIVERSE) {}
};
// Master -> world: can you take the player of requestID? (AFFIRM_TRANSFER_REQUEST); world -> master: yes
// (AFFIRM_TRANSFER_RESPONSE)
struct AffirmTransfer : public LUBitStream {
uint64_t requestID{};
AffirmTransfer(MessageType::Master id) : LUBitStream(ServiceType::MASTER, id) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
struct AffirmTransferRequest : public AffirmTransfer {
AffirmTransferRequest() : AffirmTransfer(MessageType::Master::AFFIRM_TRANSFER_REQUEST) {}
};
struct AffirmTransferResponse : public AffirmTransfer {
AffirmTransferResponse() : AffirmTransfer(MessageType::Master::AFFIRM_TRANSFER_RESPONSE) {}
};
// Dashboard -> master: which servers are running (answered with ServerListResponse)
struct RequestServerList : public LUBitStream {
RequestServerList() : LUBitStream(ServiceType::MASTER, MessageType::Master::REQUEST_SERVER_LIST) {}
};
// Master -> dashboard
struct ServerListResponse : public LUBitStream {
// More than this many worlds is a broken packet
static constexpr uint32_t MAX_INSTANCES = 10000;
struct Instance {
LWOMAPID mapID{};
LWOINSTANCEID instanceID{};
LWOCLONEID cloneID{};
uint32_t players{};
LUString ip{};
uint32_t port{};
uint8_t isPrivate{};
};
uint8_t authOnline{};
uint8_t chatOnline{};
std::vector<Instance> instances; // u32 count
ServerListResponse() : LUBitStream(ServiceType::MASTER, MessageType::Master::SERVER_LIST_RESPONSE) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
// Dashboard -> master: shut the whole server down now (scheduled restarts)
struct DashboardShutdown : public LUBitStream {
DashboardShutdown() : LUBitStream(ServiceType::MASTER, MessageType::Master::DASHBOARD_SHUTDOWN) {}
};
// Dashboard -> master -> every server: settings changed on the dashboard, reload the config
struct ConfigReload : public LUBitStream {
ConfigReload() : LUBitStream(ServiceType::MASTER, MessageType::Master::CONFIG_RELOAD) {}
};
// Dashboard -> master: shut down one world instance
struct InstanceShutdown : public LUBitStream {
uint32_t zoneID{};
uint32_t instanceID{};
InstanceShutdown() : LUBitStream(ServiceType::MASTER, MessageType::Master::INSTANCE_SHUTDOWN) {}
void Serialize(RakNet::BitStream& bitStream) const override;
bool Deserialize(RakNet::BitStream& bitStream) override;
};
}
#endif // MASTERPACKETS_H

View File

@@ -17,40 +17,30 @@ void ZoneInstanceManager::RequestZoneTransfer(dServer* server, uint32_t zoneID,
const auto nextID = ++currentRequestID;
requests[nextID] = callback;
MasterPackets::SendZoneTransferRequest(server, nextID, mythranShift, zoneID, zoneClone, stamps);
MasterPackets::RequestZoneTransfer request;
request.requestID = nextID;
request.mythranShift = mythranShift;
request.zoneID = zoneID;
request.cloneID = zoneClone;
request.stamps = stamps;
MasterPackets::SendToMaster(request, server);
}
//! Handles a zone transfer response
void ZoneInstanceManager::HandleRequestZoneTransferResponse(Packet* packet) {
CINSTREAM_SKIP_HEADER;
uint64_t requestID;
inStream.Read(requestID);
bool mythranShift;
uint8_t tmp;
inStream.Read(tmp);
mythranShift = tmp > 0;
uint32_t zoneID;
inStream.Read(zoneID);
uint32_t zoneInstance;
inStream.Read(zoneInstance);
uint32_t zoneClone;
inStream.Read(zoneClone);
uint16_t serverPort;
inStream.Read(serverPort);
LUString serverIP(255);
inStream.Read(serverIP);
Stamps stamps;
if (!stamps.Deserialize(inStream)) stamps = {};
const auto entry = requests.find(requestID);
void ZoneInstanceManager::HandleRequestZoneTransferResponse(const MasterPackets::RequestZoneTransferResponse& response) {
const auto entry = requests.find(response.requestID);
if (entry != requests.end()) {
entry->second(mythranShift, zoneID, zoneInstance, zoneClone, serverIP.string, serverPort, stamps);
entry->second(response.mythranShift > 0, response.zoneID, response.zoneInstance, response.zoneClone, response.serverIP.string, response.serverPort, response.stamps);
requests.erase(entry);
}
}
void ZoneInstanceManager::CreatePrivateZone(dServer* server, uint32_t zoneID, uint32_t zoneClone, const std::string& password) {
MasterPackets::SendZoneCreatePrivate(server, zoneID, zoneClone, password);
MasterPackets::CreatePrivateZone request;
request.zoneID = zoneID;
request.cloneID = zoneClone;
request.password = password;
MasterPackets::SendToMaster(request, server);
}
void ZoneInstanceManager::RequestPrivateZone(
@@ -63,5 +53,9 @@ void ZoneInstanceManager::RequestPrivateZone(
callback(mythranShift, zoneID, zoneInstance, zoneClone, serverIP, serverPort);
};
MasterPackets::SendZoneRequestPrivate(server, nextID, mythranShift, password);
MasterPackets::RequestPrivateZone request;
request.requestID = nextID;
request.mythranShift = mythranShift;
request.password = password;
MasterPackets::SendToMaster(request, server);
}

View File

@@ -11,6 +11,10 @@
class dServer;
namespace MasterPackets {
struct RequestZoneTransferResponse;
}
/*!
\file ZoneInstanceManager.hpp
\brief A class for handling zone transfers and zone-related functions
@@ -56,7 +60,7 @@ public:
\param requestID The request ID
\param packet The packet
*/
void HandleRequestZoneTransferResponse(Packet* packet);
void HandleRequestZoneTransferResponse(const MasterPackets::RequestZoneTransferResponse& response);
void CreatePrivateZone(dServer* server, uint32_t zoneID, uint32_t zoneClone, const std::string& password);

View File

@@ -148,14 +148,26 @@ Packet* dServer::ReceiveFromMaster() {
LOG("Established connection to master: ServiceType (%s), Zone (%i), Instance (%i)", StringifiedEnum::ToString(this->GetServerType()).data(), this->GetZoneID(), this->GetInstanceID());
mMasterConnectionActive = true;
mMasterSystemAddress = packet->systemAddress;
MasterPackets::SendServerInfo(this, packet);
LOG("SendServerInfo called for server type %i", static_cast<int>(GetServerType()));
MasterPackets::ServerInfo info;
info.port = GetPort();
info.zoneID = GetZoneID();
info.instanceID = GetInstanceID();
info.serverType = GetServerType();
info.ip = LUString(GetIP());
MasterPackets::SendToMaster(info, this);
break;
}
case ID_USER_PACKET_ENUM: {
if (static_cast<ServiceType>(packet->data[1]) == ServiceType::MASTER) {
switch (static_cast<MessageType::Master>(packet->data[3])) {
RakNet::BitStream inStream(packet->data, packet->length, false);
LUBitStream header;
if (header.ReadHeader(inStream) && header.connectionType == ServiceType::MASTER) {
// What every server does with these; the rest goes back to the server's own handlers
switch (static_cast<MessageType::Master>(header.internalPacketID)) {
case MessageType::Master::REQUEST_ZONE_TRANSFER_RESPONSE: {
ZoneInstanceManager::Instance()->HandleRequestZoneTransferResponse(packet);
MasterPackets::RequestZoneTransferResponse response;
if (response.Deserialize(inStream)) ZoneInstanceManager::Instance()->HandleRequestZoneTransferResponse(response);
else LOG("Dropped a zone transfer response from master that failed to read");
break;
}
case MessageType::Master::SHUTDOWN:

View File

@@ -7,6 +7,8 @@
#include <vector>
#include "BitStream.h"
#include "BitStreamUtils.h"
#include "MessageType/Master.h"
#include "dCommonVars.h"
namespace DashboardMessages {
@@ -29,7 +31,9 @@ namespace DashboardMessages {
* PLAYER_POSITIONS (world -> master -> dashboard): where each player in a world instance is, sent every
* second while anyone is online and once more when the world empties.
*/
struct PlayerPositions {
struct PlayerPositions : public LUBitStream {
PlayerPositions() : LUBitStream(ServiceType::MASTER, MessageType::Master::PLAYER_POSITIONS) {}
struct Player {
LWOOBJID characterId{};
float x{}, y{}, z{};
@@ -42,7 +46,7 @@ struct PlayerPositions {
uint32_t cloneId{};
std::vector<Player> players;
void Serialize(RakNet::BitStream& stream) const {
void Serialize(RakNet::BitStream& stream) const override {
stream.Write(zoneId);
stream.Write(instanceId);
stream.Write(cloneId);
@@ -56,7 +60,7 @@ struct PlayerPositions {
}
}
bool Deserialize(RakNet::BitStream& stream) {
bool Deserialize(RakNet::BitStream& stream) override {
uint16_t count{};
if (!stream.Read(zoneId) || !stream.Read(instanceId) || !stream.Read(cloneId) || !stream.Read(count) || count > MAX_PLAYERS) return false;
players.resize(count);
@@ -71,7 +75,9 @@ struct PlayerPositions {
* ANNOUNCE (dashboard -> master -> worlds): a message shown to every player online, or only to those in the listed
* zones (master only forwards it to worlds of those zones).
*/
struct Announcement {
struct Announcement : public LUBitStream {
Announcement() : LUBitStream(ServiceType::MASTER, MessageType::Master::ANNOUNCE) {}
static constexpr uint16_t MAX_TITLE = 100;
static constexpr uint16_t MAX_MESSAGE = 1000;
static constexpr uint16_t MAX_ZONES = 200;
@@ -80,7 +86,7 @@ struct Announcement {
std::string message;
std::vector<uint32_t> zones; // empty: every world
void Serialize(RakNet::BitStream& stream) const {
void Serialize(RakNet::BitStream& stream) const override {
DashboardMessages::WriteText(stream, title, MAX_TITLE);
DashboardMessages::WriteText(stream, message, MAX_MESSAGE);
const auto count = static_cast<uint16_t>(std::min<size_t>(zones.size(), MAX_ZONES));
@@ -88,7 +94,7 @@ struct Announcement {
for (uint16_t i = 0; i < count; i++) stream.Write(zones[i]);
}
bool Deserialize(RakNet::BitStream& stream) {
bool Deserialize(RakNet::BitStream& stream) override {
if (!DashboardMessages::ReadText(stream, title, MAX_TITLE) || !DashboardMessages::ReadText(stream, message, MAX_MESSAGE)) return false;
uint16_t count{};
if (!stream.Read(count) || count > MAX_ZONES) return false;

View File

@@ -7,13 +7,17 @@
#include <vector>
#include "BitStream.h"
#include "BitStreamUtils.h"
#include "MessageType/Master.h"
#include "dCommonVars.h"
/**
* DATA_CHANGED payload: the tables and rows a game server just wrote. The dashboard turns each entry into a
* table_changed event so open pages refresh without waiting for its periodic database check.
*/
struct DataChanged {
struct DataChanged : public LUBitStream {
DataChanged() : LUBitStream(ServiceType::MASTER, MessageType::Master::DATA_CHANGED) {}
struct Entry {
std::string table; // dashboard table name, e.g. "characters", "mail", "economy"
LWOOBJID id{}; // row the change is about, 0 for the table as a whole
@@ -24,7 +28,7 @@ struct DataChanged {
std::vector<Entry> entries;
void Serialize(RakNet::BitStream& stream) const {
void Serialize(RakNet::BitStream& stream) const override {
const auto count = static_cast<uint16_t>(std::min(entries.size(), MAX_ENTRIES));
stream.Write(count);
for (size_t i = 0; i < count; i++) {
@@ -36,7 +40,7 @@ struct DataChanged {
}
}
bool Deserialize(RakNet::BitStream& stream) {
bool Deserialize(RakNet::BitStream& stream) override {
uint16_t count{};
if (!stream.Read(count) || count > MAX_ENTRIES) return false;
entries.clear();

View File

@@ -9,6 +9,8 @@
#include <vector>
#include "BitStream.h"
#include "BitStreamUtils.h"
#include "MessageType/Master.h"
#include "dCommonVars.h"
/**
@@ -227,7 +229,9 @@ namespace InstanceMigration {
* INSTANCE_MIGRATE (a world for a GM command, or anything else connected to master): move everyone in one instance. targetInstance 0 picks a target for
* merges (the best fit); replaces always start a fresh instance.
*/
struct InstanceMigrationRequest {
struct InstanceMigrationRequest : public LUBitStream {
InstanceMigrationRequest() : LUBitStream(ServiceType::MASTER, MessageType::Master::INSTANCE_MIGRATE) {}
static constexpr uint16_t MAX_BY = 64;
static constexpr uint16_t MAX_WARN_SECONDS = 300;
@@ -242,7 +246,7 @@ struct InstanceMigrationRequest {
LWOOBJID requesterId{}; // the character who asked, told how it goes; 0 for none
std::string requestedBy;
void Serialize(RakNet::BitStream& stream) const {
void Serialize(RakNet::BitStream& stream) const override {
stream.Write(requestId);
stream.Write(static_cast<uint8_t>(kind));
stream.Write(zoneId);
@@ -255,7 +259,7 @@ struct InstanceMigrationRequest {
InstanceMigration::WriteText(stream, requestedBy, MAX_BY);
}
bool Deserialize(RakNet::BitStream& stream) {
bool Deserialize(RakNet::BitStream& stream) override {
uint8_t kindValue{}, shutdown{}, seamlessValue{};
if (!stream.Read(requestId) || !stream.Read(kindValue) || !stream.Read(zoneId) || !stream.Read(sourceInstance) ||
!stream.Read(targetInstance) || !stream.Read(warnSeconds) || !stream.Read(shutdown) || !stream.Read(seamlessValue) ||
@@ -272,7 +276,9 @@ struct InstanceMigrationRequest {
* MIGRATE_PLAYERS (master -> source world): send everyone to this instance. The target is already running and has
* seats held for them.
*/
struct MigratePlayersOrder {
struct MigratePlayersOrder : public LUBitStream {
MigratePlayersOrder() : LUBitStream(ServiceType::MASTER, MessageType::Master::MIGRATE_PLAYERS) {}
static constexpr uint16_t MAX_IP = 255;
uint32_t migrationId{};
@@ -292,7 +298,7 @@ struct MigratePlayersOrder {
*/
bool seamless{};
void Serialize(RakNet::BitStream& stream) const {
void Serialize(RakNet::BitStream& stream) const override {
stream.Write(migrationId);
stream.Write(targetZone);
stream.Write(targetInstance);
@@ -305,7 +311,7 @@ struct MigratePlayersOrder {
stream.Write<uint8_t>(seamless ? 1 : 0);
}
bool Deserialize(RakNet::BitStream& stream) {
bool Deserialize(RakNet::BitStream& stream) override {
uint8_t shift{}, seamlessValue{};
if (!stream.Read(migrationId) || !stream.Read(targetZone) || !stream.Read(targetInstance) || !stream.Read(targetClone) ||
!InstanceMigration::ReadText(stream, targetIp, MAX_IP) || !stream.Read(targetPort) || !stream.Read(warnSeconds) ||
@@ -321,7 +327,9 @@ struct MigratePlayersOrder {
* MIGRATE_STATUS (source world -> master, master -> every world): progress of one migration. Master passes it on
* with who asked for it, and sends its own (STARTING_TARGET, FAILED) the same way.
*/
struct MigrationStatus {
struct MigrationStatus : public LUBitStream {
MigrationStatus() : LUBitStream(ServiceType::MASTER, MessageType::Master::MIGRATE_STATUS) {}
static constexpr uint16_t MAX_MESSAGE = 300;
uint32_t migrationId{};
@@ -338,7 +346,7 @@ struct MigrationStatus {
bool Finished() const { return state == InstanceMigration::eState::DONE || state == InstanceMigration::eState::FAILED; }
void Serialize(RakNet::BitStream& stream) const {
void Serialize(RakNet::BitStream& stream) const override {
stream.Write(migrationId);
stream.Write(static_cast<uint8_t>(state));
stream.Write(static_cast<uint8_t>(kind));
@@ -352,7 +360,7 @@ struct MigrationStatus {
InstanceMigration::WriteText(stream, message, MAX_MESSAGE);
}
bool Deserialize(RakNet::BitStream& stream) {
bool Deserialize(RakNet::BitStream& stream) override {
uint8_t stateValue{}, kindValue{};
if (!stream.Read(migrationId) || !stream.Read(stateValue) || !stream.Read(kindValue) || !stream.Read(zoneId) ||
!stream.Read(sourceInstance) || !stream.Read(targetInstance) || !stream.Read(moved) || !stream.Read(remaining) ||
@@ -369,14 +377,16 @@ struct MigrationStatus {
* character doesn't keep, put back when they finish loading in the target. Everything else (position, health,
* imagination, armor, buffs, inventory, missions) is in the character XML, saved just before the transfer.
*/
struct CarriedPlayerState {
struct CarriedPlayerState : public LUBitStream {
CarriedPlayerState() : LUBitStream(ServiceType::MASTER, MessageType::Master::MIGRATE_PLAYER_STATE) {}
uint32_t targetZone{};
uint32_t targetInstance{};
LWOOBJID characterId{};
LWOOBJID petItemId{}; // the pet that was out (its item), summoned again; 0 for none
bool seamless{}; // the client kept its scene: skip LOAD_STATIC_ZONE and load the player at once
void Serialize(RakNet::BitStream& stream) const {
void Serialize(RakNet::BitStream& stream) const override {
stream.Write(targetZone);
stream.Write(targetInstance);
stream.Write(characterId);
@@ -384,7 +394,7 @@ struct CarriedPlayerState {
stream.Write<uint8_t>(seamless ? 1 : 0);
}
bool Deserialize(RakNet::BitStream& stream) {
bool Deserialize(RakNet::BitStream& stream) override {
uint8_t seamlessValue{};
if (!stream.Read(targetZone) || !stream.Read(targetInstance) || !stream.Read(characterId) || !stream.Read(petItemId) || !stream.Read(seamlessValue)) return false;
seamless = seamlessValue != 0;

View File

@@ -10,6 +10,8 @@
#include <vector>
#include "BitStream.h"
#include "BitStreamUtils.h"
#include "MessageType/Master.h"
#include "dCommonVars.h"
/**
@@ -93,7 +95,9 @@ namespace MessageCapture {
}
// MESSAGE_CAPTURE_CONTROL payload
struct MessageCaptureControl {
struct MessageCaptureControl : public LUBitStream {
MessageCaptureControl() : LUBitStream(ServiceType::MASTER, MessageType::Master::MESSAGE_CAPTURE_CONTROL) {}
uint32_t captureId{};
eMessageCaptureControl action{};
LWOOBJID characterId{};
@@ -103,7 +107,7 @@ struct MessageCaptureControl {
std::vector<uint16_t> only; // START: capture only these message IDs (empty: all)
std::vector<uint16_t> skip; // START: never capture these message IDs
void Serialize(RakNet::BitStream& stream) const {
void Serialize(RakNet::BitStream& stream) const override {
stream.Write(captureId);
stream.Write(action);
stream.Write(characterId);
@@ -114,7 +118,7 @@ struct MessageCaptureControl {
MessageCapture::WriteIds(stream, skip);
}
bool Deserialize(RakNet::BitStream& stream) {
bool Deserialize(RakNet::BitStream& stream) override {
uint8_t server{}, client{};
if (!stream.Read(captureId) || !stream.Read(action) || !stream.Read(characterId) || !stream.Read(seconds) ||
!stream.Read(server) || !stream.Read(client)) return false;
@@ -166,7 +170,9 @@ struct MessageCaptureEntry {
};
// MESSAGE_CAPTURE_DATA payload
struct MessageCaptureData {
struct MessageCaptureData : public LUBitStream {
MessageCaptureData() : LUBitStream(ServiceType::MASTER, MessageType::Master::MESSAGE_CAPTURE_DATA) {}
static constexpr uint16_t MAX_ENTRIES = 500;
uint32_t captureId{};
@@ -179,7 +185,7 @@ struct MessageCaptureData {
std::vector<MessageCaptureEntry> entries;
uint32_t cloneId{}; // the world's clone (a property's owner), 0 elsewhere
void Serialize(RakNet::BitStream& stream) const {
void Serialize(RakNet::BitStream& stream) const override {
stream.Write(captureId);
stream.Write(status);
stream.Write(characterId);
@@ -193,7 +199,7 @@ struct MessageCaptureData {
stream.Write(cloneId);
}
bool Deserialize(RakNet::BitStream& stream) {
bool Deserialize(RakNet::BitStream& stream) override {
uint16_t count{};
if (!stream.Read(captureId) || !stream.Read(status) || !stream.Read(characterId) || !stream.Read(zoneId) ||
!stream.Read(instanceId) || !stream.Read(reason) || !stream.Read(dropped) || !stream.Read(count) || count > MAX_ENTRIES) return false;

View File

@@ -6,6 +6,8 @@
#include <string>
#include "BitStream.h"
#include "BitStreamUtils.h"
#include "MessageType/Master.h"
#include "dCommonVars.h"
/**
@@ -35,7 +37,9 @@ enum class ePlayerAction : uint8_t {
RELOAD_LIVE_OPS, // load the running live events and open challenges again (every world, answering 1 each; see LiveEvents.h)
};
struct PlayerActionRequest {
struct PlayerActionRequest : public LUBitStream {
PlayerActionRequest() : LUBitStream(ServiceType::MASTER, MessageType::Master::PLAYER_ACTION) {}
uint32_t requestId{};
ePlayerAction action{};
uint32_t accountId{};
@@ -61,7 +65,7 @@ struct PlayerActionRequest {
return length;
}
void Serialize(RakNet::BitStream& bitStream) const {
void Serialize(RakNet::BitStream& bitStream) const override {
bitStream.Write(requestId);
bitStream.Write(action);
bitStream.Write(accountId);
@@ -79,7 +83,7 @@ struct PlayerActionRequest {
bitStream.Write(instanceId);
}
bool Deserialize(RakNet::BitStream& bitStream) {
bool Deserialize(RakNet::BitStream& bitStream) override {
uint8_t approvedByte{};
uint16_t length{};
if (!(bitStream.Read(requestId) && bitStream.Read(action) && bitStream.Read(accountId) && bitStream.Read(characterId) &&
@@ -96,7 +100,9 @@ struct PlayerActionRequest {
}
};
struct PlayerActionResult {
struct PlayerActionResult : public LUBitStream {
PlayerActionResult() : LUBitStream(ServiceType::MASTER, MessageType::Master::PLAYER_ACTION_RESULT) {}
uint32_t requestId{};
ePlayerAction action{};
// Number of sessions/characters the action applied to. Zero means the player was not online.
@@ -104,14 +110,14 @@ struct PlayerActionResult {
// Set by master when not every world answered before the timeout
bool timedOut{};
void Serialize(RakNet::BitStream& bitStream) const {
void Serialize(RakNet::BitStream& bitStream) const override {
bitStream.Write(requestId);
bitStream.Write(action);
bitStream.Write(affected);
bitStream.Write<uint8_t>(timedOut);
}
bool Deserialize(RakNet::BitStream& bitStream) {
bool Deserialize(RakNet::BitStream& bitStream) override {
uint8_t timedOutByte{};
const bool ok = bitStream.Read(requestId) && bitStream.Read(action) && bitStream.Read(affected) && bitStream.Read(timedOutByte);
timedOut = timedOutByte != 0;