/* * Decompiled with CFR 0.152. */ package net.minecraft.world.level.levelgen; import java.util.Arrays; import java.util.Optional; import java.util.function.Function; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; import net.minecraft.resources.Identifier; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.BlockColumn; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.NoiseChunk; import net.minecraft.world.level.levelgen.Noises; import net.minecraft.world.level.levelgen.PositionalRandomFactory; import net.minecraft.world.level.levelgen.RandomState; import net.minecraft.world.level.levelgen.SurfaceRules; import net.minecraft.world.level.levelgen.WorldGenerationContext; import net.minecraft.world.level.levelgen.carver.CarvingContext; import net.minecraft.world.level.levelgen.synth.NormalNoise; public class SurfaceSystem { private static final BlockState WHITE_TERRACOTTA = Blocks.WHITE_TERRACOTTA.defaultBlockState(); private static final BlockState ORANGE_TERRACOTTA = Blocks.ORANGE_TERRACOTTA.defaultBlockState(); private static final BlockState TERRACOTTA = Blocks.TERRACOTTA.defaultBlockState(); private static final BlockState YELLOW_TERRACOTTA = Blocks.YELLOW_TERRACOTTA.defaultBlockState(); private static final BlockState BROWN_TERRACOTTA = Blocks.BROWN_TERRACOTTA.defaultBlockState(); private static final BlockState RED_TERRACOTTA = Blocks.RED_TERRACOTTA.defaultBlockState(); private static final BlockState LIGHT_GRAY_TERRACOTTA = Blocks.LIGHT_GRAY_TERRACOTTA.defaultBlockState(); private static final BlockState PACKED_ICE = Blocks.PACKED_ICE.defaultBlockState(); private static final BlockState SNOW_BLOCK = Blocks.SNOW_BLOCK.defaultBlockState(); private final BlockState defaultBlock; private final int seaLevel; private final BlockState[] clayBands; private final NormalNoise clayBandsOffsetNoise; private final NormalNoise badlandsPillarNoise; private final NormalNoise badlandsPillarRoofNoise; private final NormalNoise badlandsSurfaceNoise; private final NormalNoise icebergPillarNoise; private final NormalNoise icebergPillarRoofNoise; private final NormalNoise icebergSurfaceNoise; private final PositionalRandomFactory noiseRandom; private final NormalNoise surfaceNoise; private final NormalNoise surfaceSecondaryNoise; public SurfaceSystem(RandomState randomState, BlockState defaultBlock, int seaLevel, PositionalRandomFactory noiseRandom) { this.defaultBlock = defaultBlock; this.seaLevel = seaLevel; this.noiseRandom = noiseRandom; this.clayBandsOffsetNoise = randomState.getOrCreateNoise(Noises.CLAY_BANDS_OFFSET); this.clayBands = SurfaceSystem.generateBands(noiseRandom.fromHashOf(Identifier.withDefaultNamespace("clay_bands"))); this.surfaceNoise = randomState.getOrCreateNoise(Noises.SURFACE); this.surfaceSecondaryNoise = randomState.getOrCreateNoise(Noises.SURFACE_SECONDARY); this.badlandsPillarNoise = randomState.getOrCreateNoise(Noises.BADLANDS_PILLAR); this.badlandsPillarRoofNoise = randomState.getOrCreateNoise(Noises.BADLANDS_PILLAR_ROOF); this.badlandsSurfaceNoise = randomState.getOrCreateNoise(Noises.BADLANDS_SURFACE); this.icebergPillarNoise = randomState.getOrCreateNoise(Noises.ICEBERG_PILLAR); this.icebergPillarRoofNoise = randomState.getOrCreateNoise(Noises.ICEBERG_PILLAR_ROOF); this.icebergSurfaceNoise = randomState.getOrCreateNoise(Noises.ICEBERG_SURFACE); } public void buildSurface(RandomState randomState, BiomeManager biomeManager, Registry biomes, boolean useLegacyRandom, WorldGenerationContext generationContext, final ChunkAccess protoChunk, NoiseChunk noiseChunk, SurfaceRules.RuleSource ruleSource) { final BlockPos.MutableBlockPos columnPos = new BlockPos.MutableBlockPos(); final ChunkPos chunkPos = protoChunk.getPos(); int minBlockX = chunkPos.getMinBlockX(); int minBlockZ = chunkPos.getMinBlockZ(); BlockColumn column = new BlockColumn(){ @Override public BlockState getBlock(int blockY) { return protoChunk.getBlockState(columnPos.setY(blockY)); } @Override public void setBlock(int blockY, BlockState state) { LevelHeightAccessor heightAccessor = protoChunk.getHeightAccessorForGeneration(); if (heightAccessor.isInsideBuildHeight(blockY)) { protoChunk.setBlockState(columnPos.setY(blockY), state); if (!state.getFluidState().isEmpty()) { protoChunk.markPosForPostprocessing(columnPos); } } } public String toString() { return "ChunkBlockColumn " + String.valueOf(chunkPos); } }; SurfaceRules.Context context = new SurfaceRules.Context(this, randomState, protoChunk, noiseChunk, biomeManager::getBiome, biomes, generationContext); SurfaceRules.SurfaceRule rule = (SurfaceRules.SurfaceRule)ruleSource.apply(context); BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos(); for (int x = 0; x < 16; ++x) { for (int z = 0; z < 16; ++z) { int blockX = minBlockX + x; int blockZ = minBlockZ + z; int startingHeight = protoChunk.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) + 1; columnPos.setX(blockX).setZ(blockZ); Holder surfaceBiome = biomeManager.getBiome(blockPos.set(blockX, useLegacyRandom ? 0 : startingHeight, blockZ)); if (surfaceBiome.is(Biomes.ERODED_BADLANDS)) { this.erodedBadlandsExtension(column, blockX, blockZ, startingHeight, protoChunk); } int height = protoChunk.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) + 1; context.updateXZ(blockX, blockZ); int stoneAboveDepth = 0; int waterHeight = Integer.MIN_VALUE; int nextCeilingStoneY = Integer.MAX_VALUE; int endY = protoChunk.getMinY(); for (int y = height; y >= endY; --y) { BlockState state; BlockState old = column.getBlock(y); if (old.isAir()) { stoneAboveDepth = 0; waterHeight = Integer.MIN_VALUE; continue; } if (!old.getFluidState().isEmpty()) { if (waterHeight != Integer.MIN_VALUE) continue; waterHeight = y + 1; continue; } if (nextCeilingStoneY >= y) { nextCeilingStoneY = DimensionType.WAY_BELOW_MIN_Y; for (int lookaheadY = y - 1; lookaheadY >= endY - 1; --lookaheadY) { BlockState nextState = column.getBlock(lookaheadY); if (this.isStone(nextState)) continue; nextCeilingStoneY = lookaheadY + 1; break; } } int stoneBelowDepth = y - nextCeilingStoneY + 1; context.updateY(++stoneAboveDepth, stoneBelowDepth, waterHeight, blockX, y, blockZ); if (old != this.defaultBlock || (state = rule.tryApply(blockX, y, blockZ)) == null) continue; column.setBlock(y, state); } if (!surfaceBiome.is(Biomes.FROZEN_OCEAN) && !surfaceBiome.is(Biomes.DEEP_FROZEN_OCEAN)) continue; this.frozenOceanExtension(context.getMinSurfaceLevel(), surfaceBiome.value(), column, blockPos, blockX, blockZ, startingHeight); } } } protected int getSurfaceDepth(int blockX, int blockZ) { double noiseValue = this.surfaceNoise.getValue(blockX, 0.0, blockZ); return (int)(noiseValue * 2.75 + 3.0 + this.noiseRandom.at(blockX, 0, blockZ).nextDouble() * 0.25); } protected double getSurfaceSecondary(int blockX, int blockZ) { return this.surfaceSecondaryNoise.getValue(blockX, 0.0, blockZ); } private boolean isStone(BlockState state) { return !state.isAir() && state.getFluidState().isEmpty(); } public int getSeaLevel() { return this.seaLevel; } @Deprecated public Optional topMaterial(SurfaceRules.RuleSource ruleSource, CarvingContext carvingContext, Function> biomeGetter, ChunkAccess chunk, NoiseChunk noiseChunk, BlockPos pos, boolean underFluid) { SurfaceRules.Context context = new SurfaceRules.Context(this, carvingContext.randomState(), chunk, noiseChunk, biomeGetter, (Registry)carvingContext.registryAccess().lookupOrThrow(Registries.BIOME), carvingContext); SurfaceRules.SurfaceRule rule = (SurfaceRules.SurfaceRule)ruleSource.apply(context); int blockX = pos.getX(); int blockY = pos.getY(); int blockZ = pos.getZ(); context.updateXZ(blockX, blockZ); context.updateY(1, 1, underFluid ? blockY + 1 : Integer.MIN_VALUE, blockX, blockY, blockZ); BlockState state = rule.tryApply(blockX, blockY, blockZ); return Optional.ofNullable(state); } private void erodedBadlandsExtension(BlockColumn column, int blockX, int blockZ, int height, LevelHeightAccessor protoChunk) { BlockState oldState; int y; double pillarNoiseScale = 0.2; double pillarBuffer = Math.min(Math.abs(this.badlandsSurfaceNoise.getValue(blockX, 0.0, blockZ) * 8.25), this.badlandsPillarNoise.getValue((double)blockX * 0.2, 0.0, (double)blockZ * 0.2) * 15.0); if (pillarBuffer <= 0.0) { return; } double floorNoiseSampleResolution = 0.75; double floorAmplitude = 1.5; double pillarFloor = Math.abs(this.badlandsPillarRoofNoise.getValue((double)blockX * 0.75, 0.0, (double)blockZ * 0.75) * 1.5); double extensionTop = 64.0 + Math.min(pillarBuffer * pillarBuffer * 2.5, Math.ceil(pillarFloor * 50.0) + 24.0); int startY = Mth.floor(extensionTop); if (height > startY) { return; } for (y = startY; y >= protoChunk.getMinY() && !(oldState = column.getBlock(y)).is(this.defaultBlock.getBlock()); --y) { if (!oldState.is(Blocks.WATER)) continue; return; } for (y = startY; y >= protoChunk.getMinY() && column.getBlock(y).isAir(); --y) { column.setBlock(y, this.defaultBlock); } } private void frozenOceanExtension(int minSurfaceLevel, Biome surfaceBiome, BlockColumn column, BlockPos.MutableBlockPos blockPos, int blockX, int blockZ, int height) { double extensionBottom; double pillarScale = 1.28; double iceberg = Math.min(Math.abs(this.icebergSurfaceNoise.getValue(blockX, 0.0, blockZ) * 8.25), this.icebergPillarNoise.getValue((double)blockX * 1.28, 0.0, (double)blockZ * 1.28) * 15.0); if (iceberg <= 1.8) { return; } double roofScale = 1.17; double roofAmplitude = 1.5; double icebergRoof = Math.abs(this.icebergPillarRoofNoise.getValue((double)blockX * 1.17, 0.0, (double)blockZ * 1.17) * 1.5); double top = Math.min(iceberg * iceberg * 1.2, Math.ceil(icebergRoof * 40.0) + 14.0); if (surfaceBiome.shouldMeltFrozenOceanIcebergSlightly(blockPos.set(blockX, this.seaLevel, blockZ), this.seaLevel)) { top -= 2.0; } if (top > 2.0) { extensionBottom = (double)this.seaLevel - top - 7.0; top += (double)this.seaLevel; } else { top = 0.0; extensionBottom = 0.0; } double extensionTop = top; RandomSource random = this.noiseRandom.at(blockX, 0, blockZ); int maxSnowDepth = 2 + random.nextInt(4); int minSnowHeight = this.seaLevel + 18 + random.nextInt(10); int snowDepth = 0; for (int y = Math.max(height, (int)extensionTop + 1); y >= minSurfaceLevel; --y) { if (!(column.getBlock(y).isAir() && y < (int)extensionTop && random.nextDouble() > 0.01) && (!column.getBlock(y).is(Blocks.WATER) || y <= (int)extensionBottom || y >= this.seaLevel || extensionBottom == 0.0 || !(random.nextDouble() > 0.15))) continue; if (snowDepth <= maxSnowDepth && y > minSnowHeight) { column.setBlock(y, SNOW_BLOCK); ++snowDepth; continue; } column.setBlock(y, PACKED_ICE); } } private static BlockState[] generateBands(RandomSource random) { Object[] clayBands = new BlockState[192]; Arrays.fill(clayBands, TERRACOTTA); for (int i = 0; i < clayBands.length; ++i) { if ((i += random.nextInt(5) + 1) >= clayBands.length) continue; clayBands[i] = ORANGE_TERRACOTTA; } SurfaceSystem.makeBands(random, (BlockState[])clayBands, 1, YELLOW_TERRACOTTA); SurfaceSystem.makeBands(random, (BlockState[])clayBands, 2, BROWN_TERRACOTTA); SurfaceSystem.makeBands(random, (BlockState[])clayBands, 1, RED_TERRACOTTA); int whiteBandCount = random.nextIntBetweenInclusive(9, 15); int i = 0; for (int start = 0; i < whiteBandCount && start < clayBands.length; ++i, start += random.nextInt(16) + 4) { clayBands[start] = WHITE_TERRACOTTA; if (start - 1 > 0 && random.nextBoolean()) { clayBands[start - 1] = LIGHT_GRAY_TERRACOTTA; } if (start + 1 >= clayBands.length || !random.nextBoolean()) continue; clayBands[start + 1] = LIGHT_GRAY_TERRACOTTA; } return clayBands; } private static void makeBands(RandomSource random, BlockState[] clayBands, int baseWidth, BlockState state) { int bandCount = random.nextIntBetweenInclusive(6, 15); for (int i = 0; i < bandCount; ++i) { int width = baseWidth + random.nextInt(3); int start = random.nextInt(clayBands.length); for (int p = 0; start + p < clayBands.length && p < width; ++p) { clayBands[start + p] = state; } } } protected BlockState getBand(int worldX, int y, int worldZ) { int offset = (int)Math.round(this.clayBandsOffsetNoise.getValue(worldX, 0.0, worldZ) * 4.0); return this.clayBands[(y + offset + this.clayBands.length) % this.clayBands.length]; } }