Files
DarkflameServer/dNet/MasterPackets.cpp
Aaron Kimbrell 951edfdde2 fix(master): a server list's outdated flags are read all or none
A list cut short inside the flags was read as if complete; an older master's list, which has none, still
reads. The test checks every cut.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 06:32:35 -05:00

319 lines
10 KiB
C++

#include "MasterPackets.h"
#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>
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::SendToMaster(const LUBitStream& msg, dServer* server) {
RakNet::BitStream bitStream;
msg.WritePacket(bitStream);
(server ? server : Game::server)->SendToMaster(bitStream);
}
void MasterPackets::SendTo(const SystemAddress& sysAddr, const LUBitStream& msg) {
RakNet::BitStream bitStream;
msg.WritePacket(bitStream);
Game::server->Send(bitStream, sysAddr, false);
}
namespace MasterPackets {
void RequestZoneTransfer::Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write(requestID);
bitStream.Write(mythranShift);
bitStream.Write(zoneID);
bitStream.Write(cloneID);
stamps.Serialize(bitStream);
}
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;
}
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);
if (cloneID != 0) bitStream.Write(cloneID);
}
bool PrepZone::Deserialize(RakNet::BitStream& bitStream) {
VALIDATE_READ(bitStream.Read(zoneID));
cloneID = 0;
if (bitStream.GetNumberOfUnreadBits() >= 32) VALIDATE_READ(bitStream.Read(cloneID));
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);
}
bitStream.Write(ugcEnabled);
bitStream.Write(ugcOnline);
bitStream.Write(ugcPid);
for (const auto& instance : instances) bitStream.Write(static_cast<uint8_t>(instance.state));
bitStream.Write<uint32_t>(endpoints.size());
for (const auto& endpoint : endpoints) {
bitStream.Write(endpoint.type);
bitStream.Write(endpoint.zoneID);
bitStream.Write(endpoint.instanceID);
bitStream.Write(endpoint.ip);
bitStream.Write(endpoint.port);
bitStream.Write(endpoint.host);
}
for (const auto& instance : instances) bitStream.Write(instance.outdated);
}
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));
}
VALIDATE_READ(bitStream.Read(ugcEnabled));
VALIDATE_READ(bitStream.Read(ugcOnline));
VALIDATE_READ(bitStream.Read(ugcPid));
for (auto& instance : instances) {
uint8_t state{};
VALIDATE_READ(bitStream.Read(state));
if (state > static_cast<uint8_t>(eState::DRAINING)) return false;
instance.state = static_cast<eState>(state);
}
uint32_t endpointCount{};
VALIDATE_READ(bitStream.Read(endpointCount));
if (endpointCount > MAX_INSTANCES + MAX_OTHER_ENDPOINTS) return false;
endpoints.resize(endpointCount);
for (auto& endpoint : endpoints) {
VALIDATE_READ(bitStream.Read(endpoint.type));
VALIDATE_READ(bitStream.Read(endpoint.zoneID));
VALIDATE_READ(bitStream.Read(endpoint.instanceID));
VALIDATE_READ(bitStream.Read(endpoint.ip));
VALIDATE_READ(bitStream.Read(endpoint.port));
VALIDATE_READ(bitStream.Read(endpoint.host));
}
// Each world's outdated flag, when the master is new enough to send them: all of them or none
if (bitStream.GetNumberOfUnreadBits() > 0) {
if (bitStream.GetNumberOfUnreadBits() < instances.size() * 8) return false;
for (auto& instance : instances) VALIDATE_READ(bitStream.Read(instance.outdated));
}
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;
}
}