💰 Currency / Authority¶
🎯 Purpose¶
The Authority folder defines the authority hooks for the Currency system.
It is intended to answer a single question:
Who is allowed to mutate this wallet?
This folder does not provide replication, prediction, rollback, or reconciliation. Those responsibilities belong to your chosen netcode layer.
⚠️ Important Notes¶
- This is not a multiplayer framework
- Authority answers yes/no only
- Authority does not mutate state or perform side effects
- Replication remains the responsibility of the host project
- Currency authority is netcode-agnostic
🧩 What Lives Here¶
ICurrencyAuthority¶
The minimal authority contract:
public interface ICurrencyAuthority
{
bool HasAuthority(GameObject owner, CurrencyId currency, CurOpKind kind);
bool HasAuthorityTransfer(GameObject from, GameObject to, CurrencyId currency);
}
You implement this interface on any component that defines who may mutate which wallet.
Common placements:
- On the wallet owner
- On a scene-level manager
- On a netcode authority object
CurrencyAuthority¶
Static resolver with scene-scoped caching.
It refreshes automatically on common Unity lifecycle events such as scene load/unload, active scene changes, and domain reload.
Resolution order:
- Scene cache
- Local component on the context GameObject
- Parent transforms
- Scene roots (including inactive)
- Global scan
Key methods:
public static ICurrencyAuthority Resolve(MonoBehaviour ctx);
public static ICurrencyAuthority Resolve(Component c);
public static ICurrencyAuthority Resolve(GameObject go);
public static void Invalidate();
public static void Invalidate(Scene scene);
The resolver returns usable implementations only:
- Enabled behaviours
- Active-in-hierarchy behaviours
- Or non-
Behaviourimplementations
CurrencyAuthorityBinder¶
A built-in binder for single-player or local development:
[AddComponentMenu("RevFramework/Currency/Authority/Currency Authority Binder (Local)")]
public sealed class CurrencyAuthorityBinder : MonoBehaviour, ICurrencyAuthority
{
public bool alwaysTrue = true;
public bool HasAuthority(GameObject owner, CurrencyId id, CurOpKind kind)
=> alwaysTrue && owner;
public bool HasAuthorityTransfer(GameObject from, GameObject to, CurrencyId id)
=> alwaysTrue && from && to;
}
Attach this to a scene object or owner to allow local mutations.
For multiplayer, replace it with a project-specific binder.
🧠 Usage Guidance¶
How Currency uses authority¶
Authority is enforced through the AuthorityCurrencyService decorator.
Added during composition:
svc = CurrencyFactories.WithAuthority(inner, context);
Behaviour:
- If a binder exists and denies, the operation returns
CurOpCode.Unauthorized - If no binder is found, the operation returns
CurOpCode.Unauthorized - When the authority decorator is composed but no binder is discoverable, mutating operations are blocked until an
ICurrencyAuthorityimplementation is available - Authority is checked for all mutating operations, including audit-aware overloads
- Read operations (
GetBalance,EnsureWallet) are not authority-gated
If you want permissive behaviour, add a binder that returns true.
Typically only one binder should be active per resolution path. If multiple binders exist, the first valid one found is used.
Typical stacks¶
Single-player¶
SceneCurrencyService → Caps → Audit → Authority (Local Binder)
Multiplayer¶
SceneCurrencyService → Caps → Audit → Authority → Idempotency → Batch
RequireEscrow may also be present depending on policy.
Quick start¶
Single-player¶
svc = CurrencyFactories.WithCapsAuditAuthority(inner, policy, this);
This enables explicit authority with a permissive local binder.
Multiplayer¶
- Implement an
ICurrencyAuthoritybinder for your authority rules - Attach it to the appropriate GameObject
- Compose the authority decorator:
svc = CurrencyFactories.WithCapsAuditAuthority(inner, policy, this);
- Run mutations on the authority side
- Replicate balances through your netcode layer
If authority denies:
CurOpResult.Code == CurOpCode.Unauthorized
🧪 Diagnostics¶
Troubleshooting¶
Unauthorizedusually means no authority binder was found, or the active binder returnedfalse- Missing client updates usually means replication has not been implemented
FAQs¶
Where should the binder live? On the wallet owner. Parents, scene, and global resolution are fallbacks.
Do I need to call Invalidate()?
Only when binders are added or removed dynamically at runtime.
Can I register multiple binders? The resolver uses the first valid one it finds. Placement matters.
Does Currency replicate balances? No. Replication is outside the scope of this folder.
🚫 Not for Production Use¶
Example netcode rules such as NGO, Mirror, or Fusion authority checks are not provided here. This folder supplies the seam only.
🔗 Related Documentation¶
- Core — composition helpers such as
WithAuthorityandWithCapsAuditAuthority - Internal —
AuthorityCurrencyServicedecorator - Policies — optional cap and transfer rule enforcement
- Adapters — inventory-backed currency implementations