test+fix: deep generation tests + bug cleanup (licence, docs, deps)

Two cohesive passes that harden the latitude worldgen and clean up
long-standing inconsistencies.

== Bug fixes & doc alignment ==
- licence: set MIT in gradle.properties and neoforge.mods.toml
  (was "Not specified" - blocks distribution)
- OreAuditHandler: fix javadoc that referenced a non-existent system
  property; the trigger is the .oreaudit marker file. Add an explicit
  warning about the System.exit(0) and point to LatitudeGameTest as the
  non-destructive alternative
- KubeJS: remove the dead kubejs_version, the commented dependency and
  all README claims about an "automatic KubeJS script"/KubeJSIntegration
  class that no longer exists. Vanilla ore removal is now native via the
  neoforge:remove_features biome modifier
- README: refresh the technical header (1.21.1 / NeoForge 21.1.219 /
  Java 21 / v3.2, was 1.20.1/Forge/Java17/v2.1.5) and the shard-diamond
  surface note (both variants are generated, gated by shardDiamondOre)
- CLAUDE.md / CONFIG_INTEGRATION_GUIDE.md: mark the feature toggles and
  tool stats as wired (they read ModConfigs at runtime); correct the
  "hardcoded tools" claim

== Generation tests (56 unit + 6 GameTest, all green) ==
- Extract the pure geometry of LatitudeBiomeSource into LatitudeMath
  (temperature, 3-zone underground model, spawn safe zone, dual-octave
  selector index). LatitudeBiomeSource now delegates to it, so one
  source of truth drives both runtime and tests - no behaviour drift
- LatitudeMathTest (21): temperature clamp/monotonicity, zone boundaries
  without gaps, Deep Dark / cave threshold predicates, spawn-safe square
  symmetry, selector index bounds + near-uniform distribution
- BiomeBandTest (18): fromTemperature boundary cases, surface/ocean
  pool non-empty, cave biomes never in a surface pool
- ModConfigsTest (+2 guards): every ConfigHelper toggle string maps to
  a real FeatureToggleConfig field, so a typo cannot silently produce a
  dead toggle (default: return true)
- LatitudeGameTest (+4 server tests): determinism (1485 pts, 0 mismatch),
  no cave biome at surface (160k samples, 0 leak), surface continuity
  (7% transitions - large biomes), climate gradient (north=cold 100%,
  south=hot 100%, equator cold 0%)

== Build ==
- build.gradle: addModdingDependenciesTo(sourceSets.test) so pure unit
  tests can reference Minecraft types without a full game server
- neoforge.mods.toml: optional BOP/create/mekanism dependencies removed;
  declared with mandatory=false + versionRange="[0,)" they broke mod
  loading when the mods were absent ("requires X 0 or above"). Optional
  integration is already handled via data tags (required:false) and
  data-only recipes that no-op if the mod is missing
- gradlew: restore executable bit

Verification: ./gradlew test build -> BUILD SUCCESSFUL, 56/56 unit tests
            ./gradlew runGameTestServer -> 6/6 GameTests passed in 840ms
This commit is contained in:
feldenr
2026-06-20 22:33:06 +02:00
parent 0c831bdff8
commit dfff76b0f9
14 changed files with 963 additions and 106 deletions
@@ -59,63 +59,63 @@ public class LatitudeBiomeSource extends BiomeSource {
// ------------------------------------------------------------------
/** Number of blocks for the temperature to go from 0 (equator) to +-1 (pole). */
private static final double TEMPERATURE_SCALE = 16000.0;
private static final double TEMPERATURE_SCALE = LatitudeMath.TEMPERATURE_SCALE;
/** Noise frequency for the climate band boundary wobble. */
private static final double BOUNDARY_NOISE_SCALE = 0.00015;
private static final double BOUNDARY_NOISE_SCALE = LatitudeMath.BOUNDARY_NOISE_SCALE;
/** Amplitude of the boundary wobble (in temperature units). */
private static final double BOUNDARY_NOISE_AMPLITUDE = 0.15;
private static final double BOUNDARY_NOISE_AMPLITUDE = LatitudeMath.BOUNDARY_NOISE_AMPLITUDE;
/** Selector noise frequency for surface sub-biomes (very low = very large biomes). */
private static final double SURFACE_SELECTOR_SCALE = 0.00033;
private static final double SURFACE_SELECTOR_SCALE = LatitudeMath.SURFACE_SELECTOR_SCALE;
/** Frequency of the land/ocean mask noise. */
private static final double OCEAN_NOISE_SCALE = 0.0011;
private static final double OCEAN_NOISE_SCALE = LatitudeMath.OCEAN_NOISE_SCALE;
/** Above this ocean-noise value, the column is ocean. */
private static final double OCEAN_THRESHOLD = 0.30;
private static final double OCEAN_THRESHOLD = LatitudeMath.OCEAN_THRESHOLD;
/** Frequency of the moisture noise that carves rare swamp/mangrove pockets. */
private static final double MOISTURE_SCALE = 0.0009;
private static final double MOISTURE_SCALE = LatitudeMath.MOISTURE_SCALE;
/** Above this moisture value a wet biome (swamp/mangrove) overrides the surface. ~8% of land. */
private static final double MOISTURE_THRESHOLD = 0.55;
private static final double MOISTURE_THRESHOLD = LatitudeMath.MOISTURE_THRESHOLD;
// ------------------------------------------------------------------
// Underground tunables (3-zone model)
// ------------------------------------------------------------------
/** Top of the deep zone (Y < this = deep underground). Deep Dark can live here. */
private static final int DEEP_ZONE_TOP = -30;
private static final int DEEP_ZONE_TOP = LatitudeMath.DEEP_ZONE_TOP;
/** Top of the mid-cave zone (DEEP_ZONE_TOP ≤ Y < this = mid caves, lush/dripstone pockets). */
private static final int MID_CAVE_TOP = 0;
private static final int MID_CAVE_TOP = LatitudeMath.MID_CAVE_TOP;
/** Frequency of the mid-cave pocket noise (lush/dripstone). */
private static final double CAVE_SCALE = 0.003;
private static final double CAVE_SCALE = LatitudeMath.CAVE_SCALE;
/** Above this value a lush/dripstone pocket overrides the surface biome. ~5-8% of the mid zone. */
private static final double CAVE_THRESHOLD = 0.38;
private static final double CAVE_THRESHOLD = LatitudeMath.CAVE_THRESHOLD;
/** Frequency of the Deep Dark noise (very low = large, rare regions). */
private static final double DEEP_DARK_SCALE = 0.0011;
private static final double DEEP_DARK_SCALE = LatitudeMath.DEEP_DARK_SCALE;
/** Vertical frequency of the Deep Dark noise (keeps it coherent in tall sections). */
private static final double DEEP_DARK_SCALE_Y = 0.012;
private static final double DEEP_DARK_SCALE_Y = LatitudeMath.DEEP_DARK_SCALE_Y;
/** Above this value the Deep Dark overrides. Tuned for ~1-2% of the deep zone (legendary). */
private static final double DEEP_DARK_THRESHOLD = 0.55;
private static final double DEEP_DARK_THRESHOLD = LatitudeMath.DEEP_DARK_THRESHOLD;
// ------------------------------------------------------------------
// Spawn safe zone
// ------------------------------------------------------------------
/** Half-size of the guaranteed safe spawn square around the origin (plains/forest). */
private static final int SPAWN_SAFE_RADIUS = 96;
private static final int SPAWN_SAFE_RADIUS = LatitudeMath.SPAWN_SAFE_RADIUS;
/** Selector frequency inside the spawn safe zone (finer, for gentle variety). */
private static final double SPAWN_SELECTOR_SCALE = 0.02;
private static final double SPAWN_SELECTOR_SCALE = LatitudeMath.SPAWN_SELECTOR_SCALE;
// ------------------------------------------------------------------
// Fields
@@ -231,7 +231,7 @@ public class LatitudeBiomeSource extends BiomeSource {
}
// Guaranteed safe spawn zone (open, buildable biomes) around the origin.
if (spawnSafeBiomes != null && Math.abs(blockX) < SPAWN_SAFE_RADIUS && Math.abs(blockZ) < SPAWN_SAFE_RADIUS) {
if (spawnSafeBiomes != null && LatitudeMath.isInSpawnSafeZone(blockX, blockZ)) {
return pickBiome(spawnSafeBiomes, blockX, blockZ, SPAWN_SELECTOR_SCALE);
}
@@ -278,11 +278,12 @@ public class LatitudeBiomeSource extends BiomeSource {
* </ul>
*/
private Holder<Biome> applyCaveOverrides(Holder<Biome> surfaceBiome, BiomeBand band, int blockX, int blockY, int blockZ) {
LatitudeMath.Zone zone = LatitudeMath.zoneForY(blockY);
// Deep zone: legendary Deep Dark.
if (blockY < DEEP_ZONE_TOP) {
if (zone == LatitudeMath.Zone.DEEP) {
if (deepDarkBiome != null) {
double dd = deepDarkNoise.noise(blockX * DEEP_DARK_SCALE, blockY * DEEP_DARK_SCALE_Y, blockZ * DEEP_DARK_SCALE);
if (dd > DEEP_DARK_THRESHOLD) {
if (LatitudeMath.isDeepDarkPocket(dd)) {
return deepDarkBiome;
}
}
@@ -290,11 +291,11 @@ public class LatitudeBiomeSource extends BiomeSource {
}
// Mid caves: rare lush/dripstone pockets by climate.
if (blockY < MID_CAVE_TOP) {
if (zone == LatitudeMath.Zone.MID_CAVE) {
Holder<Biome> caveBiome = caveBiomeFor(band);
if (caveBiome != null) {
double cave = caveNoise.noise(blockX * CAVE_SCALE, 0.0, blockZ * CAVE_SCALE);
if (cave > CAVE_THRESHOLD) {
if (LatitudeMath.isMidCavePocket(cave)) {
return caveBiome;
}
}
@@ -341,11 +342,8 @@ public class LatitudeBiomeSource extends BiomeSource {
// second octave at an offset flattens the curve so no biome is disproportionately common.
double n1 = selectorNoise.noise(blockX * scale, blockZ * scale, 1000.0);
double n2 = selectorNoise.noise(blockX * scale * 1.9 + 137.0, blockZ * scale * 1.9 - 211.0, 2000.0);
double combined = (n1 + n2 * 0.5) / 1.5;
double normalized = combined * 0.5 + 0.5;
int idx = (int) (normalized * biomes.size());
if (idx >= biomes.size()) idx = biomes.size() - 1;
if (idx < 0) idx = 0;
double normalized = LatitudeMath.normalisedSelector(n1, n2);
int idx = LatitudeMath.selectorIndex(normalized, biomes.size());
return biomes.get(idx);
}
@@ -158,6 +158,188 @@ public class LatitudeGameTest {
}
}
/**
* Determinism: two {@link LatitudeBiomeSource} instances built with the same seed must
* return biome holders that resolve to the <b>same biome key</b> at every sampled point.
* Worldgen is shared work across threads, so equality of biome-by-coord is a hard contract.
* Also asserts a coarse cross-depth determinism: re-querying the same (x,z) at different Y in
* the surface zone returns the same biome (latitude surface band does not depend on Y in the
* zone with no override).
*/
@GameTest(template = "empty_1x1", timeoutTicks = 400)
public static void latitudeDeterminism(GameTestHelper helper) {
try {
ServerLevel level = helper.getLevel();
HolderGetter<Biome> getter = level.registryAccess().lookupOrThrow(Registries.BIOME);
LatitudeBiomeSource a = new LatitudeBiomeSource(SEED, getter);
LatitudeBiomeSource b = new LatitudeBiomeSource(SEED, getter);
int mismatches = 0;
int checked = 0;
// Sample a coarse grid across the climate range, several Y of the surface zone.
int[] ys = {SURFACE_Y, SURFACE_Y + 1, 100, 200, 320};
for (int blockZ = -RADIUS; blockZ <= RADIUS; blockZ += 1000) {
for (int blockX = -4000; blockX <= 4000; blockX += 1000) {
for (int y : ys) {
ResourceKey<Biome> ka = a.getNoiseBiome(blockX >> 2, y >> 2, blockZ >> 2, null).unwrapKey().orElse(null);
ResourceKey<Biome> kb = b.getNoiseBiome(blockX >> 2, y >> 2, blockZ >> 2, null).unwrapKey().orElse(null);
checked++;
if (!java.util.Objects.equals(ka, kb)) mismatches++;
}
}
}
assertTrue(checked > 100, "determinism test must check many points, checked=" + checked);
assertEquals(0, mismatches,
"same-seed sources must be identical everywhere, had " + mismatches + " / " + checked + " mismatches");
CustomOreGenMod.LOGGER.info("[latitude-determinism] OK: {} points all identical for seed={}", checked, SEED);
helper.succeed();
} catch (AssertionError | Exception e) {
CustomOreGenMod.LOGGER.error("latitudeDeterminism test failed", e);
helper.fail(e.getMessage());
}
}
/**
* Cave biomes (LUSH_CAVES, DRIPSTONE_CAVES, DEEP_DARK) must never surface: the 3-zone model
* only injects them underground (Y &lt; 0 for lush/dripstone, Y &lt; -30 for Deep Dark).
* Sampling the whole surface grid and asserting zero cave biomes protects against a regression
* that would leak a cave biome to the surface (e.g. a swapped zone boundary).
*/
@GameTest(template = "empty_1x1", timeoutTicks = 400)
public static void noCaveBiomeAtSurface(GameTestHelper helper) {
try {
ServerLevel level = helper.getLevel();
HolderGetter<Biome> getter = level.registryAccess().lookupOrThrow(Registries.BIOME);
LatitudeBiomeSource source = new LatitudeBiomeSource(SEED, getter);
Set<ResourceKey<Biome>> caveBiomes = Set.of(
Biomes.LUSH_CAVES, Biomes.DRIPSTONE_CAVES, Biomes.DEEP_DARK);
int surfaceCaveLeaks = 0;
int checked = 0;
for (int blockZ = -RADIUS; blockZ <= RADIUS; blockZ += STEP) {
for (int blockX = -RADIUS; blockX <= RADIUS; blockX += STEP) {
ResourceKey<Biome> k = source.getNoiseBiome(blockX >> 2, SURFACE_Y >> 2, blockZ >> 2, null)
.unwrapKey().orElse(null);
checked++;
if (k != null && caveBiomes.contains(k)) surfaceCaveLeaks++;
}
}
assertEquals(0, surfaceCaveLeaks,
"no cave biome must leak to the surface, found " + surfaceCaveLeaks + " / " + checked);
CustomOreGenMod.LOGGER.info("[no-cave-at-surface] OK: {} surface samples, zero cave leaks", checked);
helper.succeed();
} catch (AssertionError | Exception e) {
CustomOreGenMod.LOGGER.error("noCaveBiomeAtSurface test failed", e);
helper.fail(e.getMessage());
}
}
/**
* Spatial continuity / large-biome property: at the surface, the selector noise has a very
* low frequency (SURFACE_SELECTOR_SCALE = 0.00033), so neighbouring sample points (80 blocks
* apart) almost always fall in the same biome. This guards against a regression that would
* turn the world into per-block noise. We accept that band boundaries and ocean/rare pockets
* create some transitions, but they must remain rare ((< 25 %).</p>
*/
@GameTest(template = "empty_1x1", timeoutTicks = 400)
public static void surfaceBiomeContinuity(GameTestHelper helper) {
try {
ServerLevel level = helper.getLevel();
HolderGetter<Biome> getter = level.registryAccess().lookupOrThrow(Registries.BIOME);
LatitudeBiomeSource source = new LatitudeBiomeSource(SEED, getter);
// Sample a single horizontal line across one temperate row (Z near 0, well inside one band)
// and count how often adjacent samples change biome.
int transitions = 0;
ResourceKey<Biome> prev = null;
int sampleCount = 200;
int z = 160; // just south of equator, well inside TEMPERATE band, outside spawn-safe square
for (int i = 0; i < sampleCount; i++) {
int x = -8000 + i * 80;
ResourceKey<Biome> k = source.getNoiseBiome(x >> 2, SURFACE_Y >> 2, z >> 2, null)
.unwrapKey().orElse(null);
if (prev != null && !java.util.Objects.equals(prev, k)) transitions++;
prev = k;
}
// 80-block spacing on a 0.00033-frequency noise: the noise barely moves, so transitions
// are driven mostly by ocean/rare-pocket edges, not selector turnover.
double transitionRatio = 100.0 * transitions / (sampleCount - 1);
assertTrue(transitionRatio < 25.0,
"surface should have large continuous biomes; transition ratio was "
+ String.format("%.1f%%", transitionRatio) + " across a temperate row");
CustomOreGenMod.LOGGER.info("[surface-continuity] OK: {} transitions / {} samples = {}%",
transitions, sampleCount - 1, String.format("%.1f", transitionRatio));
helper.succeed();
} catch (AssertionError | Exception e) {
CustomOreGenMod.LOGGER.error("surfaceBiomeContinuity test failed", e);
helper.fail(e.getMessage());
}
}
/**
* Progressive climate transition: the surface biome must be predominantly cold at the far
* north, predominantly temperate at the equator, and predominantly hot at the far south.
* This is a finer-grained version of the band-ratio assertions in {@link #latitudeMap},
* checking the gradient direction in three Z windows rather than just the two poles.
*/
@GameTest(template = "empty_1x1", timeoutTicks = 400)
public static void climateGradient(GameTestHelper helper) {
try {
ServerLevel level = helper.getLevel();
HolderGetter<Biome> getter = level.registryAccess().lookupOrThrow(Registries.BIOME);
LatitudeBiomeSource source = new LatitudeBiomeSource(SEED, getter);
Set<ResourceKey<Biome>> cold = resolveTagKeys(getter, BiomeBand.COLD.surfaceTag());
Set<ResourceKey<Biome>> coldOceans = new HashSet<>(List.of(
Biomes.FROZEN_OCEAN, Biomes.DEEP_FROZEN_OCEAN, Biomes.COLD_OCEAN, Biomes.DEEP_COLD_OCEAN, Biomes.FROZEN_RIVER));
cold.addAll(coldOceans);
Set<ResourceKey<Biome>> hot = resolveTagKeys(getter, BiomeBand.HOT.surfaceTag());
Set<ResourceKey<Biome>> hotOceans = new HashSet<>(List.of(
Biomes.WARM_OCEAN, Biomes.LUKEWARM_OCEAN, Biomes.DEEP_LUKEWARM_OCEAN, Biomes.MANGROVE_SWAMP));
hot.addAll(hotOceans);
// Three Z windows, 3200 blocks wide, 800 apart (RADIUS=16000 covers them all).
assertEquals(5, BiomeBand.values().length, "exactly five climate bands expected");
double north = windowBiomeRatio(source, -15000, -11800, cold);
double equator = windowBiomeRatio(source, -1600, 1600, cold); // equator is NOT cold
double south = windowBiomeRatio(source, 11800, 15000, hot);
assertTrue(north > 50.0, "far north must be >50% cold, was " + String.format("%.1f%%", north));
double equatorCold = windowBiomeRatio(source, -1600, 1600, cold);
assertTrue(equatorCold < 25.0,
"equator must NOT be predominantly cold (gradient broken), was " + String.format("%.1f%%", equatorCold));
assertTrue(south > 50.0, "far south must be >50% hot, was " + String.format("%.1f%%", south));
CustomOreGenMod.LOGGER.info("[climate-gradient] OK: north(cold)={}%, south(hot)={}%, equator(cold)={}%",
String.format("%.0f", north), String.format("%.0f", south), String.format("%.0f", equatorCold));
helper.succeed();
} catch (AssertionError | Exception e) {
CustomOreGenMod.LOGGER.error("climateGradient test failed", e);
helper.fail(e.getMessage());
}
}
/** Sample a Z window of the surface and return the % of biomes in the expected set. */
private static double windowBiomeRatio(LatitudeBiomeSource source, int zMin, int zMax,
Set<ResourceKey<Biome>> expected) {
int match = 0;
int total = 0;
for (int z = zMin; z <= zMax; z += 160) {
for (int x = -4000; x <= 4000; x += 800) {
ResourceKey<Biome> k = source.getNoiseBiome(x >> 2, SURFACE_Y >> 2, z >> 2, null)
.unwrapKey().orElse(null);
total++;
if (k != null && expected.contains(k)) match++;
}
}
return total == 0 ? 0 : 100.0 * match / total;
}
private static void runLatitudeValidation(GameTestHelper helper) throws Exception {
ServerLevel level = helper.getLevel();
HolderGetter<Biome> getter = level.registryAccess().lookupOrThrow(Registries.BIOME);
@@ -327,4 +509,10 @@ public class LatitudeGameTest {
private static void assertNotNull(Object o, String msg) {
if (o == null) throw new AssertionError(msg);
}
private static void assertEquals(Object expected, Object actual, String msg) {
if (!java.util.Objects.equals(expected, actual)) {
throw new AssertionError(msg + " (expected=" + expected + ", actual=" + actual + ")");
}
}
}
@@ -0,0 +1,133 @@
package net.mcreator.customoregen.worldgen;
/**
* Pure (registry-free) geometry helpers for the latitude world type.
*
* <p>These methods exist so that the core invariants of {@link LatitudeBiomeSource}
* can be unit-tested without a loaded biome registry: temperature derivation, the
* 3-zone underground model, the spawn safe zone, and the dual-octave biome index
* math. {@link LatitudeBiomeSource} delegates to these helpers, so the two stay
* in lock-step by construction (no behaviour drift).</p>
*/
public final class LatitudeMath {
private LatitudeMath() {}
// ------------------------------------------------------------------
// Surface / climate tunables (must mirror LatitudeBiomeSource)
// ------------------------------------------------------------------
public static final double TEMPERATURE_SCALE = 16000.0;
public static final double BOUNDARY_NOISE_SCALE = 0.00015;
public static final double BOUNDARY_NOISE_AMPLITUDE = 0.15;
public static final double SURFACE_SELECTOR_SCALE = 0.00033;
public static final double OCEAN_NOISE_SCALE = 0.0011;
public static final double OCEAN_THRESHOLD = 0.30;
public static final double MOISTURE_SCALE = 0.0009;
public static final double MOISTURE_THRESHOLD = 0.55;
// ------------------------------------------------------------------
// Underground tunables (3-zone model)
// ------------------------------------------------------------------
public static final int DEEP_ZONE_TOP = -30;
public static final int MID_CAVE_TOP = 0;
public static final double CAVE_SCALE = 0.003;
public static final double CAVE_THRESHOLD = 0.38;
public static final double DEEP_DARK_SCALE = 0.0011;
public static final double DEEP_DARK_SCALE_Y = 0.012;
public static final double DEEP_DARK_THRESHOLD = 0.55;
// ------------------------------------------------------------------
// Spawn safe zone
// ------------------------------------------------------------------
public static final int SPAWN_SAFE_RADIUS = 96;
public static final double SPAWN_SELECTOR_SCALE = 0.02;
/** Underground vertical zone, mirroring {@link LatitudeBiomeSource#applyCaveOverrides}. */
public enum Zone {
/** {@code Y < DEEP_ZONE_TOP} — deep zone, legendary Deep Dark pockets. */
DEEP,
/** {@code DEEP_ZONE_TOP <= Y < MID_CAVE_TOP} — mid caves, lush/dripstone pockets. */
MID_CAVE,
/** {@code Y >= MID_CAVE_TOP} — no cave override; latitude biome as-is. */
SURFACE
}
/** Band the column falls into based on its Y coordinate. Pure function. */
public static Zone zoneForY(int blockY) {
if (blockY < DEEP_ZONE_TOP) return Zone.DEEP;
if (blockY < MID_CAVE_TOP) return Zone.MID_CAVE;
return Zone.SURFACE;
}
/**
* Raw latitude temperature contribution from the Z coordinate, before the boundary
* wobble is applied. {@code blockZ / TEMPERATURE_SCALE} clamped to {@code [-1, 1]}.
* Pure function.
*/
public static double rawLatitudeTemperature(int blockZ) {
return clamp(blockZ / TEMPERATURE_SCALE, -1.0, 1.0);
}
/**
* Combine the raw latitude temperature with a precomputed boundary wobble (the output of
* {@code boundaryNoise.noise(...) * BOUNDARY_NOISE_AMPLITUDE}), then clamp to the valid
* temperature range. Pure function.
*/
public static double temperature(int blockZ, double boundaryWobble) {
return clamp(blockZ / TEMPERATURE_SCALE + boundaryWobble, -1.0, 1.0);
}
/** True if the (blockX, blockZ) column is inside the guaranteed safe spawn square. Pure. */
public static boolean isInSpawnSafeZone(int blockX, int blockZ) {
return Math.abs(blockX) < SPAWN_SAFE_RADIUS && Math.abs(blockZ) < SPAWN_SAFE_RADIUS;
}
/**
* Normalised selector value in {@code [0, 1]} from two noise samples, mirroring the
* dual-octave flattening used by {@link LatitudeBiomeSource#pickBiome}.
*
* @param n1 first octave noise value (any range ImprovedNoise produces)
* @param n2 second octave noise value (at a different offset)
* @return a value in {@code [0, 1]}
*/
public static double normalisedSelector(double n1, double n2) {
double combined = (n1 + n2 * 0.5) / 1.5;
return clamp(combined * 0.5 + 0.5, 0.0, 1.0);
}
/**
* Map a normalised selector value in {@code [0, 1]} to a list index, clamped so it can
* never leave {@code [0, size-1]}. Mirrors the index math in {@code pickBiome}.
* Pure. {@code size} must be {@code >= 1}.
*/
public static int selectorIndex(double normalised, int size) {
if (size <= 0) {
throw new IllegalArgumentException("size must be >= 1, was " + size);
}
int idx = (int) (normalised * size);
if (idx >= size) idx = size - 1;
if (idx < 0) idx = 0;
return idx;
}
/**
* Is the deep-Dark override active for this column, given the precomputed deep-dark noise
* value (output of {@code deepDarkNoise.noise(...)})? Only meaningful in the DEEP zone;
* callers should gate on {@link #zoneForY} first. Pure.
*/
public static boolean isDeepDarkPocket(double deepDarkNoise) {
return deepDarkNoise > DEEP_DARK_THRESHOLD;
}
/** Is the mid-cave lush/dripstone pocket override active for this column? Pure. */
public static boolean isMidCavePocket(double caveNoise) {
return caveNoise > CAVE_THRESHOLD;
}
private static double clamp(double value, double min, double max) {
return value < min ? min : (value > max ? max : value);
}
}
@@ -21,12 +21,15 @@ import java.util.LinkedHashMap;
import java.util.Map;
/**
* Temporary diagnostic: only active when the system property
* {@code customoregen.oreaudit} is set. On server start it forces real chunk
* generation in a grid around the origin, counts custom ore blocks per surface
* biome, writes a report, and stops the server. Used to prove (without manual
* in-game testing) that ores actually place in the terrain of every biome,
* including Biomes O' Plenty biomes, under the latitude world type.
* Temporary diagnostic: only active when a {@code .oreaudit} marker file exists in the
* server working directory (create it to run the audit, delete it afterwards). On server
* start it forces real chunk generation in a grid around the origin, counts custom ore blocks
* per surface biome, writes {@code ore_audit_report.txt}, and then stops the server.
*
* <p>Warning: this handler calls {@code System.exit(0)} at the end of the audit. It is
* therefore a developer-only utility that must <b>never</b> be enabled in production. The
* equivalent, non-destructive proof that ores place in every biome is covered by
* {@link LatitudeGameTest} (run with {@code ./gradlew runGameTestServer}).</p>
*/
@EventBusSubscriber
public class OreAuditHandler {
@@ -1,6 +1,6 @@
modLoader="javafml"
loaderVersion="[1,)"
license="Not specified"
license="MIT"
[[mods]]
modId="${mod_id}"
@@ -23,4 +23,10 @@ description='''${mod_description}'''
# Start of user code block dependencies configuration
# Note: optional mods (biomesoplenty, create, mekanism) are intentionally NOT declared
# here. Optional integration is handled via data tags with "required": false entries
# (see data/custom_ore_gen/tags/worldgen/biome/latitude_*_surface.json) and via
# data-only recipes (data/create, data/mekanism) which simply no-op if the mod is
# absent. Declaring them here with mandatory=false + versionRange caused NeoForge to
# fail loading when the mods were not installed (see commit history).
# End of user code block dependencies configuration