Align schema bounds with the hydrology validator

Studio schema annotations now match the validator ranges; mouthLevelingDistance must fit inside maximumRouteLength.
This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-01 21:15:36 -04:00
1 parent 00f5a16c20
commit 124bcd9372
9 files changed
+44 -25

No files matched your search

@@ -186,6 +186,10 @@ final class PackHydrologyValidator {
SourceBudget undergroundBudget = underground == null SourceBudget undergroundBudget = underground == null
? SourceBudget.defaults() ? SourceBudget.defaults()
: validateUnderground(path + ".underground", underground, dimension, errors, warnings); : validateUnderground(path + ".underground", underground, dimension, errors, warnings);
if (underground != null
&& integerValue(underground, "mouthLevelingDistance", 64) > routingValues.maximumRouteLength()) {
errors.add(path + ".underground.mouthLevelingDistance must not exceed routing.maximumRouteLength.");
}
if (grottos != null) { if (grottos != null) {
validateGrottos(path + ".grottos", grottos, routingValues, errors); validateGrottos(path + ".grottos", grottos, routingValues, errors);
} }
@@ -18,7 +18,7 @@ public class IrisRiverMouthConfig {
private double flareRatio = 1.6D; private double flareRatio = 1.6D;
@MinNumber(0) @MinNumber(0)
@MaxNumber(64) @MaxNumber(32)
@Desc("Maximum number of blocks the recorded mouth footprint may enter the ocean before terminating.") @Desc("Maximum number of blocks the recorded mouth footprint may enter the ocean before terminating.")
private int maximumOceanApron = 8; private int maximumOceanApron = 8;
} }
@@ -74,8 +74,8 @@ public class IrisRiverPolicy {
@Desc("Multiplier applied to terrain-guided routing cost.") @Desc("Multiplier applied to terrain-guided routing cost.")
private Double routingMultiplier = null; private Double routingMultiplier = null;
@MinNumber(0.0001) @MinNumber(0)
@MaxNumber(16) @MaxNumber(4)
@Desc("Multiplier applied to the eroded bank width outside the shore band.") @Desc("Multiplier applied to the eroded bank width outside the shore band.")
private Double bankMultiplier = null; private Double bankMultiplier = null;
@@ -20,7 +20,7 @@ public class IrisRiverRoutingConfig {
private int tileSize = 2048; private int tileSize = 2048;
@MinNumber(8) @MinNumber(8)
@MaxNumber(512) @MaxNumber(64)
@Desc("Spacing in blocks between terrain samples used to build the drainage graph.") @Desc("Spacing in blocks between terrain samples used to build the drainage graph.")
private int sampleSpacing = 64; private int sampleSpacing = 64;
@@ -52,22 +52,22 @@ public class IrisRiverRoutingConfig {
private KList<IrisRiverInlandOutlet> inlandOutlets = new KList<>(); private KList<IrisRiverInlandOutlet> inlandOutlets = new KList<>();
@MinNumber(0) @MinNumber(0)
@MaxNumber(64) @MaxNumber(8)
@Desc("How strongly routes prefer lower ground.") @Desc("How strongly routes prefer lower ground.")
private double valleyPreference = 1.5D; private double valleyPreference = 1.5D;
@MinNumber(0) @MinNumber(0)
@MaxNumber(1024) @MaxNumber(128)
@Desc("Cost per block of rise along a route.") @Desc("Cost per block of rise along a route.")
private double uphillPenalty = 24D; private double uphillPenalty = 24D;
@MinNumber(0) @MinNumber(0)
@MaxNumber(64) @MaxNumber(16)
@Desc("Cost per unit of terrain slope along a route.") @Desc("Cost per unit of terrain slope along a route.")
private double slopePenalty = 2D; private double slopePenalty = 2D;
@MinNumber(0) @MinNumber(0)
@MaxNumber(16) @MaxNumber(1)
@Desc("How strongly routes are drawn toward existing drainage.") @Desc("How strongly routes are drawn toward existing drainage.")
private double confluenceAttraction = 0.2D; private double confluenceAttraction = 0.2D;
} }
@@ -12,18 +12,18 @@ import lombok.experimental.Accessors;
@Desc("Controls the lip, shore band and eroded valley around a surface river.") @Desc("Controls the lip, shore band and eroded valley around a surface river.")
@Data @Data
public class IrisSurfaceRiverBankConfig { public class IrisSurfaceRiverBankConfig {
@MinNumber(1) @MinNumber(0)
@MaxNumber(8) @MaxNumber(4)
@Desc("Blocks the bank top sits above the water surface at the channel edge.") @Desc("Blocks the bank top sits above the water surface at the channel edge.")
private int freeboard = 1; private int freeboard = 1;
@MinNumber(0) @MinNumber(0.5)
@MaxNumber(8) @MaxNumber(6)
@Desc("Width in blocks of the shore band that receives shore biome content.") @Desc("Width in blocks of the shore band that receives shore biome content.")
private double shoreWidth = 1.5D; private double shoreWidth = 1.5D;
@MinNumber(1) @MinNumber(0.5)
@MaxNumber(16) @MaxNumber(12)
@Desc("Horizontal run in blocks of eroded bank per block of cut depth.") @Desc("Horizontal run in blocks of eroded bank per block of cut depth.")
private double blendSlope = 3D; private double blendSlope = 3D;
@@ -33,7 +33,7 @@ public class IrisSurfaceRiverBankConfig {
private int minimumBlendWidth = 4; private int minimumBlendWidth = 4;
@MinNumber(1) @MinNumber(1)
@MaxNumber(128) @MaxNumber(64)
@Desc("Widest eroded band outside the shore, in blocks.") @Desc("Widest eroded band outside the shore, in blocks.")
private int maximumBlendWidth = 32; private int maximumBlendWidth = 32;
@@ -18,13 +18,13 @@ public class IrisSurfaceRiverChannelConfig {
@Desc("Wet bed depth in blocks at the channel center.") @Desc("Wet bed depth in blocks at the channel center.")
private IrisStyledRange depth = range(2D, 4D, 768D); private IrisStyledRange depth = range(2D, 4D, 768D);
@MinNumber(1) @MinNumber(0)
@MaxNumber(16) @MaxNumber(3)
@Desc("Blocks the water surface sits below the lowest natural bank beside the channel.") @Desc("Blocks the water surface sits below the lowest natural bank beside the channel.")
private int inset = 1; private int inset = 1;
@MinNumber(0) @MinNumber(1)
@MaxNumber(64) @MaxNumber(32)
@Desc("Maximum cut below natural terrain at the channel center before a course is rejected.") @Desc("Maximum cut below natural terrain at the channel center before a course is rejected.")
private int maximumIncision = 10; private int maximumIncision = 10;
@@ -33,8 +33,8 @@ public class IrisSurfaceRiverChannelConfig {
@Desc("Coherent variation of the wet outline and bed as a fraction of the channel size.") @Desc("Coherent variation of the wet outline and bed as a fraction of the channel size.")
private double roughness = 0.25D; private double roughness = 0.25D;
@MinNumber(3) @MinNumber(4)
@MaxNumber(128) @MaxNumber(64)
@Desc("Wavelength in blocks of the outline and bed variation.") @Desc("Wavelength in blocks of the outline and bed variation.")
private int roughnessWavelength = 16; private int roughnessWavelength = 16;
@@ -13,12 +13,12 @@ import lombok.experimental.Accessors;
@Data @Data
public class IrisSurfaceRiverFlowConfig { public class IrisSurfaceRiverFlowConfig {
@MinNumber(1) @MinNumber(1)
@MaxNumber(16) @MaxNumber(8)
@Desc("Shortest level run in blocks between one-block steps before consecutive steps are labelled a cascade.") @Desc("Shortest level run in blocks between one-block steps before consecutive steps are labelled a cascade.")
private int cascadeRun = 2; private int cascadeRun = 2;
@MinNumber(2) @MinNumber(2)
@MaxNumber(24) @MaxNumber(32)
@Desc("Smallest natural cliff in blocks that becomes a waterfall instead of a cascade.") @Desc("Smallest natural cliff in blocks that becomes a waterfall instead of a cascade.")
private int waterfallMinimumDrop = 6; private int waterfallMinimumDrop = 6;
} }
@@ -33,8 +33,8 @@ public class IrisUndergroundRiverConfig {
@Desc("Allow accepted underground courses to connect transactionally to existing caves.") @Desc("Allow accepted underground courses to connect transactionally to existing caves.")
private boolean connectToExistingCaves = true; private boolean connectToExistingCaves = true;
@MinNumber(0) @MinNumber(16)
@MaxNumber(2048) @MaxNumber(512)
@Desc("Distance in blocks over which an ocean-bound underground river levels to sea level before its mouth.") @Desc("Distance in blocks over which an ocean-bound underground river levels to sea level before its mouth.")
private int mouthLevelingDistance = 64; private int mouthLevelingDistance = 64;
@@ -113,6 +113,21 @@ public class PackHydrologyValidatorTest {
assertTrue(result.errors().toString(), result.errors().isEmpty()); assertTrue(result.errors().toString(), result.errors().isEmpty());
} }
@Test
public void rejectsMouthLevelingDistanceBeyondTheRouteLength() throws Exception {
File pack = pack(riversDimension("""
{
"enabled": true,
"routing": {"tileSize": 1024, "sampleSpacing": 64, "maximumRouteLength": 256, "oceanOutlets": true},
"underground": {"mouthLevelingDistance": 512}
}
""", ""));
PackHydrologyValidator.Validation result = validate(pack);
assertContains(result.errors(), "mouthLevelingDistance must not exceed routing.maximumRouteLength");
}
@Test @Test
public void acceptsEverySchemaBoundAtItsMaximum() throws Exception { public void acceptsEverySchemaBoundAtItsMaximum() throws Exception {
File pack = pack(riversDimension(""" File pack = pack(riversDimension("""