package net.minecraft.world.level.levelgen; import com.mojang.datafixers.util.Either; import com.mojang.logging.LogUtils; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.Arrays; import java.util.function.BiFunction; import java.util.function.Function; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.Holder.Direct; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.util.CubicSpline; import net.minecraft.util.KeyDispatchDataCodec; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.util.VisibleForDebug; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.levelgen.DensityFunctions.Marker.Type; import net.minecraft.world.level.levelgen.DensityFunctions.Spline.Coordinate; import net.minecraft.world.level.levelgen.DensityFunctions.Spline.Point; import net.minecraft.world.level.levelgen.DensityFunctions.WeirdScaledSampler.RarityValueMapper; import net.minecraft.world.level.levelgen.synth.BlendedNoise; import net.minecraft.world.level.levelgen.synth.SimplexNoise; import net.minecraft.world.level.levelgen.synth.NormalNoise.NoiseParameters; import org.slf4j.Logger; public final class DensityFunctions { private static final Codec CODEC = BuiltInRegistries.DENSITY_FUNCTION_TYPE .byNameCodec() .dispatch(densityFunction -> densityFunction.codec().codec(), Function.identity()); protected static final double MAX_REASONABLE_NOISE_VALUE = 1000000.0; static final Codec NOISE_VALUE_CODEC = Codec.doubleRange(-1000000.0, 1000000.0); public static final Codec DIRECT_CODEC = Codec.either(NOISE_VALUE_CODEC, CODEC) .xmap( either -> either.map(DensityFunctions::constant, Function.identity()), densityFunction -> densityFunction instanceof DensityFunctions.Constant constant ? Either.left(constant.value()) : Either.right(densityFunction) ); public static MapCodec bootstrap(Registry> registry) { register(registry, "blend_alpha", DensityFunctions.BlendAlpha.CODEC); register(registry, "blend_offset", DensityFunctions.BlendOffset.CODEC); register(registry, "beardifier", DensityFunctions.BeardifierMarker.CODEC); register(registry, "old_blended_noise", BlendedNoise.CODEC); for (Type type : Type.values()) { register(registry, type.getSerializedName(), type.codec); } register(registry, "noise", DensityFunctions.Noise.CODEC); register(registry, "end_islands", DensityFunctions.EndIslandDensityFunction.CODEC); register(registry, "weird_scaled_sampler", DensityFunctions.WeirdScaledSampler.CODEC); register(registry, "shifted_noise", DensityFunctions.ShiftedNoise.CODEC); register(registry, "range_choice", DensityFunctions.RangeChoice.CODEC); register(registry, "shift_a", DensityFunctions.ShiftA.CODEC); register(registry, "shift_b", DensityFunctions.ShiftB.CODEC); register(registry, "shift", DensityFunctions.Shift.CODEC); register(registry, "blend_density", DensityFunctions.BlendDensity.CODEC); register(registry, "clamp", DensityFunctions.Clamp.CODEC); for (net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type type2 : net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type.values()) { register(registry, type2.getSerializedName(), type2.codec); } for (net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type type3 : net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.values()) { register(registry, type3.getSerializedName(), type3.codec); } register(registry, "spline", DensityFunctions.Spline.CODEC); register(registry, "constant", DensityFunctions.Constant.CODEC); return register(registry, "y_clamped_gradient", DensityFunctions.YClampedGradient.CODEC); } private static MapCodec register( Registry> registry, String name, KeyDispatchDataCodec codec ) { return Registry.register(registry, name, codec.codec()); } static KeyDispatchDataCodec singleArgumentCodec(Codec codec, Function fromFunction, Function toFunction) { return KeyDispatchDataCodec.of(codec.fieldOf("argument").xmap(fromFunction, toFunction)); } static KeyDispatchDataCodec singleFunctionArgumentCodec(Function fromFunction, Function toFunction) { return singleArgumentCodec(DensityFunction.HOLDER_HELPER_CODEC, fromFunction, toFunction); } static KeyDispatchDataCodec doubleFunctionArgumentCodec( BiFunction fromFunction, Function primary, Function secondary ) { return KeyDispatchDataCodec.of( RecordCodecBuilder.mapCodec( instance -> instance.group( DensityFunction.HOLDER_HELPER_CODEC.fieldOf("argument1").forGetter(primary), DensityFunction.HOLDER_HELPER_CODEC.fieldOf("argument2").forGetter(secondary) ) .apply(instance, fromFunction) ) ); } static KeyDispatchDataCodec makeCodec(MapCodec mapCodec) { return KeyDispatchDataCodec.of(mapCodec); } private DensityFunctions() { } public static DensityFunction interpolated(DensityFunction wrapped) { return new DensityFunctions.Marker(Type.Interpolated, wrapped); } public static DensityFunction flatCache(DensityFunction wrapped) { return new DensityFunctions.Marker(Type.FlatCache, wrapped); } public static DensityFunction cache2d(DensityFunction wrapped) { return new DensityFunctions.Marker(Type.Cache2D, wrapped); } public static DensityFunction cacheOnce(DensityFunction wrapped) { return new DensityFunctions.Marker(Type.CacheOnce, wrapped); } public static DensityFunction cacheAllInCell(DensityFunction wrapped) { return new DensityFunctions.Marker(Type.CacheAllInCell, wrapped); } public static DensityFunction mappedNoise(Holder noiseData, @Deprecated double xzScale, double yScale, double fromY, double toY) { return mapFromUnitTo(new DensityFunctions.Noise(new DensityFunction.NoiseHolder(noiseData), xzScale, yScale), fromY, toY); } public static DensityFunction mappedNoise(Holder noiseData, double yScale, double fromY, double toY) { return mappedNoise(noiseData, 1.0, yScale, fromY, toY); } public static DensityFunction mappedNoise(Holder noiseData, double fromY, double toY) { return mappedNoise(noiseData, 1.0, 1.0, fromY, toY); } public static DensityFunction shiftedNoise2d(DensityFunction shiftX, DensityFunction shiftZ, double xzScale, Holder noiseData) { return new DensityFunctions.ShiftedNoise(shiftX, zero(), shiftZ, xzScale, 0.0, new DensityFunction.NoiseHolder(noiseData)); } public static DensityFunction noise(Holder noiseData) { return noise(noiseData, 1.0, 1.0); } public static DensityFunction noise(Holder noiseData, double xzScale, double yScale) { return new DensityFunctions.Noise(new DensityFunction.NoiseHolder(noiseData), xzScale, yScale); } public static DensityFunction noise(Holder noiseData, double yScale) { return noise(noiseData, 1.0, yScale); } public static DensityFunction rangeChoice( DensityFunction input, double minInclusive, double maxExclusive, DensityFunction whenInRange, DensityFunction whenOutOfRange ) { return new DensityFunctions.RangeChoice(input, minInclusive, maxExclusive, whenInRange, whenOutOfRange); } public static DensityFunction shiftA(Holder noiseData) { return new DensityFunctions.ShiftA(new DensityFunction.NoiseHolder(noiseData)); } public static DensityFunction shiftB(Holder noiseData) { return new DensityFunctions.ShiftB(new DensityFunction.NoiseHolder(noiseData)); } public static DensityFunction shift(Holder noiseData) { return new DensityFunctions.Shift(new DensityFunction.NoiseHolder(noiseData)); } public static DensityFunction blendDensity(DensityFunction input) { return new DensityFunctions.BlendDensity(input); } public static DensityFunction endIslands(long seed) { return new DensityFunctions.EndIslandDensityFunction(seed); } public static DensityFunction weirdScaledSampler(DensityFunction input, Holder noiseData, RarityValueMapper rarityValueMapper) { return new DensityFunctions.WeirdScaledSampler(input, new DensityFunction.NoiseHolder(noiseData), rarityValueMapper); } public static DensityFunction add(DensityFunction argument1, DensityFunction argument2) { return DensityFunctions.TwoArgumentSimpleFunction.create( net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.ADD, argument1, argument2 ); } public static DensityFunction mul(DensityFunction argument1, DensityFunction argument2) { return DensityFunctions.TwoArgumentSimpleFunction.create( net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MUL, argument1, argument2 ); } public static DensityFunction min(DensityFunction argument1, DensityFunction argument2) { return DensityFunctions.TwoArgumentSimpleFunction.create( net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MIN, argument1, argument2 ); } public static DensityFunction max(DensityFunction argument1, DensityFunction argument2) { return DensityFunctions.TwoArgumentSimpleFunction.create( net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MAX, argument1, argument2 ); } public static DensityFunction spline(CubicSpline spline) { return new DensityFunctions.Spline(spline); } public static DensityFunction zero() { return DensityFunctions.Constant.ZERO; } public static DensityFunction constant(double value) { return new DensityFunctions.Constant(value); } public static DensityFunction yClampedGradient(int fromY, int toY, double fromValue, double toValue) { return new DensityFunctions.YClampedGradient(fromY, toY, fromValue, toValue); } public static DensityFunction map(DensityFunction input, net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type type) { return DensityFunctions.Mapped.create(type, input); } private static DensityFunction mapFromUnitTo(DensityFunction densityFunction, double fromY, double toY) { double d = (fromY + toY) * 0.5; double e = (toY - fromY) * 0.5; return add(constant(d), mul(constant(e), densityFunction)); } public static DensityFunction blendAlpha() { return DensityFunctions.BlendAlpha.INSTANCE; } public static DensityFunction blendOffset() { return DensityFunctions.BlendOffset.INSTANCE; } public static DensityFunction lerp(DensityFunction deltaFunction, DensityFunction minFunction, DensityFunction maxFunction) { if (minFunction instanceof DensityFunctions.Constant constant) { return lerp(deltaFunction, constant.value, maxFunction); } else { DensityFunction densityFunction = cacheOnce(deltaFunction); DensityFunction densityFunction2 = add(mul(densityFunction, constant(-1.0)), constant(1.0)); return add(mul(minFunction, densityFunction2), mul(maxFunction, densityFunction)); } } public static DensityFunction lerp(DensityFunction deltaFunction, double min, DensityFunction maxFunction) { return add(mul(deltaFunction, add(maxFunction, constant(-min))), constant(min)); } record Ap2( net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type type, DensityFunction argument1, DensityFunction argument2, double minValue, double maxValue ) implements DensityFunctions.TwoArgumentSimpleFunction { // $VF: Unable to simplify switch-on-enum, as the enum class was not able to be found. // Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!) @Override public double compute(DensityFunction.FunctionContext context) { double d = this.argument1.compute(context); return switch (this.type.ordinal()) { case 0 -> d + this.argument2.compute(context); case 1 -> d == 0.0 ? 0.0 : d * this.argument2.compute(context); case 2 -> d < this.argument2.minValue() ? d : Math.min(d, this.argument2.compute(context)); case 3 -> d > this.argument2.maxValue() ? d : Math.max(d, this.argument2.compute(context)); default -> throw new MatchException(null, null); }; } // $VF: Unable to simplify switch-on-enum, as the enum class was not able to be found. // Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!) @Override public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { this.argument1.fillArray(array, contextProvider); switch (this.type.ordinal()) { case 0: double[] ds = new double[array.length]; this.argument2.fillArray(ds, contextProvider); for (int i = 0; i < array.length; i++) { array[i] += ds[i]; } break; case 1: for (int j = 0; j < array.length; j++) { double d = array[j]; array[j] = d == 0.0 ? 0.0 : d * this.argument2.compute(contextProvider.forIndex(j)); } break; case 2: double e = this.argument2.minValue(); for (int k = 0; k < array.length; k++) { double f = array[k]; array[k] = f < e ? f : Math.min(f, this.argument2.compute(contextProvider.forIndex(k))); } break; case 3: double e = this.argument2.maxValue(); for (int k = 0; k < array.length; k++) { double f = array[k]; array[k] = f > e ? f : Math.max(f, this.argument2.compute(contextProvider.forIndex(k))); } } } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(DensityFunctions.TwoArgumentSimpleFunction.create(this.type, this.argument1.mapAll(visitor), this.argument2.mapAll(visitor))); } } protected static enum BeardifierMarker implements DensityFunctions.BeardifierOrMarker { INSTANCE; @Override public double compute(DensityFunction.FunctionContext context) { return 0.0; } @Override public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { Arrays.fill(array, 0.0); } @Override public double minValue() { return 0.0; } @Override public double maxValue() { return 0.0; } } public interface BeardifierOrMarker extends DensityFunction.SimpleFunction { KeyDispatchDataCodec CODEC = KeyDispatchDataCodec.of(MapCodec.unit(DensityFunctions.BeardifierMarker.INSTANCE)); @Override default KeyDispatchDataCodec codec() { return CODEC; } } protected static enum BlendAlpha implements DensityFunction.SimpleFunction { INSTANCE; public static final KeyDispatchDataCodec CODEC = KeyDispatchDataCodec.of(MapCodec.unit(INSTANCE)); @Override public double compute(DensityFunction.FunctionContext context) { return 1.0; } @Override public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { Arrays.fill(array, 1.0); } @Override public double minValue() { return 1.0; } @Override public double maxValue() { return 1.0; } @Override public KeyDispatchDataCodec codec() { return CODEC; } } record BlendDensity(DensityFunction input) implements DensityFunctions.TransformerWithContext { static final KeyDispatchDataCodec CODEC = DensityFunctions.singleFunctionArgumentCodec( DensityFunctions.BlendDensity::new, DensityFunctions.BlendDensity::input ); @Override public double transform(DensityFunction.FunctionContext context, double value) { return context.getBlender().blendDensity(context, value); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new DensityFunctions.BlendDensity(this.input.mapAll(visitor))); } @Override public double minValue() { return Double.NEGATIVE_INFINITY; } @Override public double maxValue() { return Double.POSITIVE_INFINITY; } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected static enum BlendOffset implements DensityFunction.SimpleFunction { INSTANCE; public static final KeyDispatchDataCodec CODEC = KeyDispatchDataCodec.of(MapCodec.unit(INSTANCE)); @Override public double compute(DensityFunction.FunctionContext context) { return 0.0; } @Override public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { Arrays.fill(array, 0.0); } @Override public double minValue() { return 0.0; } @Override public double maxValue() { return 0.0; } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected record Clamp(DensityFunction input, double minValue, double maxValue) implements DensityFunctions.PureTransformer { private static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( DensityFunction.DIRECT_CODEC.fieldOf("input").forGetter(DensityFunctions.Clamp::input), DensityFunctions.NOISE_VALUE_CODEC.fieldOf("min").forGetter(DensityFunctions.Clamp::minValue), DensityFunctions.NOISE_VALUE_CODEC.fieldOf("max").forGetter(DensityFunctions.Clamp::maxValue) ) .apply(instance, DensityFunctions.Clamp::new) ); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double transform(double value) { return Mth.clamp(value, this.minValue, this.maxValue); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return new DensityFunctions.Clamp(this.input.mapAll(visitor), this.minValue, this.maxValue); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } record Constant(double value) implements DensityFunction.SimpleFunction { static final KeyDispatchDataCodec CODEC = DensityFunctions.singleArgumentCodec( DensityFunctions.NOISE_VALUE_CODEC, DensityFunctions.Constant::new, DensityFunctions.Constant::value ); static final DensityFunctions.Constant ZERO = new DensityFunctions.Constant(0.0); @Override public double compute(DensityFunction.FunctionContext context) { return this.value; } @Override public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { Arrays.fill(array, this.value); } @Override public double minValue() { return this.value; } @Override public double maxValue() { return this.value; } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected static final class EndIslandDensityFunction implements DensityFunction.SimpleFunction { public static final KeyDispatchDataCodec CODEC = KeyDispatchDataCodec.of( MapCodec.unit(new DensityFunctions.EndIslandDensityFunction(0L)) ); private static final float ISLAND_THRESHOLD = -0.9F; private final SimplexNoise islandNoise; public EndIslandDensityFunction(long seed) { RandomSource randomSource = new LegacyRandomSource(seed); randomSource.consumeCount(17292); this.islandNoise = new SimplexNoise(randomSource); } private static float getHeightValue(SimplexNoise noise, int x, int z) { int i = x / 2; int j = z / 2; int k = x % 2; int l = z % 2; float f = 100.0F - Mth.sqrt(x * x + z * z) * 8.0F; f = Mth.clamp(f, -100.0F, 80.0F); for (int m = -12; m <= 12; m++) { for (int n = -12; n <= 12; n++) { long o = i + m; long p = j + n; if (o * o + p * p > 4096L && noise.getValue(o, p) < -0.9F) { float g = (Mth.abs((float)o) * 3439.0F + Mth.abs((float)p) * 147.0F) % 13.0F + 9.0F; float h = k - m * 2; float q = l - n * 2; float r = 100.0F - Mth.sqrt(h * h + q * q) * g; r = Mth.clamp(r, -100.0F, 80.0F); f = Math.max(f, r); } } } return f; } @Override public double compute(DensityFunction.FunctionContext context) { return (getHeightValue(this.islandNoise, context.blockX() / 8, context.blockZ() / 8) - 8.0) / 128.0; } @Override public double minValue() { return -0.84375; } @Override public double maxValue() { return 0.5625; } @Override public KeyDispatchDataCodec codec() { return CODEC; } } @VisibleForDebug public record HolderHolder(Holder function) implements DensityFunction { @Override public double compute(DensityFunction.FunctionContext context) { return this.function.value().compute(context); } @Override public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { this.function.value().fillArray(array, contextProvider); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new DensityFunctions.HolderHolder(new Direct<>(this.function.value().mapAll(visitor)))); } @Override public double minValue() { return this.function.isBound() ? this.function.value().minValue() : Double.NEGATIVE_INFINITY; } @Override public double maxValue() { return this.function.isBound() ? this.function.value().maxValue() : Double.POSITIVE_INFINITY; } @Override public KeyDispatchDataCodec codec() { throw new UnsupportedOperationException("Calling .codec() on HolderHolder"); } } protected record Mapped(net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type type, DensityFunction input, double minValue, double maxValue) implements DensityFunctions.PureTransformer { public static DensityFunctions.Mapped create(net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type type, DensityFunction input) { double d = input.minValue(); double e = transform(type, d); double f = transform(type, input.maxValue()); return type != net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type.ABS && type != net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type.SQUARE ? new DensityFunctions.Mapped(type, input, e, f) : new DensityFunctions.Mapped(type, input, Math.max(0.0, d), Math.max(e, f)); } // $VF: Unable to simplify switch-on-enum, as the enum class was not able to be found. // Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!) private static double transform(net.minecraft.world.level.levelgen.DensityFunctions.Mapped.Type type, double value) { return switch (type.ordinal()) { case 0 -> Math.abs(value); case 1 -> value * value; case 2 -> value * value * value; case 3 -> value > 0.0 ? value : value * 0.5; case 4 -> value > 0.0 ? value : value * 0.25; case 5 -> { double d = Mth.clamp(value, -1.0, 1.0); yield d / 2.0 - d * d * d / 24.0; } default -> throw new MatchException(null, null); }; } @Override public double transform(double value) { return transform(this.type, value); } public DensityFunctions.Mapped mapAll(DensityFunction.Visitor visitor) { return create(this.type, this.input.mapAll(visitor)); } @Override public KeyDispatchDataCodec codec() { return this.type.codec; } } protected record Marker(Type type, DensityFunction wrapped) implements DensityFunctions.MarkerOrMarked { @Override public double compute(DensityFunction.FunctionContext context) { return this.wrapped.compute(context); } @Override public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { this.wrapped.fillArray(array, contextProvider); } @Override public double minValue() { return this.wrapped.minValue(); } @Override public double maxValue() { return this.wrapped.maxValue(); } } public interface MarkerOrMarked extends DensityFunction { Type type(); DensityFunction wrapped(); @Override default KeyDispatchDataCodec codec() { return this.type().codec; } @Override default DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new DensityFunctions.Marker(this.type(), this.wrapped().mapAll(visitor))); } } record MulOrAdd( net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type specificType, DensityFunction input, double minValue, double maxValue, double argument ) implements DensityFunctions.PureTransformer, DensityFunctions.TwoArgumentSimpleFunction { @Override public net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type type() { return this.specificType == net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type.MUL ? net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MUL : net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.ADD; } @Override public DensityFunction argument1() { return DensityFunctions.constant(this.argument); } @Override public DensityFunction argument2() { return this.input; } // $VF: Unable to simplify switch-on-enum, as the enum class was not able to be found. // Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!) @Override public double transform(double value) { return switch (this.specificType.ordinal()) { case 0 -> value * this.argument; case 1 -> value + this.argument; default -> throw new MatchException(null, null); }; } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { DensityFunction densityFunction = this.input.mapAll(visitor); double d = densityFunction.minValue(); double e = densityFunction.maxValue(); double f; double g; if (this.specificType == net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type.ADD) { f = d + this.argument; g = e + this.argument; } else if (this.argument >= 0.0) { f = d * this.argument; g = e * this.argument; } else { f = e * this.argument; g = d * this.argument; } return new DensityFunctions.MulOrAdd(this.specificType, densityFunction, f, g, this.argument); } } protected record Noise(DensityFunction.NoiseHolder noise, @Deprecated double xzScale, double yScale) implements DensityFunction { public static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( DensityFunction.NoiseHolder.CODEC.fieldOf("noise").forGetter(DensityFunctions.Noise::noise), Codec.DOUBLE.fieldOf("xz_scale").forGetter(DensityFunctions.Noise::xzScale), Codec.DOUBLE.fieldOf("y_scale").forGetter(DensityFunctions.Noise::yScale) ) .apply(instance, DensityFunctions.Noise::new) ); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double compute(DensityFunction.FunctionContext context) { return this.noise.getValue(context.blockX() * this.xzScale, context.blockY() * this.yScale, context.blockZ() * this.xzScale); } @Override public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { contextProvider.fillAllDirectly(array, this); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new DensityFunctions.Noise(visitor.visitNoise(this.noise), this.xzScale, this.yScale)); } @Override public double minValue() { return -this.maxValue(); } @Override public double maxValue() { return this.noise.maxValue(); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } interface PureTransformer extends DensityFunction { DensityFunction input(); @Override default double compute(DensityFunction.FunctionContext context) { return this.transform(this.input().compute(context)); } @Override default void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { this.input().fillArray(array, contextProvider); for (int i = 0; i < array.length; i++) { array[i] = this.transform(array[i]); } } double transform(double value); } record RangeChoice(DensityFunction input, double minInclusive, double maxExclusive, DensityFunction whenInRange, DensityFunction whenOutOfRange) implements DensityFunction { public static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( DensityFunction.HOLDER_HELPER_CODEC.fieldOf("input").forGetter(DensityFunctions.RangeChoice::input), DensityFunctions.NOISE_VALUE_CODEC.fieldOf("min_inclusive").forGetter(DensityFunctions.RangeChoice::minInclusive), DensityFunctions.NOISE_VALUE_CODEC.fieldOf("max_exclusive").forGetter(DensityFunctions.RangeChoice::maxExclusive), DensityFunction.HOLDER_HELPER_CODEC.fieldOf("when_in_range").forGetter(DensityFunctions.RangeChoice::whenInRange), DensityFunction.HOLDER_HELPER_CODEC.fieldOf("when_out_of_range").forGetter(DensityFunctions.RangeChoice::whenOutOfRange) ) .apply(instance, DensityFunctions.RangeChoice::new) ); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double compute(DensityFunction.FunctionContext context) { double d = this.input.compute(context); return d >= this.minInclusive && d < this.maxExclusive ? this.whenInRange.compute(context) : this.whenOutOfRange.compute(context); } @Override public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { this.input.fillArray(array, contextProvider); for (int i = 0; i < array.length; i++) { double d = array[i]; if (d >= this.minInclusive && d < this.maxExclusive) { array[i] = this.whenInRange.compute(contextProvider.forIndex(i)); } else { array[i] = this.whenOutOfRange.compute(contextProvider.forIndex(i)); } } } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply( new DensityFunctions.RangeChoice( this.input.mapAll(visitor), this.minInclusive, this.maxExclusive, this.whenInRange.mapAll(visitor), this.whenOutOfRange.mapAll(visitor) ) ); } @Override public double minValue() { return Math.min(this.whenInRange.minValue(), this.whenOutOfRange.minValue()); } @Override public double maxValue() { return Math.max(this.whenInRange.maxValue(), this.whenOutOfRange.maxValue()); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected record Shift(DensityFunction.NoiseHolder offsetNoise) implements DensityFunctions.ShiftNoise { static final KeyDispatchDataCodec CODEC = DensityFunctions.singleArgumentCodec( DensityFunction.NoiseHolder.CODEC, DensityFunctions.Shift::new, DensityFunctions.Shift::offsetNoise ); @Override public double compute(DensityFunction.FunctionContext context) { return this.compute(context.blockX(), context.blockY(), context.blockZ()); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new DensityFunctions.Shift(visitor.visitNoise(this.offsetNoise))); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected record ShiftA(DensityFunction.NoiseHolder offsetNoise) implements DensityFunctions.ShiftNoise { static final KeyDispatchDataCodec CODEC = DensityFunctions.singleArgumentCodec( DensityFunction.NoiseHolder.CODEC, DensityFunctions.ShiftA::new, DensityFunctions.ShiftA::offsetNoise ); @Override public double compute(DensityFunction.FunctionContext context) { return this.compute(context.blockX(), 0.0, context.blockZ()); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new DensityFunctions.ShiftA(visitor.visitNoise(this.offsetNoise))); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } protected record ShiftB(DensityFunction.NoiseHolder offsetNoise) implements DensityFunctions.ShiftNoise { static final KeyDispatchDataCodec CODEC = DensityFunctions.singleArgumentCodec( DensityFunction.NoiseHolder.CODEC, DensityFunctions.ShiftB::new, DensityFunctions.ShiftB::offsetNoise ); @Override public double compute(DensityFunction.FunctionContext context) { return this.compute(context.blockZ(), context.blockX(), 0.0); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new DensityFunctions.ShiftB(visitor.visitNoise(this.offsetNoise))); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } interface ShiftNoise extends DensityFunction { DensityFunction.NoiseHolder offsetNoise(); @Override default double minValue() { return -this.maxValue(); } @Override default double maxValue() { return this.offsetNoise().maxValue() * 4.0; } default double compute(double x, double y, double z) { return this.offsetNoise().getValue(x * 0.25, y * 0.25, z * 0.25) * 4.0; } @Override default void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { contextProvider.fillAllDirectly(array, this); } } protected record ShiftedNoise( DensityFunction shiftX, DensityFunction shiftY, DensityFunction shiftZ, double xzScale, double yScale, DensityFunction.NoiseHolder noise ) implements DensityFunction { private static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( DensityFunction.HOLDER_HELPER_CODEC.fieldOf("shift_x").forGetter(DensityFunctions.ShiftedNoise::shiftX), DensityFunction.HOLDER_HELPER_CODEC.fieldOf("shift_y").forGetter(DensityFunctions.ShiftedNoise::shiftY), DensityFunction.HOLDER_HELPER_CODEC.fieldOf("shift_z").forGetter(DensityFunctions.ShiftedNoise::shiftZ), Codec.DOUBLE.fieldOf("xz_scale").forGetter(DensityFunctions.ShiftedNoise::xzScale), Codec.DOUBLE.fieldOf("y_scale").forGetter(DensityFunctions.ShiftedNoise::yScale), DensityFunction.NoiseHolder.CODEC.fieldOf("noise").forGetter(DensityFunctions.ShiftedNoise::noise) ) .apply(instance, DensityFunctions.ShiftedNoise::new) ); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double compute(DensityFunction.FunctionContext context) { double d = context.blockX() * this.xzScale + this.shiftX.compute(context); double e = context.blockY() * this.yScale + this.shiftY.compute(context); double f = context.blockZ() * this.xzScale + this.shiftZ.compute(context); return this.noise.getValue(d, e, f); } @Override public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { contextProvider.fillAllDirectly(array, this); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply( new DensityFunctions.ShiftedNoise( this.shiftX.mapAll(visitor), this.shiftY.mapAll(visitor), this.shiftZ.mapAll(visitor), this.xzScale, this.yScale, visitor.visitNoise(this.noise) ) ); } @Override public double minValue() { return -this.maxValue(); } @Override public double maxValue() { return this.noise.maxValue(); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } public record Spline(CubicSpline spline) implements DensityFunction { private static final Codec> SPLINE_CODEC = CubicSpline.codec(Coordinate.CODEC); private static final MapCodec DATA_CODEC = SPLINE_CODEC.fieldOf("spline") .xmap(DensityFunctions.Spline::new, DensityFunctions.Spline::spline); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double compute(DensityFunction.FunctionContext context) { return this.spline.apply(new Point(context)); } @Override public double minValue() { return this.spline.minValue(); } @Override public double maxValue() { return this.spline.maxValue(); } @Override public void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { contextProvider.fillAllDirectly(array, this); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new DensityFunctions.Spline(this.spline.mapAll(coordinate -> coordinate.mapAll(visitor)))); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } interface TransformerWithContext extends DensityFunction { DensityFunction input(); @Override default double compute(DensityFunction.FunctionContext context) { return this.transform(context, this.input().compute(context)); } @Override default void fillArray(double[] array, DensityFunction.ContextProvider contextProvider) { this.input().fillArray(array, contextProvider); for (int i = 0; i < array.length; i++) { array[i] = this.transform(contextProvider.forIndex(i), array[i]); } } double transform(DensityFunction.FunctionContext context, double value); } interface TwoArgumentSimpleFunction extends DensityFunction { Logger LOGGER = LogUtils.getLogger(); // $VF: Unable to simplify switch-on-enum, as the enum class was not able to be found. // Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!) static DensityFunctions.TwoArgumentSimpleFunction create( net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type type, DensityFunction argument1, DensityFunction argument2 ) { double d = argument1.minValue(); double e = argument2.minValue(); double f = argument1.maxValue(); double g = argument2.maxValue(); if (type == net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MIN || type == net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MAX) { boolean bl = d >= g; boolean bl2 = e >= f; if (bl || bl2) { LOGGER.warn("Creating a " + type + " function between two non-overlapping inputs: " + argument1 + " and " + argument2); } } double h = switch (type.ordinal()) { case 0 -> d + e; case 1 -> d > 0.0 && e > 0.0 ? d * e : (f < 0.0 && g < 0.0 ? f * g : Math.min(d * g, f * e)); case 2 -> Math.min(d, e); case 3 -> Math.max(d, e); default -> throw new MatchException(null, null); }; double i = switch (type.ordinal()) { case 0 -> f + g; case 1 -> d > 0.0 && e > 0.0 ? f * g : (f < 0.0 && g < 0.0 ? d * e : Math.max(d * e, f * g)); case 2 -> Math.min(f, g); case 3 -> Math.max(f, g); default -> throw new MatchException(null, null); }; if (type == net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.MUL || type == net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.ADD) { if (argument1 instanceof DensityFunctions.Constant constant) { return new DensityFunctions.MulOrAdd( type == net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.ADD ? net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type.ADD : net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type.MUL, argument2, h, i, constant.value ); } if (argument2 instanceof DensityFunctions.Constant constant) { return new DensityFunctions.MulOrAdd( type == net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type.ADD ? net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type.ADD : net.minecraft.world.level.levelgen.DensityFunctions.MulOrAdd.Type.MUL, argument1, h, i, constant.value ); } } return new DensityFunctions.Ap2(type, argument1, argument2, h, i); } net.minecraft.world.level.levelgen.DensityFunctions.TwoArgumentSimpleFunction.Type type(); DensityFunction argument1(); DensityFunction argument2(); @Override default KeyDispatchDataCodec codec() { return this.type().codec; } } protected record WeirdScaledSampler(DensityFunction input, DensityFunction.NoiseHolder noise, RarityValueMapper rarityValueMapper) implements DensityFunctions.TransformerWithContext { private static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( DensityFunction.HOLDER_HELPER_CODEC.fieldOf("input").forGetter(DensityFunctions.WeirdScaledSampler::input), DensityFunction.NoiseHolder.CODEC.fieldOf("noise").forGetter(DensityFunctions.WeirdScaledSampler::noise), RarityValueMapper.CODEC.fieldOf("rarity_value_mapper").forGetter(DensityFunctions.WeirdScaledSampler::rarityValueMapper) ) .apply(instance, DensityFunctions.WeirdScaledSampler::new) ); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double transform(DensityFunction.FunctionContext context, double value) { double d = this.rarityValueMapper.mapper.get(value); return d * Math.abs(this.noise.getValue(context.blockX() / d, context.blockY() / d, context.blockZ() / d)); } @Override public DensityFunction mapAll(DensityFunction.Visitor visitor) { return visitor.apply(new DensityFunctions.WeirdScaledSampler(this.input.mapAll(visitor), visitor.visitNoise(this.noise), this.rarityValueMapper)); } @Override public double minValue() { return 0.0; } @Override public double maxValue() { return this.rarityValueMapper.maxRarity * this.noise.maxValue(); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } record YClampedGradient(int fromY, int toY, double fromValue, double toValue) implements DensityFunction.SimpleFunction { private static final MapCodec DATA_CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( Codec.intRange(DimensionType.MIN_Y * 2, DimensionType.MAX_Y * 2).fieldOf("from_y").forGetter(DensityFunctions.YClampedGradient::fromY), Codec.intRange(DimensionType.MIN_Y * 2, DimensionType.MAX_Y * 2).fieldOf("to_y").forGetter(DensityFunctions.YClampedGradient::toY), DensityFunctions.NOISE_VALUE_CODEC.fieldOf("from_value").forGetter(DensityFunctions.YClampedGradient::fromValue), DensityFunctions.NOISE_VALUE_CODEC.fieldOf("to_value").forGetter(DensityFunctions.YClampedGradient::toValue) ) .apply(instance, DensityFunctions.YClampedGradient::new) ); public static final KeyDispatchDataCodec CODEC = DensityFunctions.makeCodec(DATA_CODEC); @Override public double compute(DensityFunction.FunctionContext context) { return Mth.clampedMap((double)context.blockY(), (double)this.fromY, (double)this.toY, this.fromValue, this.toValue); } @Override public double minValue() { return Math.min(this.fromValue, this.toValue); } @Override public double maxValue() { return Math.max(this.fromValue, this.toValue); } @Override public KeyDispatchDataCodec codec() { return CODEC; } } }