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
- Overview
- Who Should Use This
- Requirements
- Installation
- Quick Start
- Core API
- Shuffling
- Gacha / Weighted Draws
- Pity System
- Save & Restore State
- 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.
- Pluggable —
IRandomEngineandIPitySystemare 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
- Search for OSL Deterministic Core in the Unity Asset Store, or open it directly at Unity Asset Store (OSL Deterministic Core).
- Click Add to My Assets.
- Open Unity and navigate to Window > Package Manager.
- Select Packages: My Assets from the dropdown.
- Locate OSL Deterministic Core, download it, and click Import.
- Verify that the
Assets/OSL/Deterministic/folder is present. - (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.RandomorSystem.Randominto 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. |