package net.minecraft.world.level.chunk; import java.util.function.Predicate; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.BiomeResolver; import net.minecraft.world.level.biome.Biomes; import net.minecraft.world.level.biome.Climate.Sampler; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; public class LevelChunkSection { public static final int SECTION_WIDTH = 16; public static final int SECTION_HEIGHT = 16; public static final int SECTION_SIZE = 4096; public static final int BIOME_CONTAINER_BITS = 2; private short nonEmptyBlockCount; private short tickingBlockCount; private short tickingFluidCount; private final PalettedContainer states; private PalettedContainerRO> biomes; private LevelChunkSection(LevelChunkSection section) { this.nonEmptyBlockCount = section.nonEmptyBlockCount; this.tickingBlockCount = section.tickingBlockCount; this.tickingFluidCount = section.tickingFluidCount; this.states = section.states.copy(); this.biomes = section.biomes.copy(); } public LevelChunkSection(PalettedContainer states, PalettedContainerRO> biomes) { this.states = states; this.biomes = biomes; this.recalcBlockCounts(); } public LevelChunkSection(Registry biomeRegistry) { this.states = new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); this.biomes = new PalettedContainer<>(biomeRegistry.asHolderIdMap(), biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); } public BlockState getBlockState(int x, int y, int z) { return this.states.get(x, y, z); } public FluidState getFluidState(int x, int y, int z) { return this.states.get(x, y, z).getFluidState(); } public void acquire() { this.states.acquire(); } public void release() { this.states.release(); } public BlockState setBlockState(int x, int y, int z, BlockState state) { return this.setBlockState(x, y, z, state, true); } public BlockState setBlockState(int x, int y, int z, BlockState state, boolean useLocks) { BlockState blockState; if (useLocks) { blockState = this.states.getAndSet(x, y, z, state); } else { blockState = this.states.getAndSetUnchecked(x, y, z, state); } FluidState fluidState = blockState.getFluidState(); FluidState fluidState2 = state.getFluidState(); if (!blockState.isAir()) { this.nonEmptyBlockCount--; if (blockState.isRandomlyTicking()) { this.tickingBlockCount--; } } if (!fluidState.isEmpty()) { this.tickingFluidCount--; } if (!state.isAir()) { this.nonEmptyBlockCount++; if (state.isRandomlyTicking()) { this.tickingBlockCount++; } } if (!fluidState2.isEmpty()) { this.tickingFluidCount++; } return blockState; } /** * @return {@code true} if this section consists only of air-like blocks. */ public boolean hasOnlyAir() { return this.nonEmptyBlockCount == 0; } public boolean isRandomlyTicking() { return this.isRandomlyTickingBlocks() || this.isRandomlyTickingFluids(); } /** * @return {@code true} if this section has any blocks that require random ticks. */ public boolean isRandomlyTickingBlocks() { return this.tickingBlockCount > 0; } /** * @return {@code true} if this section has any fluids that require random ticks. */ public boolean isRandomlyTickingFluids() { return this.tickingFluidCount > 0; } public void recalcBlockCounts() { class BlockCounter implements PalettedContainer.CountConsumer { public int nonEmptyBlockCount; public int tickingBlockCount; public int tickingFluidCount; public void accept(BlockState blockState, int i) { FluidState fluidState = blockState.getFluidState(); if (!blockState.isAir()) { this.nonEmptyBlockCount += i; if (blockState.isRandomlyTicking()) { this.tickingBlockCount += i; } } if (!fluidState.isEmpty()) { this.nonEmptyBlockCount += i; if (fluidState.isRandomlyTicking()) { this.tickingFluidCount += i; } } } } BlockCounter lv = new BlockCounter(); this.states.count(lv); this.nonEmptyBlockCount = (short)lv.nonEmptyBlockCount; this.tickingBlockCount = (short)lv.tickingBlockCount; this.tickingFluidCount = (short)lv.tickingFluidCount; } public PalettedContainer getStates() { return this.states; } public PalettedContainerRO> getBiomes() { return this.biomes; } public void read(FriendlyByteBuf buffer) { this.nonEmptyBlockCount = buffer.readShort(); this.states.read(buffer); PalettedContainer> palettedContainer = this.biomes.recreate(); palettedContainer.read(buffer); this.biomes = palettedContainer; } public void readBiomes(FriendlyByteBuf buffer) { PalettedContainer> palettedContainer = this.biomes.recreate(); palettedContainer.read(buffer); this.biomes = palettedContainer; } public void write(FriendlyByteBuf buffer) { buffer.writeShort(this.nonEmptyBlockCount); this.states.write(buffer); this.biomes.write(buffer); } public int getSerializedSize() { return 2 + this.states.getSerializedSize() + this.biomes.getSerializedSize(); } /** * @return {@code true} if this section has any states matching the given predicate. As the internal representation uses a {@link net.minecraft.world.level.chunk.Palette}, this is more efficient than looping through every position in the section, or indeed the chunk. */ public boolean maybeHas(Predicate predicate) { return this.states.maybeHas(predicate); } public Holder getNoiseBiome(int x, int y, int z) { return this.biomes.get(x, y, z); } public void fillBiomesFromNoise(BiomeResolver biomeResolver, Sampler climateSampler, int x, int y, int z) { PalettedContainer> palettedContainer = this.biomes.recreate(); int i = 4; for (int j = 0; j < 4; j++) { for (int k = 0; k < 4; k++) { for (int l = 0; l < 4; l++) { palettedContainer.getAndSetUnchecked(j, k, l, biomeResolver.getNoiseBiome(x + j, y + k, z + l, climateSampler)); } } } this.biomes = palettedContainer; } public LevelChunkSection copy() { return new LevelChunkSection(this); } }