Make world generation fully deterministic; add golden-hash verification harness

Generation now produces bit-identical output across repeated runs, server
restarts, and parallel generation (golden-hash verified, 289/289 chunks).

Engine fixes:
- MatterGenerator: components flagged complete before generateLayer finished
  (raiseFlagUnchecked), letting concurrent neighbors consume half-written
  mantle data; switched to completion-locked raiseFlagSuspend
- MantleObjectComponent: procedural objects (trees/ruins/formations/coral/
  fungi/crystals) were absent from computeRadius, so canopies crossed chunk
  borders without ordering guarantees; variant footprints now counted
- MantleCarvingComponent: carve profile ordering depended on IdentityHashMap
  iteration and hashCode sort ties; replaced with first-seen column-scan
  order and creation-sequence tiebreaks
- IrisObject/IrisPostModifier: shared BlockData instances mutated in place
  (vine facing x3, auto-waterlog x2, leaf persistence); clone before mutate
- IrisObject: slope-following placement mutated the shared pack-defined
  rotation config; now builds a local rotation on a copied placement
- IrisObjectPlacement: surface-warp CNG seeded from first-caller chunk RNG;
  now seeded from the engine seed manager
- GenerationCacheWarmer: engine init pre-bakes placement/decorator/ore/
  procedural caches in sorted order, removing lazy first-touch ordering
- IrisProceduralPlacement: expose getVariantObjects for radius computation

Verification harness:
- /iris dev goldenhash: buffered chunk generation (no world writes), SHA-256
  per-chunk block+biome digests, golden capture/verify with per-chunk
  mismatch reporting, auto-diagnosis (repeat-gen + mantle-reset comparison,
  block-level diffs), deep per-chunk blockstate dumps, threads/deep params,
  full mantle reset (saveAll + tectonic file wipe) for reproducible runs
- IrisEngineSVC: mantle trim/unload suspended while a goldenhash scan runs
- MantleCarvingComponentTop2BlendTest: sequence-tiebreak regression test
This commit is contained in:
Brian Neumann-Fopiano committed 2026-06-11 12:40:21 -04:00
1 parent 58d24ca55e
commit 6a68884ce9
14 files changed
+758 -46

No files matched your search

@@ -23,7 +23,7 @@ public class MantleCarvingComponentTop2BlendTest {
@BeforeClass
public static void setup() throws Exception {
Class<?> weightedProfileClass = Class.forName("art.arcane.iris.engine.mantle.components.MantleCarvingComponent$WeightedProfile");
weightedProfileConstructor = weightedProfileClass.getDeclaredConstructor(IrisCaveProfile.class, double[].class, double.class, Class.forName("art.arcane.iris.engine.object.IrisRange"));
weightedProfileConstructor = weightedProfileClass.getDeclaredConstructor(IrisCaveProfile.class, double[].class, double.class, Class.forName("art.arcane.iris.engine.object.IrisRange"), int.class);
weightedProfileConstructor.setAccessible(true);
limitMethod = MantleCarvingComponent.class.getDeclaredMethod("limitAndMergeBlendedProfiles", List.class, int.class, int.class);
limitMethod.setAccessible(true);
@@ -76,6 +76,33 @@ public class MantleCarvingComponentTop2BlendTest {
assertEquals(1.0D, byProfile.get(second)[254], 0D);
}
@Test
public void topTwoTieBreaksBySequenceNotIdentity() throws Exception {
IrisCaveProfile first = new IrisCaveProfile().setEnabled(true).setBaseWeight(1.0D);
IrisCaveProfile second = new IrisCaveProfile().setEnabled(true).setBaseWeight(1.0D);
IrisCaveProfile third = new IrisCaveProfile().setEnabled(true).setBaseWeight(1.0D);
double[] weightsA = new double[256];
double[] weightsB = new double[256];
double[] weightsC = new double[256];
weightsA[0] = 0.5D;
weightsB[0] = 0.5D;
weightsC[0] = 0.5D;
List<Object> weighted = new ArrayList<>();
weighted.add(weightedProfileConstructor.newInstance(first, weightsA, average(weightsA), null, 0));
weighted.add(weightedProfileConstructor.newInstance(second, weightsB, average(weightsB), null, 1));
weighted.add(weightedProfileConstructor.newInstance(third, weightsC, average(weightsC), null, 2));
List<?> limited = invokeLimit(weighted, 2);
assertEquals(2, limited.size());
Map<IrisCaveProfile, double[]> byProfile = extractWeightsByProfile(limited);
assertEquals(true, byProfile.containsKey(first));
assertEquals(true, byProfile.containsKey(second));
assertEquals(false, byProfile.containsKey(third));
}
private WeightedInput createWeightedProfiles() throws Exception {
IrisCaveProfile first = new IrisCaveProfile().setEnabled(true).setBaseWeight(1.31D);
IrisCaveProfile second = new IrisCaveProfile().setEnabled(true).setBaseWeight(1.17D);
@@ -99,9 +126,9 @@ public class MantleCarvingComponentTop2BlendTest {
thirdWeights[255] = 0.4D;
List<Object> weighted = new ArrayList<>();
weighted.add(weightedProfileConstructor.newInstance(first, firstWeights, average(firstWeights), null));
weighted.add(weightedProfileConstructor.newInstance(second, secondWeights, average(secondWeights), null));
weighted.add(weightedProfileConstructor.newInstance(third, thirdWeights, average(thirdWeights), null));
weighted.add(weightedProfileConstructor.newInstance(first, firstWeights, average(firstWeights), null, 0));
weighted.add(weightedProfileConstructor.newInstance(second, secondWeights, average(secondWeights), null, 1));
weighted.add(weightedProfileConstructor.newInstance(third, thirdWeights, average(thirdWeights), null, 2));
return new WeightedInput(weighted, profiles);
}