Skip to content

📦 Abstractions

Interfaces, identifiers, and core result types that every other Inventory subsystem builds on. These are stable runtime contracts and are safe to reference from runtime or editor assemblies.

These types contain no runtime mutation logic or container behaviour. They define contracts, identifiers, and result semantics only.


🎯 Purpose

Abstractions provide:

  • Shared contracts across all Inventory subsystems
  • Stable identifiers and value types
  • Result semantics for mutation operations

They allow systems to depend on behaviour without depending on concrete implementations.


🧩 What Lives Here

Core Interfaces

  • IInventoryService Scene-scoped inventory API for mutations (write operations). Exposes result-first methods (GiveExact, RemoveByGuid, SplitResult, ResizeContainer, etc.).

Service-level mutations consult authority and may return InvOpCode.NoAuthority when denied.

Direct container access or convenience wrappers may bypass authority. Use the service when authority enforcement is required.

Inherits from IReadOnlyInventoryService.


  • IReadOnlyInventoryService Read-only view of inventory data.

Exposes:

  • Get(GameObject owner, ContainerId container)
  • Search(GameObject owner, ContainerId container, string query)
  • event Action<InventoryDelta> OnContainerChanged

Events are raised after service-level mutations.

Direct container mutations do not emit deltas.


  • IInventorySearch / IInventorySorter Strategy seams for searching and sorting.

Default implementations are provided by SceneInventoryService.

Projects may replace these strategies during service initialisation if they control the service instance.


Canonical IDs

  • ContainerId

  • Normalised to trimmed, lowercase

  • Defaults to "backpack" when null or empty
  • Supports implicit conversion to and from string

  • ItemGuid

  • Normalised to trimmed, lowercase

  • Provides IsEmpty for "no GUID"

Both are lightweight value types with strict equality.

Internal systems use canonicalised lowercase keys for stable and case-insensitive behaviour.


Result Contracts

  • InvOpResult Represents (Success, Code, Message) for mutation outcomes

  • InvOpCode Enumerates possible operation results

Example:

var res = svc.RemoveByGuid(player, swordGuid, 2, "Backpack");
if (!res.Success)
    Debug.LogWarning($"Remove failed: {res.Code} {res.Message}");
var res = svc.SplitResult(player, "Backpack", slotIndex, 3, -1);
if (!res.Success)
    ui.ShowError(res.ToUserMessage("Split"));

Convenience Helpers

Located in Inventory/Extensions/Results:

  • InvOpResultExtensions.ToUserMessage(this InvOpResult, string prefix)

Converts a result into a short user-facing message.

A matching helper exists for Currency (CurOpResultUtil) when REV_CURRENCY_PRESENT is defined.


🧠 Usage Guidance

Why result-first?

  • Expected failures do not rely on exceptions
  • Operations return structured outcomes instead of booleans
  • Results can be passed directly into UI, logs, or diagnostics

⚠️ Important Notes

  • These types define contracts only
  • They do not implement runtime behaviour
  • They should not be extended with gameplay logic

Concrete implementations (services, containers, UI bridges) should exist behind these contracts.


🧩 Common Result Codes

Code Meaning
Ok Operation succeeded
NoSpace Not enough capacity
NotFound Item not found
NotEnoughQuantity Too many removed
FilterMismatch Slot cannot accept item
SwapBlocked Merge or swap blocked
NoAuthority Mutation denied
Partial Partial success (Success = false)