mojira.dev
MC-311717

MC_DEBUG_ENABLE_FARLANDS generates empty space instead of Far Lands terrain

The new debug flag MC_DEBUG_ENABLE_FARLANDS disables noise coordinate wrapping in GradientNoise.wrap in order to restore the Far Lands. Instead of the expected solid wall, terrain generation past the Far Lands boundary places almost no blocks at all. The Nether and the End still produce a wall, but only inside a narrow band right at the boundary; further out they are empty too.

Steps to Reproduce:

  1. Launch the game with -DMC_DEBUG_ENABLED -DMC_DEBUG_ENABLE_FARLANDS

  2. Create a new normal world and run /tp @s 12550824 100 0

  3. Observe the generated terrain

  4. Run /tp @s 13000000 100 0 and observe again

  5. Repeat both coordinates in the Nether and in the End

Observed Results:

  • Overworld: no wall at either coordinate. Terrain is sparse or entirely absent.

  • Nether and End: a solid wall appears at 12550824, but is gone at 13000000, leaving empty space.

Expected Results:
The classic Far Lands: a solid vertical wall extending along the X axis, in every dimension, for the whole range up to the world border.

Analysis:
The Far Lands begin where the highest frequency octave of BlendedNoise passes 2^31. base_3d_noise uses xz_scale 0.25, so the noise space coordinate is blockX * 684.412 * 0.25 = blockX * 171.103, and 2^31 / 171.103 is approximately 12550824. From that point on Mth.floor, which is an (int) cast, saturates at Integer.MAX_VALUE, so the interpolation weight relativeX grows linearly with the coordinate instead of staying within [0, 1). That part is the intended, historical behaviour.

What breaks is the precision of the interpolation. In 26.3-snapshot-7 ImprovedNoise was split into GradientNoise and PerlinNoise, and the interpolation weights changed from double to float. Mth.smoothstep(x) = x*x*x*(x*(x*6-15)+10) behaves like 6*x^5, which overflows float once x passes roughly 5e7, that is only about 10000 blocks past the boundary. The resulting Infinity values then turn into NaN as soon as the FBM octaves are summed, and again when BlendedNoise lerps between its min limit and max limit noises, because +Inf + (-Inf) is NaN.

NaN then survives the whole density pipeline. Mth.clamp returns NaN, since value < min is false and Math.min(NaN, max) is NaN, so the clamp inside UnaryFunction.SqueezeSampler cannot recover it. Finally Aquifer.computeSubstance tests density > 0.0, which is false for NaN, so no block is placed.

Before the float change the same values stayed finite in double: about 2.8e43 at X = 12700000 and about 7.3e55 at the world border. squeeze clamped them to +/-1 and the wall appeared as expected.

There is a second, independent reason the Overworld differs from the Nether and the End. Even inside the narrow band where the values are still finite, NoiseRouterData routes sloped_cheese through DensityFunctions.rangeChoice(slopedCheese, -1000000.0F, 1.5625F, surfaceWithEntrances, underground). Far Lands values of magnitude around 1e9 fall outside that range with either sign, so the underground branch is taken, and there slopedCheese.mul(-0.64F).add(1.5F).clamp(0.0F, 0.5F) clamps the Far Lands away completely. The terrain is then decided purely by the cave noises, which are combined with min and therefore biased towards air. The Nether and the End feed base_3d_noise straight into slide and squeeze with no such gate, which is why only those two dimensions still show a wall at all.

Suggested Fix:
Bounding relativeX to 1e6 is equivalent to bounding the coordinate itself to Integer.MAX_VALUE + 1e6,
because floor is already saturated there. That makes the fix a single branch in GradientNoise.wrap, and
it leaves the fast path untouched, so normal world generation is unaffected:

private static final double FARLANDS_LIMIT = Integer.MAX_VALUE + 1.0E6;

protected static double wrap(final double x) {
    if (x >= -HALF_ROUND_OFF && x < HALF_ROUND_OFF) {
        return x;
    } else if (SharedConstants.DEBUG_ENABLE_FARLANDS) {
        return Mth.clamp(x, -FARLANDS_LIMIT, FARLANDS_LIMIT);
    } else {
        return x - Math.floor(x / 3.3554432E7 + 0.5) * 3.3554432E7;
    }
}

Linked issues

Comments 0

No comments.

Andrey Pfau

(Unassigned)

Unconfirmed

26.3 Pre-Release 2

Retrieved