package net.minecraft.world.level.chunk;

import org.apache.logging.log4j.LogManager;
import net.minecraft.core.Registry;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkTickList;
import it.unimi.dsi.fastutil.shorts.ShortListIterator;
import net.minecraft.world.level.LevelAccessor;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import java.util.stream.StreamSupport;
import java.util.stream.Stream;
import java.util.Collections;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
import java.util.function.Predicate;
import java.util.List;
import net.minecraft.world.phys.AABB;
import net.minecraft.util.Mth;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.ReportedException;
import net.minecraft.CrashReportCategory;
import net.minecraft.CrashReport;
import net.minecraft.world.level.levelgen.DebugLevelSource;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import java.util.Collection;
import com.google.common.collect.Sets;
import java.util.Set;
import java.util.Iterator;
import net.minecraft.world.entity.EntityType;
import com.google.common.collect.Maps;
import net.minecraft.world.level.EmptyTickList;
import net.minecraft.world.level.ChunkPos;
import java.util.function.Consumer;
import net.minecraft.server.level.ChunkHolder;
import java.util.function.Supplier;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.TickList;
import it.unimi.dsi.fastutil.shorts.ShortList;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.feature.StructureFeature;
import net.minecraft.world.entity.Entity;
import net.minecraft.util.ClassInstanceMultiMap;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.Level;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.core.BlockPos;
import java.util.Map;
import javax.annotation.Nullable;
import org.apache.logging.log4j.Logger;

public class LevelChunk implements ChunkAccess {
    private static final Logger LOGGER;
    @Nullable
    public static final LevelChunkSection EMPTY_SECTION;
    private final LevelChunkSection[] sections;
    private ChunkBiomeContainer biomes;
    private final Map<BlockPos, CompoundTag> pendingBlockEntities;
    private boolean loaded;
    private final Level level;
    private final Map<Heightmap.Types, Heightmap> heightmaps;
    private final UpgradeData upgradeData;
    private final Map<BlockPos, BlockEntity> blockEntities;
    private final ClassInstanceMultiMap<Entity>[] entitySections;
    private final Map<StructureFeature<?>, StructureStart<?>> structureStarts;
    private final Map<StructureFeature<?>, LongSet> structuresRefences;
    private final ShortList[] postProcessing;
    private TickList<Block> blockTicks;
    private TickList<Fluid> liquidTicks;
    private boolean lastSaveHadEntities;
    private long lastSaveTime;
    private volatile boolean unsaved;
    private long inhabitedTime;
    @Nullable
    private Supplier<ChunkHolder.FullChunkStatus> fullStatus;
    @Nullable
    private Consumer<LevelChunk> postLoad;
    private final ChunkPos chunkPos;
    private volatile boolean isLightCorrect;
    
    public LevelChunk(final Level bqb, final ChunkPos bph, final ChunkBiomeContainer cgz) {
        this(bqb, bph, cgz, UpgradeData.EMPTY, EmptyTickList.empty(), EmptyTickList.empty(), 0L, null, null);
    }
    
    public LevelChunk(final Level bqb, final ChunkPos bph, final ChunkBiomeContainer cgz, final UpgradeData cht, final TickList<Block> bqr5, final TickList<Fluid> bqr6, final long long7, @Nullable final LevelChunkSection[] arr, @Nullable final Consumer<LevelChunk> consumer) {
        this.sections = new LevelChunkSection[16];
        this.pendingBlockEntities = Maps.newHashMap();
        this.heightmaps = Maps.newEnumMap(Heightmap.Types.class);
        this.blockEntities = Maps.newHashMap();
        this.structureStarts = Maps.newHashMap();
        this.structuresRefences = Maps.newHashMap();
        this.postProcessing = new ShortList[16];
        this.entitySections = new ClassInstanceMultiMap[16];
        this.level = bqb;
        this.chunkPos = bph;
        this.upgradeData = cht;
        for (final Heightmap.Types a15 : Heightmap.Types.values()) {
            if (ChunkStatus.FULL.heightmapsAfter().contains(a15)) {
                this.heightmaps.put(a15, new Heightmap(this, a15));
            }
        }
        for (int integer12 = 0; integer12 < this.entitySections.length; ++integer12) {
            this.entitySections[integer12] = new ClassInstanceMultiMap<Entity>(Entity.class);
        }
        this.biomes = cgz;
        this.blockTicks = bqr5;
        this.liquidTicks = bqr6;
        this.inhabitedTime = long7;
        this.postLoad = consumer;
        if (arr != null) {
            if (this.sections.length == arr.length) {
                System.arraycopy(arr, 0, this.sections, 0, this.sections.length);
            }
            else {
                LevelChunk.LOGGER.warn("Could not set level chunk sections, array length is {} instead of {}", arr.length, this.sections.length);
            }
        }
    }
    
    public LevelChunk(final Level bqb, final ProtoChunk chr) {
        this(bqb, chr.getPos(), chr.getBiomes(), chr.getUpgradeData(), chr.getBlockTicks(), chr.getLiquidTicks(), chr.getInhabitedTime(), chr.getSections(), null);
        for (final CompoundTag le5 : chr.getEntities()) {
            EntityType.loadEntityRecursive(le5, bqb, aom -> {
                this.addEntity(aom);
                return aom;
            });
        }
        for (final BlockEntity cdl5 : chr.getBlockEntities().values()) {
            this.addBlockEntity(cdl5);
        }
        this.pendingBlockEntities.putAll(chr.getBlockEntityNbts());
        for (int integer4 = 0; integer4 < chr.getPostProcessing().length; ++integer4) {
            this.postProcessing[integer4] = chr.getPostProcessing()[integer4];
        }
        this.setAllStarts(chr.getAllStarts());
        this.setAllReferences(chr.getAllReferences());
        for (final Map.Entry<Heightmap.Types, Heightmap> entry5 : chr.getHeightmaps()) {
            if (ChunkStatus.FULL.heightmapsAfter().contains(entry5.getKey())) {
                this.getOrCreateHeightmapUnprimed(entry5.getKey()).setRawData(entry5.getValue().getRawData());
            }
        }
        this.setLightCorrect(chr.isLightCorrect());
        this.unsaved = true;
    }
    
    @Override
    public Heightmap getOrCreateHeightmapUnprimed(final Heightmap.Types a) {
        return this.heightmaps.computeIfAbsent(a, a -> new Heightmap(this, a));
    }
    
    @Override
    public Set<BlockPos> getBlockEntitiesPos() {
        final Set<BlockPos> set2 = Sets.newHashSet(this.pendingBlockEntities.keySet());
        set2.addAll(this.blockEntities.keySet());
        return set2;
    }
    
    @Override
    public LevelChunkSection[] getSections() {
        return this.sections;
    }
    
    @Override
    public BlockState getBlockState(final BlockPos fu) {
        final int integer3 = fu.getX();
        final int integer4 = fu.getY();
        final int integer5 = fu.getZ();
        if (this.level.isDebug()) {
            BlockState cfj6 = null;
            if (integer4 == 60) {
                cfj6 = Blocks.BARRIER.defaultBlockState();
            }
            if (integer4 == 70) {
                cfj6 = DebugLevelSource.getBlockStateFor(integer3, integer5);
            }
            return (cfj6 == null) ? Blocks.AIR.defaultBlockState() : cfj6;
        }
        try {
            if (integer4 >= 0 && integer4 >> 4 < this.sections.length) {
                final LevelChunkSection chk6 = this.sections[integer4 >> 4];
                if (!LevelChunkSection.isEmpty(chk6)) {
                    return chk6.getBlockState(integer3 & 0xF, integer4 & 0xF, integer5 & 0xF);
                }
            }
            return Blocks.AIR.defaultBlockState();
        }
        catch (Throwable throwable6) {
            final CrashReport j7 = CrashReport.forThrowable(throwable6, "Getting block state");
            final CrashReportCategory k8 = j7.addCategory("Block being got");
            k8.setDetail("Location", () -> CrashReportCategory.formatLocation(integer3, integer4, integer5));
            throw new ReportedException(j7);
        }
    }
    
    @Override
    public FluidState getFluidState(final BlockPos fu) {
        return this.getFluidState(fu.getX(), fu.getY(), fu.getZ());
    }
    
    public FluidState getFluidState(final int integer1, final int integer2, final int integer3) {
        try {
            if (integer2 >= 0 && integer2 >> 4 < this.sections.length) {
                final LevelChunkSection chk5 = this.sections[integer2 >> 4];
                if (!LevelChunkSection.isEmpty(chk5)) {
                    return chk5.getFluidState(integer1 & 0xF, integer2 & 0xF, integer3 & 0xF);
                }
            }
            return Fluids.EMPTY.defaultFluidState();
        }
        catch (Throwable throwable5) {
            final CrashReport j6 = CrashReport.forThrowable(throwable5, "Getting fluid state");
            final CrashReportCategory k7 = j6.addCategory("Block being got");
            k7.setDetail("Location", () -> CrashReportCategory.formatLocation(integer1, integer2, integer3));
            throw new ReportedException(j6);
        }
    }
    
    @Nullable
    @Override
    public BlockState setBlockState(final BlockPos fu, final BlockState cfj, final boolean boolean3) {
        final int integer5 = fu.getX() & 0xF;
        final int integer6 = fu.getY();
        final int integer7 = fu.getZ() & 0xF;
        LevelChunkSection chk8 = this.sections[integer6 >> 4];
        if (chk8 == LevelChunk.EMPTY_SECTION) {
            if (cfj.isAir()) {
                return null;
            }
            chk8 = new LevelChunkSection(integer6 >> 4 << 4);
            this.sections[integer6 >> 4] = chk8;
        }
        final boolean boolean4 = chk8.isEmpty();
        final BlockState cfj2 = chk8.setBlockState(integer5, integer6 & 0xF, integer7, cfj);
        if (cfj2 == cfj) {
            return null;
        }
        final Block bvr11 = cfj.getBlock();
        final Block bvr12 = cfj2.getBlock();
        this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING).update(integer5, integer6, integer7, cfj);
        this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES).update(integer5, integer6, integer7, cfj);
        this.heightmaps.get(Heightmap.Types.OCEAN_FLOOR).update(integer5, integer6, integer7, cfj);
        this.heightmaps.get(Heightmap.Types.WORLD_SURFACE).update(integer5, integer6, integer7, cfj);
        final boolean boolean5 = chk8.isEmpty();
        if (boolean4 != boolean5) {
            this.level.getChunkSource().getLightEngine().updateSectionStatus(fu, boolean5);
        }
        if (!this.level.isClientSide) {
            cfj2.onRemove(this.level, fu, cfj, boolean3);
        }
        else if (bvr12 != bvr11 && bvr12 instanceof EntityBlock) {
            this.level.removeBlockEntity(fu);
        }
        if (!chk8.getBlockState(integer5, integer6 & 0xF, integer7).is(bvr11)) {
            return null;
        }
        if (bvr12 instanceof EntityBlock) {
            final BlockEntity cdl14 = this.getBlockEntity(fu, EntityCreationType.CHECK);
            if (cdl14 != null) {
                cdl14.clearCache();
            }
        }
        if (!this.level.isClientSide) {
            cfj.onPlace(this.level, fu, cfj2, boolean3);
        }
        if (bvr11 instanceof EntityBlock) {
            BlockEntity cdl14 = this.getBlockEntity(fu, EntityCreationType.CHECK);
            if (cdl14 == null) {
                cdl14 = ((EntityBlock)bvr11).newBlockEntity(this.level);
                this.level.setBlockEntity(fu, cdl14);
            }
            else {
                cdl14.clearCache();
            }
        }
        this.unsaved = true;
        return cfj2;
    }
    
    @Nullable
    public LevelLightEngine getLightEngine() {
        return this.level.getChunkSource().getLightEngine();
    }
    
    @Override
    public void addEntity(final Entity aom) {
        this.lastSaveHadEntities = true;
        final int integer3 = Mth.floor(aom.getX() / 16.0);
        final int integer4 = Mth.floor(aom.getZ() / 16.0);
        if (integer3 != this.chunkPos.x || integer4 != this.chunkPos.z) {
            LevelChunk.LOGGER.warn("Wrong location! ({}, {}) should be ({}, {}), {}", integer3, integer4, this.chunkPos.x, this.chunkPos.z, aom);
            aom.removed = true;
        }
        int integer5 = Mth.floor(aom.getY() / 16.0);
        if (integer5 < 0) {
            integer5 = 0;
        }
        if (integer5 >= this.entitySections.length) {
            integer5 = this.entitySections.length - 1;
        }
        aom.inChunk = true;
        aom.xChunk = this.chunkPos.x;
        aom.yChunk = integer5;
        aom.zChunk = this.chunkPos.z;
        this.entitySections[integer5].add(aom);
    }
    
    @Override
    public void setHeightmap(final Heightmap.Types a, final long[] arr) {
        this.heightmaps.get(a).setRawData(arr);
    }
    
    public void removeEntity(final Entity aom) {
        this.removeEntity(aom, aom.yChunk);
    }
    
    public void removeEntity(final Entity aom, int integer) {
        if (integer < 0) {
            integer = 0;
        }
        if (integer >= this.entitySections.length) {
            integer = this.entitySections.length - 1;
        }
        this.entitySections[integer].remove(aom);
    }
    
    @Override
    public int getHeight(final Heightmap.Types a, final int integer2, final int integer3) {
        return this.heightmaps.get(a).getFirstAvailable(integer2 & 0xF, integer3 & 0xF) - 1;
    }
    
    @Nullable
    private BlockEntity createBlockEntity(final BlockPos fu) {
        final BlockState cfj3 = this.getBlockState(fu);
        final Block bvr4 = cfj3.getBlock();
        if (!bvr4.isEntityBlock()) {
            return null;
        }
        return ((EntityBlock)bvr4).newBlockEntity(this.level);
    }
    
    @Nullable
    @Override
    public BlockEntity getBlockEntity(final BlockPos fu) {
        return this.getBlockEntity(fu, EntityCreationType.CHECK);
    }
    
    @Nullable
    public BlockEntity getBlockEntity(final BlockPos fu, final EntityCreationType a) {
        BlockEntity cdl4 = this.blockEntities.get(fu);
        if (cdl4 == null) {
            final CompoundTag le5 = this.pendingBlockEntities.remove(fu);
            if (le5 != null) {
                final BlockEntity cdl5 = this.promotePendingBlockEntity(fu, le5);
                if (cdl5 != null) {
                    return cdl5;
                }
            }
        }
        if (cdl4 == null) {
            if (a == EntityCreationType.IMMEDIATE) {
                cdl4 = this.createBlockEntity(fu);
                this.level.setBlockEntity(fu, cdl4);
            }
        }
        else if (cdl4.isRemoved()) {
            this.blockEntities.remove(fu);
            return null;
        }
        return cdl4;
    }
    
    public void addBlockEntity(final BlockEntity cdl) {
        this.setBlockEntity(cdl.getBlockPos(), cdl);
        if (this.loaded || this.level.isClientSide()) {
            this.level.setBlockEntity(cdl.getBlockPos(), cdl);
        }
    }
    
    @Override
    public void setBlockEntity(final BlockPos fu, final BlockEntity cdl) {
        if (!(this.getBlockState(fu).getBlock() instanceof EntityBlock)) {
            return;
        }
        cdl.setLevelAndPosition(this.level, fu);
        cdl.clearRemoved();
        final BlockEntity cdl2 = this.blockEntities.put(fu.immutable(), cdl);
        if (cdl2 != null && cdl2 != cdl) {
            cdl2.setRemoved();
        }
    }
    
    @Override
    public void setBlockEntityNbt(final CompoundTag le) {
        this.pendingBlockEntities.put(new BlockPos(le.getInt("x"), le.getInt("y"), le.getInt("z")), le);
    }
    
    @Nullable
    @Override
    public CompoundTag getBlockEntityNbtForSaving(final BlockPos fu) {
        final BlockEntity cdl3 = this.getBlockEntity(fu);
        if (cdl3 != null && !cdl3.isRemoved()) {
            final CompoundTag le4 = cdl3.save(new CompoundTag());
            le4.putBoolean("keepPacked", false);
            return le4;
        }
        CompoundTag le4 = this.pendingBlockEntities.get(fu);
        if (le4 != null) {
            le4 = le4.copy();
            le4.putBoolean("keepPacked", true);
        }
        return le4;
    }
    
    @Override
    public void removeBlockEntity(final BlockPos fu) {
        if (this.loaded || this.level.isClientSide()) {
            final BlockEntity cdl3 = this.blockEntities.remove(fu);
            if (cdl3 != null) {
                cdl3.setRemoved();
            }
        }
    }
    
    public void runPostLoad() {
        if (this.postLoad != null) {
            this.postLoad.accept(this);
            this.postLoad = null;
        }
    }
    
    public void markUnsaved() {
        this.unsaved = true;
    }
    
    public void getEntities(@Nullable final Entity aom, final AABB deg, final List<Entity> list, @Nullable final Predicate<? super Entity> predicate) {
        int integer6 = Mth.floor((deg.minY - 2.0) / 16.0);
        int integer7 = Mth.floor((deg.maxY + 2.0) / 16.0);
        integer6 = Mth.clamp(integer6, 0, this.entitySections.length - 1);
        integer7 = Mth.clamp(integer7, 0, this.entitySections.length - 1);
        for (int integer8 = integer6; integer8 <= integer7; ++integer8) {
            final ClassInstanceMultiMap<Entity> adk9 = this.entitySections[integer8];
            final List<Entity> list2 = adk9.getAllInstances();
            for (int integer9 = list2.size(), integer10 = 0; integer10 < integer9; ++integer10) {
                final Entity aom2 = list2.get(integer10);
                if (aom2.getBoundingBox().intersects(deg) && aom2 != aom) {
                    if (predicate == null || predicate.test(aom2)) {
                        list.add(aom2);
                    }
                    if (aom2 instanceof EnderDragon) {
                        for (final EnderDragonPart baa17 : ((EnderDragon)aom2).getSubEntities()) {
                            if (baa17 != aom && baa17.getBoundingBox().intersects(deg) && (predicate == null || predicate.test(baa17))) {
                                list.add(baa17);
                            }
                        }
                    }
                }
            }
        }
    }
    
    public <T extends Entity> void getEntities(@Nullable final EntityType<?> aoq, final AABB deg, final List<? super T> list, final Predicate<? super T> predicate) {
        int integer6 = Mth.floor((deg.minY - 2.0) / 16.0);
        int integer7 = Mth.floor((deg.maxY + 2.0) / 16.0);
        integer6 = Mth.clamp(integer6, 0, this.entitySections.length - 1);
        integer7 = Mth.clamp(integer7, 0, this.entitySections.length - 1);
        for (int integer8 = integer6; integer8 <= integer7; ++integer8) {
            for (final Entity aom10 : this.entitySections[integer8].<Entity>find(Entity.class)) {
                if (aoq != null && aom10.getType() != aoq) {
                    continue;
                }
                final T aom11 = (T)aom10;
                if (!aom10.getBoundingBox().intersects(deg) || !predicate.test(aom11)) {
                    continue;
                }
                list.add(aom11);
            }
        }
    }
    
    public <T extends Entity> void getEntitiesOfClass(final Class<? extends T> class1, final AABB deg, final List<T> list, @Nullable final Predicate<? super T> predicate) {
        int integer6 = Mth.floor((deg.minY - 2.0) / 16.0);
        int integer7 = Mth.floor((deg.maxY + 2.0) / 16.0);
        integer6 = Mth.clamp(integer6, 0, this.entitySections.length - 1);
        integer7 = Mth.clamp(integer7, 0, this.entitySections.length - 1);
        for (int integer8 = integer6; integer8 <= integer7; ++integer8) {
            for (final T aom10 : this.entitySections[integer8].find(class1)) {
                if (aom10.getBoundingBox().intersects(deg) && (predicate == null || predicate.test(aom10))) {
                    list.add(aom10);
                }
            }
        }
    }
    
    public boolean isEmpty() {
        return false;
    }
    
    @Override
    public ChunkPos getPos() {
        return this.chunkPos;
    }
    
    public void replaceWithPacketData(@Nullable final ChunkBiomeContainer cgz, final FriendlyByteBuf mg, final CompoundTag le, final int integer) {
        final boolean boolean6 = cgz != null;
        final Predicate<BlockPos> predicate7 = boolean6 ? (fu -> true) : (fu -> (integer & 1 << (fu.getY() >> 4)) != 0x0);
        Sets.newHashSet(this.blockEntities.keySet()).stream().filter(predicate7).forEach(this.level::removeBlockEntity);
        for (int integer2 = 0; integer2 < this.sections.length; ++integer2) {
            LevelChunkSection chk9 = this.sections[integer2];
            if ((integer & 1 << integer2) == 0x0) {
                if (boolean6 && chk9 != LevelChunk.EMPTY_SECTION) {
                    this.sections[integer2] = LevelChunk.EMPTY_SECTION;
                }
            }
            else {
                if (chk9 == LevelChunk.EMPTY_SECTION) {
                    chk9 = new LevelChunkSection(integer2 << 4);
                    this.sections[integer2] = chk9;
                }
                chk9.read(mg);
            }
        }
        if (cgz != null) {
            this.biomes = cgz;
        }
        for (final Heightmap.Types a11 : Heightmap.Types.values()) {
            final String string12 = a11.getSerializationKey();
            if (le.contains(string12, 12)) {
                this.setHeightmap(a11, le.getLongArray(string12));
            }
        }
        for (final BlockEntity cdl9 : this.blockEntities.values()) {
            cdl9.clearCache();
        }
    }
    
    @Override
    public ChunkBiomeContainer getBiomes() {
        return this.biomes;
    }
    
    public void setLoaded(final boolean boolean1) {
        this.loaded = boolean1;
    }
    
    public Level getLevel() {
        return this.level;
    }
    
    @Override
    public Collection<Map.Entry<Heightmap.Types, Heightmap>> getHeightmaps() {
        return Collections.unmodifiableSet(this.heightmaps.entrySet());
    }
    
    public Map<BlockPos, BlockEntity> getBlockEntities() {
        return this.blockEntities;
    }
    
    public ClassInstanceMultiMap<Entity>[] getEntitySections() {
        return this.entitySections;
    }
    
    @Override
    public CompoundTag getBlockEntityNbt(final BlockPos fu) {
        return this.pendingBlockEntities.get(fu);
    }
    
    @Override
    public Stream<BlockPos> getLights() {
        return StreamSupport.<BlockPos>stream(BlockPos.betweenClosed(this.chunkPos.getMinBlockX(), 0, this.chunkPos.getMinBlockZ(), this.chunkPos.getMaxBlockX(), 255, this.chunkPos.getMaxBlockZ()).spliterator(), false).filter(fu -> this.getBlockState(fu).getLightEmission() != 0);
    }
    
    @Override
    public TickList<Block> getBlockTicks() {
        return this.blockTicks;
    }
    
    @Override
    public TickList<Fluid> getLiquidTicks() {
        return this.liquidTicks;
    }
    
    @Override
    public void setUnsaved(final boolean boolean1) {
        this.unsaved = boolean1;
    }
    
    @Override
    public boolean isUnsaved() {
        return this.unsaved || (this.lastSaveHadEntities && this.level.getGameTime() != this.lastSaveTime);
    }
    
    public void setLastSaveHadEntities(final boolean boolean1) {
        this.lastSaveHadEntities = boolean1;
    }
    
    @Override
    public void setLastSaveTime(final long long1) {
        this.lastSaveTime = long1;
    }
    
    @Nullable
    @Override
    public StructureStart<?> getStartForFeature(final StructureFeature<?> cml) {
        return this.structureStarts.get(cml);
    }
    
    @Override
    public void setStartForFeature(final StructureFeature<?> cml, final StructureStart<?> ctz) {
        this.structureStarts.put(cml, ctz);
    }
    
    @Override
    public Map<StructureFeature<?>, StructureStart<?>> getAllStarts() {
        return this.structureStarts;
    }
    
    @Override
    public void setAllStarts(final Map<StructureFeature<?>, StructureStart<?>> map) {
        this.structureStarts.clear();
        this.structureStarts.putAll(map);
    }
    
    @Override
    public LongSet getReferencesForFeature(final StructureFeature<?> cml) {
        return this.structuresRefences.computeIfAbsent(cml, cml -> new LongOpenHashSet());
    }
    
    @Override
    public void addReferenceForFeature(final StructureFeature<?> cml, final long long2) {
        this.structuresRefences.computeIfAbsent(cml, cml -> new LongOpenHashSet()).add(long2);
    }
    
    @Override
    public Map<StructureFeature<?>, LongSet> getAllReferences() {
        return this.structuresRefences;
    }
    
    @Override
    public void setAllReferences(final Map<StructureFeature<?>, LongSet> map) {
        this.structuresRefences.clear();
        this.structuresRefences.putAll(map);
    }
    
    @Override
    public long getInhabitedTime() {
        return this.inhabitedTime;
    }
    
    @Override
    public void setInhabitedTime(final long long1) {
        this.inhabitedTime = long1;
    }
    
    public void postProcessGeneration() {
        final ChunkPos bph2 = this.getPos();
        for (int integer3 = 0; integer3 < this.postProcessing.length; ++integer3) {
            if (this.postProcessing[integer3] != null) {
                for (final Short short5 : this.postProcessing[integer3]) {
                    final BlockPos fu6 = ProtoChunk.unpackOffsetCoordinates(short5, integer3, bph2);
                    final BlockState cfj7 = this.getBlockState(fu6);
                    final BlockState cfj8 = Block.updateFromNeighbourShapes(cfj7, this.level, fu6);
                    this.level.setBlock(fu6, cfj8, 20);
                }
                this.postProcessing[integer3].clear();
            }
        }
        this.unpackTicks();
        for (final BlockPos fu7 : Sets.<BlockPos>newHashSet(this.pendingBlockEntities.keySet())) {
            this.getBlockEntity(fu7);
        }
        this.pendingBlockEntities.clear();
        this.upgradeData.upgrade(this);
    }
    
    @Nullable
    private BlockEntity promotePendingBlockEntity(final BlockPos fu, final CompoundTag le) {
        final BlockState cfj5 = this.getBlockState(fu);
        BlockEntity cdl4;
        if ("DUMMY".equals(le.getString("id"))) {
            final Block bvr6 = cfj5.getBlock();
            if (bvr6 instanceof EntityBlock) {
                cdl4 = ((EntityBlock)bvr6).newBlockEntity(this.level);
            }
            else {
                cdl4 = null;
                LevelChunk.LOGGER.warn("Tried to load a DUMMY block entity @ {} but found not block entity block {} at location", fu, cfj5);
            }
        }
        else {
            cdl4 = BlockEntity.loadStatic(cfj5, le);
        }
        if (cdl4 != null) {
            cdl4.setLevelAndPosition(this.level, fu);
            this.addBlockEntity(cdl4);
        }
        else {
            LevelChunk.LOGGER.warn("Tried to load a block entity for block {} but failed at location {}", cfj5, fu);
        }
        return cdl4;
    }
    
    @Override
    public UpgradeData getUpgradeData() {
        return this.upgradeData;
    }
    
    @Override
    public ShortList[] getPostProcessing() {
        return this.postProcessing;
    }
    
    public void unpackTicks() {
        if (this.blockTicks instanceof ProtoTickList) {
            ((ProtoTickList)this.blockTicks).copyOut(this.level.getBlockTicks(), fu -> this.getBlockState(fu).getBlock());
            this.blockTicks = EmptyTickList.empty();
        }
        else if (this.blockTicks instanceof ChunkTickList) {
            ((ChunkTickList)this.blockTicks).copyOut(this.level.getBlockTicks());
            this.blockTicks = EmptyTickList.empty();
        }
        if (this.liquidTicks instanceof ProtoTickList) {
            ((ProtoTickList)this.liquidTicks).copyOut(this.level.getLiquidTicks(), fu -> this.getFluidState(fu).getType());
            this.liquidTicks = EmptyTickList.empty();
        }
        else if (this.liquidTicks instanceof ChunkTickList) {
            ((ChunkTickList)this.liquidTicks).copyOut(this.level.getLiquidTicks());
            this.liquidTicks = EmptyTickList.empty();
        }
    }
    
    public void packTicks(final ServerLevel zd) {
        if (this.blockTicks == EmptyTickList.empty()) {
            this.blockTicks = new ChunkTickList<Block>(Registry.BLOCK::getKey, zd.getBlockTicks().fetchTicksInChunk(this.chunkPos, true, false), zd.getGameTime());
            this.setUnsaved(true);
        }
        if (this.liquidTicks == EmptyTickList.empty()) {
            this.liquidTicks = new ChunkTickList<Fluid>(Registry.FLUID::getKey, zd.getLiquidTicks().fetchTicksInChunk(this.chunkPos, true, false), zd.getGameTime());
            this.setUnsaved(true);
        }
    }
    
    @Override
    public ChunkStatus getStatus() {
        return ChunkStatus.FULL;
    }
    
    public ChunkHolder.FullChunkStatus getFullStatus() {
        if (this.fullStatus == null) {
            return ChunkHolder.FullChunkStatus.BORDER;
        }
        return this.fullStatus.get();
    }
    
    public void setFullStatus(final Supplier<ChunkHolder.FullChunkStatus> supplier) {
        this.fullStatus = supplier;
    }
    
    @Override
    public boolean isLightCorrect() {
        return this.isLightCorrect;
    }
    
    @Override
    public void setLightCorrect(final boolean boolean1) {
        this.isLightCorrect = boolean1;
        this.setUnsaved(true);
    }
    
    static {
        LOGGER = LogManager.getLogger();
        EMPTY_SECTION = null;
    }
    
    public enum EntityCreationType {
        IMMEDIATE, 
        QUEUED, 
        CHECK;
    }
}
