331 lines
10 KiB
Java
331 lines
10 KiB
Java
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<CompoundTag> entities = Lists.<CompoundTag>newArrayList();
|
|
@Nullable
|
|
private CarvingMask carvingMask;
|
|
@Nullable
|
|
private BelowZeroRetrogen belowZeroRetrogen;
|
|
private final ProtoChunkTicks<Block> blockTicks;
|
|
private final ProtoChunkTicks<Fluid> fluidTicks;
|
|
|
|
public ProtoChunk(
|
|
ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor, Registry<Biome> 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<Block> blockTicks,
|
|
ProtoChunkTicks<Fluid> liquidTicks,
|
|
LevelHeightAccessor levelHeightAccessor,
|
|
Registry<Biome> biomeRegistry,
|
|
@Nullable BlendingData blendingData
|
|
) {
|
|
super(chunkPos, upgradeData, levelHeightAccessor, biomeRegistry, 0L, sections, blendingData);
|
|
this.blockTicks = blockTicks;
|
|
this.fluidTicks = liquidTicks;
|
|
}
|
|
|
|
@Override
|
|
public TickContainerAccess<Block> getBlockTicks() {
|
|
return this.blockTicks;
|
|
}
|
|
|
|
@Override
|
|
public TickContainerAccess<Fluid> 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<Heightmap.Types> enumSet = this.getPersistedStatus().heightmapsAfter();
|
|
EnumSet<Heightmap.Types> 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<BlockPos, BlockEntity> 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<CompoundTag> 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<Biome> 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<BlockPos, CompoundTag> 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 <T> LevelChunkTicks<T> unpackTicks(ProtoChunkTicks<T> ticks) {
|
|
return new LevelChunkTicks<>(ticks.scheduledTicks());
|
|
}
|
|
|
|
public LevelChunkTicks<Block> unpackBlockTicks() {
|
|
return unpackTicks(this.blockTicks);
|
|
}
|
|
|
|
public LevelChunkTicks<Fluid> unpackFluidTicks() {
|
|
return unpackTicks(this.fluidTicks);
|
|
}
|
|
|
|
@Override
|
|
public LevelHeightAccessor getHeightAccessorForGeneration() {
|
|
return (LevelHeightAccessor)(this.isUpgrading() ? BelowZeroRetrogen.UPGRADE_HEIGHT_ACCESSOR : this);
|
|
}
|
|
}
|