🛠️ Services¶
The Services folder contains the runtime backbone of the Inventory system:
SceneInventoryService — the scene-scoped manager that owns all containers, applies mutations, and raises per-slot diffs.
UI bridges and authoritative gameplay flows route through this service.
CharacterInventory also exposes direct container access for local / single-player or UI-only usage.
🌍 What Is SceneInventoryService?¶
SceneInventoryService is the central runtime authority for inventory state in a scene.
It:
- Acts as a scene-local singleton (extra instances self-destroy or self-disable)
- Lazily creates
(owner, container)→InventoryContainerpairs - Computes slot-level diffs via snapshots and raises
OnContainerChanged - Enforces authority using
IInventoryAuthority(default = allow) - Exposes a fully result-first API (
InvOpResult) - Applies container sizing using
ContainerSizePolicyand backpack rules
It is the only class that:
- Owns container lifetimes
- Emits InventoryDelta
- Enforces authority
🚀 Quick Start¶
var svc = SceneInventoryService.Instance;
// Add a sword (authority-safe)
svc.GiveExact(
player,
new ItemStack { def = sword, quantity = 1 },
"Backpack"
);
// Listen for diffs
svc.OnContainerChanged += delta =>
{
Debug.Log($"{delta.container}: {delta.changes.Count} slot(s) changed");
};
Setup steps:
- Create an empty GameObject
- Add
SceneInventoryService - Assign an ItemDatabase
- (Optional) Assign a ContainerSizePolicy
🔔 Events¶
OnContainerChanged¶
Raised after service-level mutations.
Each InventoryDelta contains:
owner→ GameObject that owns the containercontainer→ canonical container name (trimmed + lowercase)changes→ per-slot diffs(index, before, after)
Notes:
- Uses internal snapshots to compute diffs
- Resize operations emit a full resnap (all slots included)
- Ideal for incremental UI repainting or lightweight syncing
🔑 Authority¶
All service-level mutating operations begin with an authority gate:
if (!RequireAuthority(owner, out var fail))
return fail;
Behaviour:
- No binder present → defaults to allowed (single-player friendly)
InventoryAuthorityBinder→ grants or denies mutations- Custom binder → implement netcode-appropriate rules
Denied mutations return:
InvOpResult.Fail(InvOpCode.NoAuthority,
"No authority to mutate this inventory.");
Example:
var res = svc.RemoveByGuid(player, potionGuid, 1, "Backpack");
if (res.Code == InvOpCode.NoAuthority)
Debug.LogWarning("Not allowed!");
Reads (Get, Search, etc.) are never gated.
✨ Common Operations (Result-First)¶
svc.GiveExact(player, stack, "Backpack"); // strict add
svc.AddMax(player, stack, out var rem, "Backpack"); // partial add
svc.RemoveByGuid(player, guid, 2, "Backpack"); // strict remove
svc.MergeThenSwap(player, "Backpack", 0, 1); // merge → swap
svc.ResizeContainer(player, "Backpack", 32, false, out var truncated);
All service APIs return InvOpResult, containing:
SuccessCodeMessage
🔎 Sorting & Searching¶
Sorting¶
svc.Sort(player, "Backpack", InventorySortSpec.ByNameAsc());
- Stable sorting
- Optional empty-slots-last behaviour
- Idempotent when data is unchanged
Searching¶
svc.Search(player, "Backpack", "potion"); // full signature
Via extensions:
svc.Search(player, "potion"); // defaults to "Backpack"
svc.Search(player, "potion", "Hotbar"); // explicit container
- Case-insensitive search over name, category, and tags
🧠 Container Creation & Sizing¶
Containers are created lazily on first access:
Get(owner, container)GiveExact,AddMax,SplitResult, etc.
Sizing rules:
- Apply
ContainerSizePolicyif present - Backpack special-case via
CharacterInventory.backpackSize - Fallback defaults (Backpack = 24, others = 12)
Changing policies does not resize existing containers.
⚠️ Gotchas¶
- Authority: Always check result codes — never assume success
- Deferral: Resize, clear, and sort collapse into a single delta
- Singleton:
- Duplicate services self-destroy in
Awake - Duplicate services self-disable in
OnEnable - Canonical names: Container names are normalized (trim + lowercase)
- Backpack sizing: May be overridden by policy when
policyOverridesBackpackis enabled
📎 Related¶
- Abstractions → interfaces and result contracts
- Containers → slot logic and delta foundations
- Data → item definitions and databases
- UI → reacting to deltas with
InventoryUiBridge