feat(D.5.2): PublicUpdatePropertyInt (0x02CE) parser
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>
This commit is contained in:
parent
8df0b64676
commit
242bc9286d
2 changed files with 80 additions and 0 deletions
44
src/AcDream.Core.Net/Messages/PublicUpdatePropertyInt.cs
Normal file
44
src/AcDream.Core.Net/Messages/PublicUpdatePropertyInt.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
using System.Buffers.Binary;
|
||||
using AcDream.Core.Net.Messages;
|
||||
|
||||
namespace AcDream.Core.Net.Tests.Messages;
|
||||
|
||||
public sealed class PublicUpdatePropertyIntTests
|
||||
{
|
||||
private static byte[] Build(uint guid, uint property, int value, byte seq = 1, uint opcode = 0x02CEu)
|
||||
{
|
||||
var b = new byte[17];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(b.AsSpan(0), opcode);
|
||||
b[4] = seq;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(b.AsSpan(5), guid);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(b.AsSpan(9), property);
|
||||
BinaryPrimitives.WriteInt32LittleEndian(b.AsSpan(13), value);
|
||||
return b;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryParse_uiEffectsUpdate_returnsGuidPropValue()
|
||||
{
|
||||
var p = PublicUpdatePropertyInt.TryParse(Build(0x50000001u, property: 18u, value: 0x9));
|
||||
Assert.NotNull(p);
|
||||
Assert.Equal(0x50000001u, p!.Value.Guid);
|
||||
Assert.Equal(18u, p.Value.Property);
|
||||
Assert.Equal(0x9, p.Value.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryParse_wrongOpcode_returnsNull()
|
||||
=> Assert.Null(PublicUpdatePropertyInt.TryParse(Build(1, 18, 1, opcode: 0x02CDu)));
|
||||
|
||||
[Fact]
|
||||
public void TryParse_truncated_returnsNull()
|
||||
=> Assert.Null(PublicUpdatePropertyInt.TryParse(new byte[16]));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue