/* * Decompiled with CFR 0.152. * * Could not load the following classes: * com.google.common.collect.Maps * com.google.common.collect.Sets * com.mojang.logging.LogUtils * it.unimi.dsi.fastutil.longs.LongOpenHashSet * it.unimi.dsi.fastutil.longs.LongSet * it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap * it.unimi.dsi.fastutil.shorts.ShortArrayList * it.unimi.dsi.fastutil.shorts.ShortList * org.jspecify.annotations.Nullable * org.slf4j.Logger */ package net.minecraft.world.level.chunk; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongSet; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.shorts.ShortArrayList; import it.unimi.dsi.fastutil.shorts.ShortList; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.ReportedException; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.core.QuartPos; import net.minecraft.core.SectionPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.Mth; import net.minecraft.util.ProblemReporter; 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.biome.BiomeGenerationSettings; import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.biome.BiomeResolver; import net.minecraft.world.level.biome.Climate; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.LightChunk; import net.minecraft.world.level.chunk.PalettedContainerFactory; import net.minecraft.world.level.chunk.StructureAccess; import net.minecraft.world.level.chunk.UpgradeData; import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.gameevent.GameEventListenerRegistry; import net.minecraft.world.level.levelgen.BelowZeroRetrogen; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.NoiseChunk; import net.minecraft.world.level.levelgen.blending.BlendingData; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureStart; import net.minecraft.world.level.lighting.ChunkSkyLightSources; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.ticks.SavedTick; import net.minecraft.world.ticks.TickContainerAccess; import org.jspecify.annotations.Nullable; import org.slf4j.Logger; public abstract class ChunkAccess implements LightChunk, StructureAccess, BiomeManager.NoiseBiomeSource { public static final int NO_FILLED_SECTION = -1; private static final Logger LOGGER = LogUtils.getLogger(); private static final LongSet EMPTY_REFERENCE_SET = new LongOpenHashSet(); protected final @Nullable ShortList[] postProcessing; private volatile boolean unsaved; private volatile boolean isLightCorrect; protected final ChunkPos chunkPos; private long inhabitedTime; @Deprecated private @Nullable BiomeGenerationSettings carverBiomeSettings; protected @Nullable NoiseChunk noiseChunk; protected final UpgradeData upgradeData; protected @Nullable BlendingData blendingData; protected final Map heightmaps = Maps.newEnumMap(Heightmap.Types.class); protected ChunkSkyLightSources skyLightSources; private final Map structureStarts = Maps.newHashMap(); private final Map structuresRefences = Maps.newHashMap(); protected final Map pendingBlockEntities = Maps.newHashMap(); protected final Map blockEntities = new Object2ObjectOpenHashMap(); protected final LevelHeightAccessor levelHeightAccessor; protected final LevelChunkSection[] sections; public ChunkAccess(ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor, PalettedContainerFactory containerFactory, long inhabitedTime, LevelChunkSection @Nullable [] sections, @Nullable BlendingData blendingData) { this.chunkPos = chunkPos; this.upgradeData = upgradeData; this.levelHeightAccessor = levelHeightAccessor; this.sections = new LevelChunkSection[levelHeightAccessor.getSectionsCount()]; this.inhabitedTime = inhabitedTime; this.postProcessing = new ShortList[levelHeightAccessor.getSectionsCount()]; this.blendingData = blendingData; this.skyLightSources = new ChunkSkyLightSources(levelHeightAccessor); if (sections != null) { if (this.sections.length == sections.length) { System.arraycopy(sections, 0, this.sections, 0, this.sections.length); } else { LOGGER.warn("Could not set level chunk sections, array length is {} instead of {}", (Object)sections.length, (Object)this.sections.length); } } ChunkAccess.replaceMissingSections(containerFactory, this.sections); } private static void replaceMissingSections(PalettedContainerFactory containerFactory, LevelChunkSection[] sections) { for (int i = 0; i < sections.length; ++i) { if (sections[i] != null) continue; sections[i] = new LevelChunkSection(containerFactory); } } public GameEventListenerRegistry getListenerRegistry(int section) { return GameEventListenerRegistry.NOOP; } public @Nullable BlockState setBlockState(BlockPos pos, BlockState state) { return this.setBlockState(pos, state, 3); } public abstract @Nullable BlockState setBlockState(BlockPos var1, BlockState var2, int var3); public abstract void setBlockEntity(BlockEntity var1); public abstract void addEntity(Entity var1); public int getHighestFilledSectionIndex() { LevelChunkSection[] sections = this.getSections(); for (int sectionIndex = sections.length - 1; sectionIndex >= 0; --sectionIndex) { LevelChunkSection section = sections[sectionIndex]; if (section.hasOnlyAir()) continue; return sectionIndex; } return -1; } @Deprecated(forRemoval=true) public int getHighestSectionPosition() { int sectionIndex = this.getHighestFilledSectionIndex(); return sectionIndex == -1 ? this.getMinY() : SectionPos.sectionToBlockCoord(this.getSectionYFromSectionIndex(sectionIndex)); } public Set getBlockEntitiesPos() { HashSet result = Sets.newHashSet(this.pendingBlockEntities.keySet()); result.addAll(this.blockEntities.keySet()); return result; } public LevelChunkSection[] getSections() { return this.sections; } public LevelChunkSection getSection(int sectionIndex) { return this.getSections()[sectionIndex]; } public Collection> getHeightmaps() { return Collections.unmodifiableSet(this.heightmaps.entrySet()); } public void setHeightmap(Heightmap.Types key, long[] data) { this.getOrCreateHeightmapUnprimed(key).setRawData(this, key, data); } public Heightmap getOrCreateHeightmapUnprimed(Heightmap.Types type) { return this.heightmaps.computeIfAbsent(type, k -> new Heightmap(this, (Heightmap.Types)k)); } public boolean hasPrimedHeightmap(Heightmap.Types type) { return this.heightmaps.get(type) != null; } public int getHeight(Heightmap.Types type, int x, int z) { Heightmap heightmap = this.heightmaps.get(type); if (heightmap == null) { if (SharedConstants.IS_RUNNING_IN_IDE && this instanceof LevelChunk) { LOGGER.error("Unprimed heightmap: {} {} {}", new Object[]{type, x, z}); } Heightmap.primeHeightmaps(this, EnumSet.of(type)); heightmap = this.heightmaps.get(type); } return heightmap.getFirstAvailable(x & 0xF, z & 0xF) - 1; } public ChunkPos getPos() { return this.chunkPos; } @Override public @Nullable StructureStart getStartForStructure(Structure structure) { return this.structureStarts.get(structure); } @Override public void setStartForStructure(Structure structure, StructureStart structureStart) { this.structureStarts.put(structure, structureStart); this.markUnsaved(); } public Map getAllStarts() { return Collections.unmodifiableMap(this.structureStarts); } public void setAllStarts(Map starts) { this.structureStarts.clear(); this.structureStarts.putAll(starts); this.markUnsaved(); } @Override public LongSet getReferencesForStructure(Structure structure) { return this.structuresRefences.getOrDefault(structure, EMPTY_REFERENCE_SET); } @Override public void addReferenceForStructure(Structure structure, long reference) { this.structuresRefences.computeIfAbsent(structure, k -> new LongOpenHashSet()).add(reference); this.markUnsaved(); } @Override public Map getAllReferences() { return Collections.unmodifiableMap(this.structuresRefences); } @Override public void setAllReferences(Map data) { this.structuresRefences.clear(); this.structuresRefences.putAll(data); this.markUnsaved(); } public boolean isYSpaceEmpty(int yStartInclusive, int yEndInclusive) { if (yStartInclusive < this.getMinY()) { yStartInclusive = this.getMinY(); } if (yEndInclusive > this.getMaxY()) { yEndInclusive = this.getMaxY(); } for (int y = yStartInclusive; y <= yEndInclusive; y += 16) { if (this.getSection(this.getSectionIndex(y)).hasOnlyAir()) continue; return false; } return true; } public void markUnsaved() { this.unsaved = true; } public boolean tryMarkSaved() { if (this.unsaved) { this.unsaved = false; return true; } return false; } public boolean isUnsaved() { return this.unsaved; } public abstract ChunkStatus getPersistedStatus(); public ChunkStatus getHighestGeneratedStatus() { ChunkStatus status = this.getPersistedStatus(); BelowZeroRetrogen belowZeroRetrogen = this.getBelowZeroRetrogen(); if (belowZeroRetrogen != null) { ChunkStatus targetStatus = belowZeroRetrogen.targetStatus(); return ChunkStatus.max(targetStatus, status); } return status; } public abstract void removeBlockEntity(BlockPos var1); public void markPosForPostprocessing(BlockPos blockPos) { LOGGER.warn("Trying to mark a block for PostProcessing @ {}, but this operation is not supported.", (Object)blockPos); } public @Nullable ShortList[] getPostProcessing() { return this.postProcessing; } public void addPackedPostProcess(ShortList packedOffsets, int sectionIndex) { ChunkAccess.getOrCreateOffsetList(this.getPostProcessing(), sectionIndex).addAll(packedOffsets); } public void setBlockEntityNbt(CompoundTag entityTag) { BlockPos posFromTag = BlockEntity.getPosFromTag(this.chunkPos, entityTag); if (!this.blockEntities.containsKey(posFromTag)) { this.pendingBlockEntities.put(posFromTag, entityTag); } } public @Nullable CompoundTag getBlockEntityNbt(BlockPos blockPos) { return this.pendingBlockEntities.get(blockPos); } public abstract @Nullable CompoundTag getBlockEntityNbtForSaving(BlockPos var1, HolderLookup.Provider var2); @Override public final void findBlockLightSources(BiConsumer consumer) { this.findBlocks(state -> state.getLightEmission() != 0, consumer); } public void findBlocks(Predicate predicate, BiConsumer consumer) { BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); for (int sectionY = this.getMinSectionY(); sectionY <= this.getMaxSectionY(); ++sectionY) { LevelChunkSection section = this.getSection(this.getSectionIndexFromSectionY(sectionY)); if (!section.maybeHas(predicate)) continue; BlockPos origin = SectionPos.of(this.chunkPos, sectionY).origin(); for (int y = 0; y < 16; ++y) { for (int z = 0; z < 16; ++z) { for (int x = 0; x < 16; ++x) { BlockState state = section.getBlockState(x, y, z); if (!predicate.test(state)) continue; consumer.accept(mutablePos.setWithOffset(origin, x, y, z), state); } } } } } public abstract TickContainerAccess getBlockTicks(); public abstract TickContainerAccess getFluidTicks(); public boolean canBeSerialized() { return true; } public abstract PackedTicks getTicksForSerialization(long var1); public UpgradeData getUpgradeData() { return this.upgradeData; } public boolean isOldNoiseGeneration() { return this.blendingData != null; } public @Nullable BlendingData getBlendingData() { return this.blendingData; } public long getInhabitedTime() { return this.inhabitedTime; } public void incrementInhabitedTime(long inhabitedTimeDelta) { this.inhabitedTime += inhabitedTimeDelta; } public void setInhabitedTime(long inhabitedTime) { this.inhabitedTime = inhabitedTime; } public static ShortList getOrCreateOffsetList(@Nullable ShortList[] list, int sectionIndex) { ShortList result = list[sectionIndex]; if (result == null) { list[sectionIndex] = result = new ShortArrayList(); } return result; } public boolean isLightCorrect() { return this.isLightCorrect; } public void setLightCorrect(boolean isLightCorrect) { this.isLightCorrect = isLightCorrect; this.markUnsaved(); } @Override public int getMinY() { return this.levelHeightAccessor.getMinY(); } @Override public int getHeight() { return this.levelHeightAccessor.getHeight(); } public NoiseChunk getOrCreateNoiseChunk(Function factory) { if (this.noiseChunk == null) { this.noiseChunk = factory.apply(this); } return this.noiseChunk; } @Deprecated public BiomeGenerationSettings carverBiome(Supplier source) { if (this.carverBiomeSettings == null) { this.carverBiomeSettings = source.get(); } return this.carverBiomeSettings; } @Override public Holder getNoiseBiome(int quartX, int quartY, int quartZ) { try { int quartMinY = QuartPos.fromBlock(this.getMinY()); int quartMaxY = quartMinY + QuartPos.fromBlock(this.getHeight()) - 1; int clampedQuartY = Mth.clamp(quartY, quartMinY, quartMaxY); int sectionIndex = this.getSectionIndex(QuartPos.toBlock(clampedQuartY)); return this.sections[sectionIndex].getNoiseBiome(quartX & 3, clampedQuartY & 3, quartZ & 3); } catch (Throwable t) { CrashReport report = CrashReport.forThrowable(t, "Getting biome"); CrashReportCategory category = report.addCategory("Biome being got"); category.setDetail("Location", () -> CrashReportCategory.formatLocation((LevelHeightAccessor)this, quartX, quartY, quartZ)); throw new ReportedException(report); } } public void fillBiomesFromNoise(BiomeResolver biomeResolver, Climate.Sampler sampler) { ChunkPos pos = this.getPos(); int quartMinX = QuartPos.fromBlock(pos.getMinBlockX()); int quartMinZ = QuartPos.fromBlock(pos.getMinBlockZ()); LevelHeightAccessor heightAccessor = this.getHeightAccessorForGeneration(); for (int sectionY = heightAccessor.getMinSectionY(); sectionY <= heightAccessor.getMaxSectionY(); ++sectionY) { LevelChunkSection section = this.getSection(this.getSectionIndexFromSectionY(sectionY)); int quartMinY = QuartPos.fromSection(sectionY); section.fillBiomesFromNoise(biomeResolver, sampler, quartMinX, quartMinY, quartMinZ); } } public boolean hasAnyStructureReferences() { return !this.getAllReferences().isEmpty(); } public @Nullable BelowZeroRetrogen getBelowZeroRetrogen() { return null; } public boolean isUpgrading() { return this.getBelowZeroRetrogen() != null; } public LevelHeightAccessor getHeightAccessorForGeneration() { return this; } public void initializeLightSources() { this.skyLightSources.fillFrom(this); } @Override public ChunkSkyLightSources getSkyLightSources() { return this.skyLightSources; } public static ProblemReporter.PathElement problemPath(ChunkPos pos) { return new ChunkPathElement(pos); } public ProblemReporter.PathElement problemPath() { return ChunkAccess.problemPath(this.getPos()); } private record ChunkPathElement(ChunkPos pos) implements ProblemReporter.PathElement { @Override public String get() { return "chunk@" + String.valueOf(this.pos); } } public record PackedTicks(List> blocks, List> fluids) { } }