RevFramework — Currency System¶
Overview¶
Service-first, result-driven economy layer
The Currency module is a service-first, result-driven economy layer designed for real production games — not prototypes.
It provides clean composition, optional auditing, authority validation, escrow, idempotency, batching, and persistence without hard coupling to UI, inventory, or netcode.
This page is a high-level overview for developers who are not reading the full MkDocs site. It does not duplicate the full Currency README or API reference.
Key design goals¶
- Service-first:
ICurrencyServiceis the authority for ledger state and mutation for all wallet state and mutations. - Result-driven: Every mutation returns a
CurOpResultwith an explicit outcome code. - Composable: Behaviour is added via decorators (Caps, Audit, Authority, Escrow, RequireEscrow, Idempotency, BatchEvents).
- Netcode-agnostic: Authority enforcement is optional; replication is user-defined.
- Low lock-in: Integration is via interfaces; concrete implementations remain internal.
Atomicity
Single operations are atomic. Cross-operation atomicity depends on orchestration and stack composition.
What's included¶
| Category | Purpose |
|---|---|
| SceneCurrencyService | Baseline in-memory wallet service managing balances per owner. Emits CurrencyDelta via OnWalletChanged. |
| CurrencyServiceBootstrap | Scene entry point that composes and publishes a recommended decorator stack. |
| CurrencyFactories | Supported factory helpers for composing currency decorators safely. |
| CurOpResult / CurOpCode | Deterministic result types for all mutations (Ok, Overflow, Unauthorized, etc.). |
| CurrencyPolicy | ScriptableObject defining min/max caps and the RequireEscrow guard. |
| CurrencyExchangeTable | Authoring asset for table-driven currency exchange (rates, fees, rounding). |
| WalletSnapshot / CurrencyJsonSave | Optional snapshot and JSON persistence helpers. |
| CurrencyAudit | Optional audit trail with query helpers (read-only to consumers). |
| CurrencyTxn / CurrencyBatching | Orchestrated multi-operation commits with best-effort rollback and optional batch events. |
| CurrencyAwaiters | Async / coroutine helpers that complete on currency events. Invalidation — owner destroyed, service gone — is polled, since neither raises one. |
| ItemBackedCurrencyAdapter | Optional adapter that bridges Inventory-backed values into Currency. |
| Currency UI Bars | Optional UGUI / TMP / UITK components for live wallet display. |
| Teaching Panels | Editor/runtime panels for learning, debugging, and visual inspection. |
Hook-up summary¶
1. Add the service¶
Add SceneCurrencyService to your scene. This provides the base wallet store for all currency owners.
2. Add a bootstrap (recommended)¶
Add CurrencyServiceBootstrap to compose and publish the service stack.
A typical recommended stack is:
Caps → Audit → Authority → (Escrow / RequireEscrow) → Idempotency → BatchEvents
Escrow must be composed
Escrow capability (WithEscrow) must be explicitly composed; RequireEscrow only enforces it.
3. Define currencies (optional)¶
Create CurrencyDefinition assets:
id(e.g."gold")displayNameicon
Optionally group them under a CurrencySet for discovery and formatting.
4. Add UI (optional)¶
Use:
CurrencyBar(UGUI)CurrencyBarTMP(TextMeshPro)CurrencyBarUITK(UI Toolkit)
Bind an owner and a CurrencyDefinition. UI components subscribe to OnWalletChanged and auto-rebind when the service appears or swaps.
5. Test & inspect¶
In Play Mode, open any Currency teaching panel to:
- perform credits/debits/transfers
- inspect caps, audit, escrow, persistence
- see batch events and deltas live
Integration with other RevFramework modules¶
| Module | Interaction |
|---|---|
| Inventory | Optional adapter treats item stacks as currency values. |
| Crafting | Costs and rewards debit/credit wallets via ICurrencyService. |
| Pickups | Pickup effects may trigger wallet credits. |
| Economy / Stores | Purchases return CurOpResult for deterministic handling. |
| Netcode | Authority is enforced when composed; replication and reconciliation remain user-defined. |
Multiplayer note¶
RevFramework Currency is authority-gated, not replicated.
- Authority is enforced only when the stack includes
WithAuthority(...). - Replication, rollback, and reconciliation remain your responsibility.
- Use Idempotency (
|rid:<token>) to prevent double-spends on retries.
Quote
We provide guardrails — you provide the networking.
Extension points (supported)¶
- Implement
ICurrencyServiceto back wallets with a server or backend. - Implement
ICurrencyAuthorityfor custom permission logic. - Compose custom stacks via
CurrencyFactories. - Build UI or tools by subscribing to
OnWalletChanged.
Avoid depending on concrete service implementations or internal decorators.
Typical usage¶
var result = svc.Credit(player, new CurrencyId("gold"), 100);
if (!result.Success)
Debug.LogWarning(result.Code);
Or, for orchestrated multi-operation commits:
CurrencyTxn.Begin(svc, "QuestReward", "QuestReward")
.Debit(player, new CurrencyId("gold"), 500)
.Credit(player, new CurrencyId("gems"), 5)
.Commit();
Do not add a |rid: idempotency token to a transaction's sourceId
Begin passes that one string to every staged leg verbatim; it does not derive a distinct id per leg. Because the idempotency decorator keys per operation — kind, currency, request id and amount — two legs sharing a kind and a currency collide. With different amounts the second fails as IdempotencyMismatch; with equal amounts it is silently dropped as a replay and the transaction reports success having moved half the money.
The example above survives only because its legs differ in both kind and currency. Add a second Debit(player, gold, 500) and it would quietly charge once. For retry-safety, dedupe at your own transaction boundary instead — see the Currency FAQ.
Where to learn more¶
- Full MkDocs documentation:
/currency/ - Video deep-dives: Decorators, Escrow, Idempotency, Transactions
- In-Editor Teaching Panels for live demonstrations
Summary¶
The Currency System provides a composable, deterministic wallet service:
- Single-operation atomic
- Result-driven and explicit
- Auditable (optional)
- Authority-aware (when composed)
- Escrow-capable (when composed)
- Best-effort rollback orchestration
- UI-agnostic and netcode-agnostic
Use the service. Compose deliberately. Keep logic out of UI.