Table of Contents

OSL Deterministic Core

A dependency-free deterministic RNG suite for reproducible state, replay and lockstep-style workflows, weighted gacha, pity logic, and repeatable tests.

OSL Deterministic Core gives gameplay programmers a reproducible PCG32 sequence with a restorable 16-byte state and independent streams. It helps isolate random decisions for replay, lockstep-style samples, rollback state, automated QA, and procedural content without claiming to make the rest of a Unity simulation deterministic.


Table of Contents

  1. Overview
  2. Who Should Use This
  3. Requirements
  4. Installation
  5. Quick Start
  6. Core API
  7. Shuffling
  8. Gacha / Weighted Draws
  9. Pity System
  10. Save & Restore State
  11. Troubleshooting

Overview

Key highlights:

  • Reproducible RNG state — the same seed, stream, state, input, and call order produce the same package RNG sequence.
  • Allocation-conscious core — the core RNG hot path has a best-effort zero-allocation test; table and collection construction can allocate.
  • Zero third-party dependencies — pure .NET Standard 2.1. No native plugins.
  • Integer-first probability — weighted draws can use integer comparisons to reduce floating-point drift risk.
  • PluggableIRandomEngine and IPitySystem are extension points.
  • Test-friendly — includes EditMode tests for deterministic behavior, distribution checks, state round-trips, and pity semantics.

Who Should Use This

Use OSL Deterministic Core when your game or tool needs:

  • Reproducible random sequences
  • Replay-friendly random state management
  • Deterministic simulations
  • Gacha, weighted draw, shuffle, or pity behavior that should be testable

Requirements

  • Unity 2021.3 LTS or newer
  • Tested with Unity 2021.3.45f2 and 6000.3.12f1 for package-isolation import and compile
  • Scripting runtime: .NET Standard 2.1

Installation

  1. Search for OSL Deterministic Core in the Unity Asset Store, or open it directly at Unity Asset Store (OSL Deterministic Core).
  2. Click Add to My Assets.
  3. Open Unity and navigate to Window > Package Manager.
  4. Select Packages: My Assets from the dropdown.
  5. Locate OSL Deterministic Core, download it, and click Import.
  6. Verify that the Assets/OSL/Deterministic/ folder is present.
  7. (Optional) Import the sample package from the Package Manager Samples section.

Quick Start

using OSL.Deterministic.Runtime.Core;

// Seed-only: identical seed produces identical sequences.
var rng = new OSLRandom(seed: 0xC0FFEE);

// Seed + stream id: derive independent sub-RNGs from one master seed.
var combatRng = new OSLRandom(new PCG32(seed: 0xC0FFEE, stream: 1));
var lootRng   = new OSLRandom(new PCG32(seed: 0xC0FFEE, stream: 2));

Avoid mixing UnityEngine.Random or System.Random into deterministic gameplay code that relies on this package.


Core API

uint  u  = rng.NextUInt();           // [0, 2^32)
uint  u2 = rng.NextUInt(100u);       // [0, 100)  unbiased
int   i  = rng.NextInt(10, 20);      // [10, 20)
float f  = rng.NextFloat();          // [0, 1)
bool  b  = rng.NextBool();
bool  c  = rng.Chance(0.15f);        // 15% true
int   r  = rng.Range(1, 7);          // d6

Shuffling

using OSL.Deterministic.Runtime.Shuffling;

int[] deck = new int[52];
for (int i = 0; i < deck.Length; i++) deck[i] = i;

OSLDeckShuffler.Shuffle(deck, rng);            // in-place, zero alloc
OSLDeckShuffler.Shuffle(deck, rng, 0, 10);     // shuffle only the first 10 elements

Gacha / Weighted Draws

using OSL.Deterministic.Runtime.Gacha;

var table = new OSLGachaTable<GachaEntry<string>>(new []
{
    new GachaEntry<string>(60, "common",   "C"),
    new GachaEntry<string>(30, "uncommon", "U"),
    new GachaEntry<string>(10, "rare",     "R"),
});

var result = table.Draw(rng);   // returns one entry, weighted by first parameter

Pity System

The pity system can guarantee a rare drop within a configurable number of consecutive non-rare draws.

using OSL.Deterministic.Runtime.Pity;

var pity = new SimpleHardPity(threshold: 90);
// Pass the pity tracker alongside your gacha table for guaranteed distribution.

Save & Restore State

Span<byte> buf = stackalloc byte[rng.StateSize];  // 16 bytes for PCG32
rng.SaveState(buf);

// Later — restore the RNG state.
rng.LoadState(buf);

Use this to:

  • Restore a deterministic sequence after loading saved state.
  • Keep replay or simulation flows consistent.
  • Reproduce a deterministic sequence during testing.

Troubleshooting

Symptom Likely cause Action
Results differ between runs Seed, stream, or call order changed Check the seed and the sequence of RNG calls.
Replay diverges Non-deterministic API was mixed into deterministic logic Avoid mixing unrelated random APIs or time-dependent values.
Weighted draws look unusual in small tests Sample size is too small Test with a larger number of draws.
Restored state does not continue as expected RNG state was not restored with the gameplay state Save and load the RNG state together with the relevant gameplay state.