mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-08 12:57:13 +00:00
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:
@@ -11,7 +11,7 @@
|
||||
#include "Database.h"
|
||||
#include "dConfig.h"
|
||||
#include "dChatFilter.h"
|
||||
#include "PlayerAction.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "MessageType/Master.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "AssetManager.h"
|
||||
@@ -273,20 +273,26 @@ namespace {
|
||||
}();
|
||||
return handlers;
|
||||
}
|
||||
|
||||
// Messages from master that dServer doesn't handle itself
|
||||
const PacketDispatcher<MessageType::Master>& MasterHandlers() {
|
||||
static const auto handlers = [] {
|
||||
PacketDispatcher<MessageType::Master> handlers;
|
||||
handlers.On<PlayerActionRequest>(MessageType::Master::PLAYER_ACTION, [](const PlayerActionRequest& request, const SystemAddress&) {
|
||||
// Words added or removed on the dashboard: web chat is checked with the same filter as the worlds
|
||||
if (request.action == ePlayerAction::RELOAD_CHAT_FILTER && Game::chatFilter) {
|
||||
Game::chatFilter->ReloadCustomWords();
|
||||
LOG("Reloaded the chat filter's words (changed on the dashboard)");
|
||||
}
|
||||
});
|
||||
return handlers;
|
||||
}();
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
|
||||
// Messages from master that dServer doesn't handle itself
|
||||
void HandleMasterPacket(Packet* packet) {
|
||||
if (packet->length < 4 || static_cast<ServiceType>(packet->data[1]) != ServiceType::MASTER) return;
|
||||
if (static_cast<MessageType::Master>(packet->data[3]) != MessageType::Master::PLAYER_ACTION) return;
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
PlayerActionRequest request;
|
||||
if (!request.Deserialize(inStream)) return;
|
||||
// Words added or removed on the dashboard: web chat is checked with the same filter as the worlds
|
||||
if (request.action == ePlayerAction::RELOAD_CHAT_FILTER && Game::chatFilter) {
|
||||
Game::chatFilter->ReloadCustomWords();
|
||||
LOG("Reloaded the chat filter's words (changed on the dashboard)");
|
||||
}
|
||||
MasterHandlers().Dispatch(packet, ServiceType::MASTER);
|
||||
}
|
||||
|
||||
void HandlePacket(Packet* packet) {
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "BinaryPathFinder.h"
|
||||
#include "ServiceType.h"
|
||||
#include "MessageType/Master.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "PacketDispatcher.h"
|
||||
#include "Game.h"
|
||||
#include "BitStreamUtils.h"
|
||||
#include "dCommonVars.h"
|
||||
@@ -62,7 +64,7 @@
|
||||
#include "ChallengeRoutes.h"
|
||||
#include "Inspector.h"
|
||||
#include "CDClientBrowser.h"
|
||||
#include "MessageCapture.h"
|
||||
#include "master/MessageCapture.h"
|
||||
#include "PublicRoutes.h"
|
||||
#include "Showcase.h"
|
||||
#include "FeaturedProperties.h"
|
||||
@@ -75,8 +77,8 @@
|
||||
#include "WorldView.h"
|
||||
#include "PrometheusMetrics.h"
|
||||
#include "Background.h"
|
||||
#include "DashboardMessages.h"
|
||||
#include "DataChanged.h"
|
||||
#include "master/DashboardMessages.h"
|
||||
#include "master/DataChanged.h"
|
||||
#include "EmailService.h"
|
||||
#include "AuthMiddleware.h"
|
||||
#include "DashboardAuthService.h"
|
||||
@@ -181,229 +183,145 @@ namespace {
|
||||
} catch (const std::exception&) {}
|
||||
}
|
||||
|
||||
void HandleMasterPacket(Packet* packet) {
|
||||
if (packet->length < 4) return;
|
||||
if (static_cast<ServiceType>(packet->data[1]) != ServiceType::MASTER) return;
|
||||
void OnServerList(const MasterPackets::ServerListResponse& list, const SystemAddress&) {
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
ServerState::g_AuthStatus.online = list.authOnline != 0;
|
||||
ServerState::g_AuthStatus.lastSeen = std::chrono::steady_clock::now();
|
||||
ServerState::g_ChatStatus.online = list.chatOnline != 0;
|
||||
ServerState::g_ChatStatus.lastSeen = std::chrono::steady_clock::now();
|
||||
|
||||
switch (static_cast<MessageType::Master>(packet->data[3])) {
|
||||
case MessageType::Master::SERVER_LIST_RESPONSE: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
uint8_t authOnline = 0;
|
||||
uint8_t chatOnline = 0;
|
||||
uint32_t instanceCount = 0;
|
||||
|
||||
inStream.Read(authOnline);
|
||||
inStream.Read(chatOnline);
|
||||
inStream.Read(instanceCount);
|
||||
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
ServerState::g_AuthStatus.online = authOnline != 0;
|
||||
ServerState::g_AuthStatus.lastSeen = std::chrono::steady_clock::now();
|
||||
ServerState::g_ChatStatus.online = chatOnline != 0;
|
||||
ServerState::g_ChatStatus.lastSeen = std::chrono::steady_clock::now();
|
||||
|
||||
ServerState::g_WorldInstances.clear();
|
||||
for (uint32_t i = 0; i < instanceCount; i++) {
|
||||
WorldInstanceInfo info;
|
||||
LUString ip;
|
||||
// Same types as MasterServer writes them (map and instance IDs are 16 bits)
|
||||
LWOMAPID mapID = 0;
|
||||
LWOINSTANCEID instanceID = 0;
|
||||
inStream.Read(mapID);
|
||||
inStream.Read(instanceID);
|
||||
info.mapID = mapID;
|
||||
info.instanceID = instanceID;
|
||||
inStream.Read(info.cloneID);
|
||||
inStream.Read(info.players);
|
||||
inStream.Read(ip);
|
||||
info.ip = ip.string;
|
||||
inStream.Read(info.port);
|
||||
uint8_t isPrivate = 0;
|
||||
inStream.Read(isPrivate);
|
||||
info.isPrivate = isPrivate != 0;
|
||||
info.zoneName = GetZoneDisplayName(info.mapID);
|
||||
AddPropertyDetails(info);
|
||||
ServerState::g_WorldInstances.push_back(info);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Received server list: auth=%s chat=%s worlds=%u",
|
||||
authOnline ? "online" : "offline",
|
||||
chatOnline ? "online" : "offline",
|
||||
instanceCount);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::SERVER_INFO: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
uint32_t theirPort = 0;
|
||||
uint32_t theirZoneID = 0;
|
||||
uint32_t theirInstanceID = 0;
|
||||
ServiceType theirServerType;
|
||||
LUString theirIP;
|
||||
|
||||
inStream.Read(theirPort);
|
||||
inStream.Read(theirZoneID);
|
||||
inStream.Read(theirInstanceID);
|
||||
inStream.Read(theirServerType);
|
||||
inStream.Read(theirIP);
|
||||
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
switch (theirServerType) {
|
||||
case ServiceType::AUTH:
|
||||
if (theirIP.string == "offline") {
|
||||
ServerState::g_AuthStatus.online = false;
|
||||
} else {
|
||||
ServerState::g_AuthStatus.online = true;
|
||||
ServerState::g_AuthStatus.lastSeen = std::chrono::steady_clock::now();
|
||||
}
|
||||
break;
|
||||
case ServiceType::CHAT:
|
||||
if (theirIP.string == "offline") {
|
||||
ServerState::g_ChatStatus.online = false;
|
||||
} else {
|
||||
ServerState::g_ChatStatus.online = true;
|
||||
ServerState::g_ChatStatus.lastSeen = std::chrono::steady_clock::now();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::WORLD_READY: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
LWOMAPID zoneID;
|
||||
LWOINSTANCEID instanceID;
|
||||
LWOCLONEID cloneID;
|
||||
LUString ip;
|
||||
uint32_t port;
|
||||
uint8_t isPrivate;
|
||||
|
||||
inStream.Read(zoneID);
|
||||
inStream.Read(instanceID);
|
||||
inStream.Read(cloneID);
|
||||
inStream.Read(ip);
|
||||
inStream.Read(port);
|
||||
inStream.Read(isPrivate);
|
||||
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
ServerState::g_WorldInstances.clear();
|
||||
for (const auto& instance : list.instances) {
|
||||
WorldInstanceInfo info;
|
||||
info.mapID = zoneID;
|
||||
info.instanceID = instanceID;
|
||||
info.cloneID = cloneID;
|
||||
info.players = 0;
|
||||
info.ip = ip.string;
|
||||
info.port = port;
|
||||
info.isPrivate = isPrivate != 0;
|
||||
info.zoneName = GetZoneDisplayName(zoneID);
|
||||
info.mapID = instance.mapID;
|
||||
info.instanceID = instance.instanceID;
|
||||
info.cloneID = instance.cloneID;
|
||||
info.players = instance.players;
|
||||
info.ip = instance.ip.string;
|
||||
info.port = instance.port;
|
||||
info.isPrivate = instance.isPrivate != 0;
|
||||
info.zoneName = GetZoneDisplayName(info.mapID);
|
||||
AddPropertyDetails(info);
|
||||
// Master can report a world more than once (in the server list and when it becomes ready): replace, don't add
|
||||
auto& instances = ServerState::g_WorldInstances;
|
||||
const auto existing = std::ranges::find_if(instances, [&](const WorldInstanceInfo& w) { return w.mapID == zoneID && w.instanceID == instanceID; });
|
||||
if (existing != instances.end()) {
|
||||
info.players = existing->players;
|
||||
*existing = info;
|
||||
ServerState::g_WorldInstances.push_back(info);
|
||||
}
|
||||
|
||||
LOG_DEBUG("Received server list: auth=%s chat=%s worlds=%u",
|
||||
list.authOnline ? "online" : "offline",
|
||||
list.chatOnline ? "online" : "offline",
|
||||
static_cast<uint32_t>(list.instances.size()));
|
||||
}
|
||||
|
||||
void OnServerInfo(const MasterPackets::ServerInfo& serverInfo, const SystemAddress&) {
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
switch (serverInfo.serverType) {
|
||||
case ServiceType::AUTH:
|
||||
if (serverInfo.ip.string == "offline") {
|
||||
ServerState::g_AuthStatus.online = false;
|
||||
} else {
|
||||
instances.push_back(info);
|
||||
}
|
||||
|
||||
LOG("World ready: zone %i instance %i", zoneID, instanceID);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::PLAYER_ADDED: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
LWOMAPID zoneID;
|
||||
LWOINSTANCEID instanceID;
|
||||
inStream.Read(zoneID);
|
||||
inStream.Read(instanceID);
|
||||
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
for (auto& world : ServerState::g_WorldInstances) {
|
||||
if (world.mapID == zoneID && world.instanceID == instanceID) {
|
||||
world.players++;
|
||||
break;
|
||||
}
|
||||
ServerState::g_AuthStatus.online = true;
|
||||
ServerState::g_AuthStatus.lastSeen = std::chrono::steady_clock::now();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::PLAYER_REMOVED: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
LWOMAPID zoneID;
|
||||
LWOINSTANCEID instanceID;
|
||||
inStream.Read(zoneID);
|
||||
inStream.Read(instanceID);
|
||||
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
for (auto& world : ServerState::g_WorldInstances) {
|
||||
if (world.mapID == zoneID && world.instanceID == instanceID) {
|
||||
if (world.players > 0) world.players--;
|
||||
break;
|
||||
}
|
||||
case ServiceType::CHAT:
|
||||
if (serverInfo.ip.string == "offline") {
|
||||
ServerState::g_ChatStatus.online = false;
|
||||
} else {
|
||||
ServerState::g_ChatStatus.online = true;
|
||||
ServerState::g_ChatStatus.lastSeen = std::chrono::steady_clock::now();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::PLAYER_POSITIONS: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
PlayerPositions positions;
|
||||
if (positions.Deserialize(inStream)) LiveWorld::HandlePlayerPositions(positions);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::MESSAGE_CAPTURE_DATA: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
MessageCaptureData data;
|
||||
if (data.Deserialize(inStream)) Inspector::HandleData(data);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::DATA_CHANGED: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
DataChanged changed;
|
||||
if (changed.Deserialize(inStream)) BroadcastDataChanged(changed);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::PLAYER_ACTION_RESULT: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
PlayerActionResult result;
|
||||
if (result.Deserialize(inStream)) PlayerActions::HandleResult(result);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::SHUTDOWN_RESPONSE: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
|
||||
LWOMAPID zoneID;
|
||||
LWOINSTANCEID instanceID;
|
||||
inStream.Read(zoneID);
|
||||
inStream.Read(instanceID);
|
||||
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
auto& instances = ServerState::g_WorldInstances;
|
||||
instances.erase(
|
||||
std::remove_if(instances.begin(), instances.end(),
|
||||
[zoneID, instanceID](const WorldInstanceInfo& w) {
|
||||
return w.mapID == zoneID && w.instanceID == instanceID;
|
||||
}),
|
||||
instances.end());
|
||||
|
||||
LOG("World shutdown: zone %i instance %i", zoneID, instanceID);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnWorldReady(const MasterPackets::WorldReadyInfo& ready, const SystemAddress&) {
|
||||
const LWOMAPID zoneID = ready.zoneID;
|
||||
const LWOINSTANCEID instanceID = ready.instanceID;
|
||||
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
WorldInstanceInfo info;
|
||||
info.mapID = zoneID;
|
||||
info.instanceID = instanceID;
|
||||
info.cloneID = ready.cloneID;
|
||||
info.players = 0;
|
||||
info.ip = ready.ip.string;
|
||||
info.port = ready.port;
|
||||
info.isPrivate = ready.isPrivate != 0;
|
||||
info.zoneName = GetZoneDisplayName(zoneID);
|
||||
AddPropertyDetails(info);
|
||||
// Master can report a world more than once (in the server list and when it becomes ready): replace, don't add
|
||||
auto& instances = ServerState::g_WorldInstances;
|
||||
const auto existing = std::ranges::find_if(instances, [&](const WorldInstanceInfo& w) { return w.mapID == zoneID && w.instanceID == instanceID; });
|
||||
if (existing != instances.end()) {
|
||||
info.players = existing->players;
|
||||
*existing = info;
|
||||
} else {
|
||||
instances.push_back(info);
|
||||
}
|
||||
|
||||
LOG("World ready: zone %i instance %i", zoneID, instanceID);
|
||||
}
|
||||
|
||||
void OnPlayerAdded(const MasterPackets::PlayerAdded& added, const SystemAddress&) {
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
for (auto& world : ServerState::g_WorldInstances) {
|
||||
if (world.mapID == added.zoneID && world.instanceID == added.instanceID) {
|
||||
world.players++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnPlayerRemoved(const MasterPackets::PlayerRemoved& removed, const SystemAddress&) {
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
for (auto& world : ServerState::g_WorldInstances) {
|
||||
if (world.mapID == removed.zoneID && world.instanceID == removed.instanceID) {
|
||||
if (world.players > 0) world.players--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnWorldShutDown(const MasterPackets::WorldShutDown& shutDown, const SystemAddress&) {
|
||||
const LWOMAPID zoneID = shutDown.zoneID;
|
||||
const LWOINSTANCEID instanceID = shutDown.instanceID;
|
||||
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
auto& instances = ServerState::g_WorldInstances;
|
||||
instances.erase(
|
||||
std::remove_if(instances.begin(), instances.end(),
|
||||
[zoneID, instanceID](const WorldInstanceInfo& w) {
|
||||
return w.mapID == zoneID && w.instanceID == instanceID;
|
||||
}),
|
||||
instances.end());
|
||||
|
||||
LOG("World shutdown: zone %i instance %i", zoneID, instanceID);
|
||||
}
|
||||
|
||||
// Packets from master
|
||||
const PacketDispatcher<MessageType::Master>& MasterHandlers() {
|
||||
static const auto handlers = [] {
|
||||
PacketDispatcher<MessageType::Master> handlers;
|
||||
using MessageType::Master;
|
||||
handlers.On<MasterPackets::ServerListResponse>(Master::SERVER_LIST_RESPONSE, OnServerList);
|
||||
handlers.On<MasterPackets::ServerInfo>(Master::SERVER_INFO, OnServerInfo);
|
||||
handlers.On<MasterPackets::WorldReadyInfo>(Master::WORLD_READY, OnWorldReady);
|
||||
handlers.On<MasterPackets::PlayerAdded>(Master::PLAYER_ADDED, OnPlayerAdded);
|
||||
handlers.On<MasterPackets::PlayerRemoved>(Master::PLAYER_REMOVED, OnPlayerRemoved);
|
||||
handlers.On<PlayerPositions>(Master::PLAYER_POSITIONS, [](const PlayerPositions& positions, const SystemAddress&) { LiveWorld::HandlePlayerPositions(positions); });
|
||||
handlers.On<MessageCaptureData>(Master::MESSAGE_CAPTURE_DATA, [](const MessageCaptureData& data, const SystemAddress&) { Inspector::HandleData(data); });
|
||||
handlers.On<DataChanged>(Master::DATA_CHANGED, [](const DataChanged& changed, const SystemAddress&) { BroadcastDataChanged(changed); });
|
||||
handlers.On<PlayerActionResult>(Master::PLAYER_ACTION_RESULT, [](const PlayerActionResult& result, const SystemAddress&) { PlayerActions::HandleResult(result); });
|
||||
handlers.On<MasterPackets::WorldShutDown>(Master::SHUTDOWN_RESPONSE, OnWorldShutDown);
|
||||
return handlers;
|
||||
}();
|
||||
return handlers;
|
||||
}
|
||||
|
||||
void HandleMasterPacket(Packet* packet) {
|
||||
MasterHandlers().Dispatch(packet, ServiceType::MASTER);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@@ -622,9 +540,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Only once the master link is up; sent earlier, the request is dropped and auth/chat look offline
|
||||
if (g_Server->GetIsConnectedToMaster() && std::chrono::steady_clock::now() >= g_NextServerListRequest) {
|
||||
RakNet::BitStream bitStream;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::REQUEST_SERVER_LIST);
|
||||
g_Server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(MasterPackets::RequestServerList());
|
||||
g_NextServerListRequest = std::chrono::steady_clock::now() + SERVER_LIST_INTERVAL;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "RouteUtils.h"
|
||||
#include "PlayerActions.h"
|
||||
#include "PlayerAction.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "WSRoutes.h"
|
||||
#include "Database.h"
|
||||
#include "HTTPContext.h"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "AnnouncementSchedule.h"
|
||||
#include "LiveWorld.h"
|
||||
#include "DashboardRoutes.h"
|
||||
#include "DashboardMessages.h"
|
||||
#include "master/DashboardMessages.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "Scheduler.h"
|
||||
#include "Background.h"
|
||||
#include "PlayerActions.h"
|
||||
#include "PlayerAction.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "WSRoutes.h"
|
||||
#include "ClientAssets.h"
|
||||
#include "DashboardRoutes.h"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "RouteUtils.h"
|
||||
#include "DashboardRoutes.h"
|
||||
#include "PlayerActions.h"
|
||||
#include "PlayerAction.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "WSRoutes.h"
|
||||
#include "Permissions.h"
|
||||
#include "Database.h"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "LiveWorld.h"
|
||||
#include "LiveOpsRules.h"
|
||||
#include "PlayerActions.h"
|
||||
#include "PlayerAction.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "ServerState.h"
|
||||
#include "GameLabels.h"
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Inspector.h"
|
||||
#include "MasterPackets.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
@@ -11,7 +12,7 @@
|
||||
#include "Game.h"
|
||||
#include "InspectorFormat.h"
|
||||
#include "Logger.h"
|
||||
#include "MessageCapture.h"
|
||||
#include "master/MessageCapture.h"
|
||||
#include "MessageType/Master.h"
|
||||
#include "Permissions.h"
|
||||
#include "RouteUtils.h"
|
||||
@@ -85,10 +86,7 @@ namespace {
|
||||
|
||||
void SendControl(const MessageCaptureControl& control) {
|
||||
if (!Connected()) return;
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::MESSAGE_CAPTURE_CONTROL);
|
||||
control.Serialize(bitStream);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(control);
|
||||
}
|
||||
|
||||
// (Re)start in whichever world has the player, for the time that is left
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "json.hpp"
|
||||
#include "magic_enum.hpp"
|
||||
#include "IMessageCaptures.h"
|
||||
#include "MessageCapture.h"
|
||||
#include "master/MessageCapture.h"
|
||||
#include "MessageType/Game.h"
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "InstanceLoad.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "InstanceLimits.h"
|
||||
#include "DashboardRoutes.h"
|
||||
#include "ServerState.h"
|
||||
@@ -62,9 +63,7 @@ namespace {
|
||||
// Master reloads its settings and zone limits on CONFIG_RELOAD (and passes it on to every server)
|
||||
bool TellMaster() {
|
||||
if (!Game::server || !Game::server->GetIsConnectedToMaster()) return false;
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::CONFIG_RELOAD);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(MasterPackets::ConfigReload());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "LiveWorld.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "WorldView.h"
|
||||
#include "Permissions.h"
|
||||
#include "DashboardRoutes.h"
|
||||
@@ -11,7 +12,7 @@
|
||||
#include "Alerts.h"
|
||||
#include "BitStreamUtils.h"
|
||||
#include "ServerState.h"
|
||||
#include "DashboardMessages.h"
|
||||
#include "master/DashboardMessages.h"
|
||||
#include "Database.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
@@ -73,11 +74,11 @@ namespace {
|
||||
|
||||
void SendAnnouncement(const std::string& title, const std::string& message, const std::vector<uint32_t>& zones = {}) {
|
||||
if (!Game::server || !Game::server->GetIsConnectedToMaster()) return;
|
||||
Announcement announcement{ title, message, zones };
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::ANNOUNCE);
|
||||
announcement.Serialize(bitStream);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
Announcement announcement;
|
||||
announcement.title = title;
|
||||
announcement.message = message;
|
||||
announcement.zones = zones;
|
||||
MasterPackets::SendToMaster(announcement);
|
||||
}
|
||||
|
||||
std::string Duration(int64_t seconds) {
|
||||
@@ -130,9 +131,7 @@ namespace {
|
||||
g_Restart.reset();
|
||||
SaveRestart();
|
||||
if (Game::server && Game::server->GetIsConnectedToMaster()) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::DASHBOARD_SHUTDOWN);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(MasterPackets::DashboardShutdown());
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -235,11 +234,10 @@ namespace LiveWorld {
|
||||
}
|
||||
if (!running) return JsonError(reply, eHTTPStatusCode::NOT_FOUND, "That world instance isn't running");
|
||||
if (!Game::server || !Game::server->GetIsConnectedToMaster()) return JsonError(reply, eHTTPStatusCode::SERVICE_UNAVAILABLE, "Not connected to the master server");
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::INSTANCE_SHUTDOWN);
|
||||
bitStream.Write(zone);
|
||||
bitStream.Write(instance);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::InstanceShutdown request;
|
||||
request.zoneID = zone;
|
||||
request.instanceID = instance;
|
||||
MasterPackets::SendToMaster(request);
|
||||
Audit(context, "shutdown_instance", "Zone " + std::to_string(zone) + " instance " + std::to_string(instance) + " (" + std::to_string(players) + " player(s))");
|
||||
JsonSuccess(reply, { {"message", "Shutting it down"} });
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "RouteUtils.h"
|
||||
#include "CharacterTools.h"
|
||||
#include "PlayerActions.h"
|
||||
#include "PlayerAction.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "ClientAssets.h"
|
||||
#include "GameLabels.h"
|
||||
#include "WSRoutes.h"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "ClientAssets.h"
|
||||
#include "GameLabels.h"
|
||||
#include "PlayerActions.h"
|
||||
#include "PlayerAction.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "Strikes.h"
|
||||
#include "WSRoutes.h"
|
||||
#include "Database.h"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "PlayerActions.h"
|
||||
#include "MasterPackets.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <deque>
|
||||
@@ -90,10 +91,7 @@ uint32_t PlayerActions::Request(PlayerActionRequest request, uint32_t ownerAccou
|
||||
return request.requestId;
|
||||
}
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::PLAYER_ACTION);
|
||||
request.Serialize(bitStream);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(request);
|
||||
return request.requestId;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "json.hpp"
|
||||
#include "PlayerAction.h"
|
||||
#include "master/PlayerAction.h"
|
||||
|
||||
/**
|
||||
* Sends player actions (kick, refresh, rescue) through master to every world server and reports the outcome.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "SettingsRoutes.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "Permissions.h"
|
||||
#include "SettingsCatalog.h"
|
||||
#include "SettingsHistory.h"
|
||||
@@ -160,9 +161,7 @@ namespace {
|
||||
void ReloadEverywhere() {
|
||||
Game::config->ReloadConfig();
|
||||
if (!Game::server || !Game::server->GetIsConnectedToMaster()) return;
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::CONFIG_RELOAD);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(MasterPackets::ConfigReload());
|
||||
}
|
||||
|
||||
bool ValidName(const std::string& name) {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "RouteUtils.h"
|
||||
#include "VanityJson.h"
|
||||
#include "PlayerActions.h"
|
||||
#include "PlayerAction.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "DashboardRoutes.h"
|
||||
#include "ClientAssets.h"
|
||||
#include "BinaryPathFinder.h"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "Game.h"
|
||||
#include "Database.h"
|
||||
#include "Logger.h"
|
||||
#include "DataChanged.h"
|
||||
#include "master/DataChanged.h"
|
||||
#include "Alerts.h"
|
||||
#include "LiveWorld.h"
|
||||
#include "GeneralUtils.h"
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "RouteUtils.h"
|
||||
#include "CDClientDatabase.h"
|
||||
#include "DashboardMessages.h"
|
||||
#include "master/DashboardMessages.h"
|
||||
#include "Database.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "RocketLaunchpadControlComponent.h"
|
||||
#include "MasterPackets.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@@ -136,10 +137,9 @@ LWOCLONEID RocketLaunchpadControlComponent::GetSelectedCloneId(LWOOBJID player)
|
||||
}
|
||||
|
||||
void RocketLaunchpadControlComponent::TellMasterToPrepZone(int zoneID) {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::PREP_ZONE);
|
||||
bitStream.Write(zoneID);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::PrepZone request;
|
||||
request.zoneID = zoneID;
|
||||
MasterPackets::SendToMaster(request);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include "EchoSyncSkill.h"
|
||||
#include "RequestServerProjectileImpact.h"
|
||||
#include "DoClientProjectileImpact.h"
|
||||
#include "MessageCapture.h"
|
||||
#include "master/MessageCapture.h"
|
||||
|
||||
namespace {
|
||||
using json = nlohmann::json;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "DashboardActions.h"
|
||||
|
||||
#include "PlayerAction.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "Database.h"
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
#include "DashboardNotify.h"
|
||||
#include "MasterPackets.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <set>
|
||||
|
||||
#include "BitStreamUtils.h"
|
||||
#include "DataChanged.h"
|
||||
#include "master/DataChanged.h"
|
||||
#include "Game.h"
|
||||
#include "dServer.h"
|
||||
#include "MessageType/Master.h"
|
||||
#include "ServiceType.h"
|
||||
#include "DashboardMessages.h"
|
||||
#include "master/DashboardMessages.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "Entity.h"
|
||||
#include "Character.h"
|
||||
@@ -44,10 +45,7 @@ namespace DashboardNotify {
|
||||
for (const auto& [table, id] : g_Pending) message.entries.push_back({ table, id });
|
||||
g_Pending.clear();
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::DATA_CHANGED);
|
||||
message.Serialize(bitStream);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(message);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,10 +73,7 @@ namespace DashboardNotify {
|
||||
message.players.push_back({ character->GetID(), position.x, position.y, position.z });
|
||||
}
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::PLAYER_POSITIONS);
|
||||
message.Serialize(bitStream);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(message);
|
||||
}
|
||||
|
||||
std::string ChatLine(const std::string& title, const std::string& message) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "MessageInspector.h"
|
||||
#include "MasterPackets.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
@@ -11,7 +12,7 @@
|
||||
#include "Game.h"
|
||||
#include "GameMessageDecoder.h"
|
||||
#include "Logger.h"
|
||||
#include "MessageCapture.h"
|
||||
#include "master/MessageCapture.h"
|
||||
#include "MessageType/Client.h"
|
||||
#include "MessageType/Master.h"
|
||||
#include "PlayerManager.h"
|
||||
@@ -64,10 +65,7 @@ namespace {
|
||||
data.dropped = dropped;
|
||||
data.entries = std::move(entries);
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::MESSAGE_CAPTURE_DATA);
|
||||
data.Serialize(bitStream);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(data);
|
||||
}
|
||||
|
||||
// Send what is waiting: a couple of batches per call (the rest goes next time), or everything when `all`
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "DEVGMCommands.h"
|
||||
#include "MasterPackets.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
@@ -527,9 +528,7 @@ namespace DEVGMCommands {
|
||||
|
||||
void ShutdownUniverse(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
//Tell the master server that we're going to be shutting down whole "universe":
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::SHUTDOWN_UNIVERSE);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(MasterPackets::ShutdownUniverse());
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Sent universe shutdown notification to master.");
|
||||
|
||||
//Tell chat to send an announcement to all servers
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "WorldMigration.h"
|
||||
#include "MasterPackets.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
@@ -74,10 +75,7 @@ namespace {
|
||||
status.failed = failed;
|
||||
status.remaining = remaining;
|
||||
status.message = message;
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::MIGRATE_STATUS);
|
||||
status.Serialize(bitStream);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(status);
|
||||
}
|
||||
|
||||
void SendStatus(eState state, uint16_t remaining, const std::string& message) {
|
||||
@@ -147,10 +145,7 @@ namespace {
|
||||
g_Leaving[sysAddr] = { playerId, 0.0f };
|
||||
|
||||
{
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::MIGRATE_PLAYER_STATE);
|
||||
carried.Serialize(bitStream);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(carried);
|
||||
}
|
||||
|
||||
// Seamless: take every object we sent away first. The client deletes them but keeps its own player object when
|
||||
@@ -360,10 +355,7 @@ void WorldMigration::RequestMigration(Entity* requester, eKind kind, uint32_t ta
|
||||
}
|
||||
LOG("Asking master to %s this instance (migration %u, requested by %s)", KindName(kind), request.requestId, request.requestedBy.c_str());
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::INSTANCE_MIGRATE);
|
||||
request.Serialize(bitStream);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(request);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "InstanceMigration.h"
|
||||
#include "master/InstanceMigration.h"
|
||||
#include "RakNetTypes.h"
|
||||
#include "dCommonVars.h"
|
||||
|
||||
|
||||
@@ -21,6 +21,22 @@ using std::make_unique;
|
||||
|
||||
namespace {
|
||||
const InstancePtr g_Empty{ nullptr };
|
||||
|
||||
// Where the player of request goes: instance
|
||||
void SendZoneTransferResponse(const PendingInstanceRequest& request, const Instance& instance) {
|
||||
const auto& zoneId = instance.GetZoneID();
|
||||
MasterPackets::RequestZoneTransferResponse response;
|
||||
response.requestID = request.id;
|
||||
response.mythranShift = request.mythranShift;
|
||||
response.zoneID = zoneId.GetMapID();
|
||||
response.zoneInstance = zoneId.GetInstanceID();
|
||||
response.zoneClone = zoneId.GetCloneID();
|
||||
response.serverPort = static_cast<uint16_t>(instance.GetPort());
|
||||
response.serverIP = LUString(instance.GetIP(), 255);
|
||||
response.stamps = request.stamps;
|
||||
response.stamps.Add(eStamps::PASSPORT_AUTH_WORLD_SESSION_CONFIRM_TO_AUTH, zoneId.GetInstanceID());
|
||||
MasterPackets::SendTo(request.sysAddr, response);
|
||||
}
|
||||
}
|
||||
|
||||
InstanceManager::InstanceManager(const std::string& externalIP) : mExternalIP{ externalIP } {
|
||||
@@ -156,20 +172,7 @@ void InstanceManager::ReadyInstance(const InstancePtr& instance) {
|
||||
|
||||
LOG("Responding to pending request %llu -> %i (%i)", request, zoneId.GetMapID(), zoneId.GetCloneID());
|
||||
|
||||
auto stamps = request.stamps;
|
||||
stamps.Add(eStamps::PASSPORT_AUTH_WORLD_SESSION_CONFIRM_TO_AUTH, zoneId.GetInstanceID());
|
||||
MasterPackets::SendZoneTransferResponse(
|
||||
Game::server,
|
||||
request.sysAddr,
|
||||
request.id,
|
||||
request.mythranShift,
|
||||
zoneId.GetMapID(),
|
||||
zoneId.GetInstanceID(),
|
||||
zoneId.GetCloneID(),
|
||||
instance->GetIP(),
|
||||
instance->GetPort(),
|
||||
stamps
|
||||
);
|
||||
SendZoneTransferResponse(request, *instance);
|
||||
}
|
||||
|
||||
pending.clear();
|
||||
@@ -178,13 +181,9 @@ void InstanceManager::ReadyInstance(const InstancePtr& instance) {
|
||||
void InstanceManager::RequestAffirmation(const InstancePtr& instance, const PendingInstanceRequest& request) {
|
||||
instance->GetPendingAffirmations().push_back(request);
|
||||
|
||||
CBITSTREAM;
|
||||
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::AFFIRM_TRANSFER_REQUEST);
|
||||
|
||||
bitStream.Write(request.id);
|
||||
|
||||
Game::server->Send(bitStream, instance->GetSysAddr(), false);
|
||||
MasterPackets::AffirmTransferRequest affirm;
|
||||
affirm.requestID = request.id;
|
||||
MasterPackets::SendTo(instance->GetSysAddr(), affirm);
|
||||
|
||||
LOG("Sent affirmation request %llu to %i/%i", request.id,
|
||||
static_cast<int>(instance->GetZoneID().GetMapID()),
|
||||
@@ -200,22 +199,7 @@ void InstanceManager::AffirmTransfer(const InstancePtr& instance, const uint64_t
|
||||
|
||||
if (request.id != transferID) continue;
|
||||
|
||||
const auto& zoneId = instance->GetZoneID();
|
||||
|
||||
auto stamps = request.stamps;
|
||||
stamps.Add(eStamps::PASSPORT_AUTH_WORLD_SESSION_CONFIRM_TO_AUTH, zoneId.GetInstanceID());
|
||||
MasterPackets::SendZoneTransferResponse(
|
||||
Game::server,
|
||||
request.sysAddr,
|
||||
request.id,
|
||||
request.mythranShift,
|
||||
zoneId.GetMapID(),
|
||||
zoneId.GetInstanceID(),
|
||||
zoneId.GetCloneID(),
|
||||
instance->GetIP(),
|
||||
instance->GetPort(),
|
||||
stamps
|
||||
);
|
||||
SendZoneTransferResponse(request, *instance);
|
||||
|
||||
pending.erase(pending.begin() + i);
|
||||
|
||||
@@ -240,7 +224,7 @@ void InstanceManager::RedirectPendingRequests(const InstancePtr& instance) {
|
||||
}
|
||||
}
|
||||
|
||||
const InstancePtr& InstanceManager::GetInstanceBySysAddr(SystemAddress& sysAddr) {
|
||||
const InstancePtr& InstanceManager::GetInstanceBySysAddr(const SystemAddress& sysAddr) {
|
||||
for (const auto& instance : m_Instances) {
|
||||
if (instance && instance->GetSysAddr() == sysAddr) {
|
||||
return instance;
|
||||
@@ -418,11 +402,7 @@ bool Instance::GetShutdownComplete() const {
|
||||
}
|
||||
|
||||
void Instance::Shutdown() {
|
||||
CBITSTREAM;
|
||||
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::SHUTDOWN);
|
||||
|
||||
Game::server->Send(bitStream, this->m_SysAddr, false);
|
||||
MasterPackets::SendTo(this->m_SysAddr, MasterPackets::Shutdown());
|
||||
|
||||
LOG("Triggered world shutdown for zone/clone/instance %i/%i/%i", GetMapID(), GetCloneID(), GetInstanceID());
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ public:
|
||||
|
||||
void RedirectPendingRequests(const InstancePtr& instance);
|
||||
|
||||
const InstancePtr& GetInstanceBySysAddr(SystemAddress& sysAddr);
|
||||
const InstancePtr& GetInstanceBySysAddr(const SystemAddress& sysAddr);
|
||||
|
||||
const InstancePtr& FindInstance(LWOMAPID mapID, bool isFriendTransfer, LWOCLONEID cloneId = 0);
|
||||
const InstancePtr& FindInstance(LWOMAPID mapID, LWOINSTANCEID instanceID);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@
|
||||
#include "CDClientManager.h"
|
||||
#include "Game.h"
|
||||
#include "InstanceManager.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "Logger.h"
|
||||
#include "MessageType/Master.h"
|
||||
#include "ServiceType.h"
|
||||
@@ -114,10 +115,7 @@ namespace {
|
||||
order.targetZone = migration.zone;
|
||||
order.targetInstance = migration.target;
|
||||
order.targetPort = 0;
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::MIGRATE_PLAYERS);
|
||||
order.Serialize(bitStream);
|
||||
Game::server->Send(bitStream, source->GetSysAddr(), false);
|
||||
MasterPackets::SendTo(source->GetSysAddr(), order);
|
||||
}
|
||||
|
||||
// Ends a migration. The source goes back to taking players unless it is being shut down.
|
||||
@@ -153,10 +151,7 @@ namespace {
|
||||
// Without a loading screen the "dimensional shift" notice would be the only sign; leave it out then
|
||||
order.seamless = migration.seamless;
|
||||
order.mythranShift = !migration.seamless;
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::MIGRATE_PLAYERS);
|
||||
order.Serialize(bitStream);
|
||||
Game::server->Send(bitStream, source.GetSysAddr(), false);
|
||||
MasterPackets::SendTo(source.GetSysAddr(), order);
|
||||
|
||||
migration.state = migration.warnSeconds > 0 ? eState::WARNING : eState::MOVING;
|
||||
migration.deadline = Clock::now() + std::chrono::seconds(migration.warnSeconds) + MOVE_TIMEOUT;
|
||||
@@ -257,15 +252,14 @@ void MigrationCoordinator::HandleStatus(const SystemAddress& from, const Migrati
|
||||
Report(migration, status.state, status.message, status.remaining);
|
||||
}
|
||||
|
||||
void MigrationCoordinator::HandleCarriedState(const SystemAddress& from, const CarriedPlayerState& state, const unsigned char* data, uint32_t length) {
|
||||
void MigrationCoordinator::HandleCarriedState(const SystemAddress& from, const CarriedPlayerState& state) {
|
||||
for (const auto& [id, migration] : g_Active) {
|
||||
if (migration.zone != state.targetZone || migration.target != state.targetInstance) continue;
|
||||
const auto& source = FindInstance(migration.zone, migration.source);
|
||||
if (!source || source->GetSysAddr() != from) continue;
|
||||
const auto& target = FindInstance(migration.zone, migration.target);
|
||||
if (!target) return;
|
||||
RakNet::BitStream forward(const_cast<unsigned char*>(data), length, false);
|
||||
Game::server->Send(forward, target->GetSysAddr(), false);
|
||||
MasterPackets::SendTo(target->GetSysAddr(), state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "InstanceMigration.h"
|
||||
#include "master/InstanceMigration.h"
|
||||
#include "RakNetTypes.h"
|
||||
|
||||
class Instance;
|
||||
@@ -25,7 +25,7 @@ namespace MigrationCoordinator {
|
||||
void HandleStatus(const SystemAddress& from, const MigrationStatus& status);
|
||||
|
||||
// A source world sent state to carry over for one player: passed on to the target world as is
|
||||
void HandleCarriedState(const SystemAddress& from, const CarriedPlayerState& state, const unsigned char* data, uint32_t length);
|
||||
void HandleCarriedState(const SystemAddress& from, const CarriedPlayerState& state);
|
||||
|
||||
// A world server went away; migrations it was part of end
|
||||
void OnInstanceGone(const Instance& instance);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -2,9 +2,9 @@
|
||||
#include "ConfigSync.h"
|
||||
#include "EconomyLedger.h"
|
||||
#include "DashboardNotify.h"
|
||||
#include "DashboardMessages.h"
|
||||
#include "PlayerAction.h"
|
||||
#include "MessageCapture.h"
|
||||
#include "master/DashboardMessages.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "master/MessageCapture.h"
|
||||
#include "MessageInspector.h"
|
||||
#include "LiveEvents.h"
|
||||
#include <iostream>
|
||||
@@ -62,6 +62,7 @@
|
||||
#include "WorldRoutePacket.h"
|
||||
#include "ChatServerLink.h"
|
||||
#include "PacketDispatcher.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "GameMessageHandler.h"
|
||||
#include "GameMessages.h"
|
||||
#include "Mail.h"
|
||||
@@ -69,7 +70,6 @@
|
||||
#include "SkillComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
#include "Game.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "PropertyManagementComponent.h"
|
||||
#include "AssetManager.h"
|
||||
#include "LevelProgressionComponent.h"
|
||||
@@ -576,7 +576,10 @@ int main(int argc, char** argv) {
|
||||
if (!ready && Game::server->GetIsConnectedToMaster()) {
|
||||
LOG("Finished loading world with zone (%i), ready up!", Game::server->GetZoneID());
|
||||
|
||||
MasterPackets::SendWorldReady(Game::server, Game::server->GetZoneID(), Game::server->GetInstanceID());
|
||||
MasterPackets::WorldReady worldReady;
|
||||
worldReady.zoneID = static_cast<LWOMAPID>(Game::server->GetZoneID());
|
||||
worldReady.instanceID = static_cast<LWOINSTANCEID>(Game::server->GetInstanceID());
|
||||
MasterPackets::SendToMaster(worldReady);
|
||||
|
||||
ready = true;
|
||||
}
|
||||
@@ -682,18 +685,11 @@ void HandlePacketChat(Packet* packet) {
|
||||
}
|
||||
}
|
||||
|
||||
void HandleMasterPacket(Packet* packet) {
|
||||
if (packet->length < 2) return;
|
||||
if (static_cast<ServiceType>(packet->data[1]) != ServiceType::MASTER || packet->length < 4) return;
|
||||
switch (static_cast<MessageType::Master>(packet->data[3])) {
|
||||
|
||||
case MessageType::Master::SESSION_KEY_RESPONSE: {
|
||||
namespace {
|
||||
void OnSessionKeyResponse(const MasterPackets::SessionKeyResponse& response, const SystemAddress& masterAddr) {
|
||||
//Read our session key and to which user it belongs:
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
uint32_t sessionKey = 0;
|
||||
inStream.Read(sessionKey);
|
||||
LUWString username;
|
||||
inStream.Read(username);
|
||||
const uint32_t sessionKey = response.sessionKey;
|
||||
const LUWString& username = response.username;
|
||||
|
||||
//Find them:
|
||||
auto it = g_PendingUsers.find(username.GetAsString());
|
||||
@@ -711,7 +707,7 @@ void HandleMasterPacket(Packet* packet) {
|
||||
} else {
|
||||
LOG("User %s authenticated with correct key.", username.GetAsString().c_str());
|
||||
|
||||
UserManager::Instance()->DeleteUser(packet->systemAddress);
|
||||
UserManager::Instance()->DeleteUser(masterAddr);
|
||||
|
||||
//Create our user and send them in:
|
||||
UserManager::Instance()->CreateUser(it->second.sysAddr, username.GetAsString(), userHash);
|
||||
@@ -754,11 +750,10 @@ void HandleMasterPacket(Packet* packet) {
|
||||
|
||||
//Notify master:
|
||||
{
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::PLAYER_ADDED);
|
||||
bitStream.Write<LWOMAPID>(Game::server->GetZoneID());
|
||||
bitStream.Write<LWOINSTANCEID>(g_InstanceID);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::PlayerAdded added;
|
||||
added.zoneID = static_cast<LWOMAPID>(Game::server->GetZoneID());
|
||||
added.instanceID = static_cast<LWOINSTANCEID>(g_InstanceID);
|
||||
MasterPackets::SendToMaster(added);
|
||||
}
|
||||
|
||||
if (seamlessCharacter != LWOOBJID_EMPTY) {
|
||||
@@ -766,89 +761,20 @@ void HandleMasterPacket(Packet* packet) {
|
||||
WorldMigration::OnSeamlessArrival(Game::entityManager->GetEntity(seamlessCharacter));
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MessageType::Master::AFFIRM_TRANSFER_REQUEST: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
uint64_t requestID;
|
||||
inStream.Read(requestID);
|
||||
|
||||
void OnAffirmTransferRequest(const MasterPackets::AffirmTransferRequest& request, const SystemAddress& sysAddr) {
|
||||
const uint64_t requestID = request.requestID;
|
||||
LOG("Got affirmation request of transfer %llu", requestID);
|
||||
|
||||
CBITSTREAM;
|
||||
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::AFFIRM_TRANSFER_RESPONSE);
|
||||
bitStream.Write(requestID);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
|
||||
break;
|
||||
MasterPackets::AffirmTransferResponse response;
|
||||
response.requestID = requestID;
|
||||
MasterPackets::SendToMaster(response);
|
||||
}
|
||||
|
||||
case MessageType::Master::SHUTDOWN: {
|
||||
Game::lastSignal = -1;
|
||||
LOG("Got shutdown request from master, zone (%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID());
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::MIGRATE_PLAYERS: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
MigratePlayersOrder order;
|
||||
if (order.Deserialize(inStream)) WorldMigration::HandleOrder(order);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::MIGRATE_PLAYER_STATE: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
CarriedPlayerState state;
|
||||
if (state.Deserialize(inStream)) WorldMigration::StoreCarriedState(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::MIGRATE_STATUS: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
MigrationStatus status;
|
||||
if (status.Deserialize(inStream)) WorldMigration::HandleStatus(status);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::PLAYER_ACTION: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
PlayerActionRequest request;
|
||||
if (!request.Deserialize(inStream)) break;
|
||||
|
||||
PlayerActionResult result;
|
||||
result.requestId = request.requestId;
|
||||
result.action = request.action;
|
||||
result.affected = DashboardActions::Apply(request);
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::PLAYER_ACTION_RESULT);
|
||||
result.Serialize(bitStream);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::MESSAGE_CAPTURE_CONTROL: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
MessageCaptureControl control;
|
||||
if (control.Deserialize(inStream)) MessageInspector::Control(control);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::ANNOUNCE: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
Announcement announcement;
|
||||
if (announcement.Deserialize(inStream)) DashboardNotify::Announce(announcement.title, announcement.message);
|
||||
break;
|
||||
}
|
||||
|
||||
case MessageType::Master::NEW_SESSION_ALERT: {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
uint32_t sessionKey{};
|
||||
inStream.Read(sessionKey);
|
||||
|
||||
LUString username;
|
||||
inStream.Read(username);
|
||||
void OnNewSessionAlert(const MasterPackets::NewSessionAlert& alert, const SystemAddress& sysAddr) {
|
||||
const uint32_t sessionKey = alert.sessionKey;
|
||||
const LUString& username = alert.username;
|
||||
LOG("Got new session alert for user %s", username.string.c_str());
|
||||
//Find them:
|
||||
User* user = UserManager::Instance()->GetUser(username.string.c_str());
|
||||
@@ -863,9 +789,43 @@ void HandleMasterPacket(Packet* packet) {
|
||||
Game::server->Disconnect(user->GetSystemAddress(), eServerDisconnectIdentifiers::INVALID_SESSION_KEY);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
void OnPlayerAction(const PlayerActionRequest& request, const SystemAddress& sysAddr) {
|
||||
PlayerActionResult result;
|
||||
result.requestId = request.requestId;
|
||||
result.action = request.action;
|
||||
result.affected = DashboardActions::Apply(request);
|
||||
MasterPackets::SendToMaster(result);
|
||||
}
|
||||
|
||||
// Packets from master that dServer hands back to us
|
||||
const PacketDispatcher<MessageType::Master>& MasterHandlers() {
|
||||
static const auto handlers = [] {
|
||||
PacketDispatcher<MessageType::Master> handlers;
|
||||
using MessageType::Master;
|
||||
handlers.On<MasterPackets::SessionKeyResponse>(Master::SESSION_KEY_RESPONSE, OnSessionKeyResponse);
|
||||
handlers.On<MasterPackets::AffirmTransferRequest>(Master::AFFIRM_TRANSFER_REQUEST, OnAffirmTransferRequest);
|
||||
handlers.On<MasterPackets::Shutdown>(Master::SHUTDOWN, [](const MasterPackets::Shutdown&, const SystemAddress&) {
|
||||
Game::lastSignal = -1;
|
||||
LOG("Got shutdown request from master, zone (%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID());
|
||||
});
|
||||
handlers.On<MigratePlayersOrder>(Master::MIGRATE_PLAYERS, [](const MigratePlayersOrder& order, const SystemAddress&) { WorldMigration::HandleOrder(order); });
|
||||
handlers.On<CarriedPlayerState>(Master::MIGRATE_PLAYER_STATE, [](const CarriedPlayerState& state, const SystemAddress&) { WorldMigration::StoreCarriedState(state); });
|
||||
handlers.On<MigrationStatus>(Master::MIGRATE_STATUS, [](const MigrationStatus& status, const SystemAddress&) { WorldMigration::HandleStatus(status); });
|
||||
handlers.On<PlayerActionRequest>(Master::PLAYER_ACTION, OnPlayerAction);
|
||||
handlers.On<MessageCaptureControl>(Master::MESSAGE_CAPTURE_CONTROL, [](const MessageCaptureControl& control, const SystemAddress&) { MessageInspector::Control(control); });
|
||||
handlers.On<Announcement>(Master::ANNOUNCE, [](const Announcement& announcement, const SystemAddress&) { DashboardNotify::Announce(announcement.title, announcement.message); });
|
||||
handlers.On<MasterPackets::NewSessionAlert>(Master::NEW_SESSION_ALERT, OnNewSessionAlert);
|
||||
return handlers;
|
||||
}();
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
|
||||
void HandleMasterPacket(Packet* packet) {
|
||||
if (packet->length < 4) return;
|
||||
if (!MasterHandlers().Dispatch(packet, ServiceType::MASTER)) {
|
||||
LOG("Unknown packet ID from master %i", int(packet->data[3]));
|
||||
}
|
||||
}
|
||||
@@ -1151,11 +1111,10 @@ void CleanupDisconnectedUser(const SystemAddress& sysAddr) {
|
||||
PropertyManagementComponent::Instance()->Save();
|
||||
}
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::PLAYER_REMOVED);
|
||||
bitStream.Write<LWOMAPID>(Game::server->GetZoneID());
|
||||
bitStream.Write<LWOINSTANCEID>(g_InstanceID);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::PlayerRemoved removed;
|
||||
removed.zoneID = static_cast<LWOMAPID>(Game::server->GetZoneID());
|
||||
removed.instanceID = static_cast<LWOINSTANCEID>(g_InstanceID);
|
||||
MasterPackets::SendToMaster(removed);
|
||||
}
|
||||
|
||||
// The world server's handlers for what clients send (ServiceType::WORLD). WorldPackets has the structs; each one is
|
||||
@@ -1202,10 +1161,9 @@ namespace {
|
||||
}
|
||||
|
||||
//Request the session info from Master:
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::REQUEST_SESSION_KEY);
|
||||
bitStream.Write(username);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::RequestSessionKey request;
|
||||
request.username = username;
|
||||
MasterPackets::SendToMaster(request);
|
||||
|
||||
//Insert info into our pending list
|
||||
TempSessionInfo info;
|
||||
@@ -1742,7 +1700,5 @@ void FinalizeShutdown() {
|
||||
}
|
||||
|
||||
void SendShutdownMessageToMaster() {
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::SHUTDOWN_RESPONSE);
|
||||
Game::server->SendToMaster(bitStream);
|
||||
MasterPackets::SendToMaster(MasterPackets::ShutdownResponse());
|
||||
}
|
||||
|
||||
@@ -180,6 +180,9 @@ Every non-game-message packet is an `LUBitStream` subclass. The base owns the fu
|
||||
doesn't link `dGame`) is in `ClientPackets`; chat-service packets the client receives are in `ChatPackets::Client`.
|
||||
- Chat -> client packets are wrapped in `ChatPackets::WorldRoutePacket` (`dNet/WorldRoutePacket.h`: target object ID
|
||||
+ the inner packet of any service); the world passes the inner bytes on unchanged.
|
||||
- MASTER structs: `MasterPackets.h` for the core ones (session keys, zone transfer, private zones, player counts,
|
||||
world ready, shutdown, server list); topic groups (dashboard player actions, data changes, positions and
|
||||
announcements, message capture, instance migration) in `dNet/master/<Topic>.h`, all included by `MasterPackets.h`.
|
||||
|
||||
```cpp
|
||||
// dNet/ChatPackets.h
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "InstanceMigration.h"
|
||||
#include "master/InstanceMigration.h"
|
||||
|
||||
using namespace InstanceMigration;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
SET(DNET_TESTS
|
||||
"ChatPacketsTests.cpp"
|
||||
"CommonAuthPacketsTests.cpp"
|
||||
"MasterPacketsTests.cpp"
|
||||
"WorldPacketsTests.cpp")
|
||||
|
||||
# Get the folder name and prepend it to the files above
|
||||
|
||||
@@ -433,9 +433,21 @@ TEST_F(CommonAuthPacketsTests, StampsRoundTrip) {
|
||||
}
|
||||
|
||||
TEST_F(CommonAuthPacketsTests, ZoneTransferResponseCarriesStamps) {
|
||||
const auto sendResponse = [](const Stamps& stamps) {
|
||||
MasterPackets::RequestZoneTransferResponse response;
|
||||
response.requestID = 99;
|
||||
response.mythranShift = true;
|
||||
response.zoneID = 1000;
|
||||
response.zoneInstance = 7;
|
||||
response.zoneClone = 0;
|
||||
response.serverIP = LUString("127.0.0.1", 255);
|
||||
response.serverPort = 2001;
|
||||
response.stamps = stamps;
|
||||
MasterPackets::SendTo(TestAddress(), response);
|
||||
};
|
||||
Stamps stamps;
|
||||
stamps.list = { Stamp(eStamps::PASSPORT_AUTH_START, 0, 10), Stamp(eStamps::PASSPORT_AUTH_WORLD_SESSION_CONFIRM_TO_AUTH, 7, 11) };
|
||||
const auto sent = Capture([&] { MasterPackets::SendZoneTransferResponse(Game::server, TestAddress(), 99, true, 1000, 7, 0, "127.0.0.1", 2001, stamps); });
|
||||
const auto sent = Capture([&] { sendResponse(stamps); });
|
||||
ASSERT_EQ(sent.size(), 1);
|
||||
|
||||
RakNet::BitStream bitStream(const_cast<uint8_t*>(sent[0].bytes.data()), sent[0].bytes.size(), true);
|
||||
@@ -469,7 +481,7 @@ TEST_F(CommonAuthPacketsTests, ZoneTransferResponseCarriesStamps) {
|
||||
EXPECT_EQ(copy.list[1].timestamp, 11);
|
||||
|
||||
// Without a login the list is empty: just its size field
|
||||
const auto plain = Capture([&] { MasterPackets::SendZoneTransferResponse(Game::server, TestAddress(), 99, true, 1000, 7, 0, "127.0.0.1", 2001); });
|
||||
const auto plain = Capture([&] { sendResponse({}); });
|
||||
ASSERT_EQ(plain.size(), 1);
|
||||
EXPECT_EQ(plain[0].bytes.size() + 32, sent[0].bytes.size());
|
||||
const std::vector<uint8_t> tail(plain[0].bytes.end() - 4, plain[0].bytes.end());
|
||||
|
||||
392
tests/dGameTests/dNetTests/Legacy/MasterPacketsLegacy.h
Normal file
392
tests/dGameTests/dNetTests/Legacy/MasterPacketsLegacy.h
Normal file
@@ -0,0 +1,392 @@
|
||||
#ifndef MASTERPACKETSLEGACY_H
|
||||
#define MASTERPACKETSLEGACY_H
|
||||
|
||||
// FROZEN ORACLE - DO NOT EDIT.
|
||||
// Verbatim copies of the hand written master packet code that dNet/MasterPackets.h replaced (dNet/MasterPackets.cpp,
|
||||
// dNet/ZoneInstanceManager.cpp, dMasterServer/{MasterServer,InstanceManager}.cpp, dWorldServer/WorldServer.cpp,
|
||||
// dDashboardServer/DashboardServer.cpp and the other senders at 9a392167, with the login stamps). Only the namespace changed, except:
|
||||
// - senders write into `bitStream` instead of sending it (SendToMaster and the master's peers aren't mocked);
|
||||
// - values the old code took from a server or an instance are passed in;
|
||||
// - old readers return what they read in a struct instead of acting on it.
|
||||
// These messages never reach a client, but master, worlds, auth, chat and the dashboard can run different builds
|
||||
// during an update, so their bytes are pinned too.
|
||||
|
||||
#include "BitStreamUtils.h"
|
||||
#include "dCommonVars.h"
|
||||
#include "MessageType/Master.h"
|
||||
#include "ServiceType.h"
|
||||
#include "Stamps.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace LegacyMaster {
|
||||
// dNet/MasterPackets.cpp
|
||||
inline void SendZoneTransferRequest(RakNet::BitStream& bitStream, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t cloneID, const Stamps& stamps = {}) {
|
||||
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);
|
||||
}
|
||||
|
||||
inline void SendZoneCreatePrivate(RakNet::BitStream& bitStream, uint32_t zoneID, uint32_t cloneID, const std::string& password) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::CREATE_PRIVATE_ZONE);
|
||||
|
||||
bitStream.Write(zoneID);
|
||||
bitStream.Write(cloneID);
|
||||
|
||||
bitStream.Write<uint32_t>(password.size());
|
||||
for (auto character : password) {
|
||||
bitStream.Write<char>(character);
|
||||
}
|
||||
}
|
||||
|
||||
inline void SendZoneRequestPrivate(RakNet::BitStream& bitStream, uint64_t requestID, bool mythranShift, const std::string& password) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
inline void SendWorldReady(RakNet::BitStream& bitStream, LWOMAPID zoneId, LWOINSTANCEID instanceId) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::WORLD_READY);
|
||||
|
||||
bitStream.Write(zoneId);
|
||||
bitStream.Write(instanceId);
|
||||
}
|
||||
|
||||
inline void SendZoneTransferResponse(RakNet::BitStream& bitStream, uint64_t requestID, bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, const std::string& serverIP, uint32_t serverPort, const Stamps& stamps = {}) {
|
||||
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);
|
||||
}
|
||||
|
||||
// SendServerInfo, with dServer's getters (int port, unsigned int zone, int instance) passed in
|
||||
inline void SendServerInfo(RakNet::BitStream& bitStream, int port, unsigned int zoneID, int instanceID, ServiceType serverType, const std::string& ip) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::SERVER_INFO);
|
||||
|
||||
bitStream.Write(port);
|
||||
bitStream.Write(zoneID);
|
||||
bitStream.Write(instanceID);
|
||||
bitStream.Write(serverType);
|
||||
bitStream.Write(LUString(ip));
|
||||
}
|
||||
|
||||
// dMasterServer/MasterServer.cpp: a server went offline (to the dashboard)
|
||||
inline void WriteServerOffline(RakNet::BitStream& bitStream, ServiceType type) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::SERVER_INFO);
|
||||
bitStream.Write<uint32_t>(0);
|
||||
bitStream.Write<uint32_t>(0);
|
||||
bitStream.Write<uint32_t>(0);
|
||||
bitStream.Write(type);
|
||||
bitStream.Write(LUString("offline"));
|
||||
}
|
||||
|
||||
// dMasterServer/MasterServer.cpp: a world disconnected (to the dashboard)
|
||||
inline void WriteWorldShutDown(RakNet::BitStream& bitStream, LWOMAPID mapID, LWOINSTANCEID instanceID) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::SHUTDOWN_RESPONSE);
|
||||
bitStream.Write(mapID);
|
||||
bitStream.Write(instanceID);
|
||||
}
|
||||
|
||||
// dMasterServer/MasterServer.cpp: WORLD_READY (to the dashboard)
|
||||
inline void WriteWorldReadyInfo(RakNet::BitStream& bitStream, LWOMAPID zoneID, LWOINSTANCEID instanceID, LWOCLONEID cloneID, const std::string& ip, uint32_t port, bool isPrivate) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::WORLD_READY);
|
||||
bitStream.Write(zoneID);
|
||||
bitStream.Write(instanceID);
|
||||
bitStream.Write(cloneID);
|
||||
bitStream.Write(LUString(ip));
|
||||
bitStream.Write(port);
|
||||
bitStream.Write<uint8_t>(isPrivate ? 1 : 0);
|
||||
}
|
||||
|
||||
// dMasterServer/MasterServer.cpp SET_SESSION_KEY, and dNet/AuthPackets.cpp
|
||||
inline void WriteSessionKey(RakNet::BitStream& bitStream, MessageType::Master type, uint32_t sessionKey, const LUString& username) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, type);
|
||||
bitStream.Write(sessionKey);
|
||||
bitStream.Write(username);
|
||||
}
|
||||
|
||||
// dMasterServer/MasterServer.cpp REQUEST_SESSION_KEY
|
||||
inline void WriteSessionKeyResponse(RakNet::BitStream& bitStream, uint32_t sessionKey, const LUWString& username) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::SESSION_KEY_RESPONSE);
|
||||
bitStream.Write(sessionKey);
|
||||
bitStream.Write(username);
|
||||
}
|
||||
|
||||
// dWorldServer/WorldServer.cpp
|
||||
inline void WriteRequestSessionKey(RakNet::BitStream& bitStream, const LUWString& username) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::REQUEST_SESSION_KEY);
|
||||
bitStream.Write(username);
|
||||
}
|
||||
|
||||
// dWorldServer/WorldServer.cpp and dMasterServer/MasterServer.cpp (PLAYER_ADDED and PLAYER_REMOVED)
|
||||
inline void WritePlayerCount(RakNet::BitStream& bitStream, MessageType::Master type, LWOMAPID zoneID, LWOINSTANCEID instanceID) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, type);
|
||||
bitStream.Write<LWOMAPID>(zoneID);
|
||||
bitStream.Write<LWOINSTANCEID>(instanceID);
|
||||
}
|
||||
|
||||
// dMasterServer/InstanceManager.cpp and dWorldServer/WorldServer.cpp (AFFIRM_TRANSFER_REQUEST and _RESPONSE)
|
||||
inline void WriteAffirmTransfer(RakNet::BitStream& bitStream, MessageType::Master type, uint64_t requestID) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, type);
|
||||
|
||||
bitStream.Write(requestID);
|
||||
}
|
||||
|
||||
// Messages with nothing after the header: SHUTDOWN, SHUTDOWN_RESPONSE (world), SHUTDOWN_UNIVERSE,
|
||||
// REQUEST_SERVER_LIST, CONFIG_RELOAD, DASHBOARD_SHUTDOWN
|
||||
inline void WriteEmpty(RakNet::BitStream& bitStream, MessageType::Master type) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, type);
|
||||
}
|
||||
|
||||
// dGame/dComponents/RocketLaunchpadControlComponent.cpp
|
||||
inline void TellMasterToPrepZone(RakNet::BitStream& bitStream, int zoneID) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::PREP_ZONE);
|
||||
bitStream.Write(zoneID);
|
||||
}
|
||||
|
||||
// dDashboardServer/routes/LiveWorld.cpp
|
||||
inline void WriteInstanceShutdown(RakNet::BitStream& bitStream, uint32_t zone, uint32_t instance) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::INSTANCE_SHUTDOWN);
|
||||
bitStream.Write(zone);
|
||||
bitStream.Write(instance);
|
||||
}
|
||||
|
||||
struct ServerListInstance {
|
||||
LWOMAPID mapID{};
|
||||
LWOINSTANCEID instanceID{};
|
||||
LWOCLONEID cloneID{};
|
||||
uint32_t players{};
|
||||
std::string ip;
|
||||
uint32_t port{};
|
||||
bool isPrivate{};
|
||||
};
|
||||
|
||||
// dMasterServer/MasterServer.cpp REQUEST_SERVER_LIST, with the ready instances passed in
|
||||
inline void WriteServerList(RakNet::BitStream& bitStream, bool authOnline, bool chatOnline, const std::vector<ServerListInstance>& instances) {
|
||||
BitStreamUtils::WriteHeader(bitStream, ServiceType::MASTER, MessageType::Master::SERVER_LIST_RESPONSE);
|
||||
|
||||
bitStream.Write<uint8_t>(authOnline ? 1 : 0);
|
||||
bitStream.Write<uint8_t>(chatOnline ? 1 : 0);
|
||||
|
||||
uint32_t instanceCount = instances.size();
|
||||
bitStream.Write(instanceCount);
|
||||
|
||||
for (const auto& inst : instances) {
|
||||
bitStream.Write(inst.mapID);
|
||||
bitStream.Write(inst.instanceID);
|
||||
bitStream.Write(inst.cloneID);
|
||||
bitStream.Write<uint32_t>(inst.players);
|
||||
bitStream.Write(LUString(inst.ip));
|
||||
bitStream.Write(inst.port);
|
||||
bitStream.Write<uint8_t>(inst.isPrivate ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Readers. The stream is past the packet header.
|
||||
|
||||
struct TransferRequestRead {
|
||||
uint64_t requestID = 0;
|
||||
uint8_t mythranShift = false;
|
||||
uint32_t zoneID = 0;
|
||||
uint32_t zoneClone = 0;
|
||||
Stamps stamps;
|
||||
};
|
||||
|
||||
// dMasterServer/MasterServer.cpp REQUEST_ZONE_TRANSFER
|
||||
inline TransferRequestRead ReadZoneTransferRequest(RakNet::BitStream& inStream) {
|
||||
uint64_t requestID = 0;
|
||||
uint8_t mythranShift = false;
|
||||
uint32_t zoneID = 0;
|
||||
uint32_t zoneClone = 0;
|
||||
|
||||
inStream.Read(requestID);
|
||||
inStream.Read(mythranShift);
|
||||
inStream.Read(zoneID);
|
||||
inStream.Read(zoneClone);
|
||||
// The login stamps travelling with the request (see Stamps.h)
|
||||
Stamps stamps;
|
||||
if (!stamps.Deserialize(inStream)) stamps = {};
|
||||
return { requestID, mythranShift, zoneID, zoneClone, stamps };
|
||||
}
|
||||
|
||||
struct TransferResponseRead {
|
||||
uint64_t requestID{};
|
||||
bool mythranShift{};
|
||||
uint32_t zoneID{};
|
||||
uint32_t zoneInstance{};
|
||||
uint32_t zoneClone{};
|
||||
uint16_t serverPort{};
|
||||
std::string serverIP;
|
||||
Stamps stamps;
|
||||
};
|
||||
|
||||
// dNet/ZoneInstanceManager.cpp HandleRequestZoneTransferResponse
|
||||
inline TransferResponseRead ReadZoneTransferResponse(RakNet::BitStream& inStream) {
|
||||
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 = {};
|
||||
return { requestID, mythranShift, zoneID, zoneInstance, zoneClone, serverPort, serverIP.string, stamps };
|
||||
}
|
||||
|
||||
struct ServerInfoRead {
|
||||
uint32_t theirPort = 0;
|
||||
uint32_t theirZoneID = 0;
|
||||
uint32_t theirInstanceID = 0;
|
||||
ServiceType theirServerType;
|
||||
std::string theirIP;
|
||||
};
|
||||
|
||||
// dMasterServer/MasterServer.cpp and dDashboardServer/DashboardServer.cpp SERVER_INFO
|
||||
inline ServerInfoRead ReadServerInfo(RakNet::BitStream& inStream) {
|
||||
uint32_t theirPort = 0;
|
||||
uint32_t theirZoneID = 0;
|
||||
uint32_t theirInstanceID = 0;
|
||||
ServiceType theirServerType;
|
||||
LUString theirIP;
|
||||
|
||||
inStream.Read(theirPort);
|
||||
inStream.Read(theirZoneID);
|
||||
inStream.Read(theirInstanceID);
|
||||
inStream.Read(theirServerType);
|
||||
inStream.Read(theirIP);
|
||||
return { theirPort, theirZoneID, theirInstanceID, theirServerType, theirIP.string };
|
||||
}
|
||||
|
||||
// dMasterServer/MasterServer.cpp CREATE_PRIVATE_ZONE
|
||||
inline std::pair<std::pair<uint32_t, LWOCLONEID>, std::string> ReadCreatePrivateZone(RakNet::BitStream& inStream) {
|
||||
uint32_t mapId;
|
||||
LWOCLONEID cloneId;
|
||||
std::string password;
|
||||
|
||||
inStream.Read(mapId);
|
||||
inStream.Read(cloneId);
|
||||
|
||||
uint32_t len;
|
||||
inStream.Read<uint32_t>(len);
|
||||
len = std::min<uint32_t>(len, 50); // cap the master password at 50 characters
|
||||
|
||||
for (uint32_t i = 0; len > i; i++) {
|
||||
char character;
|
||||
inStream.Read<char>(character);
|
||||
password += character;
|
||||
}
|
||||
return { { mapId, cloneId }, password };
|
||||
}
|
||||
|
||||
// dMasterServer/MasterServer.cpp REQUEST_PRIVATE_ZONE
|
||||
inline std::pair<std::pair<uint64_t, uint8_t>, std::string> ReadRequestPrivateZone(RakNet::BitStream& inStream) {
|
||||
uint64_t requestID = 0;
|
||||
uint8_t mythranShift = false;
|
||||
|
||||
std::string password;
|
||||
|
||||
inStream.Read(requestID);
|
||||
inStream.Read(mythranShift);
|
||||
|
||||
uint32_t len;
|
||||
inStream.Read<uint32_t>(len);
|
||||
len = std::min<uint32_t>(len, 50);
|
||||
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
char character; inStream.Read<char>(character);
|
||||
password += character;
|
||||
}
|
||||
return { { requestID, mythranShift }, password };
|
||||
}
|
||||
|
||||
struct ServerListRead {
|
||||
uint8_t authOnline = 0;
|
||||
uint8_t chatOnline = 0;
|
||||
std::vector<ServerListInstance> instances;
|
||||
};
|
||||
|
||||
// dDashboardServer/DashboardServer.cpp SERVER_LIST_RESPONSE
|
||||
inline ServerListRead ReadServerList(RakNet::BitStream& inStream) {
|
||||
ServerListRead out;
|
||||
uint8_t authOnline = 0;
|
||||
uint8_t chatOnline = 0;
|
||||
uint32_t instanceCount = 0;
|
||||
|
||||
inStream.Read(authOnline);
|
||||
inStream.Read(chatOnline);
|
||||
inStream.Read(instanceCount);
|
||||
out.authOnline = authOnline;
|
||||
out.chatOnline = chatOnline;
|
||||
|
||||
for (uint32_t i = 0; i < instanceCount; i++) {
|
||||
ServerListInstance info;
|
||||
LUString ip;
|
||||
// Same types as MasterServer writes them (map and instance IDs are 16 bits)
|
||||
LWOMAPID mapID = 0;
|
||||
LWOINSTANCEID instanceID = 0;
|
||||
inStream.Read(mapID);
|
||||
inStream.Read(instanceID);
|
||||
info.mapID = mapID;
|
||||
info.instanceID = instanceID;
|
||||
inStream.Read(info.cloneID);
|
||||
inStream.Read(info.players);
|
||||
inStream.Read(ip);
|
||||
info.ip = ip.string;
|
||||
inStream.Read(info.port);
|
||||
uint8_t isPrivate = 0;
|
||||
inStream.Read(isPrivate);
|
||||
info.isPrivate = isPrivate != 0;
|
||||
out.instances.push_back(info);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// dDashboardServer/DashboardServer.cpp WORLD_READY
|
||||
inline ServerListInstance ReadWorldReadyInfo(RakNet::BitStream& inStream) {
|
||||
LWOMAPID zoneID;
|
||||
LWOINSTANCEID instanceID;
|
||||
LWOCLONEID cloneID;
|
||||
LUString ip;
|
||||
uint32_t port;
|
||||
uint8_t isPrivate;
|
||||
|
||||
inStream.Read(zoneID);
|
||||
inStream.Read(instanceID);
|
||||
inStream.Read(cloneID);
|
||||
inStream.Read(ip);
|
||||
inStream.Read(port);
|
||||
inStream.Read(isPrivate);
|
||||
return { zoneID, instanceID, cloneID, 0, ip.string, port, isPrivate != 0 };
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MASTERPACKETSLEGACY_H
|
||||
472
tests/dGameTests/dNetTests/MasterPacketsTests.cpp
Normal file
472
tests/dGameTests/dNetTests/MasterPacketsTests.cpp
Normal file
@@ -0,0 +1,472 @@
|
||||
#include "MasterPackets.h"
|
||||
#include "master/DashboardMessages.h"
|
||||
#include "master/DataChanged.h"
|
||||
#include "master/InstanceMigration.h"
|
||||
#include "master/MessageCapture.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "PacketDispatcher.h"
|
||||
#include "PacketTestUtils.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "dGameMessagesTests/GameMessageTestUtils.h"
|
||||
#include "Legacy/MasterPacketsLegacy.h"
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace PacketTestUtils;
|
||||
using GameMessageTestUtils::RoundTrip;
|
||||
using GameMessageTestUtils::ExpectTruncatedFails;
|
||||
|
||||
namespace {
|
||||
const std::array<std::string, 4> g_Strings = { "", "localhost", "a password that is longer than fifty characters, which master cuts", "10.0.0.255" };
|
||||
const std::array<uint64_t, 3> g_RequestIds = { 0, 1, 0x0102030405060708ULL };
|
||||
const std::array<uint32_t, 3> g_Numbers = { 0, 1100, 0xFFFFFFFF };
|
||||
|
||||
PacketBytes StructPacket(const LUBitStream& msg) {
|
||||
RakNet::BitStream bitStream;
|
||||
msg.WritePacket(bitStream);
|
||||
return FromBitStream(bitStream);
|
||||
}
|
||||
|
||||
PacketBytes Written(const std::function<void(RakNet::BitStream&)>& write) {
|
||||
RakNet::BitStream bitStream;
|
||||
write(bitStream);
|
||||
return FromBitStream(bitStream);
|
||||
}
|
||||
|
||||
// A stream positioned after the header of msg's packet
|
||||
void LoadPayload(RakNet::BitStream& bitStream, const LUBitStream& msg) {
|
||||
msg.WritePacket(bitStream);
|
||||
bitStream.IgnoreBytes(8);
|
||||
}
|
||||
|
||||
// The old way the dashboard and migration structs were sent: the header, then Serialize
|
||||
template<typename T>
|
||||
void ExpectHeaderThenSerialize(const T& msg, MessageType::Master id) {
|
||||
RakNet::BitStream old;
|
||||
BitStreamUtils::WriteHeader(old, ServiceType::MASTER, id);
|
||||
msg.Serialize(old);
|
||||
EXPECT_PACKET_EQ(FromBitStream(old), StructPacket(msg));
|
||||
RoundTrip(msg);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MasterPacketsTests, ZoneTransferMatchesLegacy) {
|
||||
Stamps someStamps;
|
||||
someStamps.list.emplace_back(eStamps::PASSPORT_AUTH_START, 0, 1700000000);
|
||||
someStamps.list.emplace_back(eStamps::PASSPORT_AUTH_WORLD_PACKET_RECEIVED, 1100, 1700000001);
|
||||
for (const auto& stamps : { Stamps{}, someStamps })
|
||||
for (const auto requestID : g_RequestIds) {
|
||||
for (const bool mythranShift : { false, true }) {
|
||||
for (const auto zone : g_Numbers) {
|
||||
MasterPackets::RequestZoneTransfer request;
|
||||
request.requestID = requestID;
|
||||
request.mythranShift = mythranShift;
|
||||
request.zoneID = zone;
|
||||
request.cloneID = zone ^ 0x5555;
|
||||
request.stamps = stamps;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::SendZoneTransferRequest(b, requestID, mythranShift, zone, zone ^ 0x5555, stamps); }), StructPacket(request));
|
||||
|
||||
RakNet::BitStream stream; LoadPayload(stream, request);
|
||||
const auto legacy = LegacyMaster::ReadZoneTransferRequest(stream);
|
||||
const auto copy = RoundTrip(request);
|
||||
EXPECT_EQ(legacy.requestID, copy.requestID);
|
||||
EXPECT_EQ(legacy.mythranShift, copy.mythranShift);
|
||||
EXPECT_EQ(legacy.zoneID, copy.zoneID);
|
||||
EXPECT_EQ(legacy.zoneClone, copy.cloneID);
|
||||
EXPECT_EQ(legacy.stamps.size(), copy.stamps.size());
|
||||
|
||||
for (const auto& ip : g_Strings) {
|
||||
for (const uint32_t port : { 0u, 2001u, 70000u }) {
|
||||
MasterPackets::RequestZoneTransferResponse response;
|
||||
response.requestID = requestID;
|
||||
response.mythranShift = mythranShift;
|
||||
response.zoneID = zone;
|
||||
response.zoneInstance = zone / 2;
|
||||
response.zoneClone = zone / 3;
|
||||
response.serverPort = static_cast<uint16_t>(port);
|
||||
response.serverIP = LUString(ip, 255);
|
||||
response.stamps = stamps;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::SendZoneTransferResponse(b, requestID, mythranShift, zone, zone / 2, zone / 3, ip, port, stamps); }), StructPacket(response));
|
||||
|
||||
RakNet::BitStream responseStream; LoadPayload(responseStream, response);
|
||||
const auto legacyResponse = LegacyMaster::ReadZoneTransferResponse(responseStream);
|
||||
const auto responseCopy = RoundTrip(response);
|
||||
EXPECT_EQ(legacyResponse.requestID, responseCopy.requestID);
|
||||
EXPECT_EQ(legacyResponse.mythranShift, responseCopy.mythranShift > 0);
|
||||
EXPECT_EQ(legacyResponse.zoneInstance, responseCopy.zoneInstance);
|
||||
EXPECT_EQ(legacyResponse.serverPort, responseCopy.serverPort);
|
||||
EXPECT_EQ(legacyResponse.serverIP, responseCopy.serverIP.string);
|
||||
EXPECT_EQ(legacyResponse.stamps.size(), responseCopy.stamps.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Senders without the login stamps (older servers) still read, with no stamps; a cut off fixed part does not
|
||||
TEST(MasterPacketsTests, ZoneTransferStampsAreOptional) {
|
||||
RakNet::BitStream noStamps;
|
||||
noStamps.Write<uint64_t>(5);
|
||||
noStamps.Write<uint8_t>(1);
|
||||
noStamps.Write<uint32_t>(1100);
|
||||
noStamps.Write<uint32_t>(0);
|
||||
MasterPackets::RequestZoneTransfer request;
|
||||
ASSERT_TRUE(request.Deserialize(noStamps));
|
||||
EXPECT_EQ(request.requestID, 5u);
|
||||
EXPECT_TRUE(request.stamps.empty());
|
||||
|
||||
MasterPackets::RequestZoneTransfer full;
|
||||
full.requestID = 5;
|
||||
RakNet::BitStream all;
|
||||
full.Serialize(all);
|
||||
for (uint32_t bits = 0; bits < 8 * (8 + 1 + 4 + 4); bits++) {
|
||||
RakNet::BitStream prefix;
|
||||
prefix.WriteBits(all.GetData(), bits, false);
|
||||
MasterPackets::RequestZoneTransfer copy;
|
||||
EXPECT_FALSE(copy.Deserialize(prefix)) << bits;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MasterPacketsTests, ServerInfoMatchesLegacy) {
|
||||
for (const auto type : { ServiceType::AUTH, ServiceType::CHAT, ServiceType::WORLD, ServiceType::DASHBOARD }) {
|
||||
for (const auto& ip : g_Strings) {
|
||||
if (ip.size() > 33) continue; // LUString(33)
|
||||
for (const int instance : { 0, 7, -1 }) {
|
||||
MasterPackets::ServerInfo info;
|
||||
info.port = 2001;
|
||||
info.zoneID = 1100;
|
||||
info.instanceID = static_cast<uint32_t>(instance);
|
||||
info.serverType = type;
|
||||
info.ip = LUString(ip);
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::SendServerInfo(b, 2001, 1100u, instance, type, ip); }), StructPacket(info));
|
||||
|
||||
RakNet::BitStream stream; LoadPayload(stream, info);
|
||||
const auto legacy = LegacyMaster::ReadServerInfo(stream);
|
||||
const auto copy = RoundTrip(info);
|
||||
EXPECT_EQ(legacy.theirPort, copy.port);
|
||||
EXPECT_EQ(legacy.theirInstanceID, copy.instanceID);
|
||||
EXPECT_EQ(legacy.theirServerType, copy.serverType);
|
||||
EXPECT_EQ(legacy.theirIP, copy.ip.string);
|
||||
ExpectTruncatedFails(info);
|
||||
}
|
||||
}
|
||||
MasterPackets::ServerInfo offline;
|
||||
offline.serverType = type;
|
||||
offline.ip = LUString("offline");
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WriteServerOffline(b, type); }), StructPacket(offline));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MasterPacketsTests, SessionKeysMatchLegacy) {
|
||||
for (const uint32_t key : { 0u, 12345u, 0xFFFFFFFFu }) {
|
||||
for (const auto& name : g_Strings) {
|
||||
if (name.size() > 33) continue;
|
||||
MasterPackets::SetSessionKey set;
|
||||
set.sessionKey = key;
|
||||
set.username = LUString(name);
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WriteSessionKey(b, MessageType::Master::SET_SESSION_KEY, key, LUString(name)); }), StructPacket(set));
|
||||
RoundTrip(set);
|
||||
ExpectTruncatedFails(set);
|
||||
|
||||
MasterPackets::NewSessionAlert alert;
|
||||
alert.sessionKey = key;
|
||||
alert.username = LUString(name);
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WriteSessionKey(b, MessageType::Master::NEW_SESSION_ALERT, key, LUString(name)); }), StructPacket(alert));
|
||||
RoundTrip(alert);
|
||||
|
||||
MasterPackets::SessionKeyResponse response;
|
||||
response.sessionKey = key;
|
||||
response.username = LUWString(name);
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WriteSessionKeyResponse(b, key, LUWString(name)); }), StructPacket(response));
|
||||
RoundTrip(response);
|
||||
ExpectTruncatedFails(response);
|
||||
|
||||
MasterPackets::RequestSessionKey request;
|
||||
request.username = LUWString(name);
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WriteRequestSessionKey(b, LUWString(name)); }), StructPacket(request));
|
||||
RoundTrip(request);
|
||||
ExpectTruncatedFails(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MasterPacketsTests, PrivateZonesMatchLegacy) {
|
||||
for (const auto& password : g_Strings) {
|
||||
MasterPackets::CreatePrivateZone create;
|
||||
create.zoneID = 1150;
|
||||
create.cloneID = 0xAABBCCDD;
|
||||
create.password = password;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::SendZoneCreatePrivate(b, 1150, 0xAABBCCDD, password); }), StructPacket(create));
|
||||
|
||||
RakNet::BitStream stream; LoadPayload(stream, create);
|
||||
const auto legacy = LegacyMaster::ReadCreatePrivateZone(stream);
|
||||
RakNet::BitStream stream2; LoadPayload(stream2, create);
|
||||
MasterPackets::CreatePrivateZone read;
|
||||
ASSERT_TRUE(read.Deserialize(stream2));
|
||||
EXPECT_EQ(legacy.first.first, read.zoneID);
|
||||
EXPECT_EQ(legacy.first.second, read.cloneID);
|
||||
EXPECT_EQ(legacy.second, read.password); // both cut at 50 characters
|
||||
if (password.size() <= 50) RoundTrip(create);
|
||||
|
||||
for (const bool mythranShift : { false, true }) {
|
||||
MasterPackets::RequestPrivateZone request;
|
||||
request.requestID = 0x0102030405060708ULL;
|
||||
request.mythranShift = mythranShift;
|
||||
request.password = password;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::SendZoneRequestPrivate(b, 0x0102030405060708ULL, mythranShift, password); }), StructPacket(request));
|
||||
|
||||
RakNet::BitStream requestStream; LoadPayload(requestStream, request);
|
||||
const auto legacyRequest = LegacyMaster::ReadRequestPrivateZone(requestStream);
|
||||
RakNet::BitStream requestStream2; LoadPayload(requestStream2, request);
|
||||
MasterPackets::RequestPrivateZone readRequest;
|
||||
ASSERT_TRUE(readRequest.Deserialize(requestStream2));
|
||||
EXPECT_EQ(legacyRequest.first.first, readRequest.requestID);
|
||||
EXPECT_EQ(legacyRequest.first.second, readRequest.mythranShift);
|
||||
EXPECT_EQ(legacyRequest.second, readRequest.password);
|
||||
if (password.size() <= 50) RoundTrip(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MasterPacketsTests, WorldStateMatchesLegacy) {
|
||||
for (const LWOMAPID zone : { static_cast<LWOMAPID>(0), static_cast<LWOMAPID>(1100), static_cast<LWOMAPID>(0xFFFF) }) {
|
||||
for (const LWOINSTANCEID instance : { static_cast<LWOINSTANCEID>(0), static_cast<LWOINSTANCEID>(3), static_cast<LWOINSTANCEID>(0xFFFF) }) {
|
||||
MasterPackets::WorldReady ready;
|
||||
ready.zoneID = zone;
|
||||
ready.instanceID = instance;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::SendWorldReady(b, zone, instance); }), StructPacket(ready));
|
||||
RoundTrip(ready);
|
||||
ExpectTruncatedFails(ready);
|
||||
|
||||
MasterPackets::PlayerAdded added;
|
||||
added.zoneID = zone;
|
||||
added.instanceID = instance;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WritePlayerCount(b, MessageType::Master::PLAYER_ADDED, zone, instance); }), StructPacket(added));
|
||||
RoundTrip(added);
|
||||
ExpectTruncatedFails(added);
|
||||
|
||||
MasterPackets::PlayerRemoved removed;
|
||||
removed.zoneID = zone;
|
||||
removed.instanceID = instance;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WritePlayerCount(b, MessageType::Master::PLAYER_REMOVED, zone, instance); }), StructPacket(removed));
|
||||
RoundTrip(removed);
|
||||
|
||||
MasterPackets::WorldShutDown shutDown;
|
||||
shutDown.zoneID = zone;
|
||||
shutDown.instanceID = instance;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WriteWorldShutDown(b, zone, instance); }), StructPacket(shutDown));
|
||||
RoundTrip(shutDown);
|
||||
|
||||
for (const bool isPrivate : { false, true }) {
|
||||
MasterPackets::WorldReadyInfo info;
|
||||
info.zoneID = zone;
|
||||
info.instanceID = instance;
|
||||
info.cloneID = 0xDEADBEEF;
|
||||
info.ip = LUString("127.0.0.1");
|
||||
info.port = 3000;
|
||||
info.isPrivate = isPrivate ? 1 : 0;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WriteWorldReadyInfo(b, zone, instance, 0xDEADBEEF, "127.0.0.1", 3000, isPrivate); }), StructPacket(info));
|
||||
RakNet::BitStream stream; LoadPayload(stream, info);
|
||||
const auto legacy = LegacyMaster::ReadWorldReadyInfo(stream);
|
||||
const auto copy = RoundTrip(info);
|
||||
EXPECT_EQ(legacy.cloneID, copy.cloneID);
|
||||
EXPECT_EQ(legacy.ip, copy.ip.string);
|
||||
EXPECT_EQ(legacy.isPrivate, copy.isPrivate != 0);
|
||||
ExpectTruncatedFails(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const int zone : { 0, 1100, -1 }) {
|
||||
MasterPackets::PrepZone prep;
|
||||
prep.zoneID = zone;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::TellMasterToPrepZone(b, zone); }), StructPacket(prep));
|
||||
RoundTrip(prep);
|
||||
ExpectTruncatedFails(prep);
|
||||
}
|
||||
|
||||
for (const auto requestID : g_RequestIds) {
|
||||
MasterPackets::AffirmTransferRequest request;
|
||||
request.requestID = requestID;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WriteAffirmTransfer(b, MessageType::Master::AFFIRM_TRANSFER_REQUEST, requestID); }), StructPacket(request));
|
||||
RoundTrip(request);
|
||||
ExpectTruncatedFails(request);
|
||||
MasterPackets::AffirmTransferResponse response;
|
||||
response.requestID = requestID;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WriteAffirmTransfer(b, MessageType::Master::AFFIRM_TRANSFER_RESPONSE, requestID); }), StructPacket(response));
|
||||
RoundTrip(response);
|
||||
}
|
||||
|
||||
for (const auto zone : g_Numbers) {
|
||||
MasterPackets::InstanceShutdown shutdown;
|
||||
shutdown.zoneID = zone;
|
||||
shutdown.instanceID = zone / 7;
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WriteInstanceShutdown(b, zone, zone / 7); }), StructPacket(shutdown));
|
||||
RoundTrip(shutdown);
|
||||
ExpectTruncatedFails(shutdown);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MasterPacketsTests, EmptyMessagesMatchLegacy) {
|
||||
EXPECT_PACKET_EQ(Written([](RakNet::BitStream& b) { LegacyMaster::WriteEmpty(b, MessageType::Master::SHUTDOWN); }), StructPacket(MasterPackets::Shutdown()));
|
||||
EXPECT_PACKET_EQ(Written([](RakNet::BitStream& b) { LegacyMaster::WriteEmpty(b, MessageType::Master::SHUTDOWN_RESPONSE); }), StructPacket(MasterPackets::ShutdownResponse()));
|
||||
EXPECT_PACKET_EQ(Written([](RakNet::BitStream& b) { LegacyMaster::WriteEmpty(b, MessageType::Master::SHUTDOWN_UNIVERSE); }), StructPacket(MasterPackets::ShutdownUniverse()));
|
||||
EXPECT_PACKET_EQ(Written([](RakNet::BitStream& b) { LegacyMaster::WriteEmpty(b, MessageType::Master::REQUEST_SERVER_LIST); }), StructPacket(MasterPackets::RequestServerList()));
|
||||
EXPECT_PACKET_EQ(Written([](RakNet::BitStream& b) { LegacyMaster::WriteEmpty(b, MessageType::Master::CONFIG_RELOAD); }), StructPacket(MasterPackets::ConfigReload()));
|
||||
EXPECT_PACKET_EQ(Written([](RakNet::BitStream& b) { LegacyMaster::WriteEmpty(b, MessageType::Master::DASHBOARD_SHUTDOWN); }), StructPacket(MasterPackets::DashboardShutdown()));
|
||||
}
|
||||
|
||||
TEST(MasterPacketsTests, Golden) {
|
||||
MasterPackets::PlayerAdded added;
|
||||
added.zoneID = 1100;
|
||||
added.instanceID = 2;
|
||||
// 53 | MASTER (6) | PLAYER_ADDED (7) | pad | u16 1100 | u16 2
|
||||
EXPECT_PACKET_EQ(FromHex("53 06 00 07 00 00 00 00 4c 04 02 00"), StructPacket(added));
|
||||
|
||||
MasterPackets::AffirmTransferRequest affirm;
|
||||
affirm.requestID = 0x0102030405060708ULL;
|
||||
// AFFIRM_TRANSFER_REQUEST (17) | u64
|
||||
EXPECT_PACKET_EQ(FromHex("53 06 00 11 00 00 00 00 08 07 06 05 04 03 02 01"), StructPacket(affirm));
|
||||
}
|
||||
|
||||
TEST(MasterPacketsTests, ServerListMatchesLegacy) {
|
||||
for (size_t count = 0; count <= 3; count++) {
|
||||
std::vector<LegacyMaster::ServerListInstance> instances;
|
||||
MasterPackets::ServerListResponse response;
|
||||
response.authOnline = 1;
|
||||
response.chatOnline = count % 2;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
LegacyMaster::ServerListInstance instance{ static_cast<LWOMAPID>(1000 + i), static_cast<LWOINSTANCEID>(i), static_cast<LWOCLONEID>(i * 7), static_cast<uint32_t>(i * 3), "10.0.0." + std::to_string(i), static_cast<uint32_t>(3000 + i), i == 2 };
|
||||
instances.push_back(instance);
|
||||
auto& entry = response.instances.emplace_back();
|
||||
entry.mapID = instance.mapID;
|
||||
entry.instanceID = instance.instanceID;
|
||||
entry.cloneID = instance.cloneID;
|
||||
entry.players = instance.players;
|
||||
entry.ip = LUString(instance.ip);
|
||||
entry.port = instance.port;
|
||||
entry.isPrivate = instance.isPrivate ? 1 : 0;
|
||||
}
|
||||
EXPECT_PACKET_EQ(Written([&](RakNet::BitStream& b) { LegacyMaster::WriteServerList(b, true, count % 2, instances); }), StructPacket(response));
|
||||
|
||||
RakNet::BitStream stream; LoadPayload(stream, response);
|
||||
const auto legacy = LegacyMaster::ReadServerList(stream);
|
||||
const auto copy = RoundTrip(response);
|
||||
ASSERT_EQ(legacy.instances.size(), copy.instances.size());
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
EXPECT_EQ(legacy.instances[i].mapID, copy.instances[i].mapID);
|
||||
EXPECT_EQ(legacy.instances[i].players, copy.instances[i].players);
|
||||
EXPECT_EQ(legacy.instances[i].ip, copy.instances[i].ip.string);
|
||||
EXPECT_EQ(legacy.instances[i].isPrivate, copy.instances[i].isPrivate != 0);
|
||||
}
|
||||
ExpectTruncatedFails(response);
|
||||
}
|
||||
}
|
||||
|
||||
// The dashboard and migration structs kept their payloads; they now carry their own header
|
||||
TEST(MasterPacketsTests, DashboardAndMigrationStructsKeepTheirBytes) {
|
||||
PlayerActionRequest action;
|
||||
action.requestId = 7;
|
||||
action.action = ePlayerAction::CHAT_MESSAGE;
|
||||
action.text = "hello";
|
||||
action.name = "[Discord] Bob";
|
||||
ExpectHeaderThenSerialize(action, MessageType::Master::PLAYER_ACTION);
|
||||
|
||||
PlayerActionResult result;
|
||||
result.requestId = 7;
|
||||
result.affected = 3;
|
||||
result.timedOut = true;
|
||||
ExpectHeaderThenSerialize(result, MessageType::Master::PLAYER_ACTION_RESULT);
|
||||
|
||||
DataChanged changed;
|
||||
changed.entries.push_back({ "characters", 42 });
|
||||
ExpectHeaderThenSerialize(changed, MessageType::Master::DATA_CHANGED);
|
||||
|
||||
PlayerPositions positions;
|
||||
positions.zoneId = 1100;
|
||||
positions.players.push_back({ 42, 1.0f, 2.0f, 3.0f });
|
||||
ExpectHeaderThenSerialize(positions, MessageType::Master::PLAYER_POSITIONS);
|
||||
|
||||
Announcement announcement;
|
||||
announcement.title = "Title";
|
||||
announcement.message = "Message";
|
||||
announcement.zones = { 1100, 1200 };
|
||||
ExpectHeaderThenSerialize(announcement, MessageType::Master::ANNOUNCE);
|
||||
|
||||
MessageCaptureControl control;
|
||||
control.captureId = 3;
|
||||
control.seconds = 60;
|
||||
control.only = { 1, 2 };
|
||||
ExpectHeaderThenSerialize(control, MessageType::Master::MESSAGE_CAPTURE_CONTROL);
|
||||
|
||||
MessageCaptureData data;
|
||||
data.captureId = 3;
|
||||
data.entries.resize(1);
|
||||
data.entries[0].payload = "abc";
|
||||
ExpectHeaderThenSerialize(data, MessageType::Master::MESSAGE_CAPTURE_DATA);
|
||||
|
||||
InstanceMigrationRequest migrate;
|
||||
migrate.requestId = 9;
|
||||
migrate.requestedBy = "GM";
|
||||
ExpectHeaderThenSerialize(migrate, MessageType::Master::INSTANCE_MIGRATE);
|
||||
|
||||
MigratePlayersOrder order;
|
||||
order.targetIp = "127.0.0.1";
|
||||
ExpectHeaderThenSerialize(order, MessageType::Master::MIGRATE_PLAYERS);
|
||||
|
||||
MigrationStatus status;
|
||||
status.message = "Moving players";
|
||||
ExpectHeaderThenSerialize(status, MessageType::Master::MIGRATE_STATUS);
|
||||
|
||||
CarriedPlayerState state;
|
||||
state.characterId = 42;
|
||||
ExpectHeaderThenSerialize(state, MessageType::Master::MIGRATE_PLAYER_STATE);
|
||||
}
|
||||
|
||||
TEST(MasterPacketsTests, DispatcherRoutesAndDropsBadPackets) {
|
||||
// Dropped packets are logged
|
||||
auto* const previousLogger = Game::logger;
|
||||
Game::logger = new Logger("./testing.log", true, true);
|
||||
PacketDispatcher<MessageType::Master> handlers;
|
||||
int calls = 0;
|
||||
MasterPackets::PlayerAdded seen;
|
||||
handlers.On<MasterPackets::PlayerAdded>(MessageType::Master::PLAYER_ADDED, [&](const MasterPackets::PlayerAdded& msg, const SystemAddress&) { calls++; seen = msg; });
|
||||
|
||||
MasterPackets::PlayerAdded added;
|
||||
added.zoneID = 1100;
|
||||
added.instanceID = 4;
|
||||
RakNet::BitStream good;
|
||||
added.WritePacket(good);
|
||||
Packet packet{};
|
||||
packet.data = good.GetData();
|
||||
packet.length = good.GetNumberOfBytesUsed();
|
||||
EXPECT_TRUE(handlers.Dispatch(&packet, ServiceType::MASTER));
|
||||
EXPECT_EQ(calls, 1);
|
||||
EXPECT_EQ(seen.zoneID, 1100);
|
||||
EXPECT_EQ(seen.instanceID, 4);
|
||||
|
||||
// Wrong service: not ours
|
||||
EXPECT_FALSE(handlers.Dispatch(&packet, ServiceType::CHAT));
|
||||
|
||||
// Truncated: handled (dropped), the handler isn't called
|
||||
packet.length -= 1;
|
||||
EXPECT_TRUE(handlers.Dispatch(&packet, ServiceType::MASTER));
|
||||
EXPECT_EQ(calls, 1);
|
||||
|
||||
// No handler for this ID
|
||||
MasterPackets::PlayerRemoved removed;
|
||||
RakNet::BitStream other;
|
||||
removed.WritePacket(other);
|
||||
packet.data = other.GetData();
|
||||
packet.length = other.GetNumberOfBytesUsed();
|
||||
EXPECT_FALSE(handlers.Dispatch(&packet, ServiceType::MASTER));
|
||||
|
||||
delete Game::logger;
|
||||
Game::logger = previousLogger;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "DashboardMessages.h"
|
||||
#include "master/DashboardMessages.h"
|
||||
#include "EconomyScan.h"
|
||||
#include "IDashboardAdmin.h"
|
||||
#include "Totp.h"
|
||||
@@ -192,7 +192,9 @@ TEST(DashboardMessagesTests, PlayerPositionsRoundTrip) {
|
||||
}
|
||||
|
||||
TEST(DashboardMessagesTests, AnnouncementRoundTripAndLimits) {
|
||||
Announcement announcement{ "Title", "Hello everyone" };
|
||||
Announcement announcement;
|
||||
announcement.title = "Title";
|
||||
announcement.message = "Hello everyone";
|
||||
RakNet::BitStream stream;
|
||||
announcement.Serialize(stream);
|
||||
Announcement read;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "MessageCapture.h"
|
||||
#include "master/MessageCapture.h"
|
||||
#include "InspectorFormat.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "PlayerAction.h"
|
||||
#include "DataChanged.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "master/DataChanged.h"
|
||||
|
||||
TEST(PlayerActionTest, RequestRoundTrip) {
|
||||
PlayerActionRequest request;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "BackupFiles.h"
|
||||
#include "GameLabels.h"
|
||||
#include "LoginThrottle.h"
|
||||
#include "PlayerAction.h"
|
||||
#include "master/PlayerAction.h"
|
||||
#include "ServerState.h"
|
||||
|
||||
// ---- Failed sign-in throttling (DashboardAuthService) ----
|
||||
|
||||
Reference in New Issue
Block a user