From b5f7ea29f3470634ce792de685d935a94485a4d8 Mon Sep 17 00:00:00 2001 From: David Smith Date: Fri, 16 Oct 2026 09:02:45 +0200 Subject: [PATCH] fix(items): a carried spell focus no longer blocks inventory completeness HasCompleteInventory wanted a contents listing for every container-slot item, but ACE never sends one for a focus (container type 2), since it holds nothing. Anyone carrying a focus never had a complete inventory, so plugin Sort waited 60 s and gave up. Skip foci when walking packs. Claude-Session: https://claude.ai/code/session_01Lm9T9oP2Hi5FEjwsLmXeyN --- src/AcDream.Core/Items/ClientObjectTable.cs | 7 +++++-- .../Items/InventoryCompletenessTests.cs | 13 +++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/AcDream.Core/Items/ClientObjectTable.cs b/src/AcDream.Core/Items/ClientObjectTable.cs index d8ed56ba..aba45de5 100644 --- a/src/AcDream.Core/Items/ClientObjectTable.cs +++ b/src/AcDream.Core/Items/ClientObjectTable.cs @@ -1206,7 +1206,7 @@ public IReadOnlyList GetEquippedBy(uint wielderId) => .ToArray() : Array.Empty(); - /// Whether server listings cover the owner and every nested pack. + /// Whether server listings cover the owner and every nested pack (a focus needs none). public bool HasCompleteInventory(uint ownerId) { if (ownerId == 0 || _pendingMoves.Count != 0) @@ -1225,7 +1225,10 @@ public bool HasCompleteInventory(uint ownerId) { if (!_objects.TryGetValue(id, out ClientObject? item)) return false; - if (IsContainerListMember(item)) + // A focus takes a container slot but holds nothing, and the + // server never sends a listing for one. + if (IsContainerListMember(item) + && !InventoryContainerPlacementPolicy.IsFocus(item)) pending.Push(id); } } diff --git a/tests/AcDream.Core.Tests/Items/InventoryCompletenessTests.cs b/tests/AcDream.Core.Tests/Items/InventoryCompletenessTests.cs index 00656b51..60cd62bb 100644 --- a/tests/AcDream.Core.Tests/Items/InventoryCompletenessTests.cs +++ b/tests/AcDream.Core.Tests/Items/InventoryCompletenessTests.cs @@ -42,4 +42,17 @@ public void EmptyAuthoritativeInventoryIsComplete() table.InitializeInventoryManifest(1, Array.Empty()); Assert.True(table.HasCompleteInventory(1)); } + + [Fact] + public void SpellFocusNeverListedDoesNotBlockCompleteness() + { + // ACE lists a focus (container type 2) in the container slots but + // never sends its contents, since a focus holds nothing. + var table = new ClientObjectTable(); + table.InitializeInventoryManifest(1, + [new ContainerContentEntry(2, 1), new ContainerContentEntry(3, 2)]); + Assert.False(table.HasCompleteInventory(1)); + table.ReplaceContents(2, Array.Empty()); + Assert.True(table.HasCompleteInventory(1)); + } }