using System; using System.Buffers.Binary; namespace AcDream.Core.Net.Messages; /// /// Inbound PublicUpdatePropertyInt (0x02CE) — the server updates one /// PropertyInt on a visible object (carries the object guid). Standalone /// GameMessage, dispatched like / CreateObject. /// /// /// The companion PrivateUpdatePropertyInt (0x02CD) targets the player's OWN /// object (no guid) and is not parsed here — it has no item-icon impact. /// /// /// Wire layout (ACE GameMessagePublicUpdatePropertyInt, size hint 17): /// /// u32 opcode = 0x02CE /// u8 sequence // single byte (ByteSequence.NextBytes) — see PrivateUpdateVital /// u32 guid /// u32 property // PropertyInt enum; UiEffects = 18 /// i32 value /// /// The sequence is parsed-past but not honored (latest-wins; divergence DR-4). /// public static class PublicUpdatePropertyInt { public const uint Opcode = 0x02CEu; public readonly record struct Parsed(uint Guid, uint Property, int Value); /// Parse a raw 0x02CE body. Returns null on opcode mismatch / truncation. public static Parsed? TryParse(ReadOnlySpan body) { if (body.Length < 17) return null; // 4 + 1 + 4 + 4 + 4 if (BinaryPrimitives.ReadUInt32LittleEndian(body) != Opcode) return null; int pos = 4; pos += 1; // sequence byte (not honored) uint guid = BinaryPrimitives.ReadUInt32LittleEndian(body[pos..]); pos += 4; uint prop = BinaryPrimitives.ReadUInt32LittleEndian(body[pos..]); pos += 4; int value = BinaryPrimitives.ReadInt32LittleEndian(body[pos..]); return new Parsed(guid, prop, value); } }