Determinism and vanilla parity
Status: Implemented for the guarantees below. Game random sources and Java-semantics maths helpers for mods, and
clight testparity checks, are Planned.
Celestial’s core promise is that unmodded play is identical to 26.3: visuals, physics, game logic, timing and packets. CLight keeps that promise and asks mods to stay predictable themselves.
Guarantees CLight makes
- No mods means vanilla. Event dispatch and mixin targets cost one branch when unused and never change results. A mixin that passes everything through leaves the game bit-identical.
- Data-only mods behave like data packs. A data mod gives the same results as the same files used as a vanilla resource or data pack.
- Fixed handler order. Callbacks run in priority order, then mod load order (dependencies first, ties by id). That order doesn’t depend on install order, file timestamps or thread timing.
- Vanilla order for vanilla randomness. Where CLight shows a mod a random value before the game uses it (the
xpofblock.break), it rolls a preview on a copy of the random source, so the game’s own sequence stays exactly vanilla’s.
What mods should do
Don’t let your own randomness change vanilla’s
Vanilla’s random consumption order matters: an extra draw changes mob behaviour, loot and worldgen. CLight doesn’t give mods access to the game’s random sources yet, so a mod can’t disturb them. For your own gameplay randomness use Lua’s math.random (a separate generator per mod) or your own generator in C/C++, never one shared with the game.
Use game time, not wall time
Use cl_level_game_time(level) / cl_level_day_time(level) and tick counts for gameplay timers (Lua level:game_time()). cl_time_now_ns() and os.clock() are for profiling and UI animation only.
Floating point
WebAssembly floating point is strict IEEE (like Java’s), and the SDK compiles without fast-math, so a vanilla formula ported to C or C++ gives the same results. Keep it that way: don’t add -ffast-math to custom.clight_build.cflags.
Don’t depend on hash-map order
If your output depends on iteration order (spreading something to neighbours, picking “the first” player), iterate in a defined order. In Lua, pairs order is not defined; use arrays and ipairs, or sort the keys.
Planned
- The game’s random sources (
levelrandom, positional worldgen forks) and bit-identical ports of vanilla’s generators. - Java-semantics maths helpers (the sine table, floor division,
String#hashCode). clight test: run a scenario twice and diff the results, golden worldgen digests, and a “mod installed but unused” baseline.