using System;
using System.Buffers.Binary;
namespace AcDream.Core.Net.Messages;
///
/// Parsed header of a retail AC 0xF7B0 GameEvent envelope (S→C).
///
///
/// Wire layout
/// (references/ACE/Source/ACE.Server/Network/GameEvent/GameEventMessage.cs):
///
/// u32 0xF7B0 // GameMessage opcode (GameEvent envelope)
/// u32 guid // session's player guid (0 if none yet)
/// u32 gameEventSequence // per-session, incremented by server
/// u32 eventType // GameEventType sub-opcode (94 values)
/// <payload> // variable; type-dispatched
///
///
///
///
/// Payload is returned as a view into the
/// original message body — no copy. Per-event parsers slice from this.
///
///
public readonly record struct GameEventEnvelope(
uint PlayerGuid,
uint Sequence,
GameEventType EventType,
ReadOnlyMemory Payload)
{
/// GameMessage opcode of the outer envelope.
public const uint Opcode = 0xF7B0u;
/// Header size (4×u32 = 16 bytes before payload).
public const int HeaderSize = 16;
///
/// Parse a raw 0xF7B0 GameMessage body into the header + payload view.
/// Returns null if the body is shorter than the header or the outer
/// opcode doesn't match.
///
public static GameEventEnvelope? TryParse(byte[] body)
{
if (body is null || body.Length < HeaderSize) return null;
uint outer = BinaryPrimitives.ReadUInt32LittleEndian(body);
if (outer != Opcode) return null;
uint guid = BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(4));
uint sequence = BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8));
uint eventType = BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(12));
var payload = body.AsMemory(HeaderSize);
return new GameEventEnvelope(guid, sequence, (GameEventType)eventType, payload);
}
}