Skip to content

🎲 Crafting — Random

The Random folder contains runtime implementations of IRandomProvider, used by the Crafting system for all chance-based rolls.

This abstraction enables deterministic tests and replayable chance outcomes and allows custom RNG behaviour, while keeping Crafting decoupled from Unity’s global random state.

Truth-pass note: This README reflects behaviour implemented by the current runtime. Determinism depends on configuration and on the RNG call sequence remaining unchanged.


🎯 Purpose

Crafting supports probabilistic outputs (for example, a 10% chance of a rare drop). To keep outcomes testable and replayable, all randomness flows through the IRandomProvider abstraction rather than calling UnityEngine.Random directly.

This provides:

  • No hidden coupling to Unity’s global RNG state (unless you choose to use it)
  • Reproducible results when seeded and the RNG call sequence is unchanged
  • A clean seam for multiplayer, replay, or test systems

The implementations in this folder are internal defaults. Custom RNG behaviour should be supplied by consumers via their own IRandomProvider implementations.


🧩 Implementations

UnityRandomProvider

  • Uses UnityEngine.Random.value for each roll.
  • Suitable for live gameplay where coupling to Unity’s global RNG is acceptable.
  • Any system modifying UnityEngine.Random.state will influence Crafting outcomes.

SeededRandomProvider

  • Lightweight XorShift-based RNG implementation.
  • Deterministic per instance given the same seed and call sequence.
  • Enforces a non-zero internal state even if 0 is supplied as the seed.
  • Produces uniform floats in [0,1) using the top 24 bits of the PRNG (~16 million discrete values).

Selected when:

  • deterministicRng == true, or
  • CraftingServiceCore.UseDeterministicRng(seed) is called

Determinism applies to:

  • Chance outputs (per-craft rolls)
  • Per-craft rolls inside batches when using exact mode and a deterministic RNG

Note: Determinism is not bit-identical when binomial approximation is enabled for large batches (see below).


⚙️ RNG selection in CraftingServiceCore

The crafting service selects its RNG using this precedence:

  1. Custom RNG component
    If randomProviderComponent is assigned and implements IRandomProvider, it is used during dependency resolution.

  2. Deterministic RNG
    When deterministic mode is enabled, the service creates: SeededRandomProvider(seed).

  3. Unity RNG (default)
    Otherwise the service falls back to: UnityRandomProvider().

You may switch RNGs at runtime:

// Deterministic (replay-safe when call order is unchanged)
craftingService.UseDeterministicRng(seed: 12345);

// Back to UnityEngine.Random
craftingService.UseUnityRng();

Assigning a custom IRandomProvider component takes priority during dependency resolution. Calling UseDeterministicRng() or UseUnityRng() overrides the active RNG instance.


🔒 RNG contract

public interface IRandomProvider
{
    float Value01(); // uniform value in [0,1)
}

Crafting calls Value01() for every probabilistic check.

Chance outputs are evaluated:

  • At delivery time: one roll per craft (unless approximation is enabled)
  • During space preflight: via policy-based space budgeting (no RNG calls)

No chance rolls occur at accept time in the current implementation.


📦 Batched crafts and RNG

Large batch crafts can optionally trade bit-identical determinism for performance.

Exact mode (default)

When:

  • enableBinomialApprox == false, or
  • batchCount < binomialApproxThreshold

Crafting performs one RNG roll per craft inside the batch.

This mode is deterministic when using SeededRandomProvider and an unchanged RNG call sequence.

Approximation mode (optional)

When:

  • enableBinomialApprox == true, and
  • batchCount >= binomialApproxThreshold (default: 512)

Crafting performs a single approximate binomial draw instead of per-craft rolls.

  • Significantly reduces RNG calls for very large batches
  • Preserves statistical expectation
  • Not bit-identical, even with deterministic RNG enabled

To guarantee deterministic replays, disable binomial approximation.


🧪 Usage tips

  • RNG providers manage their own internal state; Crafting never mutates it directly.
  • For deterministic tests or lockstep / rollback multiplayer, seed a deterministic provider explicitly.
  • For unit testing, provide a stub RNG:
public sealed class AlwaysSuccess : IRandomProvider
{
    // Very close to 1.0f, but still inside [0,1)
    public float Value01() => 0.9999999f;
}
  • To simulate guaranteed failure, return 0f.
  • For cryptographically secure randomness, implement your own IRandomProvider.

These RNG abstractions ensure crafting chance outcomes remain reproducible, testable, and isolated from Unity’s global random state when desired.