💰 Currency System¶
🎯 Purpose¶
Currency is a modular, service-driven wallet system for managing balances such as gold, gems, tickets, energy, etc.
It is:
- Wallet-agnostic — you can replace the backing implementation (advanced use)
- Network-agnostic — authority and idempotency are optional layers
- UI-agnostic at its core — the runtime service has no UI dependency
- Inventory-agnostic at its core — the Inventory adapter compiles only when
REV_INVENTORY_PRESENTis defined - Decorator-composed — behaviour is layered explicitly via factories
Everything revolves around one contract:
ICurrencyServiceis the authority for ledger state and mutation. Everything else composes behaviour around it.
🧠 System Philosophy¶
Currency is:
- Deterministic
- Explicit
- Composition-first
- Service-driven
- Honest about its guarantees
It is not:
- A networking framework
- A replication layer
- A transactional ACID database
- A hidden global system
🧩 What Lives Here¶
| Concept | What it is |
|---|---|
ICurrencyService |
Core wallet operations (credit/debit/set/transfer + events) |
CurrencyId |
Canonical id ("gold", "gems") normalized lowercase + trimmed |
Money |
long minor units (no floats), overflow-checked |
| Decorators | Optional layers via CurrencyFactories (Caps, Audit, Authority, Escrow, RequireEscrow, Idempotency, BatchEvents) |
| Bootstrap | Scene helper that composes and publishes a stack |
| Persistence (optional) | Snapshot + JSON helpers |
| Exchange (optional) | Table-based conversion engine |
| UI (optional) | UGUI / TMP / UITK bars |
| Policies (optional) | Cap rules + RequireEscrow |
| Definitions (optional) | Metadata + formatting |
| Public API | Stable helper surfaces |
| UX | Reason-text helpers |
🧠 Usage Guidance¶
1) Add to your scene¶
Minimum setup:
SceneCurrencyService- (Recommended)
CurrencyServiceBootstrap
2) Use from gameplay code¶
var svc = CurrencyResolve.ServiceFrom(this);
var gold = new CurrencyId("gold");
var result = svc.Debit(player, gold, 50);
if (!result.Success)
Debug.LogWarning($"Debit failed: {result.Code} ({result.Message})");
3) Listen for changes¶
svc.OnWalletChanged += d =>
{
Debug.Log($"{d.owner.name} {d.currency} changed: {d.before.amount} -> {d.after.amount}");
};
🧩 Core Contract: ICurrencyService¶
bool EnsureWallet(GameObject owner);
Money GetBalance(GameObject owner, CurrencyId currency);
CurOpResult Credit(GameObject owner, CurrencyId currency, Money amount);
CurOpResult Debit(GameObject owner, CurrencyId currency, Money amount);
CurOpResult SetBalance(GameObject owner, CurrencyId currency, Money newBalance);
CurOpResult Transfer(GameObject from, GameObject to, CurrencyId currency, Money amount);
event Action<CurrencyDelta> OnWalletChanged;
⚠️ Important Notes¶
- Operations are synchronous
- Successful mutations emit
OnWalletChanged - Single operations are atomic
- Multi-operation atomicity is not provided by default
🧱 Composition & Decorators¶
You do not instantiate decorator types directly.
Use CurrencyFactories.
svc = CurrencyFactories.WithCapsThenAudit(svc, policy);
svc = CurrencyFactories.WithAuthority(svc, this);
svc = CurrencyFactories.WithIdempotency(svc);
svc = CurrencyFactories.WithBatchEvents(svc);
Recommended ordering¶
- Caps
- Audit
- Authority
- (Escrow / RequireEscrow when composed)
- Idempotency
- BatchEvents
⚠️ Important Notes¶
RequireEscrow vs Escrow¶
- RequireEscrow = guard
- Escrow = capability
Escrow must be explicitly composed. RequireEscrow only enforces it.
🧠 Usage Guidance¶
Service Resolution¶
var svc = CurrencyResolve.ServiceFrom(this);
Resolution order:
- Published override
- Cached scene result
- Scene search (
SceneCurrencyService)
Bootstrap¶
CurrencyServiceBootstrap:
- Finds inner service
- Composes stack
- Publishes via override
- Cleans up on disable
🧩 What Lives Here (Optional Systems)¶
Persistence¶
var snap = CurrencyPersistence.Capture(svc, player, ids);
CurrencyPersistence.Restore(svc, player, snap, "LoadWallets");
Exchange¶
var ex = CurrencyFactories.BuildExchange(table);
ex.TryExchange(svc, player, src, dst, 100, "Exchange");
UI¶
- CurrencyBar
- CurrencyBarTMP
- CurrencyBarUITK
Public Helpers¶
- CurrencyBatching
- TransferPolicyProviders
📦 Folder Overview¶
- Abstractions
- Core
- Internal
- Bootstrap
- Authority
- Policies
- Definitions
- Exchange
- Persistence
- UI
- PublicAPI
- EditorApi
- Adapters
- UX
⚠️ Important Notes¶
- UI, Exchange, Persistence, and Definitions are optional layers
- Adapters depend on external systems (e.g., Inventory)
- Composition depends on installed modules and defines
🚫 Not for Production Use¶
- Debug/UX helpers (e.g., reason-text utilities) are not intended as gameplay logic
- Sample/UI components should be treated as reference implementations
🔗 Related Documentation¶
- Currency Public API
- Currency Integration Surfaces
- Currency Mental Model
- Currency FAQ
🎯 Summary¶
Currency provides a stable ICurrencyService contract with optional composition:
- Caps
- Audit
- Authority
- Escrow
- RequireEscrow
- Idempotency
- Batch events
- Persistence
- Exchange
- UI
You choose the stack. The contract remains stable.