using System;
using System.Buffers.Binary;
namespace AcDream.Core.Net.Messages;
///
/// Inbound primary-attribute update GameMessage for the local player
/// (0x02E3) โ the server's authoritative answer to a RaiseAttribute
/// action (and any other server-side attribute change). A standalone
/// GameMessage like , NOT a 0xF7B0
/// GameEvent.
///
///
/// Campaign CA slice CA2 (#431): until this parser existed the client's
/// attribute model was stale from login's PlayerDescription until the next
/// login โ every post-raise derived value (skills, vitals maxima, run
/// rate) computed from old attributes.
///
///
///
/// Wire layout โ three-source agreement (ACE
/// GameMessagePrivateUpdateAttribute.cs:8-16; Chorizite
/// AttributeInfo.generated.cs:26-54; holtburger
/// player/types.rs:22-53 UpdateAttribute<false>), full
/// citations in
/// docs/research/2026-08-24-advancement-wire-and-recompute.md ยง2.5:
///
///
/// PrivateUpdateAttribute (0x02E3):
/// u32 opcode = 0x02E3
/// u8 sequence // ByteSequence, per-attribute counter
/// u32 attribute // PropertyAttribute (1=Strength..6=Self)
/// u32 ranks
/// u32 start // StartingValue / InitLevel
/// u32 xp // ExperienceSpent / CPSpent
///
///
public static class PrivateUpdateAttribute
{
public const uint Opcode = 0x02E3u;
/// Parsed attribute update.
public readonly record struct Parsed(
byte Sequence,
uint AttributeId,
uint Ranks,
uint Start,
uint Xp);
///
/// Parse a raw PrivateUpdateAttribute (0x02E3) body. Returns
/// null on opcode mismatch or truncation.
///
public static Parsed? TryParse(ReadOnlySpan body)
{
// 4 (opcode) + 1 (seq) + 4 * 4 (uints) = 21 bytes minimum.
if (body.Length < 21) return null;
uint opcode = BinaryPrimitives.ReadUInt32LittleEndian(body);
if (opcode != Opcode) return null;
int pos = 4;
byte seq = body[pos]; pos += 1;
uint attr = BinaryPrimitives.ReadUInt32LittleEndian(body[pos..]); pos += 4;
uint ranks = BinaryPrimitives.ReadUInt32LittleEndian(body[pos..]); pos += 4;
uint start = BinaryPrimitives.ReadUInt32LittleEndian(body[pos..]); pos += 4;
uint xp = BinaryPrimitives.ReadUInt32LittleEndian(body[pos..]);
return new Parsed(seq, attr, ranks, start, xp);
}
}