A density function using minecraft:sqrt with a negative input loads successfully, but produces NaN values that propagate through the world generation density graph and cause severely malformed terrain.
The input -1.0 is accepted by the density function codec because inline density function constants use Codec.doubleRange(-1000000.0, 1000000.0). The sqrt density function does not add any validation requiring its input or possible input range to be non-negative.
The world still loads, but overworld terrain generates as large disconnected walls, sheets, and floating formations instead of normal terrain.
This issue is exclusive to 26.3 snapshot 6 because sqrt density functions were introduced in this snapshot. Snapshots 1 through 5 do not support the minecraft:sqrt density function type.
Steps to reproduce
Open the create new world screen and leave the world type as "Normal".
Install and enable the attached datapack.
Create the world.
Notice that the overworld consists of severely malformed walls, sheets, and disconnected terrain formations.
Open
latest.logand notice repeated density function warnings containingNaNranges.
Expected result
A sqrt density function whose input range contains negative values should either be rejected during datapack validation or handle values outside the square root domain without introducing NaN into world generation.
Actual result
The datapack loads successfully. Negative inputs evaluate to NaN, the calculated density function bounds become NaN, and malformed terrain is generated.
Root Cause
In net.minecraft.world.level.levelgen.DensityFunctions, the SQRT mapped density function directly passes its input to Math.sqrt:
private static double transform(final DensityFunctions.Mapped.Type type, final double input) {
return switch (type) {
case ABS -> Math.abs(input);
case SQUARE -> input * input;
case CUBE -> input * input * input;
case SQRT -> Math.sqrt(input);
case HALF_NEGATIVE -> input > 0.0 ? input : input * 0.5;
case QUARTER_NEGATIVE -> input > 0.0 ? input : input * 0.25;
case RECIPROCAL -> 1.0 / input;
case NEGATE -> -input;
case SQUEEZE -> {
double c = Mth.clamp(input, -1.0, 1.0);
yield c / 2.0 - c * c * c / 24.0;
}
case LOG -> Math.log(input);
case SIGN -> Math.signum(input);
};
}The codec accepts any density function as the input and does not validate that its range is non-negative:
private final MapCodec<DensityFunctions.Mapped> codec = RecordCodecBuilder.mapCodec(
i -> i.group(
DensityFunction.CODEC.fieldOf("input").forGetter(DensityFunctions.Mapped::input)
)
.apply(i, input -> new DensityFunctions.Mapped(this, input))
);
Inline constants such as -1.0 are valid because density function constants use the following codec:
private static final Codec<Double> NOISE_VALUE_CODEC =
Codec.doubleRange(-1000000.0, 1000000.0);With an input of -1.0, both the runtime result and calculated range become NaN. These values are then incorporated into the wider overworld density graph, causing invalid range information and malformed terrain generation.
Logs:
[18:27:12] [Worker-Main-7/WARN]: Creating a MIN function between two non-overlapping inputs: Ap2[type=ADD, left=MulOrAdd[specificType=MUL, right=Mapped[type=SQUARE, input=Noise[noise=NoiseHolder[noiseData=Reference{ResourceKey[minecraft:worldgen/noise / minecraft:cave_layer]=NormalNoise[parameters=Parameters[baseAmplitude=0.955388882960065, baseOctave=-8, octaveCount=1, normalize=ENABLED, amplitudeModifiers=[]]]}, noise=net.minecraft.world.level.levelgen.synth.NoiseStack$Perlin@7feb96c4], xzScale=1.0, yScale=8.0]], leftValue=4.0], right=Ap2[type=ADD, left=Clamp[input=MulOrAdd[specificType=ADD, right=Noise[noise=NoiseHolder[noiseData=Reference{ResourceKey[minecraft:worldgen/noise / minecraft:cave_cheese]=NormalNoise[parameters=Parameters[baseAmplitude=0.8361300524356068, baseOctave=-8, octaveCount=9, normalize=ENABLED, amplitudeModifiers=[0.5, 1.0, 2.0, 1.0, 2.0, 1.0, 0.0, 2.0, 0.0]]]}, noise=net.minecraft.world.level.levelgen.synth.NoiseStack$Perlin@43ed1147], xzScale=1.0, yScale=0.6666666666666666], leftValue=0.27], min=-1.0, max=1.0], right=Clamp[input=MulOrAdd[specificType=ADD, right=MulOrAdd[specificType=MUL, right=Mapped[type=SQRT, input=Constant[value=-1.0]], leftValue=-0.64], leftValue=1.5], min=0.0, max=0.5], rightMinValue=NaN, rightMaxValue=NaN], rightMinValue=NaN, rightMaxValue=NaN] and net.minecraft.world.level.levelgen.NoiseChunk$CacheOnce@16093225
Thank you for helping us improve Minecraft! We saved your files: