New standalone parser for the server's live PropertyInt update targeting a VISIBLE object (carries guid). Wire layout: u32 opcode + u8 sequence + u32 guid + u32 property + i32 value (17 bytes total). The sequence byte is parsed-past but not honored (latest-wins; DR-4). The companion PrivateUpdatePropertyInt (0x02CD) targets the player's own object (no guid) and is not parsed here. Three tests: uiEffectsUpdate (round-trip guid/prop/value), wrongOpcode (returns null), truncated (returns null on 16-byte input). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
44 lines
1.8 KiB
C#
44 lines
1.8 KiB
C#
using System;
|
|
using System.Buffers.Binary;
|
|
|
|
namespace AcDream.Core.Net.Messages;
|
|
|
|
/// <summary>
|
|
/// Inbound <c>PublicUpdatePropertyInt (0x02CE)</c> — the server updates one
|
|
/// <c>PropertyInt</c> on a visible object (carries the object guid). Standalone
|
|
/// GameMessage, dispatched like <see cref="PrivateUpdateVital"/> / CreateObject.
|
|
///
|
|
/// <para>
|
|
/// The companion <c>PrivateUpdatePropertyInt (0x02CD)</c> targets the player's OWN
|
|
/// object (no guid) and is not parsed here — it has no item-icon impact.
|
|
/// </para>
|
|
///
|
|
/// <para>Wire layout (ACE <c>GameMessagePublicUpdatePropertyInt</c>, size hint 17):</para>
|
|
/// <code>
|
|
/// u32 opcode = 0x02CE
|
|
/// u8 sequence // single byte (ByteSequence.NextBytes) — see PrivateUpdateVital
|
|
/// u32 guid
|
|
/// u32 property // PropertyInt enum; UiEffects = 18
|
|
/// i32 value
|
|
/// </code>
|
|
/// The sequence is parsed-past but not honored (latest-wins; divergence DR-4).
|
|
/// </summary>
|
|
public static class PublicUpdatePropertyInt
|
|
{
|
|
public const uint Opcode = 0x02CEu;
|
|
|
|
public readonly record struct Parsed(uint Guid, uint Property, int Value);
|
|
|
|
/// <summary>Parse a raw 0x02CE body. Returns null on opcode mismatch / truncation.</summary>
|
|
public static Parsed? TryParse(ReadOnlySpan<byte> 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);
|
|
}
|
|
}
|