mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
Nothing in the server writes or reads a packet by hand any more, so the helpers for doing so go: - CBITSTREAM, CMSGHEADER, CINSTREAM, CINSTREAM_SKIP_HEADER, SEND_PACKET, SEND_PACKET_BROADCAST and HEADER_SIZE leave dCommonVars.h; - the free BitStreamUtils::WriteHeader (LUBitStream::WriteHeader writes the same bytes) and the unused PacketUtils::SavePacket are deleted. The last raw reads are replaced: WorldServer builds its input stream directly, the master packet logs read the header with LUBitStream::ReadHeader instead of peeking at packet->data[1] and [3], and MessageInspector reads a sent game message's header with the new NetGameMsg::ReadPacketHeader (the counterpart of WritePacket) instead of memcmp/memcpy. packet->data[0] is still compared with RakNet's own connection IDs. The frozen oracles keep using the macros verbatim through the test-only tests/dGameTests/LegacyPacketMacros.h; the HeaderSkip tests, which only tested CINSTREAM_SKIP_HEADER, are removed. docs/PacketArchitecture.md: "where we are" now describes the final state and what still touches raw bytes (RakNet IDs, replica headers, behavior bit streams), and a new section collects the known wire discrepancies found during the conversion, with client addresses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
265 lines
7.8 KiB
C++
265 lines
7.8 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;
|
|
|
|
// Reads what WritePacket writes before Serialize(): the CLIENT/GAME_MSG header, the target and the message ID.
|
|
// Returns false if the stream is too short or holds a different kind of packet.
|
|
static bool ReadPacketHeader(RakNet::BitStream& bitStream, LWOOBJID& target, MessageType::Game& msgId);
|
|
|
|
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
|