From 078919cc189a66e1b21121b07d9b110a4806e810 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 10 May 2026 09:43:46 +0200 Subject: [PATCH] feat(net): #13 register PD trailer inventory+equipped in ItemRepository MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After PlayerDescription is dispatched, the Inventory and Equipped lists produced by the parser are now fed into ItemRepository via AddOrUpdate + MoveItem so inventory/paperdoll panels see items after login. Acceptance test PlayerDescription_RegistersInventoryEntries_InItemRepository confirms ItemCount goes 0→2 for a synthetic PD with two inventory entries. 282 Net.Tests pass. Co-Authored-By: Claude Sonnet 4.6 --- src/AcDream.Core.Net/GameEventWiring.cs | 35 ++++++++++++++ .../GameEventWiringTests.cs | 47 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/src/AcDream.Core.Net/GameEventWiring.cs b/src/AcDream.Core.Net/GameEventWiring.cs index 93fd62e..c5f61e3 100644 --- a/src/AcDream.Core.Net/GameEventWiring.cs +++ b/src/AcDream.Core.Net/GameEventWiring.cs @@ -395,6 +395,41 @@ public static class GameEventWiring if (dumpPd) Console.WriteLine($"vitals: PD-ench spell={ench.SpellId} layer={ench.Layer} bucket={ench.Bucket} key={ench.StatModKey} val={ench.StatModValue}"); } + + // Issue #13 — register inventory entries with ItemRepository so + // panels (inventory, paperdoll, hotbars) light up after login. + // Equipped entries share the same ObjectId as inventory entries + // (an equipped item is also in inventory) — register both, but + // the equipped record carries the slot mask which we surface via + // MoveItem so paperdoll can render. + foreach (var inv in p.Value.Inventory) + { + if (items.GetItem(inv.Guid) is null) + { + items.AddOrUpdate(new ItemInstance + { + ObjectId = inv.Guid, + WeenieClassId = inv.ContainerType, + }); + } + } + foreach (var eq in p.Value.Equipped) + { + if (items.GetItem(eq.Guid) is null) + { + items.AddOrUpdate(new ItemInstance + { + ObjectId = eq.Guid, + WeenieClassId = 0, + }); + } + // Reflect the equip slot — paperdoll uses CurrentlyEquippedLocation. + items.MoveItem( + itemId: eq.Guid, + newContainerId: 0, + newSlot: -1, + newEquipLocation: (EquipMask)eq.EquipLocation); + } }); } } diff --git a/tests/AcDream.Core.Net.Tests/GameEventWiringTests.cs b/tests/AcDream.Core.Net.Tests/GameEventWiringTests.cs index f740efb..c414ddb 100644 --- a/tests/AcDream.Core.Net.Tests/GameEventWiringTests.cs +++ b/tests/AcDream.Core.Net.Tests/GameEventWiringTests.cs @@ -1,5 +1,6 @@ using System; using System.Buffers.Binary; +using System.IO; using System.Text; using AcDream.Core.Chat; using AcDream.Core.Combat; @@ -328,4 +329,50 @@ public sealed class GameEventWiringTests Assert.Contains("Mana Stone", e.Text); } + [Fact] + public void PlayerDescription_RegistersInventoryEntries_InItemRepository() + { + // Issue #13 acceptance test: after a PlayerDescription with non-empty + // Inventory is dispatched through WireAll, ItemRepository.ItemCount > 0. + // Wire format: strict path (no GAMEPLAY_OPTIONS bit) so inventory + + // equipped follow directly after spellbook_filters. + var dispatcher = new GameEventDispatcher(); + var items = new ItemRepository(); + var combat = new CombatState(); + var spellbook = new Spellbook(); + var chat = new ChatLog(); + GameEventWiring.WireAll(dispatcher, items, combat, spellbook, chat); + + Assert.Equal(0, items.ItemCount); // pre-condition + + var sb = new MemoryStream(); + using var w = new BinaryWriter(sb); + w.Write(0u); // propertyFlags = 0 + w.Write(0x52u); // weenieType + w.Write(0x201u); // vectorFlags = ATTRIBUTE | ENCHANTMENT + w.Write(1u); // has_health + w.Write(0u); // attribute_flags = 0 (no attrs) + w.Write(0u); // enchantment_mask = 0 + + w.Write(0u); // option_flags = None (no GAMEPLAY_OPTIONS → strict inv path) + w.Write(0u); // options1 + w.Write(0u); // legacy hotbar list count = 0 + w.Write(0u); // spellbook_filters + + // Inventory: 2 entries + w.Write(2u); + w.Write(0x50000A01u); w.Write(0u); // guid, ContainerType=NonContainer + w.Write(0x50000A02u); w.Write(1u); // guid, ContainerType=Container + + // Equipped: 0 entries + w.Write(0u); + + var env = GameEventEnvelope.TryParse(WrapEnvelope(GameEventType.PlayerDescription, sb.ToArray())); + dispatcher.Dispatch(env!.Value); + + Assert.Equal(2, items.ItemCount); + Assert.NotNull(items.GetItem(0x50000A01u)); + Assert.NotNull(items.GetItem(0x50000A02u)); + } + }