Generated reference. This page mirrors
Code/Inventories/README.md, this module's source-of-truth documentation (Standards/02_DOCUMENTATION_STANDARDS.md §1). Edit it there, not here -- this file is regenerated on every docs build and any local edit is silently overwritten.
Inventory¶
Purpose. One inventory implementation, used identically by a character, a world container, a vehicle, or a corpse. See Documentation/Architecture/08_DATA_MODEL.md §3 and ADR-0006. This is the direct fix for D-09 (character and container inventory as two separately-maintained systems) and D-10 (items with no identity, so two copies of the same thing are indistinguishable).
Responsibilities.
- IInventory / Inventory — capacity maths (space, not weight; negative-size items grant
capacity), stacking with a per-definition limit
(D-03), and the pocket
over-encumbrance guard on removal
(Legacy/03_INVENTORY_AND_ITEMS.md §1.2).
Inventory is the one shared implementation; nothing else implements IInventory.
- IInventoryHolder — the thin "whatever has an inventory" concept a character or world
container satisfies.
- IInventoryService / InventoryStore — persistence (inventories/{ownerId},
08_DATA_MODEL.md §8)
and atomic cross-inventory transfer. See that document's §3 for exactly how atomicity is
achieved without a real multi-document transaction.
- InventoryConfiguration — the character base-capacity default (40,
Legacy/03_INVENTORY_AND_ITEMS.md §1.2),
following 05_CONFIGURATION.md's
per-module GameResource pattern.
- WorldContainerComponent — the first world container type, proving a second
IInventoryHolder shares Inventory with no special case
(ROADMAP.md Milestone 5).
- InventorySlotView / CharacterInventoryComponent — the replicated, display-only snapshot
a HUD panel reads instead of the live IInventory (Milestone 6's basic inventory panel,
11_UI_ARCHITECTURE.md §3).
The same component also exposes RequestUseItem, the Interaction framework's first RPC
entry point (10_INTERACTION_FRAMEWORK.md §3):
validates the item is actually in the caller's own loaded inventory, then hands
ItemDefinition.CreateUseInteraction's result to the sibling InteractionComponent's
Runner.TryStart. Lives here rather than on a separate component so there is exactly one
loaded Inventory per character, not two independently-loaded copies that could diverge.
RequestAttachItem/RequestDetachItem (Milestone 12) live on the same component for the
same reason: attaching moves an ItemInstance out of the flat, already-loaded Inventory
into a weapon's nested WeaponAttachmentState (detaching reverses it), so it needs the one
loaded copy too. See
09_ITEM_FRAMEWORK.md §2.2.
Milestone 14: RequestUseItem now calls Applejack.Licences.ItemLicenceGate.IsPermitted
before starting the interaction, refusing to equip/use an item whose category requires a
licence the caller doesn't hold — this milestone's own real, wired exit criterion. See
28_LICENCES.md §4
and Licences.
- WeaponAttachmentStateSerializer (Milestone 12) — persists a weapon's attached items.
WeaponAttachmentState itself lives in Items (it holds only
Items-owned types); this serializer lives here because, like ContainerStateSerializer, its
payload nests real ItemInstances and needs InventoryStore's document-conversion
recursion. Registered by InventoryModule immediately after ContainerStateSerializer.
- InventoryModule — registers IInventoryService and /give (Milestone 10) — the
admin/testing way to spawn an item into your own inventory that
Guides/02_ADDING_AN_ITEM.md used to name as
unbuilt. InventoryModule.GiveAsync is a static method taking its collaborators as
parameters specifically so it's testable without booting the module
(UnitTests/Inventories/InventoryModuleGiveTests.cs),
the same shape CharacterInventoryComponent's own static extraction already uses. Self-only
— no target-player argument, matching Commands' own established minimalism. InventoryPanel
is NOT registered against IHudRegistry/IMenuRegistry -- it's a PanelComponent (resolves
the local player's CharacterInventoryComponent via Applejack.Movement.LocalPawn), and
neither registry accepts anything but TPanel : Panel, new(). Follows MoneyDisplay's
scene-placed pattern for the standing-HUD half; its originally-intended dual-registration as
the player menu's Inventory tab is a still-open question, since PlayerMenu's own
tab-swapping only supports Panel-typed content — see Code/Ui/README.md's
"Panel-vs-PanelComponent" entry
(24_PLAYER_MENU.md §4).
Not responsible for.
- Item templates, categories, behaviours, ItemInstance, and ItemId — all
Items; this module consumes ItemInstance, it does not declare it.
- Character inventory's own "which character owns which IInventoryHolder" wiring lives in
CharacterInventoryHolder here (this module depends on Character, not the reverse) rather
than inside Code/Characters/, keeping Character ignorant of Inventory entirely, per
08_DATA_MODEL.md §1, "One owner per piece of state".
- Virtual/computed container contents (one vault, per-player view) — a real idea from the
legacy design worth keeping (see
Legacy/03_INVENTORY_AND_ITEMS.md §2)
but out of scope until a system (banking) actually needs it.
- Nested containers' UI/interaction surface — the data nests for free (a Container
behaviour's state bag holds an IInventory), but opening one is Milestone 6's
Interaction framework.
Dependencies. Core (IServiceRegistry, IServiceResolver, IPersistenceBackend,
ISchemaRegistry, IGameLog), Items (ItemDefinition), Character (CharacterId, for
CharacterInventoryHolder only), Interaction (reads a sibling
InteractionComponent.Runner from CharacterInventoryComponent.RequestUseItem, per
InventoryModule.DependsOn), Licences (Milestone 14: ILicenceService,
resolved by CharacterInventoryComponent.OnAwake to gate RequestUseItem), the S&box engine
(Component, Sync, Rpc) for WorldContainerComponent/CharacterInventoryComponent.
Public API.
- IInventory, IInventoryHolder.
- IInventoryService — LoadOrCreateAsync, SaveAsync, TransferAsync.
- CharacterInventoryHolder, WorldContainerComponent.
- CharacterInventoryComponent.RequestUseItem — the [Rpc.Host] entry point for using an
item.
- CharacterInventoryComponent.RequestAttachItem/RequestDetachItem (Milestone 12) — the
[Rpc.Host] entry points for attaching/detaching a weapon attachment.
- InventoryModule.GiveAsync — the /give <itemAssetId> [count] command body, internal
static for direct testability.
- WeaponAttachmentStateSerializer (Milestone 12, internal) — persists
Applejack.Items.WeaponAttachmentState.
Events published. None yet — a future ItemAdded/ItemRemoved/ItemTransferred trio is
natural once a UI needs to react, deferred until that consumer exists.
Persistence. inventories/{ownerId} — one document per holder, schema version 1. See
08_DATA_MODEL.md §8.
Each stored ItemInstance carries its whole ItemStateBag, one entry per registered
IItemStateSerializer. This module owns ContainerState and registers
ContainerStateSerializer for it — it is an ordinary registrant, not a special case: nested
containers used to be the only state shape the document could express, which silently dropped
every other module's item state on save. See
09_ITEM_FRAMEWORK.md §2.1.
An unregistered or no-longer-registered state entry is dropped with a warning; it never fails
the save or makes the inventory unloadable.
Future improvements.
- Compact representation for stackable, stateless instances if profiling shows the per-instance
overhead is material — see ADR-0006's Notes; this is an optimisation behind IInventory,
not a second model.
- Virtual/computed container contents, once banking or a similar system needs it.
- Whether S&box's [Sync] actually supports a List<InventorySlotView> (a plain record
struct) is unverified this pass — no editor/compiler available. If it turns out unsupported,
the fallback is a fixed-size array of slots on CharacterInventoryComponent, not a redesign
of InventorySlotView itself.