mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
Every game message still written or read by hand is now a NetGameMsg with Serialize and Deserialize, in per-domain files: - MovementMessages: teleport, platforms (resync and its request), orient to angle, node rotation lock, gravity scale, jetpack mode, control scheme, respawn checkpoint, rails (set/start, ready, cancel, arrived), mount inventory ID, dismount complete, possession ack, ghost reference override and position, camera cycling (eCameraTargetCyclingMode moves here). - ZoneMessages: player loaded (the old PLAYER_LOADED case), ready for updates, player ready, restore to post load stats, server done loading, invalid zone transfer list, zone summary display/dismissed, level processing complete, object world state, and the localized announcement WorldMigration wrote by hand. - PlayerMessages: chat mode, GM level, LEGO score, currency, reputation, GM invis, pickup currency, zone and player statistics, chat commands, bug reports, verify ack. - ObjectMessages: fire event client/server side, notify client (zone) object, notify object, script network vars, failed preconditions, terminate interaction, set name, request use, request server object info. - QuickBuildMessages: notify state, enable, cancel. - ActivityMessages gains match response/update/request, leaderboard request and data, shooting gallery score/rotation/fire, activity state change. - MissionMessages gains MissionDialogueCancelled (a no-op, as before). The wire structs left in GameMessages.h move to their domains (tooltip and emote to Effects, loot and item use to Inventory, model build to Building, behavior sound to Property, skill sets to Skill) and gain the missing direction. The dismount logic moves to PossessorComponent::OnDismountComplete. Every inbound message is registered in the GameMessageHandler map; the switch is gone, and GameMessages.cpp only holds the GameMsg/NetGameMsg base code. Call sites build the structs (NotifyClientObject, TerminateInteraction, Teleport, PlatformResync, FireEventClientSide, NotifyObject and NotifyClientZoneObject get convenience constructors like PlayFXEffect). Dead senders are dropped: SendSetShootingGalleryParams (no callers, field order was a guess), SendTeamPickupItem (the struct already existed), SendRequestActivitySummaryLeaderboardData (the struct covers it). Verified with RemainingMessagesTests: every old Send* function is frozen verbatim in Legacy/RemainingMessagesLegacy.h and compared byte for byte (same bits, destination and broadcast flag) over grids of inputs; every old Handle* read sequence is frozen as a Read* oracle and compared with the struct's Deserialize; round trips, truncation and a golden packet. PlayerLoaded (0x00dc36f0), SetGMLevel (0x00dd6230), MissionDialogueCancelled (0x00d9cc10) and LocalizedAnnouncementServerToSingleClient (0x00f23c50) were checked against the client. Behaviour notes: an inbound message that fails to deserialize is dropped, so ParseChatMessage over MAX_MESSAGE_LENGTH is dropped instead of truncated, and PLAYER_LOADED / READY_FOR_UPDATES / MISSION_DIALOGUE_CANCELLED now read their (unused) client fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
261 lines
7.5 KiB
C++
261 lines
7.5 KiB
C++
#ifndef GAMEMESSAGES_H
|
|
#define GAMEMESSAGES_H
|
|
|
|
#include "dCommonVars.h"
|
|
#include <map>
|
|
#include <type_traits>
|
|
#include <string>
|
|
#include <vector>
|
|
#include "eMovementPlatformState.h"
|
|
#include "NiPoint3.h"
|
|
#include "eEndBehavior.h"
|
|
#include "eCyclingMode.h"
|
|
#include "eLootSourceType.h"
|
|
#include "Brick.h"
|
|
#include "MessageType/Game.h"
|
|
#include "eGameMasterLevel.h"
|
|
#include "LDFFormat.h"
|
|
|
|
class AMFBaseValue;
|
|
class AMFArrayValue;
|
|
class Entity;
|
|
class Item;
|
|
class User;
|
|
class Leaderboard;
|
|
class TradeItem;
|
|
class LDFBaseData;
|
|
|
|
enum class eAnimationFlags : uint32_t;
|
|
|
|
enum class eUnequippableActiveType;
|
|
enum eInventoryType : uint32_t;
|
|
enum class eGameMasterLevel : uint8_t;
|
|
enum class eMatchUpdate : int32_t;
|
|
enum class eKillType : uint32_t;
|
|
enum class eObjectWorldState : uint32_t;
|
|
enum class eTerminateType : uint32_t;
|
|
enum class eControlScheme : uint32_t;
|
|
enum class eStateChangeType : uint32_t;
|
|
enum class ePetAbilityType : uint32_t;
|
|
enum class ePetTamingNotifyType : uint32_t;
|
|
enum class eUseItemResponse : uint32_t;
|
|
enum class eQuickBuildFailReason : uint32_t;
|
|
enum class eQuickBuildState : uint32_t;
|
|
enum class BehaviorSlot : int32_t;
|
|
enum class eVendorTransactionResult : uint32_t;
|
|
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t;
|
|
enum class eMissionState : int;
|
|
enum class AiState : uint32_t;
|
|
|
|
namespace GameMessages {
|
|
/**
|
|
* A server-internal event, delivered only to the handlers entities/components/scripts registered with
|
|
* RegisterMsg. It never goes on the wire: it has no Serialize and no way to send it to a client.
|
|
*/
|
|
struct GameMsg {
|
|
GameMsg(MessageType::Game gmId) : msgId{ gmId } {}
|
|
virtual ~GameMsg() = default;
|
|
|
|
// Delivers the message to the handlers registered on the target entity.
|
|
bool Send();
|
|
bool Send(const LWOOBJID _target);
|
|
|
|
MessageType::Game msgId;
|
|
LWOOBJID target{ LWOOBJID_EMPTY };
|
|
};
|
|
|
|
/**
|
|
* A game message that goes on the wire, to (Serialize) or from (Deserialize + Handle) a client.
|
|
* It is not a GameMsg, so it cannot be delivered to local handlers by mistake. When local handlers need to
|
|
* observe a network message, wrap it in a NetGameMsgEvent.
|
|
*/
|
|
struct NetGameMsg {
|
|
NetGameMsg(MessageType::Game gmId, eGameMasterLevel lvl) : msgId{ gmId }, requiredGmLevel{ lvl } {}
|
|
NetGameMsg(MessageType::Game gmId) : NetGameMsg(gmId, eGameMasterLevel::CIVILIAN) {}
|
|
virtual ~NetGameMsg() = default;
|
|
|
|
// Sends the message to the specified client or
|
|
// all clients if UNASSIGNED_SYSTEM_ADDRESS is specified
|
|
void Send(const SystemAddress& sysAddr) const;
|
|
|
|
// Sends the message to the specified client only and never broadcasts. Given UNASSIGNED_SYSTEM_ADDRESS
|
|
// nothing is delivered (RakNet rejects a non-broadcast send without an address).
|
|
void SendToClient(const SystemAddress& sysAddr) const;
|
|
|
|
// Sends the message to every client except excluded (a RakNet broadcast with that address left out).
|
|
// Used to echo a client's message to everyone else.
|
|
void BroadcastExcept(const SystemAddress& excluded) const;
|
|
|
|
// Writes the complete client packet (CLIENT/GAME_MSG header, target, msgId, then Serialize()) into bitStream.
|
|
// This is exactly what Send(sysAddr) puts on the wire; tests use it to compare bytes without a server.
|
|
void WritePacket(RakNet::BitStream& bitStream) const;
|
|
|
|
virtual void Serialize(RakNet::BitStream& bitStream) const {}
|
|
virtual bool Deserialize(RakNet::BitStream& bitStream) { return true; }
|
|
|
|
// Called for messages received from a client, after a successful Deserialize.
|
|
virtual void Handle(Entity& entity, const SystemAddress& sysAddr) {};
|
|
|
|
MessageType::Game msgId;
|
|
LWOOBJID target{ LWOOBJID_EMPTY };
|
|
|
|
// Minimum GM level the sending client needs for Handle to be called.
|
|
eGameMasterLevel requiredGmLevel;
|
|
};
|
|
|
|
/**
|
|
* A server-internal event carrying a network message, so entity/component handlers can react to a message
|
|
* received from (or about to be sent to) a client. Uses the network message's msgId for local dispatch.
|
|
*/
|
|
template<typename Msg>
|
|
struct NetGameMsgEvent : public GameMsg {
|
|
static_assert(std::is_base_of_v<NetGameMsg, Msg>, "NetGameMsgEvent carries a NetGameMsg");
|
|
NetGameMsgEvent() : GameMsg(Msg{}.msgId) {}
|
|
NetGameMsgEvent(const Msg& message) : GameMsg(message.msgId), msg{ message } { target = message.target; }
|
|
|
|
Msg msg;
|
|
};
|
|
|
|
// Delivers a copy of a network message to the local handlers registered on msg.target (never to a client).
|
|
// Returns whether a handler handled it. Handlers see (and may modify) only the copy.
|
|
template<typename Msg>
|
|
bool DeliverLocally(const Msg& msg) {
|
|
NetGameMsgEvent<Msg> event(msg);
|
|
return event.Send();
|
|
}
|
|
|
|
// Server-internal events (GameMsg). Wire messages live in the per-domain <Domain>Messages.h files.
|
|
|
|
struct ZoneLoadedInfo : public GameMsg {
|
|
ZoneLoadedInfo() : GameMsg(MessageType::Game::ZONE_LOADED_INFO) {}
|
|
int32_t maxPlayers{};
|
|
};
|
|
|
|
struct ConfigureRacingControl : public GameMsg {
|
|
ConfigureRacingControl() : GameMsg(MessageType::Game::CONFIGURE_RACING_CONTROL) {}
|
|
LwoNameValue racingSettings{};
|
|
};
|
|
|
|
|
|
|
|
struct ActivityNotify : public GameMsg {
|
|
ActivityNotify() : GameMsg(MessageType::Game::ACTIVITY_NOTIFY) {}
|
|
|
|
LwoNameValue notification{};
|
|
};
|
|
|
|
|
|
struct ChildLoaded : public GameMsg {
|
|
ChildLoaded() : GameMsg(MessageType::Game::CHILD_LOADED) {}
|
|
|
|
LOT templateID{};
|
|
LWOOBJID childID{};
|
|
};
|
|
|
|
struct PlayerResurrectionFinished : public GameMsg {
|
|
PlayerResurrectionFinished() : GameMsg(MessageType::Game::PLAYER_RESURRECTION_FINISHED) {}
|
|
};
|
|
|
|
|
|
struct GetObjectReportInfo : public GameMsg {
|
|
AMFArrayValue* info{};
|
|
AMFArrayValue* subCategory{};
|
|
bool bVerbose{};
|
|
LWOOBJID clientID{};
|
|
|
|
GetObjectReportInfo() : GameMsg(MessageType::Game::GET_OBJECT_REPORT_INFO) {}
|
|
};
|
|
|
|
|
|
|
|
struct ResetModelToDefaults : public GameMsg {
|
|
ResetModelToDefaults() : GameMsg(MessageType::Game::RESET_MODEL_TO_DEFAULTS) {}
|
|
|
|
bool bResetPos{ true };
|
|
bool bResetRot{ true };
|
|
bool bUnSmash{ true };
|
|
bool bResetBehaviors{ true };
|
|
};
|
|
|
|
|
|
struct GetPosition : public GameMsg {
|
|
GetPosition() : GameMsg(MessageType::Game::GET_POSITION) {}
|
|
|
|
NiPoint3 pos{};
|
|
};
|
|
|
|
struct SetFaction : public GameMsg {
|
|
SetFaction() : GameMsg(MessageType::Game::SET_FACTION) {}
|
|
|
|
int32_t factionID{};
|
|
|
|
bool bIgnoreChecks{ false };
|
|
};
|
|
|
|
|
|
struct GetMissionState : public GameMsg {
|
|
GetMissionState() : GameMsg(MessageType::Game::GET_MISSION_STATE) {}
|
|
|
|
int32_t missionID{};
|
|
eMissionState missionState{};
|
|
bool cooldownInfoRequested{};
|
|
bool cooldownFinished{};
|
|
};
|
|
|
|
struct GetFlag : public GameMsg {
|
|
GetFlag() : GameMsg(MessageType::Game::GET_FLAG) {}
|
|
|
|
uint32_t flagID{};
|
|
bool flag{};
|
|
};
|
|
|
|
struct GetFactionTokenType : public GameMsg {
|
|
GetFactionTokenType() : GameMsg(MessageType::Game::GET_FACTION_TOKEN_TYPE) {}
|
|
|
|
LOT tokenType{ LOT_NULL };
|
|
};
|
|
|
|
struct MissionNeedsLot : public GameMsg {
|
|
MissionNeedsLot() : GameMsg(MessageType::Game::MISSION_NEEDS_LOT) {}
|
|
|
|
LOT item{};
|
|
};
|
|
|
|
|
|
|
|
struct IsDead : public GameMsg {
|
|
IsDead() : GameMsg(MessageType::Game::IS_DEAD) {}
|
|
|
|
bool bDead{};
|
|
};
|
|
|
|
|
|
struct GetGMInvis : public GameMsg {
|
|
GetGMInvis() : GameMsg(MessageType::Game::GET_GM_INVIS) {}
|
|
|
|
bool bGMInvis{ false };
|
|
};
|
|
|
|
struct ChildRemoved : public GameMsg {
|
|
ChildRemoved() : GameMsg(MessageType::Game::CHILD_REMOVED) {}
|
|
|
|
LWOOBJID childID{};
|
|
};
|
|
|
|
|
|
struct ObjectLoaded : public GameMsg {
|
|
ObjectLoaded() : GameMsg(MessageType::Game::OBJECT_LOADED) {}
|
|
|
|
LWOOBJID objectID{};
|
|
LOT lot{};
|
|
};
|
|
|
|
struct NotifyCombatAIStateChange : public GameMsg {
|
|
NotifyCombatAIStateChange() : GameMsg(MessageType::Game::NOTIFY_COMBAT_AI_STATE_CHANGE) {}
|
|
|
|
AiState newState{};
|
|
AiState prevState{};
|
|
};
|
|
};
|
|
#endif // GAMEMESSAGES_H
|