feat(net): D.2b-B B-Wire — ParseViewContents (0x0196)

GameEvents.ParseViewContents parses the ViewContents GameEvent payload:
containerGuid + count + [guid, containerType]×count. Records
ViewContentsEntry and ViewContents added. 3 unit tests added in
GameEventsInventoryTests.cs (two-entry, zero-count, truncated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-21 18:54:25 +02:00
parent 79c0374d53
commit 43afcc2adb
2 changed files with 68 additions and 0 deletions

View file

@ -358,6 +358,29 @@ public static class GameEvents
BinaryPrimitives.ReadUInt32LittleEndian(payload.Slice(8)));
}
/// <summary>0x0196 ViewContents: full contents list of a container you opened.
/// Layout (ACE GameEventViewContents.cs): containerGuid, count, [guid, containerType]×count.
/// Client consumer: ClientUISystem::OnViewContents (PackableList&lt;ContentProfile&gt;).</summary>
public readonly record struct ViewContentsEntry(uint Guid, uint ContainerType);
public readonly record struct ViewContents(uint ContainerGuid, System.Collections.Generic.IReadOnlyList<ViewContentsEntry> Items);
public static ViewContents? ParseViewContents(ReadOnlySpan<byte> payload)
{
if (payload.Length < 8) return null;
uint containerGuid = BinaryPrimitives.ReadUInt32LittleEndian(payload);
uint count = BinaryPrimitives.ReadUInt32LittleEndian(payload.Slice(4));
int pos = 8;
if ((long)payload.Length - pos < (long)count * 8) return null;
var items = new ViewContentsEntry[count];
for (int i = 0; i < count; i++)
{
uint guid = BinaryPrimitives.ReadUInt32LittleEndian(payload.Slice(pos)); pos += 4;
uint type = BinaryPrimitives.ReadUInt32LittleEndian(payload.Slice(pos)); pos += 4;
items[i] = new ViewContentsEntry(guid, type);
}
return new ViewContents(containerGuid, items);
}
// ── Other small-payload events ──────────────────────────────────────────
/// <summary>0x01C7 UseDone: the Use/UseWithTarget completion signal (WeenieError code).</summary>

View file

@ -0,0 +1,45 @@
using System.Buffers.Binary;
using AcDream.Core.Net.Messages;
using Xunit;
namespace AcDream.Core.Net.Tests.Messages;
public sealed class GameEventsInventoryTests
{
[Fact]
public void ParseViewContents_twoEntries_returnsContainerAndItems()
{
// containerGuid + count(2) + 2×{guid, containerType}
var b = new byte[4 + 4 + 2 * 8];
BinaryPrimitives.WriteUInt32LittleEndian(b.AsSpan(0), 0x500000C9u); // containerGuid
BinaryPrimitives.WriteUInt32LittleEndian(b.AsSpan(4), 2u); // count
BinaryPrimitives.WriteUInt32LittleEndian(b.AsSpan(8), 0x50000A01u); // guid 1
BinaryPrimitives.WriteUInt32LittleEndian(b.AsSpan(12), 0u); // type 1 (item)
BinaryPrimitives.WriteUInt32LittleEndian(b.AsSpan(16), 0x50000A02u);// guid 2
BinaryPrimitives.WriteUInt32LittleEndian(b.AsSpan(20), 1u); // type 2 (container)
var p = GameEvents.ParseViewContents(b);
Assert.NotNull(p);
Assert.Equal(0x500000C9u, p!.Value.ContainerGuid);
Assert.Equal(2, p.Value.Items.Count);
Assert.Equal(0x50000A01u, p.Value.Items[0].Guid);
Assert.Equal(0u, p.Value.Items[0].ContainerType);
Assert.Equal(0x50000A02u, p.Value.Items[1].Guid);
Assert.Equal(1u, p.Value.Items[1].ContainerType);
}
[Fact]
public void ParseViewContents_zeroCount_returnsEmptyList()
{
var b = new byte[8];
BinaryPrimitives.WriteUInt32LittleEndian(b.AsSpan(0), 0x500000C9u);
BinaryPrimitives.WriteUInt32LittleEndian(b.AsSpan(4), 0u);
var p = GameEvents.ParseViewContents(b);
Assert.NotNull(p);
Assert.Empty(p!.Value.Items);
}
[Fact]
public void ParseViewContents_truncated_returnsNull()
=> Assert.Null(GameEvents.ParseViewContents(new byte[4]));
}