2025-11-24 22:52:51 +03:00

825 lines
34 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.base.Suppliers
* com.google.common.collect.ImmutableList
* com.google.common.collect.ImmutableList$Builder
* com.mojang.datafixers.kinds.App
* com.mojang.datafixers.kinds.Applicative
* com.mojang.serialization.Codec
* com.mojang.serialization.MapCodec
* com.mojang.serialization.codecs.RecordCodecBuilder
* org.jspecify.annotations.Nullable
*/
package net.minecraft.world.level.levelgen;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.mojang.datafixers.kinds.App;
import com.mojang.datafixers.kinds.Applicative;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.KeyDispatchDataCodec;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.NoiseChunk;
import net.minecraft.world.level.levelgen.PositionalRandomFactory;
import net.minecraft.world.level.levelgen.RandomState;
import net.minecraft.world.level.levelgen.SurfaceSystem;
import net.minecraft.world.level.levelgen.VerticalAnchor;
import net.minecraft.world.level.levelgen.WorldGenerationContext;
import net.minecraft.world.level.levelgen.placement.CaveSurface;
import net.minecraft.world.level.levelgen.synth.NormalNoise;
import org.jspecify.annotations.Nullable;
public class SurfaceRules {
public static final ConditionSource ON_FLOOR = SurfaceRules.stoneDepthCheck(0, false, CaveSurface.FLOOR);
public static final ConditionSource UNDER_FLOOR = SurfaceRules.stoneDepthCheck(0, true, CaveSurface.FLOOR);
public static final ConditionSource DEEP_UNDER_FLOOR = SurfaceRules.stoneDepthCheck(0, true, 6, CaveSurface.FLOOR);
public static final ConditionSource VERY_DEEP_UNDER_FLOOR = SurfaceRules.stoneDepthCheck(0, true, 30, CaveSurface.FLOOR);
public static final ConditionSource ON_CEILING = SurfaceRules.stoneDepthCheck(0, false, CaveSurface.CEILING);
public static final ConditionSource UNDER_CEILING = SurfaceRules.stoneDepthCheck(0, true, CaveSurface.CEILING);
public static ConditionSource stoneDepthCheck(int offset, boolean addSurfaceDepth1, CaveSurface surfaceType) {
return new StoneDepthCheck(offset, addSurfaceDepth1, 0, surfaceType);
}
public static ConditionSource stoneDepthCheck(int offset, boolean addSurfaceDepth1, int secondaryDepthRange, CaveSurface surfaceType) {
return new StoneDepthCheck(offset, addSurfaceDepth1, secondaryDepthRange, surfaceType);
}
public static ConditionSource not(ConditionSource target) {
return new NotConditionSource(target);
}
public static ConditionSource yBlockCheck(VerticalAnchor anchor, int surfaceDepthMultiplier) {
return new YConditionSource(anchor, surfaceDepthMultiplier, false);
}
public static ConditionSource yStartCheck(VerticalAnchor anchor, int surfaceDepthMultiplier) {
return new YConditionSource(anchor, surfaceDepthMultiplier, true);
}
public static ConditionSource waterBlockCheck(int offset, int surfaceDepthMultiplier) {
return new WaterConditionSource(offset, surfaceDepthMultiplier, false);
}
public static ConditionSource waterStartCheck(int offset, int surfaceDepthMultiplier) {
return new WaterConditionSource(offset, surfaceDepthMultiplier, true);
}
@SafeVarargs
public static ConditionSource isBiome(ResourceKey<Biome> ... target) {
return SurfaceRules.isBiome(List.of(target));
}
private static BiomeConditionSource isBiome(List<ResourceKey<Biome>> target) {
return new BiomeConditionSource(target);
}
public static ConditionSource noiseCondition(ResourceKey<NormalNoise.NoiseParameters> noise, double minRange) {
return SurfaceRules.noiseCondition(noise, minRange, Double.MAX_VALUE);
}
public static ConditionSource noiseCondition(ResourceKey<NormalNoise.NoiseParameters> noise, double minRange, double maxRange) {
return new NoiseThresholdConditionSource(noise, minRange, maxRange);
}
public static ConditionSource verticalGradient(String randomName, VerticalAnchor trueAtAndBelow, VerticalAnchor falseAtAndAbove) {
return new VerticalGradientConditionSource(Identifier.parse(randomName), trueAtAndBelow, falseAtAndAbove);
}
public static ConditionSource steep() {
return Steep.INSTANCE;
}
public static ConditionSource hole() {
return Hole.INSTANCE;
}
public static ConditionSource abovePreliminarySurface() {
return AbovePreliminarySurface.INSTANCE;
}
public static ConditionSource temperature() {
return Temperature.INSTANCE;
}
public static RuleSource ifTrue(ConditionSource condition, RuleSource next) {
return new TestRuleSource(condition, next);
}
public static RuleSource sequence(RuleSource ... rules) {
if (rules.length == 0) {
throw new IllegalArgumentException("Need at least 1 rule for a sequence");
}
return new SequenceRuleSource(Arrays.asList(rules));
}
public static RuleSource state(BlockState state) {
return new BlockRuleSource(state);
}
public static RuleSource bandlands() {
return Bandlands.INSTANCE;
}
private static <A> MapCodec<? extends A> register(Registry<MapCodec<? extends A>> registry, String name, KeyDispatchDataCodec<? extends A> codec) {
return Registry.register(registry, name, codec.codec());
}
private record StoneDepthCheck(int offset, boolean addSurfaceDepth, int secondaryDepthRange, CaveSurface surfaceType) implements ConditionSource
{
private static final KeyDispatchDataCodec<StoneDepthCheck> CODEC = KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec(i -> i.group((App)Codec.INT.fieldOf("offset").forGetter(StoneDepthCheck::offset), (App)Codec.BOOL.fieldOf("add_surface_depth").forGetter(StoneDepthCheck::addSurfaceDepth), (App)Codec.INT.fieldOf("secondary_depth_range").forGetter(StoneDepthCheck::secondaryDepthRange), (App)CaveSurface.CODEC.fieldOf("surface_type").forGetter(StoneDepthCheck::surfaceType)).apply((Applicative)i, StoneDepthCheck::new)));
@Override
public KeyDispatchDataCodec<? extends ConditionSource> codec() {
return CODEC;
}
@Override
public Condition apply(final Context ruleContext) {
final boolean ceiling = this.surfaceType == CaveSurface.CEILING;
class StoneDepthCondition
extends LazyYCondition {
private StoneDepthCondition() {
super(context);
}
@Override
protected boolean compute() {
int stoneDepth = ceiling ? this.context.stoneDepthBelow : this.context.stoneDepthAbove;
int surfaceDepth = StoneDepthCheck.this.addSurfaceDepth ? this.context.surfaceDepth : 0;
int secondarySurfaceDepth = StoneDepthCheck.this.secondaryDepthRange == 0 ? 0 : (int)Mth.map(this.context.getSurfaceSecondary(), -1.0, 1.0, 0.0, (double)StoneDepthCheck.this.secondaryDepthRange);
return stoneDepth <= 1 + StoneDepthCheck.this.offset + surfaceDepth + secondarySurfaceDepth;
}
}
return new StoneDepthCondition();
}
}
private record NotConditionSource(ConditionSource target) implements ConditionSource
{
private static final KeyDispatchDataCodec<NotConditionSource> CODEC = KeyDispatchDataCodec.of(ConditionSource.CODEC.xmap(NotConditionSource::new, NotConditionSource::target).fieldOf("invert"));
@Override
public KeyDispatchDataCodec<? extends ConditionSource> codec() {
return CODEC;
}
@Override
public Condition apply(Context context) {
return new NotCondition((Condition)this.target.apply(context));
}
}
public static interface ConditionSource
extends Function<Context, Condition> {
public static final Codec<ConditionSource> CODEC = BuiltInRegistries.MATERIAL_CONDITION.byNameCodec().dispatch(source -> source.codec().codec(), Function.identity());
public static MapCodec<? extends ConditionSource> bootstrap(Registry<MapCodec<? extends ConditionSource>> registry) {
SurfaceRules.register(registry, "biome", BiomeConditionSource.CODEC);
SurfaceRules.register(registry, "noise_threshold", NoiseThresholdConditionSource.CODEC);
SurfaceRules.register(registry, "vertical_gradient", VerticalGradientConditionSource.CODEC);
SurfaceRules.register(registry, "y_above", YConditionSource.CODEC);
SurfaceRules.register(registry, "water", WaterConditionSource.CODEC);
SurfaceRules.register(registry, "temperature", Temperature.CODEC);
SurfaceRules.register(registry, "steep", Steep.CODEC);
SurfaceRules.register(registry, "not", NotConditionSource.CODEC);
SurfaceRules.register(registry, "hole", Hole.CODEC);
SurfaceRules.register(registry, "above_preliminary_surface", AbovePreliminarySurface.CODEC);
return SurfaceRules.register(registry, "stone_depth", StoneDepthCheck.CODEC);
}
public KeyDispatchDataCodec<? extends ConditionSource> codec();
}
private record YConditionSource(VerticalAnchor anchor, int surfaceDepthMultiplier, boolean addStoneDepth) implements ConditionSource
{
private static final KeyDispatchDataCodec<YConditionSource> CODEC = KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec(i -> i.group((App)VerticalAnchor.CODEC.fieldOf("anchor").forGetter(YConditionSource::anchor), (App)Codec.intRange((int)-20, (int)20).fieldOf("surface_depth_multiplier").forGetter(YConditionSource::surfaceDepthMultiplier), (App)Codec.BOOL.fieldOf("add_stone_depth").forGetter(YConditionSource::addStoneDepth)).apply((Applicative)i, YConditionSource::new)));
@Override
public KeyDispatchDataCodec<? extends ConditionSource> codec() {
return CODEC;
}
@Override
public Condition apply(final Context ruleContext) {
class YCondition
extends LazyYCondition {
private YCondition() {
super(context);
}
@Override
protected boolean compute() {
return this.context.blockY + (YConditionSource.this.addStoneDepth ? this.context.stoneDepthAbove : 0) >= YConditionSource.this.anchor.resolveY(this.context.context) + this.context.surfaceDepth * YConditionSource.this.surfaceDepthMultiplier;
}
}
return new YCondition();
}
}
private record WaterConditionSource(int offset, int surfaceDepthMultiplier, boolean addStoneDepth) implements ConditionSource
{
private static final KeyDispatchDataCodec<WaterConditionSource> CODEC = KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec(i -> i.group((App)Codec.INT.fieldOf("offset").forGetter(WaterConditionSource::offset), (App)Codec.intRange((int)-20, (int)20).fieldOf("surface_depth_multiplier").forGetter(WaterConditionSource::surfaceDepthMultiplier), (App)Codec.BOOL.fieldOf("add_stone_depth").forGetter(WaterConditionSource::addStoneDepth)).apply((Applicative)i, WaterConditionSource::new)));
@Override
public KeyDispatchDataCodec<? extends ConditionSource> codec() {
return CODEC;
}
@Override
public Condition apply(final Context ruleContext) {
class WaterCondition
extends LazyYCondition {
private WaterCondition() {
super(context);
}
@Override
protected boolean compute() {
return this.context.waterHeight == Integer.MIN_VALUE || this.context.blockY + (WaterConditionSource.this.addStoneDepth ? this.context.stoneDepthAbove : 0) >= this.context.waterHeight + WaterConditionSource.this.offset + this.context.surfaceDepth * WaterConditionSource.this.surfaceDepthMultiplier;
}
}
return new WaterCondition();
}
}
private static final class BiomeConditionSource
implements ConditionSource {
private static final KeyDispatchDataCodec<BiomeConditionSource> CODEC = KeyDispatchDataCodec.of(ResourceKey.codec(Registries.BIOME).listOf().fieldOf("biome_is").xmap(SurfaceRules::isBiome, e -> e.biomes));
private final List<ResourceKey<Biome>> biomes;
private final Predicate<ResourceKey<Biome>> biomeNameTest;
private BiomeConditionSource(List<ResourceKey<Biome>> biomes) {
this.biomes = biomes;
this.biomeNameTest = Set.copyOf(biomes)::contains;
}
@Override
public KeyDispatchDataCodec<? extends ConditionSource> codec() {
return CODEC;
}
@Override
public Condition apply(final Context ruleContext) {
class BiomeCondition
extends LazyYCondition {
private BiomeCondition() {
super(context);
}
@Override
protected boolean compute() {
return this.context.biome.get().is(BiomeConditionSource.this.biomeNameTest);
}
}
return new BiomeCondition();
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o instanceof BiomeConditionSource) {
BiomeConditionSource that = (BiomeConditionSource)o;
return this.biomes.equals(that.biomes);
}
return false;
}
public int hashCode() {
return this.biomes.hashCode();
}
public String toString() {
return "BiomeConditionSource[biomes=" + String.valueOf(this.biomes) + "]";
}
}
private record NoiseThresholdConditionSource(ResourceKey<NormalNoise.NoiseParameters> noise, double minThreshold, double maxThreshold) implements ConditionSource
{
private static final KeyDispatchDataCodec<NoiseThresholdConditionSource> CODEC = KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec(i -> i.group((App)ResourceKey.codec(Registries.NOISE).fieldOf("noise").forGetter(NoiseThresholdConditionSource::noise), (App)Codec.DOUBLE.fieldOf("min_threshold").forGetter(NoiseThresholdConditionSource::minThreshold), (App)Codec.DOUBLE.fieldOf("max_threshold").forGetter(NoiseThresholdConditionSource::maxThreshold)).apply((Applicative)i, NoiseThresholdConditionSource::new)));
@Override
public KeyDispatchDataCodec<? extends ConditionSource> codec() {
return CODEC;
}
@Override
public Condition apply(final Context ruleContext) {
final NormalNoise noise = ruleContext.randomState.getOrCreateNoise(this.noise);
class NoiseThresholdCondition
extends LazyXZCondition {
private NoiseThresholdCondition() {
super(context);
}
@Override
protected boolean compute() {
double value = noise.getValue(this.context.blockX, 0.0, this.context.blockZ);
return value >= NoiseThresholdConditionSource.this.minThreshold && value <= NoiseThresholdConditionSource.this.maxThreshold;
}
}
return new NoiseThresholdCondition();
}
}
private record VerticalGradientConditionSource(Identifier randomName, VerticalAnchor trueAtAndBelow, VerticalAnchor falseAtAndAbove) implements ConditionSource
{
private static final KeyDispatchDataCodec<VerticalGradientConditionSource> CODEC = KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec(i -> i.group((App)Identifier.CODEC.fieldOf("random_name").forGetter(VerticalGradientConditionSource::randomName), (App)VerticalAnchor.CODEC.fieldOf("true_at_and_below").forGetter(VerticalGradientConditionSource::trueAtAndBelow), (App)VerticalAnchor.CODEC.fieldOf("false_at_and_above").forGetter(VerticalGradientConditionSource::falseAtAndAbove)).apply((Applicative)i, VerticalGradientConditionSource::new)));
@Override
public KeyDispatchDataCodec<? extends ConditionSource> codec() {
return CODEC;
}
@Override
public Condition apply(final Context ruleContext) {
final int trueAtAndBelow = this.trueAtAndBelow().resolveY(ruleContext.context);
final int falseAtAndAbove = this.falseAtAndAbove().resolveY(ruleContext.context);
final PositionalRandomFactory randomFactory = ruleContext.randomState.getOrCreateRandomFactory(this.randomName());
class VerticalGradientCondition
extends LazyYCondition {
private VerticalGradientCondition() {
super(context);
}
@Override
protected boolean compute() {
int blockY = this.context.blockY;
if (blockY <= trueAtAndBelow) {
return true;
}
if (blockY >= falseAtAndAbove) {
return false;
}
double probability = Mth.map((double)blockY, (double)trueAtAndBelow, (double)falseAtAndAbove, 1.0, 0.0);
RandomSource random = randomFactory.at(this.context.blockX, blockY, this.context.blockZ);
return (double)random.nextFloat() < probability;
}
}
return new VerticalGradientCondition();
}
}
private static enum Steep implements ConditionSource
{
INSTANCE;
private static final KeyDispatchDataCodec<Steep> CODEC;
@Override
public KeyDispatchDataCodec<? extends ConditionSource> codec() {
return CODEC;
}
@Override
public Condition apply(Context context) {
return context.steep;
}
static {
CODEC = KeyDispatchDataCodec.of(MapCodec.unit((Object)INSTANCE));
}
}
private static enum Hole implements ConditionSource
{
INSTANCE;
private static final KeyDispatchDataCodec<Hole> CODEC;
@Override
public KeyDispatchDataCodec<? extends ConditionSource> codec() {
return CODEC;
}
@Override
public Condition apply(Context context) {
return context.hole;
}
static {
CODEC = KeyDispatchDataCodec.of(MapCodec.unit((Object)INSTANCE));
}
}
private static enum AbovePreliminarySurface implements ConditionSource
{
INSTANCE;
private static final KeyDispatchDataCodec<AbovePreliminarySurface> CODEC;
@Override
public KeyDispatchDataCodec<? extends ConditionSource> codec() {
return CODEC;
}
@Override
public Condition apply(Context context) {
return context.abovePreliminarySurface;
}
static {
CODEC = KeyDispatchDataCodec.of(MapCodec.unit((Object)INSTANCE));
}
}
private static enum Temperature implements ConditionSource
{
INSTANCE;
private static final KeyDispatchDataCodec<Temperature> CODEC;
@Override
public KeyDispatchDataCodec<? extends ConditionSource> codec() {
return CODEC;
}
@Override
public Condition apply(Context context) {
return context.temperature;
}
static {
CODEC = KeyDispatchDataCodec.of(MapCodec.unit((Object)INSTANCE));
}
}
private record TestRuleSource(ConditionSource ifTrue, RuleSource thenRun) implements RuleSource
{
private static final KeyDispatchDataCodec<TestRuleSource> CODEC = KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec(i -> i.group((App)ConditionSource.CODEC.fieldOf("if_true").forGetter(TestRuleSource::ifTrue), (App)RuleSource.CODEC.fieldOf("then_run").forGetter(TestRuleSource::thenRun)).apply((Applicative)i, TestRuleSource::new)));
@Override
public KeyDispatchDataCodec<? extends RuleSource> codec() {
return CODEC;
}
@Override
public SurfaceRule apply(Context context) {
return new TestRule((Condition)this.ifTrue.apply(context), (SurfaceRule)this.thenRun.apply(context));
}
}
public static interface RuleSource
extends Function<Context, SurfaceRule> {
public static final Codec<RuleSource> CODEC = BuiltInRegistries.MATERIAL_RULE.byNameCodec().dispatch(source -> source.codec().codec(), Function.identity());
public static MapCodec<? extends RuleSource> bootstrap(Registry<MapCodec<? extends RuleSource>> registry) {
SurfaceRules.register(registry, "bandlands", Bandlands.CODEC);
SurfaceRules.register(registry, "block", BlockRuleSource.CODEC);
SurfaceRules.register(registry, "sequence", SequenceRuleSource.CODEC);
return SurfaceRules.register(registry, "condition", TestRuleSource.CODEC);
}
public KeyDispatchDataCodec<? extends RuleSource> codec();
}
private record SequenceRuleSource(List<RuleSource> sequence) implements RuleSource
{
private static final KeyDispatchDataCodec<SequenceRuleSource> CODEC = KeyDispatchDataCodec.of(RuleSource.CODEC.listOf().xmap(SequenceRuleSource::new, SequenceRuleSource::sequence).fieldOf("sequence"));
@Override
public KeyDispatchDataCodec<? extends RuleSource> codec() {
return CODEC;
}
@Override
public SurfaceRule apply(Context context) {
if (this.sequence.size() == 1) {
return (SurfaceRule)this.sequence.get(0).apply(context);
}
ImmutableList.Builder builder = ImmutableList.builder();
for (RuleSource rule : this.sequence) {
builder.add((Object)((SurfaceRule)rule.apply(context)));
}
return new SequenceRule((List<SurfaceRule>)builder.build());
}
}
private record BlockRuleSource(BlockState resultState, StateRule rule) implements RuleSource
{
private static final KeyDispatchDataCodec<BlockRuleSource> CODEC = KeyDispatchDataCodec.of(BlockState.CODEC.xmap(BlockRuleSource::new, BlockRuleSource::resultState).fieldOf("result_state"));
private BlockRuleSource(BlockState state) {
this(state, new StateRule(state));
}
@Override
public KeyDispatchDataCodec<? extends RuleSource> codec() {
return CODEC;
}
@Override
public SurfaceRule apply(Context context) {
return this.rule;
}
}
private static enum Bandlands implements RuleSource
{
INSTANCE;
private static final KeyDispatchDataCodec<Bandlands> CODEC;
@Override
public KeyDispatchDataCodec<? extends RuleSource> codec() {
return CODEC;
}
@Override
public SurfaceRule apply(Context context) {
return context.system::getBand;
}
static {
CODEC = KeyDispatchDataCodec.of(MapCodec.unit((Object)INSTANCE));
}
}
private record SequenceRule(List<SurfaceRule> rules) implements SurfaceRule
{
@Override
public @Nullable BlockState tryApply(int blockX, int blockY, int blockZ) {
for (SurfaceRule rule : this.rules) {
BlockState state = rule.tryApply(blockX, blockY, blockZ);
if (state == null) continue;
return state;
}
return null;
}
}
private record TestRule(Condition condition, SurfaceRule followup) implements SurfaceRule
{
@Override
public @Nullable BlockState tryApply(int blockX, int blockY, int blockZ) {
if (!this.condition.test()) {
return null;
}
return this.followup.tryApply(blockX, blockY, blockZ);
}
}
private record StateRule(BlockState state) implements SurfaceRule
{
@Override
public BlockState tryApply(int blockX, int blockY, int blockZ) {
return this.state;
}
}
protected static interface SurfaceRule {
public @Nullable BlockState tryApply(int var1, int var2, int var3);
}
private record NotCondition(Condition target) implements Condition
{
@Override
public boolean test() {
return !this.target.test();
}
}
private static abstract class LazyYCondition
extends LazyCondition {
protected LazyYCondition(Context context) {
super(context);
}
@Override
protected long getContextLastUpdate() {
return this.context.lastUpdateY;
}
}
private static abstract class LazyXZCondition
extends LazyCondition {
protected LazyXZCondition(Context context) {
super(context);
}
@Override
protected long getContextLastUpdate() {
return this.context.lastUpdateXZ;
}
}
private static abstract class LazyCondition
implements Condition {
protected final Context context;
private long lastUpdate;
@Nullable Boolean result;
protected LazyCondition(Context context) {
this.context = context;
this.lastUpdate = this.getContextLastUpdate() - 1L;
}
@Override
public boolean test() {
long lastContextUpdate = this.getContextLastUpdate();
if (lastContextUpdate == this.lastUpdate) {
if (this.result == null) {
throw new IllegalStateException("Update triggered but the result is null");
}
return this.result;
}
this.lastUpdate = lastContextUpdate;
this.result = this.compute();
return this.result;
}
protected abstract long getContextLastUpdate();
protected abstract boolean compute();
}
private static interface Condition {
public boolean test();
}
protected static final class Context {
private static final int HOW_FAR_BELOW_PRELIMINARY_SURFACE_LEVEL_TO_BUILD_SURFACE = 8;
private static final int SURFACE_CELL_BITS = 4;
private static final int SURFACE_CELL_SIZE = 16;
private static final int SURFACE_CELL_MASK = 15;
private final SurfaceSystem system;
private final Condition temperature = new TemperatureHelperCondition(this);
private final Condition steep = new SteepMaterialCondition(this);
private final Condition hole = new HoleCondition(this);
private final Condition abovePreliminarySurface = new AbovePreliminarySurfaceCondition();
private final RandomState randomState;
private final ChunkAccess chunk;
private final NoiseChunk noiseChunk;
private final Function<BlockPos, Holder<Biome>> biomeGetter;
private final WorldGenerationContext context;
private long lastPreliminarySurfaceCellOrigin = Long.MAX_VALUE;
private final int[] preliminarySurfaceCache = new int[4];
private long lastUpdateXZ = -9223372036854775807L;
private int blockX;
private int blockZ;
private int surfaceDepth;
private long lastSurfaceDepth2Update = this.lastUpdateXZ - 1L;
private double surfaceSecondary;
private long lastMinSurfaceLevelUpdate = this.lastUpdateXZ - 1L;
private int minSurfaceLevel;
private long lastUpdateY = -9223372036854775807L;
private final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
private Supplier<Holder<Biome>> biome;
private int blockY;
private int waterHeight;
private int stoneDepthBelow;
private int stoneDepthAbove;
protected Context(SurfaceSystem system, RandomState randomState, ChunkAccess chunk, NoiseChunk noiseChunk, Function<BlockPos, Holder<Biome>> biomeGetter, Registry<Biome> biomes, WorldGenerationContext context) {
this.system = system;
this.randomState = randomState;
this.chunk = chunk;
this.noiseChunk = noiseChunk;
this.biomeGetter = biomeGetter;
this.context = context;
}
protected void updateXZ(int blockX, int blockZ) {
++this.lastUpdateXZ;
++this.lastUpdateY;
this.blockX = blockX;
this.blockZ = blockZ;
this.surfaceDepth = this.system.getSurfaceDepth(blockX, blockZ);
}
protected void updateY(int stoneDepthAbove, int stoneDepthBelow, int waterHeight, int blockX, int blockY, int blockZ) {
++this.lastUpdateY;
this.biome = Suppliers.memoize(() -> this.biomeGetter.apply(this.pos.set(blockX, blockY, blockZ)));
this.blockY = blockY;
this.waterHeight = waterHeight;
this.stoneDepthBelow = stoneDepthBelow;
this.stoneDepthAbove = stoneDepthAbove;
}
protected double getSurfaceSecondary() {
if (this.lastSurfaceDepth2Update != this.lastUpdateXZ) {
this.lastSurfaceDepth2Update = this.lastUpdateXZ;
this.surfaceSecondary = this.system.getSurfaceSecondary(this.blockX, this.blockZ);
}
return this.surfaceSecondary;
}
public int getSeaLevel() {
return this.system.getSeaLevel();
}
private static int blockCoordToSurfaceCell(int blockCoord) {
return blockCoord >> 4;
}
private static int surfaceCellToBlockCoord(int cellCoord) {
return cellCoord << 4;
}
protected int getMinSurfaceLevel() {
if (this.lastMinSurfaceLevelUpdate != this.lastUpdateXZ) {
int cornerCellZ;
this.lastMinSurfaceLevelUpdate = this.lastUpdateXZ;
int cornerCellX = Context.blockCoordToSurfaceCell(this.blockX);
long preliminarySurfaceCellOrigin = ChunkPos.asLong(cornerCellX, cornerCellZ = Context.blockCoordToSurfaceCell(this.blockZ));
if (this.lastPreliminarySurfaceCellOrigin != preliminarySurfaceCellOrigin) {
this.lastPreliminarySurfaceCellOrigin = preliminarySurfaceCellOrigin;
this.preliminarySurfaceCache[0] = this.noiseChunk.preliminarySurfaceLevel(Context.surfaceCellToBlockCoord(cornerCellX), Context.surfaceCellToBlockCoord(cornerCellZ));
this.preliminarySurfaceCache[1] = this.noiseChunk.preliminarySurfaceLevel(Context.surfaceCellToBlockCoord(cornerCellX + 1), Context.surfaceCellToBlockCoord(cornerCellZ));
this.preliminarySurfaceCache[2] = this.noiseChunk.preliminarySurfaceLevel(Context.surfaceCellToBlockCoord(cornerCellX), Context.surfaceCellToBlockCoord(cornerCellZ + 1));
this.preliminarySurfaceCache[3] = this.noiseChunk.preliminarySurfaceLevel(Context.surfaceCellToBlockCoord(cornerCellX + 1), Context.surfaceCellToBlockCoord(cornerCellZ + 1));
}
int preliminarySurfaceLevel = Mth.floor(Mth.lerp2((float)(this.blockX & 0xF) / 16.0f, (float)(this.blockZ & 0xF) / 16.0f, this.preliminarySurfaceCache[0], this.preliminarySurfaceCache[1], this.preliminarySurfaceCache[2], this.preliminarySurfaceCache[3]));
this.minSurfaceLevel = preliminarySurfaceLevel + this.surfaceDepth - 8;
}
return this.minSurfaceLevel;
}
private static class TemperatureHelperCondition
extends LazyYCondition {
private TemperatureHelperCondition(Context context) {
super(context);
}
@Override
protected boolean compute() {
return this.context.biome.get().value().coldEnoughToSnow(this.context.pos.set(this.context.blockX, this.context.blockY, this.context.blockZ), this.context.getSeaLevel());
}
}
private static class SteepMaterialCondition
extends LazyXZCondition {
private SteepMaterialCondition(Context context) {
super(context);
}
@Override
protected boolean compute() {
int heightEast;
int chunkBlockX = this.context.blockX & 0xF;
int chunkBlockZ = this.context.blockZ & 0xF;
int zNorth = Math.max(chunkBlockZ - 1, 0);
int zSouth = Math.min(chunkBlockZ + 1, 15);
ChunkAccess chunk = this.context.chunk;
int heightNorth = chunk.getHeight(Heightmap.Types.WORLD_SURFACE_WG, chunkBlockX, zNorth);
int heightSouth = chunk.getHeight(Heightmap.Types.WORLD_SURFACE_WG, chunkBlockX, zSouth);
if (heightSouth >= heightNorth + 4) {
return true;
}
int xWest = Math.max(chunkBlockX - 1, 0);
int xEast = Math.min(chunkBlockX + 1, 15);
int heightWest = chunk.getHeight(Heightmap.Types.WORLD_SURFACE_WG, xWest, chunkBlockZ);
return heightWest >= (heightEast = chunk.getHeight(Heightmap.Types.WORLD_SURFACE_WG, xEast, chunkBlockZ)) + 4;
}
}
private static final class HoleCondition
extends LazyXZCondition {
private HoleCondition(Context context) {
super(context);
}
@Override
protected boolean compute() {
return this.context.surfaceDepth <= 0;
}
}
private final class AbovePreliminarySurfaceCondition
implements Condition {
private AbovePreliminarySurfaceCondition() {
}
@Override
public boolean test() {
return Context.this.blockY >= Context.this.getMinSurfaceLevel();
}
}
}
}