Skip to content

ENG2-P8-04: Seedable RNG + noise crate with FFI exports (#548) #802

Description

@aram-devdocs

Parent

  • Program: ENG2 — GoudEngine v2 Rebuild (see the pinned master tracking issue)
  • Phase / Milestone: Phase 8 — Capability Gaps (eng2-p8-capabilities)
  • Batch / Group: Batch 8.1 — Highest-demand capabilities, Group D
  • Runbook spec: docs/src/runbook/phases/phase-8.md (committed with the roadmap)

Summary

Add an engine-owned seedable PRNG and a noise (simplex/perlin) generator, both exposed via FFI, so all SDKs get identical, reproducible random/noise sequences from the same seed. Closes #548.

Architecture Context

Layer: Layer 1 (Foundation, core/) for the PRNG/noise implementation; Layer 5 (FFI) for the export surface.
Modules/types touched:

  • goud_engine/src/core/rng.rs (new) — seedable PRNG (e.g. PCG32/xoshiro-family algorithm implemented or vendored deterministically; must produce identical output across platforms/languages for a given seed).
  • goud_engine/src/core/noise.rs (new) — 2D/3D simplex or perlin noise sampling over the PRNG (or a fixed permutation table), gradient-consistent across calls.
  • goud_engine/src/ffi/rng.rs (new) — FFI exports: create a seeded generator (handle), next_u32/next_f32, noise_2d/noise_3d sampling.

Boundary constraints (only those that apply):

  • FFI exports: #[no_mangle] extern "C", #[repr(C)], null checks, // SAFETY: comments.

Pattern to follow: goud_engine/src/ffi/spatial_grid/mod.rs handle-registry pattern (global registry keyed by u32 handle) for exposing one or more independent generator instances safely across FFI, so callers can run multiple seeded streams (e.g. one per system) without global mutable state contention.

Scope

  • Seedable PRNG core in core/rng.rs: deterministic algorithm chosen and documented (no OS entropy source, no std::time seeding by default — explicit seed only).
  • Noise core in core/noise.rs: 2D and 3D noise sampling with documented value range and continuity guarantees.
  • FFI surface: create/destroy a generator handle from a seed, draw next u32/f32/f64, sample noise at (x,y) / (x,y,z), optionally with frequency/octave parameters.
  • Cross-platform determinism test: same seed produces byte-identical sequences (guards against float/int representation drift between the Rust core and any SDK-side float handling).
  • Tests: spec test + unit tests for PRNG statistical sanity (not a cryptographic RNG — document that it is NOT suitable for security-sensitive use) and noise continuity/range.
  • Docs/rules updates: mdBook page noting this is the only sanctioned source of randomness for gameplay code that must stay deterministic (ties into ENG2-P5-04's world-hash determinism requirement).

Acceptance Criteria

  • Same seed + same call sequence produces identical output across at least two SDK languages (e.g. C# and Python) calling the same FFI exports.
  • Noise functions are continuous (no discontinuities at integer-coordinate boundaries) and return values in a documented range.
  • FFI RNG/noise exports have wrappers in all 10 SDKs and pass python codegen/validate_coverage.py.
  • cargo check && cargo fmt --all -- --check && cargo clippy -- -D warnings clean; cargo test green; ./codegen.sh && git diff --exit-code (drift gate)

Breaking Change & Throne Follow-up

None — additive/internal (new module, new FFI surface).

Blocked By

None.

Files Likely Touched

  • New: goud_engine/src/core/rng.rs, goud_engine/src/core/noise.rs, goud_engine/src/ffi/rng.rs
  • Generated: SDK bindings under sdks/*/ for the new RNG/noise FFI exports
  • Modified: goud_engine/src/core/mod.rs (register rng/noise modules), goud_engine/Cargo.toml (if a vetted rand/noise crate is adopted instead of a hand-rolled algorithm — see Agent Notes), codegen/goud_sdk.schema.json

Agent Notes

  • Confirmed via grep across goud_engine/Cargo.toml and the workspace root Cargo.toml: neither rand nor noise appears as a dependency anywhere in the workspace today. Confirmed via grep across goud_engine/src (excluding tests): there is no rand::, use rand, or thread_rng call site anywhere in production code — the engine currently has zero source of randomness of any kind.
  • This closes Add 2D/3D noise generation (simplex/perlin) #548 ("Add 2D/3D noise generation (simplex/perlin)"), which is currently open.
  • Cross-language determinism is the hard constraint here, not just "add a rand/noise crate dependency": if the SDKs each pull in their own language's RNG library, two SDKs given "the same seed" will diverge immediately, because algorithm choice and integer/float representation differ per language. The engine must own the PRNG/noise algorithm and expose only FFI entry points — no SDK may implement its own parallel RNG for gameplay-affecting randomness. This directly ties to ENG2-P5-04 (deterministic iteration order + world-state hash FFI hook): any non-determinism introduced here would silently break that hash across peers/replays.
  • Decide explicitly whether to hand-roll a small deterministic algorithm (e.g. PCG32, xoshiro256**, splitmix64 for seeding) or vendor a no_std-friendly crate pinned to a specific version with build-independent output — record the decision in the module's doc comment since it is a determinism-affecting choice, not an implementation detail.

Verification

cargo check && cargo fmt --all -- --check && cargo clippy -- -D warnings
cargo test
./codegen.sh && git diff --exit-code

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions