๐ฆ Containers¶
Low-level runtime data structures for item storage and equipment layouts.
These are the core objects that SceneInventoryService creates and manages.
You rarely instantiate them directly โ and typically access them through the service layer.
โ Runtime-safe
โ Not designed for standalone serialization
โ Supports granular change events
โ Shared logic between inventory and equipment
Purpose¶
Defines how items are stored, stacked, filtered, and mutated inside the Inventory system.
Everything above this layer (services, UI, gameplay) builds on these containers.
Contents¶
| Type | Purpose |
|---|---|
InventoryContainer |
General-purpose slot list with stack, split, merge, swap, filter, and resize support. |
EquipmentContainer |
Fixed single-item layout for gear slots, each with its own EquipmentFilter. |
InventoryDelta |
Immutable struct describing per-slot diffs for UI or syncing. |
ItemStackUtil / InventorySortHelpers |
Internal helpers for equality, metadata, and sort keys. |
๐ What Is a Container?¶
A container is an ordered list of slots.
Each slot holds a single ItemStack, optionally constrained by a filter:
InventorySlot.Accepts(ItemDefinition)for inventoryEquipmentFilterfor equipment
Two concrete container types exist:
- InventoryContainer โ dynamic, stack-based storage
- EquipmentContainer โ fixed, one-item-per-slot layout
๐ Quick Start¶
Containers are normally retrieved via the service:
var svc = SceneInventoryService.Instance;
// Backpack
var backpack = svc.Get(player, "Backpack");
// Equipment
var eq = player.GetComponent<CharacterEquipment>().Equipment;
Direct mutation (bypasses authority):
var sword = db.GetByGuid("sword");
backpack.TryAddAllResult(new ItemStack { def = sword, quantity = 1 });
โ ๏ธ Direct container calls bypass authority.
UseSceneInventoryServicefor authoritative gameplay changes.
๐ Events & Deferral¶
Both container types expose:
event Action OnChanged;
This fires whenever contents change.
Bulk writes can be coalesced using deferral scopes:
using (backpack.DeferEvents())
{
backpack.TryRemoveResult("potion", 1);
backpack.TryAddAllResult(new ItemStack { def = sword, quantity = 1 });
}
// โ OnChanged fires once
โจ Common InventoryContainer Operations¶
inv.TryAddAllResult(stack); // full-stack add or fail
inv.AddMaxResult(stack, out var remainder); // partial add allowed
inv.TryRemoveFromSlotResult(index, qty); // strict remove
inv.TrySplitResult(from: 0, to: 5, amount: 2); // split stack
inv.TryMergeThenSwapResult(from: 0, to: 1); // merge else swap
inv.TryResize(newSize: 32, allowTruncate: true, out var truncated);
Behaviour Highlights¶
- Strict removal โ
TryRemoveResult(guid, qty)requires full quantity - Partial merges track exact per-slot placement for safe rollback
- Slot filters (
Accepts) can block merges, swaps, and splits - Resize supports truncation and triggers a delta resnap via the service
- Deferral scopes coalesce multiple changes into a single event
โจ Common EquipmentContainer Operations¶
eq.TryEquipStrict("Weapon", new ItemStack { def = sword, quantity = 1 }, out var swapped);
eq.TryUnequip("Helmet", out var removed);
eq.SetSlotFilter("Accessory1", ringFilter);
eq.ClearAll();
Behaviour Highlights¶
- Exactly one item per slot
- Filters must pass (
EquipmentFilter.Matches) - Duplicate layout IDs warn but last wins
- Layout changes and bulk updates should use
DeferEvents() - All changes raise
OnChanged
๐ InventoryDelta¶
InventoryDelta is produced only by SceneInventoryService when a tracked
container changes, by diffing internal snapshots against container state.
svc.OnContainerChanged += delta =>
{
foreach (var change in delta.changes)
Debug.Log($"{delta.container}[{change.index}]: {change.before.def} โ {change.after.def}");
};
Key Points¶
- Contains only changed slots (per-slot diff)
- Slot-count changes (resize) emit a full resnap delta
- Ideal for UI repainting or lightweight syncing
๐งช Equality & Metadata (ItemStackUtil)¶
ItemStackUtil.StacksEqual(a, b) compares:
- Item definition
- Quantity
- Durability within
DurabilityEpsilon - Metadata as an unordered dictionary (last-write-wins semantics)
Used by:
InventoryDeltasnapshot comparison- Sorting
- Mutations that require value equality checks
โ ๏ธ Notes & Gotchas¶
InventoryContaineris runtime-only (not serializable)EquipmentContaineralways enforces quantity = 1- Resize may fail unless truncation is allowed
- Slot and equipment filters can block operations
DeferEvents()prevents event spam during batch updates- Authority is enforced only by the service, never by containers