package net.minecraft.world.level.chunk; import com.google.common.collect.Lists; import it.unimi.dsi.fastutil.shorts.ShortList; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Map; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.levelgen.BelowZeroRetrogen; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.blending.BlendingData; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.lighting.LightEngine; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.Fluids; import net.minecraft.world.ticks.LevelChunkTicks; import net.minecraft.world.ticks.ProtoChunkTicks; import net.minecraft.world.ticks.TickContainerAccess; import org.jetbrains.annotations.Nullable; public class ProtoChunk extends ChunkAccess { @Nullable private volatile LevelLightEngine lightEngine; private volatile ChunkStatus status = ChunkStatus.EMPTY; private final List entities = Lists.newArrayList(); @Nullable private CarvingMask carvingMask; @Nullable private BelowZeroRetrogen belowZeroRetrogen; private final ProtoChunkTicks blockTicks; private final ProtoChunkTicks fluidTicks; public ProtoChunk( ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor, Registry biomeRegistry, @Nullable BlendingData blendingData ) { this(chunkPos, upgradeData, null, new ProtoChunkTicks<>(), new ProtoChunkTicks<>(), levelHeightAccessor, biomeRegistry, blendingData); } public ProtoChunk( ChunkPos chunkPos, UpgradeData upgradeData, @Nullable LevelChunkSection[] sections, ProtoChunkTicks blockTicks, ProtoChunkTicks liquidTicks, LevelHeightAccessor levelHeightAccessor, Registry biomeRegistry, @Nullable BlendingData blendingData ) { super(chunkPos, upgradeData, levelHeightAccessor, biomeRegistry, 0L, sections, blendingData); this.blockTicks = blockTicks; this.fluidTicks = liquidTicks; } @Override public TickContainerAccess getBlockTicks() { return this.blockTicks; } @Override public TickContainerAccess getFluidTicks() { return this.fluidTicks; } @Override public ChunkAccess.PackedTicks getTicksForSerialization(long gametime) { return new ChunkAccess.PackedTicks(this.blockTicks.pack(gametime), this.fluidTicks.pack(gametime)); } @Override public BlockState getBlockState(BlockPos pos) { int i = pos.getY(); if (this.isOutsideBuildHeight(i)) { return Blocks.VOID_AIR.defaultBlockState(); } else { LevelChunkSection levelChunkSection = this.getSection(this.getSectionIndex(i)); return levelChunkSection.hasOnlyAir() ? Blocks.AIR.defaultBlockState() : levelChunkSection.getBlockState(pos.getX() & 15, i & 15, pos.getZ() & 15); } } @Override public FluidState getFluidState(BlockPos pos) { int i = pos.getY(); if (this.isOutsideBuildHeight(i)) { return Fluids.EMPTY.defaultFluidState(); } else { LevelChunkSection levelChunkSection = this.getSection(this.getSectionIndex(i)); return levelChunkSection.hasOnlyAir() ? Fluids.EMPTY.defaultFluidState() : levelChunkSection.getFluidState(pos.getX() & 15, i & 15, pos.getZ() & 15); } } @Nullable @Override public BlockState setBlockState(BlockPos pos, BlockState state, int flags) { int i = pos.getX(); int j = pos.getY(); int k = pos.getZ(); if (this.isOutsideBuildHeight(j)) { return Blocks.VOID_AIR.defaultBlockState(); } else { int l = this.getSectionIndex(j); LevelChunkSection levelChunkSection = this.getSection(l); boolean bl = levelChunkSection.hasOnlyAir(); if (bl && state.is(Blocks.AIR)) { return state; } else { int m = SectionPos.sectionRelative(i); int n = SectionPos.sectionRelative(j); int o = SectionPos.sectionRelative(k); BlockState blockState = levelChunkSection.setBlockState(m, n, o, state); if (this.status.isOrAfter(ChunkStatus.INITIALIZE_LIGHT)) { boolean bl2 = levelChunkSection.hasOnlyAir(); if (bl2 != bl) { this.lightEngine.updateSectionStatus(pos, bl2); } if (LightEngine.hasDifferentLightProperties(blockState, state)) { this.skyLightSources.update(this, m, j, o); this.lightEngine.checkBlock(pos); } } EnumSet enumSet = this.getPersistedStatus().heightmapsAfter(); EnumSet enumSet2 = null; for (Heightmap.Types types : enumSet) { Heightmap heightmap = (Heightmap)this.heightmaps.get(types); if (heightmap == null) { if (enumSet2 == null) { enumSet2 = EnumSet.noneOf(Heightmap.Types.class); } enumSet2.add(types); } } if (enumSet2 != null) { Heightmap.primeHeightmaps(this, enumSet2); } for (Heightmap.Types typesx : enumSet) { ((Heightmap)this.heightmaps.get(typesx)).update(m, j, o, state); } return blockState; } } } @Override public void setBlockEntity(BlockEntity blockEntity) { this.pendingBlockEntities.remove(blockEntity.getBlockPos()); this.blockEntities.put(blockEntity.getBlockPos(), blockEntity); } @Nullable @Override public BlockEntity getBlockEntity(BlockPos pos) { return (BlockEntity)this.blockEntities.get(pos); } public Map getBlockEntities() { return this.blockEntities; } public void addEntity(CompoundTag tag) { this.entities.add(tag); } @Override public void addEntity(Entity entity) { if (!entity.isPassenger()) { CompoundTag compoundTag = new CompoundTag(); entity.save(compoundTag); this.addEntity(compoundTag); } } @Override public void setStartForStructure(Structure structure, StructureStart structureStart) { BelowZeroRetrogen belowZeroRetrogen = this.getBelowZeroRetrogen(); if (belowZeroRetrogen != null && structureStart.isValid()) { BoundingBox boundingBox = structureStart.getBoundingBox(); LevelHeightAccessor levelHeightAccessor = this.getHeightAccessorForGeneration(); if (boundingBox.minY() < levelHeightAccessor.getMinY() || boundingBox.maxY() > levelHeightAccessor.getMaxY()) { return; } } super.setStartForStructure(structure, structureStart); } public List getEntities() { return this.entities; } @Override public ChunkStatus getPersistedStatus() { return this.status; } public void setPersistedStatus(ChunkStatus status) { this.status = status; if (this.belowZeroRetrogen != null && status.isOrAfter(this.belowZeroRetrogen.targetStatus())) { this.setBelowZeroRetrogen(null); } this.markUnsaved(); } @Override public Holder getNoiseBiome(int i, int j, int k) { if (this.getHighestGeneratedStatus().isOrAfter(ChunkStatus.BIOMES)) { return super.getNoiseBiome(i, j, k); } else { throw new IllegalStateException("Asking for biomes before we have biomes"); } } public static short packOffsetCoordinates(BlockPos pos) { int i = pos.getX(); int j = pos.getY(); int k = pos.getZ(); int l = i & 15; int m = j & 15; int n = k & 15; return (short)(l | m << 4 | n << 8); } public static BlockPos unpackOffsetCoordinates(short packedPos, int yOffset, ChunkPos chunkPos) { int i = SectionPos.sectionToBlockCoord(chunkPos.x, packedPos & 15); int j = SectionPos.sectionToBlockCoord(yOffset, packedPos >>> 4 & 15); int k = SectionPos.sectionToBlockCoord(chunkPos.z, packedPos >>> 8 & 15); return new BlockPos(i, j, k); } @Override public void markPosForPostprocessing(BlockPos pos) { if (!this.isOutsideBuildHeight(pos)) { ChunkAccess.getOrCreateOffsetList(this.postProcessing, this.getSectionIndex(pos.getY())).add(packOffsetCoordinates(pos)); } } @Override public void addPackedPostProcess(ShortList offsets, int index) { ChunkAccess.getOrCreateOffsetList(this.postProcessing, index).addAll(offsets); } public Map getBlockEntityNbts() { return Collections.unmodifiableMap(this.pendingBlockEntities); } @Nullable @Override public CompoundTag getBlockEntityNbtForSaving(BlockPos pos, HolderLookup.Provider registries) { BlockEntity blockEntity = this.getBlockEntity(pos); return blockEntity != null ? blockEntity.saveWithFullMetadata(registries) : (CompoundTag)this.pendingBlockEntities.get(pos); } @Override public void removeBlockEntity(BlockPos pos) { this.blockEntities.remove(pos); this.pendingBlockEntities.remove(pos); } @Nullable public CarvingMask getCarvingMask() { return this.carvingMask; } public CarvingMask getOrCreateCarvingMask() { if (this.carvingMask == null) { this.carvingMask = new CarvingMask(this.getHeight(), this.getMinY()); } return this.carvingMask; } public void setCarvingMask(CarvingMask carvingMask) { this.carvingMask = carvingMask; } public void setLightEngine(LevelLightEngine lightEngine) { this.lightEngine = lightEngine; } public void setBelowZeroRetrogen(@Nullable BelowZeroRetrogen belowZeroRetrogen) { this.belowZeroRetrogen = belowZeroRetrogen; } @Nullable @Override public BelowZeroRetrogen getBelowZeroRetrogen() { return this.belowZeroRetrogen; } private static LevelChunkTicks unpackTicks(ProtoChunkTicks ticks) { return new LevelChunkTicks<>(ticks.scheduledTicks()); } public LevelChunkTicks unpackBlockTicks() { return unpackTicks(this.blockTicks); } public LevelChunkTicks unpackFluidTicks() { return unpackTicks(this.fluidTicks); } @Override public LevelHeightAccessor getHeightAccessorForGeneration() { return (LevelHeightAccessor)(this.isUpgrading() ? BelowZeroRetrogen.UPGRADE_HEIGHT_ACCESSOR : this); } }