Skip to content

💰 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:

  1. Scene cache
  2. Local component on the context GameObject
  3. Parent transforms
  4. Scene roots (including inactive)
  5. 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-Behaviour implementations

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 ICurrencyAuthority implementation 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

  1. Implement an ICurrencyAuthority binder for your authority rules
  2. Attach it to the appropriate GameObject
  3. Compose the authority decorator:
svc = CurrencyFactories.WithCapsAuditAuthority(inner, policy, this);
  1. Run mutations on the authority side
  2. Replicate balances through your netcode layer

If authority denies:

CurOpResult.Code == CurOpCode.Unauthorized

🧪 Diagnostics

Troubleshooting

  • Unauthorized usually means no authority binder was found, or the active binder returned false
  • 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.


  • Core — composition helpers such as WithAuthority and WithCapsAuditAuthority
  • Internal — AuthorityCurrencyService decorator
  • Policies — optional cap and transfer rule enforcement
  • Adapters — inventory-backed currency implementations