Skip to content

🧰 Using the Inventory System

The Inventory system is a scene-scoped, authority-aware container service. It powers adding, removing, sorting, searching, equipping, and using items through a result-first API (InvOpResult).

Containers are created lazily. Calling Get(), GiveExact(), AddMax(), SplitResult(), etc. will create the container the first time it is accessed for a given owner + container name.

See also: - Services → lifecycle, deltas, authority, resolution rules - Data → ItemDefinition, ItemDatabase, ContainerSizePolicy - Equipment → equipping, slot filters, visuals - UIoptional reference UI (Unity UI)

If you want terse bool-returning helpers, import:

using RevGaming.RevFramework.Inventory.Extensions;

…but for gameplay logic you should always prefer the result-first APIs (GiveExact, SplitResult, TransferResult, etc.) so failure reasons are explicit.


🚀 Quickstart

1. Add SceneInventoryService

  • Create an empty GameObject
  • Add SceneInventoryService
  • Assign your ItemDatabase
  • (Optional) assign a ContainerSizePolicy

⚠ Only one SceneInventoryService may exist per scene. Extra instances auto-destroy (Awake) or auto-disable (OnEnable).


2. Add CharacterInventory to your player

This automatically adds InventoryOwnerHook and exposes:

playerCharacterInventory.Container
  • Default backpack size = 24 slots
  • May be overridden by ContainerSizePolicy

3. Subscribe to container deltas (UI / observers)

svc.OnContainerChanged += delta =>
{
    bridge.Handle(delta); // e.g. InventoryUiBridge
};

🔔 Deltas only fire after a container exists. Containers are created lazily by Get(), GiveExact(), AddMax(), etc.


🔑 Authority

All authoritative gameplay mutations should be performed through IInventoryService.

  • Service-level mutations consult IInventoryAuthority
  • Denied mutations return InvOpCode.NoAuthority

Direct calls on InventoryContainer or CharacterInventory: - Do not consult authority - Are intended for single-player, local logic, or UI-only usage

❗ If you mutate inventory from a non-authoritative instance, the call may succeed locally but is logically rejected by authority.

Single-player

InventoryAuthorityBinder   // allows all mutations

Multiplayer

  • Implement your own authority binder (NGO / Mirror / Fusion / custom)
  • Invoke mutations only on the authoritative instance (server / host)

1️⃣ Adding Items

var svc = SceneInventoryService.Instance;

var res = svc.GiveExact(
    player,
    new ItemStack { def = sword, quantity = 1 },
    "Backpack"
);

if (!res.Success)
    Debug.LogWarning($"Add failed: {res.Code} {res.Message}");

Local / single-player only

var inv = playerCharacterInventory.Container;

var stack = new ItemStack { def = sword, quantity = 1 };

var res = inv.TryAddAllResult(stack);
if (!res.Success)
    Debug.LogWarning($"Add failed: {res.Code} {res.Message}");

⚠ Direct container calls bypass authority. Use service calls for authoritative gameplay.


2️⃣ Removing Items

Authority-safe

var res = svc.RemoveByGuid(
    player,
    "sword-guid",
    2,
    "Backpack"
);

For slot-specific removal, use RemoveFromSlot(...).

Local / single-player only

var res = inv.TryRemoveResult("sword-guid", 2);

3️⃣ Splitting Stacks

var splitRes = svc.SplitResult(
    player,
    "Backpack",
    srcIndex: 0,
    amount: 2,
    targetIndex: -1 // auto-place
);

Split fails if: - amount >= stack.quantity - destination cannot accept the item - filters or capacity rules block placement


4️⃣ Equipping Items

var equipRes = playerCharacterEquipment.TryEquipFromInventoryResult(
    playerCharacterInventory,
    slotIndex: 0,
    slotId: "Weapon"
);

⚠ Equipment slots store exactly one item (quantity is forced to 1).


5️⃣ Unequipping Items

var unequipRes = playerCharacterEquipment.TryUnequipToInventoryResult(
    playerCharacterInventory,
    "Weapon"
);

6️⃣ Using an Item

var useSys = FindFirstObjectByType<ItemUseSystem>();

var useRes = useSys.UseResult(player, slotIndex: 0);
  • Effects run in order
  • At least one effect must apply
  • Exactly one item is consumed on success
  • Consumption is authority-gated

7️⃣ Snapshots (Save / Load)

var dto = InventorySnapshots.Capture(playerInv, playerEquip);
InventorySnapshots.SaveToFile("mysave", dto);

var loaded = InventorySnapshots.LoadFromFile("mysave");
if (loaded != null)
{
    InventorySnapshots.Apply(
        playerInv,
        playerEquip,
        loaded,
        myItemDatabase,
        InventorySnapshotOptions.Default
    );
}

⚠ Snapshots serialize inventory state only. They are not a full persistence or save-slot system.

Gotchas

  • Extra snapshot slots are ignored
  • Equipment quantities are forced to 1
  • Unknown GUID handling:
  • Skip
  • OR SubstitutePlaceholder (requires ItemDatabase.missingPlaceholder)

🔎 Sorting & Searching

svc.SortByRarity(player);      // rarity desc
var hits = svc.Search(player, "potion");

Sorting is stable and idempotent. Re-sorting without changes preserves relative order.


💡 Tips

  • Always check Success before acting on results
  • Use both .Code and .Message for UX
  • Prefer service calls for gameplay; containers for local/UI logic

Common InvOpCodes

Code Meaning
Ok Success
NoSpace Not enough capacity
NotFound Item not found
NotEnoughQuantity Tried to remove too many
FilterMismatch Slot cannot accept item
NoAuthority Authority denied
SwapBlocked Merge/swap not possible
Partial Partially succeeded (Success = false)

🧹 Optional / Safe to Delete

Folder Safe to Delete Notes
UI/ ✅ Yes Reference Unity UI only
Bridges/ ✅ Yes Optional integrations (e.g. Pickups)
Equipment/ ✅ Yes Only needed if you support equipping
Snapshots/ ✅ Yes Optional save/load helpers
Presentation/ ✅ Yes Rarity themes, visuals

⏭ Next Steps

  • Explore InventoryDebugPanel for hands-on testing
  • Browse Samples/Inventory/ for example setups
  • Read Services and Data

🎥 YouTube Playlist:
Inventory System Teaching Panels https://www.youtube.com/playlist?list=PLRcFCSvBkJAGXfc2fbQQrJ5WfWVLNqsBJ