💰 Currency – Bootstrap¶
Namespace: RevGaming.RevFramework.Currency.Bootstrap
This folder contains the composition entry points for the Currency system.
It defines how a runtime ICurrencyService stack is assembled, wrapped, and published for resolution.
The bootstrap layer is intentionally small and predictable: Typically one composed service per scene; if multiple bootstraps publish, the last published override becomes active..¶
Purpose¶
- Build a fully composed currency service at scene load.
- Enforce a clear, deterministic decorator order.
- Provide a simple, Inspector-driven setup for designers.
- Ensure all systems (Inventory, Pickups, UI, Abilities, Economy, etc.) resolve the same service instance.
Bootstrap code does not implement currency logic — it only wires the stack together.
Key files¶
CurrencyServiceBootstrap.cs¶
A MonoBehaviour that composes and publishes the active currency service for the scene.
High-level behaviour:
- Locates the inner baseline service (
SceneCurrencyService). - Wraps it with decorators in a fixed, intentional order.
- Automatically inserts a RequireEscrow guard when the active policy requests it.
- Publishes the final service using the supported bootstrap API.
- Runs before most user scripts (
DefaultExecutionOrder(-499)).
CurrencyBootstrap.cs¶
The supported public entry point for publishing a composed service.
using var scope = CurrencyBootstrap.Publish(service);
- Publishes a service for resolution via
CurrencyResolve. - Returns an
IDisposablescope. - Disposing the scope clears the resolver override.
This avoids leaking global state and keeps bootstrapping explicit and scoped.
CurrencyServiceOverride.cs (internal)¶
Internal storage for the currently published resolver override.
- Not part of the supported runtime API.
- Used only by bootstrappers, tests, and teachables.
- Automatically reset on domain reload.
External code should never interact with this type directly.
Default composition pipeline¶
The built-in bootstrap constructs the service stack in this order:
| Stage | Layer | Purpose |
|---|---|---|
| 1 | Caps | Enforce per-currency min/max rules |
| 2 | Audit | Record wallet deltas with reason/sourceId |
| 3 | Authority | Gate mutations via ICurrencyAuthority |
| 4 | RequireEscrow (guard) | Enforce escrow presence when policy requires it |
| 5 | Idempotency | Drop duplicate replayed requests |
| 6 | BatchEvents | Emit a single event for multi-op commits |
This ordering matches the runtime implementation exactly.
Important
The RequireEscrow layer is a guard only. It does not add escrow capability.
Typical usage¶
Add CurrencyServiceBootstrap to a GameObject in your scene:
[DefaultExecutionOrder(-499)]
public sealed class CurrencyServiceBootstrap : MonoBehaviour
{
void Awake()
{
// Composition handled internally
}
}
At runtime:
- The bootstrap locates
SceneCurrencyService. - The service stack is composed.
- The composed service is published.
- All consumers resolve via:
var svc = CurrencyResolve.ServiceFrom(this);
Customisation¶
If you need a different decorator order or a custom stack, create your own bootstrap:
public sealed class MyCurrencyBootstrap : MonoBehaviour
{
void Awake()
{
var inner = FindFirstObjectByType<SceneCurrencyService>();
ICurrencyService svc = inner;
// Example: Audit → Caps → Authority only
svc = CurrencyFactories.WithAudit(svc);
svc = CurrencyFactories.WithCaps(svc, policy);
svc = CurrencyFactories.WithAuthority(svc, this);
using var scope = CurrencyBootstrap.Publish(svc);
}
}
Custom bootstraps should resolve the raw
SceneCurrencyServicedirectly to avoid stacking on top of an already-published override.
You fully control the order and which layers are included.
Escrow notes¶
RequireEscrowis added only whenCurrencyPolicy.RequireEscrow == true.- The guard does not add escrow functionality.
- To enable real holds, you must explicitly compose:
CurrencyFactories.WithEscrow(...), orCurrencyFactories.WithEscrowAndPump(...).
If no escrow layer exists and the policy requires it:
- Debits and transfers fail with CurOpCode.ServiceMissing.
Gotchas¶
- Only one bootstrap should typically exist per scene.
- The last bootstrap to publish wins (the override is process-global; supports additive scenes if ordered intentionally).
- Removing a bootstrap without disposing the publish scope (for example, in a custom bootstrap) will leave the last published service active.
Related¶
- Core — factories, resolver, service override helpers
- Internal — decorator implementations
- Authority — authority binders and enforcement
- Policies — cap rules and escrow requirements