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

442 lines
24 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* org.apache.commons.lang3.mutable.MutableDouble
* org.jspecify.annotations.Nullable
*/
package net.minecraft.world.level.levelgen;
import java.util.Arrays;
import net.minecraft.SharedConstants;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.biome.OverworldBiomeBuilder;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.NoiseChunk;
import net.minecraft.world.level.levelgen.NoiseRouter;
import net.minecraft.world.level.levelgen.PositionalRandomFactory;
import org.apache.commons.lang3.mutable.MutableDouble;
import org.jspecify.annotations.Nullable;
public interface Aquifer {
public static Aquifer create(NoiseChunk noiseChunk, ChunkPos pos, NoiseRouter router, PositionalRandomFactory positionalRandomFactory, int minBlockY, int yBlockSize, FluidPicker fluidRule) {
return new NoiseBasedAquifer(noiseChunk, pos, router, positionalRandomFactory, minBlockY, yBlockSize, fluidRule);
}
public static Aquifer createDisabled(final FluidPicker fluidRule) {
return new Aquifer(){
@Override
public @Nullable BlockState computeSubstance(DensityFunction.FunctionContext context, double density) {
if (density > 0.0) {
return null;
}
return fluidRule.computeFluid(context.blockX(), context.blockY(), context.blockZ()).at(context.blockY());
}
@Override
public boolean shouldScheduleFluidUpdate() {
return false;
}
};
}
public @Nullable BlockState computeSubstance(DensityFunction.FunctionContext var1, double var2);
public boolean shouldScheduleFluidUpdate();
public static class NoiseBasedAquifer
implements Aquifer {
private static final int X_RANGE = 10;
private static final int Y_RANGE = 9;
private static final int Z_RANGE = 10;
private static final int X_SEPARATION = 6;
private static final int Y_SEPARATION = 3;
private static final int Z_SEPARATION = 6;
private static final int X_SPACING = 16;
private static final int Y_SPACING = 12;
private static final int Z_SPACING = 16;
private static final int X_SPACING_SHIFT = 4;
private static final int Z_SPACING_SHIFT = 4;
private static final int MAX_REASONABLE_DISTANCE_TO_AQUIFER_CENTER = 11;
private static final double FLOWING_UPDATE_SIMULARITY = NoiseBasedAquifer.similarity(Mth.square(10), Mth.square(12));
private static final int SAMPLE_OFFSET_X = -5;
private static final int SAMPLE_OFFSET_Y = 1;
private static final int SAMPLE_OFFSET_Z = -5;
private static final int MIN_CELL_SAMPLE_X = 0;
private static final int MIN_CELL_SAMPLE_Y = -1;
private static final int MIN_CELL_SAMPLE_Z = 0;
private static final int MAX_CELL_SAMPLE_X = 1;
private static final int MAX_CELL_SAMPLE_Y = 1;
private static final int MAX_CELL_SAMPLE_Z = 1;
private final NoiseChunk noiseChunk;
private final DensityFunction barrierNoise;
private final DensityFunction fluidLevelFloodednessNoise;
private final DensityFunction fluidLevelSpreadNoise;
private final DensityFunction lavaNoise;
private final PositionalRandomFactory positionalRandomFactory;
private final @Nullable FluidStatus[] aquiferCache;
private final long[] aquiferLocationCache;
private final FluidPicker globalFluidPicker;
private final DensityFunction erosion;
private final DensityFunction depth;
private boolean shouldScheduleFluidUpdate;
private final int skipSamplingAboveY;
private final int minGridX;
private final int minGridY;
private final int minGridZ;
private final int gridSizeX;
private final int gridSizeZ;
private static final int[][] SURFACE_SAMPLING_OFFSETS_IN_CHUNKS = new int[][]{{0, 0}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}};
private NoiseBasedAquifer(NoiseChunk noiseChunk, ChunkPos pos, NoiseRouter router, PositionalRandomFactory positionalRandomFactory, int minBlockY, int yBlockSize, FluidPicker globalFluidPicker) {
this.noiseChunk = noiseChunk;
this.barrierNoise = router.barrierNoise();
this.fluidLevelFloodednessNoise = router.fluidLevelFloodednessNoise();
this.fluidLevelSpreadNoise = router.fluidLevelSpreadNoise();
this.lavaNoise = router.lavaNoise();
this.erosion = router.erosion();
this.depth = router.depth();
this.positionalRandomFactory = positionalRandomFactory;
this.minGridX = NoiseBasedAquifer.gridX(pos.getMinBlockX() + -5) + 0;
this.globalFluidPicker = globalFluidPicker;
int maxGridX = NoiseBasedAquifer.gridX(pos.getMaxBlockX() + -5) + 1;
this.gridSizeX = maxGridX - this.minGridX + 1;
this.minGridY = NoiseBasedAquifer.gridY(minBlockY + 1) + -1;
int maxGridY = NoiseBasedAquifer.gridY(minBlockY + yBlockSize + 1) + 1;
int gridSizeY = maxGridY - this.minGridY + 1;
this.minGridZ = NoiseBasedAquifer.gridZ(pos.getMinBlockZ() + -5) + 0;
int maxGridZ = NoiseBasedAquifer.gridZ(pos.getMaxBlockZ() + -5) + 1;
this.gridSizeZ = maxGridZ - this.minGridZ + 1;
int totalGridSize = this.gridSizeX * gridSizeY * this.gridSizeZ;
this.aquiferCache = new FluidStatus[totalGridSize];
this.aquiferLocationCache = new long[totalGridSize];
Arrays.fill(this.aquiferLocationCache, Long.MAX_VALUE);
int maxAdjustedSurfaceLevel = this.adjustSurfaceLevel(noiseChunk.maxPreliminarySurfaceLevel(NoiseBasedAquifer.fromGridX(this.minGridX, 0), NoiseBasedAquifer.fromGridZ(this.minGridZ, 0), NoiseBasedAquifer.fromGridX(maxGridX, 9), NoiseBasedAquifer.fromGridZ(maxGridZ, 9)));
int skipSamplingAboveGridY = NoiseBasedAquifer.gridY(maxAdjustedSurfaceLevel + 12) - -1;
this.skipSamplingAboveY = NoiseBasedAquifer.fromGridY(skipSamplingAboveGridY, 11) - 1;
}
private int getIndex(int gridX, int gridY, int gridZ) {
int x = gridX - this.minGridX;
int y = gridY - this.minGridY;
int z = gridZ - this.minGridZ;
return (y * this.gridSizeZ + z) * this.gridSizeX + x;
}
@Override
public @Nullable BlockState computeSubstance(DensityFunction.FunctionContext context, double density) {
boolean mayFlow13;
double barrier23;
double barrier13;
BlockState actualFluidState;
if (density > 0.0) {
this.shouldScheduleFluidUpdate = false;
return null;
}
int posX = context.blockX();
int posY = context.blockY();
int posZ = context.blockZ();
FluidStatus globalFluid = this.globalFluidPicker.computeFluid(posX, posY, posZ);
if (posY > this.skipSamplingAboveY) {
this.shouldScheduleFluidUpdate = false;
return globalFluid.at(posY);
}
if (globalFluid.at(posY).is(Blocks.LAVA)) {
this.shouldScheduleFluidUpdate = false;
return SharedConstants.DEBUG_DISABLE_FLUID_GENERATION ? Blocks.AIR.defaultBlockState() : Blocks.LAVA.defaultBlockState();
}
int xAnchor = NoiseBasedAquifer.gridX(posX + -5);
int yAnchor = NoiseBasedAquifer.gridY(posY + 1);
int zAnchor = NoiseBasedAquifer.gridZ(posZ + -5);
int distanceSqr1 = Integer.MAX_VALUE;
int distanceSqr2 = Integer.MAX_VALUE;
int distanceSqr3 = Integer.MAX_VALUE;
int distanceSqr4 = Integer.MAX_VALUE;
int closestIndex1 = 0;
int closestIndex2 = 0;
int closestIndex3 = 0;
int closestIndex4 = 0;
for (int x1 = 0; x1 <= 1; ++x1) {
for (int y1 = -1; y1 <= 1; ++y1) {
for (int z1 = 0; z1 <= 1; ++z1) {
long location;
int spacedGridX = xAnchor + x1;
int spacedGridY = yAnchor + y1;
int spacedGridZ = zAnchor + z1;
int index = this.getIndex(spacedGridX, spacedGridY, spacedGridZ);
long existingLocation = this.aquiferLocationCache[index];
if (existingLocation != Long.MAX_VALUE) {
location = existingLocation;
} else {
RandomSource random = this.positionalRandomFactory.at(spacedGridX, spacedGridY, spacedGridZ);
this.aquiferLocationCache[index] = location = BlockPos.asLong(NoiseBasedAquifer.fromGridX(spacedGridX, random.nextInt(10)), NoiseBasedAquifer.fromGridY(spacedGridY, random.nextInt(9)), NoiseBasedAquifer.fromGridZ(spacedGridZ, random.nextInt(10)));
}
int dx = BlockPos.getX(location) - posX;
int dy = BlockPos.getY(location) - posY;
int dz = BlockPos.getZ(location) - posZ;
int newDistance = dx * dx + dy * dy + dz * dz;
if (distanceSqr1 >= newDistance) {
closestIndex4 = closestIndex3;
closestIndex3 = closestIndex2;
closestIndex2 = closestIndex1;
closestIndex1 = index;
distanceSqr4 = distanceSqr3;
distanceSqr3 = distanceSqr2;
distanceSqr2 = distanceSqr1;
distanceSqr1 = newDistance;
continue;
}
if (distanceSqr2 >= newDistance) {
closestIndex4 = closestIndex3;
closestIndex3 = closestIndex2;
closestIndex2 = index;
distanceSqr4 = distanceSqr3;
distanceSqr3 = distanceSqr2;
distanceSqr2 = newDistance;
continue;
}
if (distanceSqr3 >= newDistance) {
closestIndex4 = closestIndex3;
closestIndex3 = index;
distanceSqr4 = distanceSqr3;
distanceSqr3 = newDistance;
continue;
}
if (distanceSqr4 < newDistance) continue;
closestIndex4 = index;
distanceSqr4 = newDistance;
}
}
}
FluidStatus closestStatus1 = this.getAquiferStatus(closestIndex1);
double similarity12 = NoiseBasedAquifer.similarity(distanceSqr1, distanceSqr2);
BlockState fluidState = closestStatus1.at(posY);
BlockState blockState = actualFluidState = SharedConstants.DEBUG_DISABLE_FLUID_GENERATION ? Blocks.AIR.defaultBlockState() : fluidState;
if (similarity12 <= 0.0) {
FluidStatus closestStatus2;
this.shouldScheduleFluidUpdate = similarity12 >= FLOWING_UPDATE_SIMULARITY ? !closestStatus1.equals(closestStatus2 = this.getAquiferStatus(closestIndex2)) : false;
return actualFluidState;
}
if (fluidState.is(Blocks.WATER) && this.globalFluidPicker.computeFluid(posX, posY - 1, posZ).at(posY - 1).is(Blocks.LAVA)) {
this.shouldScheduleFluidUpdate = true;
return actualFluidState;
}
MutableDouble barrierNoiseValue = new MutableDouble(Double.NaN);
FluidStatus closestStatus2 = this.getAquiferStatus(closestIndex2);
double barrier12 = similarity12 * this.calculatePressure(context, barrierNoiseValue, closestStatus1, closestStatus2);
if (density + barrier12 > 0.0) {
this.shouldScheduleFluidUpdate = false;
return null;
}
FluidStatus closestStatus3 = this.getAquiferStatus(closestIndex3);
double similarity13 = NoiseBasedAquifer.similarity(distanceSqr1, distanceSqr3);
if (similarity13 > 0.0 && density + (barrier13 = similarity12 * similarity13 * this.calculatePressure(context, barrierNoiseValue, closestStatus1, closestStatus3)) > 0.0) {
this.shouldScheduleFluidUpdate = false;
return null;
}
double similarity23 = NoiseBasedAquifer.similarity(distanceSqr2, distanceSqr3);
if (similarity23 > 0.0 && density + (barrier23 = similarity12 * similarity23 * this.calculatePressure(context, barrierNoiseValue, closestStatus2, closestStatus3)) > 0.0) {
this.shouldScheduleFluidUpdate = false;
return null;
}
boolean mayFlow12 = !closestStatus1.equals(closestStatus2);
boolean mayFlow23 = similarity23 >= FLOWING_UPDATE_SIMULARITY && !closestStatus2.equals(closestStatus3);
boolean bl = mayFlow13 = similarity13 >= FLOWING_UPDATE_SIMULARITY && !closestStatus1.equals(closestStatus3);
this.shouldScheduleFluidUpdate = mayFlow12 || mayFlow23 || mayFlow13 ? true : similarity13 >= FLOWING_UPDATE_SIMULARITY && NoiseBasedAquifer.similarity(distanceSqr1, distanceSqr4) >= FLOWING_UPDATE_SIMULARITY && !closestStatus1.equals(this.getAquiferStatus(closestIndex4));
return actualFluidState;
}
@Override
public boolean shouldScheduleFluidUpdate() {
return this.shouldScheduleFluidUpdate;
}
private static double similarity(int distanceSqr1, int distanceSqr2) {
double threshold = 25.0;
return 1.0 - (double)(distanceSqr2 - distanceSqr1) / 25.0;
}
private double calculatePressure(DensityFunction.FunctionContext context, MutableDouble barrierNoiseValue, FluidStatus statusClosest1, FluidStatus statusClosest2) {
double noiseValue;
double centerPoint;
int posY = context.blockY();
BlockState type1 = statusClosest1.at(posY);
BlockState type2 = statusClosest2.at(posY);
if (type1.is(Blocks.LAVA) && type2.is(Blocks.WATER) || type1.is(Blocks.WATER) && type2.is(Blocks.LAVA)) {
return 2.0;
}
int fluidYDiff = Math.abs(statusClosest1.fluidLevel - statusClosest2.fluidLevel);
if (fluidYDiff == 0) {
return 0.0;
}
double averageFluidY = 0.5 * (double)(statusClosest1.fluidLevel + statusClosest2.fluidLevel);
double howFarAboveAverageFluidPoint = (double)posY + 0.5 - averageFluidY;
double baseValue = (double)fluidYDiff / 2.0;
double topBias = 0.0;
double furthestRocksFromTopBias = 2.5;
double furthestHolesFromTopBias = 1.5;
double bottomBias = 3.0;
double furthestRocksFromBottomBias = 10.0;
double furthestHolesFromBottomBias = 3.0;
double distanceFromBarrierEdgeTowardsMiddle = baseValue - Math.abs(howFarAboveAverageFluidPoint);
double gradient = howFarAboveAverageFluidPoint > 0.0 ? ((centerPoint = 0.0 + distanceFromBarrierEdgeTowardsMiddle) > 0.0 ? centerPoint / 1.5 : centerPoint / 2.5) : ((centerPoint = 3.0 + distanceFromBarrierEdgeTowardsMiddle) > 0.0 ? centerPoint / 3.0 : centerPoint / 10.0);
double amplitude = 2.0;
if (gradient < -2.0 || gradient > 2.0) {
noiseValue = 0.0;
} else {
double currentNoiseValue = barrierNoiseValue.doubleValue();
if (Double.isNaN(currentNoiseValue)) {
double barrierNoise = this.barrierNoise.compute(context);
barrierNoiseValue.setValue(barrierNoise);
noiseValue = barrierNoise;
} else {
noiseValue = currentNoiseValue;
}
}
return 2.0 * (noiseValue + gradient);
}
private static int gridX(int blockCoord) {
return blockCoord >> 4;
}
private static int fromGridX(int gridCoord, int blockOffset) {
return (gridCoord << 4) + blockOffset;
}
private static int gridY(int blockCoord) {
return Math.floorDiv(blockCoord, 12);
}
private static int fromGridY(int gridCoord, int blockOffset) {
return gridCoord * 12 + blockOffset;
}
private static int gridZ(int blockCoord) {
return blockCoord >> 4;
}
private static int fromGridZ(int gridCoord, int blockOffset) {
return (gridCoord << 4) + blockOffset;
}
private FluidStatus getAquiferStatus(int index) {
FluidStatus status;
FluidStatus oldStatus = this.aquiferCache[index];
if (oldStatus != null) {
return oldStatus;
}
long location = this.aquiferLocationCache[index];
this.aquiferCache[index] = status = this.computeFluid(BlockPos.getX(location), BlockPos.getY(location), BlockPos.getZ(location));
return status;
}
private FluidStatus computeFluid(int x, int y, int z) {
FluidStatus globalFluid = this.globalFluidPicker.computeFluid(x, y, z);
int lowestPreliminarySurface = Integer.MAX_VALUE;
int topOfAquiferCell = y + 12;
int bottomOfAquiferCell = y - 12;
boolean surfaceAtCenterIsUnderGlobalFluidLevel = false;
for (int[] offset : SURFACE_SAMPLING_OFFSETS_IN_CHUNKS) {
FluidStatus globalFluidAtSurface;
boolean topOfAquiferCellPokesAboveSurface;
boolean start;
int sampleX = x + SectionPos.sectionToBlockCoord(offset[0]);
int sampleZ = z + SectionPos.sectionToBlockCoord(offset[1]);
int preliminarySurfaceLevel = this.noiseChunk.preliminarySurfaceLevel(sampleX, sampleZ);
int adjustedSurfaceLevel = this.adjustSurfaceLevel(preliminarySurfaceLevel);
boolean bl = start = offset[0] == 0 && offset[1] == 0;
if (start && bottomOfAquiferCell > adjustedSurfaceLevel) {
return globalFluid;
}
boolean bl2 = topOfAquiferCellPokesAboveSurface = topOfAquiferCell > adjustedSurfaceLevel;
if ((topOfAquiferCellPokesAboveSurface || start) && !(globalFluidAtSurface = this.globalFluidPicker.computeFluid(sampleX, adjustedSurfaceLevel, sampleZ)).at(adjustedSurfaceLevel).isAir()) {
if (start) {
surfaceAtCenterIsUnderGlobalFluidLevel = true;
}
if (topOfAquiferCellPokesAboveSurface) {
return globalFluidAtSurface;
}
}
lowestPreliminarySurface = Math.min(lowestPreliminarySurface, preliminarySurfaceLevel);
}
int fluidSurfaceLevel = this.computeSurfaceLevel(x, y, z, globalFluid, lowestPreliminarySurface, surfaceAtCenterIsUnderGlobalFluidLevel);
return new FluidStatus(fluidSurfaceLevel, this.computeFluidType(x, y, z, globalFluid, fluidSurfaceLevel));
}
private int adjustSurfaceLevel(int preliminarySurfaceLevel) {
return preliminarySurfaceLevel + 8;
}
private int computeSurfaceLevel(int x, int y, int z, FluidStatus globalFluid, int lowestPreliminarySurface, boolean surfaceAtCenterIsUnderGlobalFluidLevel) {
double fullyFloodidness;
double partiallyFloodedness;
DensityFunction.SinglePointContext context = new DensityFunction.SinglePointContext(x, y, z);
if (OverworldBiomeBuilder.isDeepDarkRegion(this.erosion, this.depth, context)) {
partiallyFloodedness = -1.0;
fullyFloodidness = -1.0;
} else {
int distanceBelowSurface = lowestPreliminarySurface + 8 - y;
int floodednessMaxDepth = 64;
double floodednessFactor = surfaceAtCenterIsUnderGlobalFluidLevel ? Mth.clampedMap((double)distanceBelowSurface, 0.0, 64.0, 1.0, 0.0) : 0.0;
double floodednessNoiseValue = Mth.clamp(this.fluidLevelFloodednessNoise.compute(context), -1.0, 1.0);
double fullyFloodedThreshold = Mth.map(floodednessFactor, 1.0, 0.0, -0.3, 0.8);
double partiallyFloodedThreshold = Mth.map(floodednessFactor, 1.0, 0.0, -0.8, 0.4);
partiallyFloodedness = floodednessNoiseValue - partiallyFloodedThreshold;
fullyFloodidness = floodednessNoiseValue - fullyFloodedThreshold;
}
int fluidSurfaceLevel = fullyFloodidness > 0.0 ? globalFluid.fluidLevel : (partiallyFloodedness > 0.0 ? this.computeRandomizedFluidSurfaceLevel(x, y, z, lowestPreliminarySurface) : DimensionType.WAY_BELOW_MIN_Y);
return fluidSurfaceLevel;
}
private int computeRandomizedFluidSurfaceLevel(int x, int y, int z, int lowestPreliminarySurface) {
int fluidCellWidth = 16;
int fluidCellHeight = 40;
int fluidLevelCellX = Math.floorDiv(x, 16);
int fluidLevelCellY = Math.floorDiv(y, 40);
int fluidLevelCellZ = Math.floorDiv(z, 16);
int fluidCellMiddleY = fluidLevelCellY * 40 + 20;
int maxSpread = 10;
double fluidLevelSpread = this.fluidLevelSpreadNoise.compute(new DensityFunction.SinglePointContext(fluidLevelCellX, fluidLevelCellY, fluidLevelCellZ)) * 10.0;
int fluidLevelSpreadQuantized = Mth.quantize(fluidLevelSpread, 3);
int targetFluidSurfaceLevel = fluidCellMiddleY + fluidLevelSpreadQuantized;
return Math.min(lowestPreliminarySurface, targetFluidSurfaceLevel);
}
private BlockState computeFluidType(int x, int y, int z, FluidStatus globalFluid, int fluidSurfaceLevel) {
BlockState fluidType = globalFluid.fluidType;
if (fluidSurfaceLevel <= -10 && fluidSurfaceLevel != DimensionType.WAY_BELOW_MIN_Y && globalFluid.fluidType != Blocks.LAVA.defaultBlockState()) {
int fluidTypeCellZ;
int fluidTypeCellY;
int fluidTypeCellWidth = 64;
int fluidTypeCellHeight = 40;
int fluidTypeCellX = Math.floorDiv(x, 64);
double lavaNoiseValue = this.lavaNoise.compute(new DensityFunction.SinglePointContext(fluidTypeCellX, fluidTypeCellY = Math.floorDiv(y, 40), fluidTypeCellZ = Math.floorDiv(z, 64)));
if (Math.abs(lavaNoiseValue) > 0.3) {
fluidType = Blocks.LAVA.defaultBlockState();
}
}
return fluidType;
}
}
public static interface FluidPicker {
public FluidStatus computeFluid(int var1, int var2, int var3);
}
public record FluidStatus(int fluidLevel, BlockState fluidType) {
public BlockState at(int blockY) {
return blockY < this.fluidLevel ? this.fluidType : Blocks.AIR.defaultBlockState();
}
}
}