feat(net): D.2b-B B-Wire — register ViewContents + InventoryPutObjectIn3D/SaveFailed/CloseGroundContainer
Register four previously-unwired GameEvent handlers after InventoryPutObjInContainer: - ViewContents (0x0196): iterates ParseViewContents entries and calls items.RecordMembership(entry.Guid, containerId) for each — so the object table is correct before the container-open UI mounts. - InventoryPutObjectIn3D (0x019A): calls items.MoveItem(guid, 0u) to unparent a dropped item from its container (it's now a ground object). - InventoryServerSaveFailed (0x00A0): parse + log; rollback behavior deferred to B-Drag (acdream has no speculative moves yet). - CloseGroundContainer (0x0052): parse + log; no table change needed (the container-open view is UI-only). Two dispatcher tests cover ViewContents membership and Put3D unparenting. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d97d84d7ca
commit
3a9c188233
2 changed files with 75 additions and 0 deletions
|
|
@ -249,6 +249,46 @@ public static class GameEventWiring
|
|||
if (p is not null) items.MoveItem(p.Value.ItemGuid, p.Value.ContainerGuid,
|
||||
newSlot: (int)p.Value.Placement);
|
||||
});
|
||||
|
||||
// B-Wire: ViewContents (0x0196) — the server's full contents list for a
|
||||
// container you opened. Record membership for each entry so the table is
|
||||
// correct before the container-open UI mounts the second item list.
|
||||
dispatcher.Register(GameEventType.ViewContents, e =>
|
||||
{
|
||||
var p = GameEvents.ParseViewContents(e.Payload.Span);
|
||||
if (p is null) return;
|
||||
foreach (var entry in p.Value.Items)
|
||||
items.RecordMembership(entry.Guid, containerId: p.Value.ContainerGuid);
|
||||
});
|
||||
|
||||
// B-Wire: InventoryPutObjectIn3D (0x019A) — server confirms an item dropped
|
||||
// to the world. Unparent it from its container (it's now a ground object) so
|
||||
// the inventory grid drops the cell; the object itself survives.
|
||||
dispatcher.Register(GameEventType.InventoryPutObjectIn3D, e =>
|
||||
{
|
||||
var guid = GameEvents.ParsePutObjectIn3D(e.Payload.Span);
|
||||
if (guid is not null) items.MoveItem(guid.Value, newContainerId: 0u);
|
||||
});
|
||||
|
||||
// B-Wire: InventoryServerSaveFailed (0x00A0) — rollback of a speculative move.
|
||||
// acdream does not do speculative moves yet (read-only inventory); parse + log
|
||||
// so the wire is correct. B-Drag wires the rollback behavior. (Divergence note.)
|
||||
dispatcher.Register(GameEventType.InventoryServerSaveFailed, e =>
|
||||
{
|
||||
var p = GameEvents.ParseInventoryServerSaveFailed(e.Payload.Span);
|
||||
if (p is not null)
|
||||
Console.WriteLine($"[B-Wire] InventoryServerSaveFailed guid=0x{p.Value.ItemGuid:X8} err=0x{p.Value.WeenieError:X}");
|
||||
});
|
||||
|
||||
// B-Wire: CloseGroundContainer (0x0052) — server closed a ground-container
|
||||
// view. No table change (the view is UI-only, wired in container-open); log.
|
||||
dispatcher.Register(GameEventType.CloseGroundContainer, e =>
|
||||
{
|
||||
var guid = GameEvents.ParseCloseGroundContainer(e.Payload.Span);
|
||||
if (guid is not null)
|
||||
Console.WriteLine($"[B-Wire] CloseGroundContainer guid=0x{guid.Value:X8}");
|
||||
});
|
||||
|
||||
dispatcher.Register(GameEventType.IdentifyObjectResponse, e =>
|
||||
{
|
||||
var p = AppraiseInfoParser.TryParse(e.Payload.Span);
|
||||
|
|
|
|||
|
|
@ -535,4 +535,39 @@ public sealed class GameEventWiringTests
|
|||
Assert.Equal(1500, player!.Properties.Ints[5]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireAll_ViewContents_RecordsMembershipInClientObjectTable()
|
||||
{
|
||||
var (d, items, _, _, _) = MakeAll();
|
||||
|
||||
// containerGuid 0xC9 + 2 entries
|
||||
byte[] payload = new byte[8 + 2 * 8];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(0), 0x500000C9u);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(4), 2u);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(8), 0x50000A01u);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(12), 0u);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(16), 0x50000A02u);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(20), 1u);
|
||||
|
||||
var env = GameEventEnvelope.TryParse(WrapEnvelope(GameEventType.ViewContents, payload));
|
||||
d.Dispatch(env!.Value);
|
||||
|
||||
Assert.Equal(0x500000C9u, items.Get(0x50000A01u)!.ContainerId);
|
||||
Assert.Equal(0x500000C9u, items.Get(0x50000A02u)!.ContainerId);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireAll_InventoryPutObjectIn3D_UnparentsFromContainer()
|
||||
{
|
||||
var (d, items, _, _, _) = MakeAll();
|
||||
items.RecordMembership(0x50000A01u, containerId: 0x500000C9u);
|
||||
|
||||
byte[] payload = new byte[4];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload, 0x50000A01u);
|
||||
var env = GameEventEnvelope.TryParse(WrapEnvelope(GameEventType.InventoryPutObjectIn3D, payload));
|
||||
d.Dispatch(env!.Value);
|
||||
|
||||
Assert.Equal(0u, items.Get(0x50000A01u)!.ContainerId);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue