--- a/net/minecraft/world/level/chunk/ChunkAccess.java
+++ b/net/minecraft/world/level/chunk/ChunkAccess.java
@@ -12,8 +12,8 @@
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.Map;
-import java.util.Set;
 import java.util.Map.Entry;
+import java.util.Set;
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -58,14 +58,16 @@
 import org.slf4j.Logger;
 
 public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiomeSource, LightChunk, StructureAccess {
+
     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 ShortList[] postProcessing;
     protected volatile boolean unsaved;
     private volatile boolean isLightCorrect;
-    protected final ChunkPos chunkPos;
+    protected final ChunkPos chunkPos; public final long coordinateKey; public final int locX; public final int locZ; // Paper - cache coordinate key
     private long inhabitedTime;
+    /** @deprecated */
     @Nullable
     @Deprecated
     private BiomeGenerationSettings carverBiomeSettings;
@@ -75,54 +77,105 @@
     @Nullable
     protected BlendingData blendingData;
     public final Map<Heightmap.Types, Heightmap> heightmaps = Maps.newEnumMap(Heightmap.Types.class);
-    protected ChunkSkyLightSources skyLightSources;
+    // Paper - starlight - remove skyLightSources
     private final Map<Structure, StructureStart> structureStarts = Maps.newHashMap();
     private final Map<Structure, LongSet> structuresRefences = Maps.newHashMap();
     protected final Map<BlockPos, CompoundTag> pendingBlockEntities = Maps.newHashMap();
-    public final Map<BlockPos, BlockEntity> blockEntities = new Object2ObjectOpenHashMap<>();
+    public final Map<BlockPos, BlockEntity> blockEntities = new Object2ObjectOpenHashMap();
     protected final LevelHeightAccessor levelHeightAccessor;
     protected final LevelChunkSection[] sections;
 
-    public ChunkAccess(
-        ChunkPos pos,
-        UpgradeData upgradeData,
-        LevelHeightAccessor heightLimitView,
-        Registry<Biome> biomeRegistry,
-        long inhabitedTime,
-        @Nullable LevelChunkSection[] sectionArray,
-        @Nullable BlendingData blendingData
-    ) {
-        this.chunkPos = pos;
+    // CraftBukkit start - SPIGOT-6814: move to IChunkAccess to account for 1.17 to 1.18 chunk upgrading.
+    private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
+    public org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer(ChunkAccess.DATA_TYPE_REGISTRY);
+    // CraftBukkit end
+    // Paper start - rewrite light engine
+    private volatile ca.spottedleaf.starlight.common.light.SWMRNibbleArray[] blockNibbles;
+
+    private volatile ca.spottedleaf.starlight.common.light.SWMRNibbleArray[] skyNibbles;
+
+    private volatile boolean[] skyEmptinessMap;
+
+    private volatile boolean[] blockEmptinessMap;
+
+    public ca.spottedleaf.starlight.common.light.SWMRNibbleArray[] getBlockNibbles() {
+        return this.blockNibbles;
+    }
+
+    public void setBlockNibbles(final ca.spottedleaf.starlight.common.light.SWMRNibbleArray[] nibbles) {
+        this.blockNibbles = nibbles;
+    }
+
+    public ca.spottedleaf.starlight.common.light.SWMRNibbleArray[] getSkyNibbles() {
+        return this.skyNibbles;
+    }
+
+    public void setSkyNibbles(final ca.spottedleaf.starlight.common.light.SWMRNibbleArray[] nibbles) {
+        this.skyNibbles = nibbles;
+    }
+
+    public boolean[] getSkyEmptinessMap() {
+        return this.skyEmptinessMap;
+    }
+
+    public void setSkyEmptinessMap(final boolean[] emptinessMap) {
+        this.skyEmptinessMap = emptinessMap;
+    }
+
+    public boolean[] getBlockEmptinessMap() {
+        return this.blockEmptinessMap;
+    }
+
+    public void setBlockEmptinessMap(final boolean[] emptinessMap) {
+        this.blockEmptinessMap = emptinessMap;
+    }
+    // Paper end - rewrite light engine
+
+    public ChunkAccess(ChunkPos pos, UpgradeData upgradeData, LevelHeightAccessor heightLimitView, Registry<Biome> biomeRegistry, long inhabitedTime, @Nullable LevelChunkSection[] sectionArray, @Nullable BlendingData blendingData) {
+        // Paper start - rewrite light engine
+        if (!(this instanceof ImposterProtoChunk)) {
+            this.setBlockNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(heightLimitView));
+            this.setSkyNibbles(ca.spottedleaf.starlight.common.light.StarLightEngine.getFilledEmptyLight(heightLimitView));
+        }
+        // Paper end - rewrite light engine
+        this.locX = pos.x; this.locZ = pos.z; // Paper - reduce need for field lookups
+        this.chunkPos = pos; this.coordinateKey = ChunkPos.asLong(locX, locZ); // Paper - cache long key
         this.upgradeData = upgradeData;
         this.levelHeightAccessor = heightLimitView;
         this.sections = new LevelChunkSection[heightLimitView.getSectionsCount()];
         this.inhabitedTime = inhabitedTime;
         this.postProcessing = new ShortList[heightLimitView.getSectionsCount()];
         this.blendingData = blendingData;
-        this.skyLightSources = new ChunkSkyLightSources(heightLimitView);
+        // Paper - starlight - remove skyLightSources
         if (sectionArray != null) {
             if (this.sections.length == sectionArray.length) {
                 System.arraycopy(sectionArray, 0, this.sections, 0, this.sections.length);
             } else {
-                LOGGER.warn("Could not set level chunk sections, array length is {} instead of {}", sectionArray.length, this.sections.length);
+                ChunkAccess.LOGGER.warn("Could not set level chunk sections, array length is {} instead of {}", sectionArray.length, this.sections.length);
             }
         }
 
-        replaceMissingSections(biomeRegistry, this.sections);
+        this.replaceMissingSections(biomeRegistry, this.sections); // Paper - Anti-Xray - make it a non-static method
+        // CraftBukkit start
+        this.biomeRegistry = biomeRegistry;
     }
+    public final Registry<Biome> biomeRegistry;
+    // CraftBukkit end
 
-    private static void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sectionArray) {
-        for (int i = 0; i < sectionArray.length; i++) {
+    private void replaceMissingSections(Registry<Biome> biomeRegistry, LevelChunkSection[] sectionArray) { // Paper - Anti-Xray - static -> non-static
+        for (int i = 0; i < sectionArray.length; ++i) {
             if (sectionArray[i] == null) {
-                sectionArray[i] = new LevelChunkSection(biomeRegistry);
+                sectionArray[i] = new LevelChunkSection(biomeRegistry, this.levelHeightAccessor instanceof net.minecraft.world.level.Level ? (net.minecraft.world.level.Level) this.levelHeightAccessor : null, this.chunkPos, this.levelHeightAccessor.getSectionYFromSectionIndex(i)); // Paper start - Anti-Xray - Add parameters
             }
         }
+
     }
 
     public GameEventListenerRegistry getListenerRegistry(int ySectionCoord) {
         return GameEventListenerRegistry.NOOP;
     }
 
+    public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper
     @Nullable
     public abstract BlockState setBlockState(BlockPos pos, BlockState state, boolean moved);
 
@@ -131,11 +184,12 @@
     public abstract void addEntity(Entity entity);
 
     public int getHighestFilledSectionIndex() {
-        LevelChunkSection[] levelChunkSections = this.getSections();
+        LevelChunkSection[] achunksection = this.getSections();
 
-        for (int i = levelChunkSections.length - 1; i >= 0; i--) {
-            LevelChunkSection levelChunkSection = levelChunkSections[i];
-            if (!levelChunkSection.hasOnlyAir()) {
+        for (int i = achunksection.length - 1; i >= 0; --i) {
+            LevelChunkSection chunksection = achunksection[i];
+
+            if (!chunksection.hasOnlyAir()) {
                 return i;
             }
         }
@@ -143,16 +197,17 @@
         return -1;
     }
 
-    @Deprecated(
-        forRemoval = true
-    )
+    /** @deprecated */
+    @Deprecated(forRemoval = true)
     public int getHighestSectionPosition() {
         int i = this.getHighestFilledSectionIndex();
+
         return i == -1 ? this.getMinBuildHeight() : SectionPos.sectionToBlockCoord(this.getSectionYFromSectionIndex(i));
     }
 
     public Set<BlockPos> getBlockEntitiesPos() {
         Set<BlockPos> set = Sets.newHashSet(this.pendingBlockEntities.keySet());
+
         set.addAll(this.blockEntities.keySet());
         return set;
     }
@@ -174,7 +229,9 @@
     }
 
     public Heightmap getOrCreateHeightmapUnprimed(Heightmap.Types type) {
-        return this.heightmaps.computeIfAbsent(type, type2 -> new Heightmap(this, type2));
+        return (Heightmap) this.heightmaps.computeIfAbsent(type, (heightmap_type1) -> {
+            return new Heightmap(this, heightmap_type1);
+        });
     }
 
     public boolean hasPrimedHeightmap(Heightmap.Types type) {
@@ -182,14 +239,15 @@
     }
 
     public int getHeight(Heightmap.Types type, int x, int z) {
-        Heightmap heightmap = this.heightmaps.get(type);
+        Heightmap heightmap = (Heightmap) this.heightmaps.get(type);
+
         if (heightmap == null) {
             if (SharedConstants.IS_RUNNING_IN_IDE && this instanceof LevelChunk) {
-                LOGGER.error("Unprimed heightmap: " + type + " " + x + " " + z);
+                ChunkAccess.LOGGER.error("Unprimed heightmap: " + String.valueOf(type) + " " + x + " " + z);
             }
 
             Heightmap.primeHeightmaps(this, EnumSet.of(type));
-            heightmap = this.heightmaps.get(type);
+            heightmap = (Heightmap) this.heightmaps.get(type);
         }
 
         return heightmap.getFirstAvailable(x & 15, z & 15) - 1;
@@ -202,7 +260,7 @@
     @Nullable
     @Override
     public StructureStart getStartForStructure(Structure structure) {
-        return this.structureStarts.get(structure);
+        return (StructureStart) this.structureStarts.get(structure);
     }
 
     @Override
@@ -223,12 +281,14 @@
 
     @Override
     public LongSet getReferencesForStructure(Structure structure) {
-        return this.structuresRefences.getOrDefault(structure, EMPTY_REFERENCE_SET);
+        return (LongSet) this.structuresRefences.getOrDefault(structure, ChunkAccess.EMPTY_REFERENCE_SET);
     }
 
     @Override
     public void addReferenceForStructure(Structure structure, long reference) {
-        this.structuresRefences.computeIfAbsent(structure, type2 -> new LongOpenHashSet()).add(reference);
+        ((LongSet) this.structuresRefences.computeIfAbsent(structure, (structure1) -> {
+            return new LongOpenHashSet();
+        })).add(reference);
         this.unsaved = true;
     }
 
@@ -253,8 +313,8 @@
             upperHeight = this.getMaxBuildHeight() - 1;
         }
 
-        for (int i = lowerHeight; i <= upperHeight; i += 16) {
-            if (!this.getSection(this.getSectionIndex(i)).hasOnlyAir()) {
+        for (int k = lowerHeight; k <= upperHeight; k += 16) {
+            if (!this.getSection(this.getSectionIndex(k)).hasOnlyAir()) {
                 return false;
             }
         }
@@ -264,29 +324,32 @@
 
     public void setUnsaved(boolean needsSaving) {
         this.unsaved = needsSaving;
+        if (!needsSaving) this.persistentDataContainer.dirty(false); // CraftBukkit - SPIGOT-6814: chunk was saved, pdc is no longer dirty
     }
 
     public boolean isUnsaved() {
-        return this.unsaved;
+        return this.unsaved || this.persistentDataContainer.dirty(); // CraftBukkit - SPIGOT-6814: chunk is unsaved if pdc was mutated
     }
 
     public abstract ChunkStatus getStatus();
 
     public ChunkStatus getHighestGeneratedStatus() {
-        ChunkStatus chunkStatus = this.getStatus();
-        BelowZeroRetrogen belowZeroRetrogen = this.getBelowZeroRetrogen();
-        if (belowZeroRetrogen != null) {
-            ChunkStatus chunkStatus2 = belowZeroRetrogen.targetStatus();
-            return chunkStatus2.isOrAfter(chunkStatus) ? chunkStatus2 : chunkStatus;
+        ChunkStatus chunkstatus = this.getStatus();
+        BelowZeroRetrogen belowzeroretrogen = this.getBelowZeroRetrogen();
+
+        if (belowzeroretrogen != null) {
+            ChunkStatus chunkstatus1 = belowzeroretrogen.targetStatus();
+
+            return chunkstatus1.isOrAfter(chunkstatus) ? chunkstatus1 : chunkstatus;
         } else {
-            return chunkStatus;
+            return chunkstatus;
         }
     }
 
     public abstract void removeBlockEntity(BlockPos pos);
 
     public void markPosForPostprocessing(BlockPos pos) {
-        LOGGER.warn("Trying to mark a block for PostProcessing @ {}, but this operation is not supported.", pos);
+        ChunkAccess.LOGGER.warn("Trying to mark a block for PostProcessing @ {}, but this operation is not supported.", pos);
     }
 
     public ShortList[] getPostProcessing() {
@@ -294,7 +357,7 @@
     }
 
     public void addPackedPostProcess(short packedPos, int index) {
-        getOrCreateOffsetList(this.getPostProcessing(), index).add(packedPos);
+        ChunkAccess.getOrCreateOffsetList(this.getPostProcessing(), index).add(packedPos);
     }
 
     public void setBlockEntityNbt(CompoundTag nbt) {
@@ -303,7 +366,7 @@
 
     @Nullable
     public CompoundTag getBlockEntityNbt(BlockPos pos) {
-        return this.pendingBlockEntities.get(pos);
+        return (CompoundTag) this.pendingBlockEntities.get(pos);
     }
 
     @Nullable
@@ -311,29 +374,34 @@
 
     @Override
     public final void findBlockLightSources(BiConsumer<BlockPos, BlockState> callback) {
-        this.findBlocks(blockState -> blockState.getLightEmission() != 0, callback);
+        this.findBlocks((iblockdata) -> {
+            return iblockdata.getLightEmission() != 0;
+        }, callback);
     }
 
     public void findBlocks(Predicate<BlockState> predicate, BiConsumer<BlockPos, BlockState> consumer) {
-        BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
+        BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
 
-        for (int i = this.getMinSection(); i < this.getMaxSection(); i++) {
-            LevelChunkSection levelChunkSection = this.getSection(this.getSectionIndexFromSectionY(i));
-            if (levelChunkSection.maybeHas(predicate)) {
-                BlockPos blockPos = SectionPos.of(this.chunkPos, i).origin();
+        for (int i = this.getMinSection(); i < this.getMaxSection(); ++i) {
+            LevelChunkSection chunksection = this.getSection(this.getSectionIndexFromSectionY(i));
 
-                for (int j = 0; j < 16; j++) {
-                    for (int k = 0; k < 16; k++) {
-                        for (int l = 0; l < 16; l++) {
-                            BlockState blockState = levelChunkSection.getBlockState(l, j, k);
-                            if (predicate.test(blockState)) {
-                                consumer.accept(mutableBlockPos.setWithOffset(blockPos, l, j, k), blockState);
+            if (chunksection.maybeHas(predicate)) {
+                BlockPos blockposition = SectionPos.of(this.chunkPos, i).origin();
+
+                for (int j = 0; j < 16; ++j) {
+                    for (int k = 0; k < 16; ++k) {
+                        for (int l = 0; l < 16; ++l) {
+                            BlockState iblockdata = chunksection.getBlockState(l, j, k);
+
+                            if (predicate.test(iblockdata)) {
+                                consumer.accept(blockposition_mutableblockposition.setWithOffset(blockposition, l, j, k), iblockdata);
                             }
                         }
                     }
                 }
             }
         }
+
     }
 
     public abstract TickContainerAccess<Block> getBlockTicks();
@@ -400,16 +468,17 @@
 
     public NoiseChunk getOrCreateNoiseChunk(Function<ChunkAccess, NoiseChunk> chunkNoiseSamplerCreator) {
         if (this.noiseChunk == null) {
-            this.noiseChunk = chunkNoiseSamplerCreator.apply(this);
+            this.noiseChunk = (NoiseChunk) chunkNoiseSamplerCreator.apply(this);
         }
 
         return this.noiseChunk;
     }
 
+    /** @deprecated */
     @Deprecated
     public BiomeGenerationSettings carverBiome(Supplier<BiomeGenerationSettings> generationSettingsCreator) {
         if (this.carverBiomeSettings == null) {
-            this.carverBiomeSettings = generationSettingsCreator.get();
+            this.carverBiomeSettings = (BiomeGenerationSettings) generationSettingsCreator.get();
         }
 
         return this.carverBiomeSettings;
@@ -418,30 +487,57 @@
     @Override
     public Holder<Biome> getNoiseBiome(int biomeX, int biomeY, int biomeZ) {
         try {
-            int i = QuartPos.fromBlock(this.getMinBuildHeight());
-            int j = i + QuartPos.fromBlock(this.getHeight()) - 1;
-            int k = Mth.clamp(biomeY, i, j);
-            int l = this.getSectionIndex(QuartPos.toBlock(k));
-            return this.sections[l].getNoiseBiome(biomeX & 3, k & 3, biomeZ & 3);
-        } catch (Throwable var8) {
-            CrashReport crashReport = CrashReport.forThrowable(var8, "Getting biome");
-            CrashReportCategory crashReportCategory = crashReport.addCategory("Biome being got");
-            crashReportCategory.setDetail("Location", () -> CrashReportCategory.formatLocation(this, biomeX, biomeY, biomeZ));
-            throw new ReportedException(crashReport);
+            int l = QuartPos.fromBlock(this.getMinBuildHeight());
+            int i1 = l + QuartPos.fromBlock(this.getHeight()) - 1;
+            int j1 = Mth.clamp(biomeY, l, i1);
+            int k1 = this.getSectionIndex(QuartPos.toBlock(j1));
+
+            return this.sections[k1].getNoiseBiome(biomeX & 3, j1 & 3, biomeZ & 3);
+        } catch (Throwable throwable) {
+            CrashReport crashreport = CrashReport.forThrowable(throwable, "Getting biome");
+            CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Biome being got");
+
+            crashreportsystemdetails.setDetail("Location", () -> {
+                return CrashReportCategory.formatLocation(this, biomeX, biomeY, biomeZ);
+            });
+            throw new ReportedException(crashreport);
         }
     }
 
+    // CraftBukkit start
+    public void setBiome(int i, int j, int k, Holder<Biome> biome) {
+        try {
+            int l = QuartPos.fromBlock(this.getMinBuildHeight());
+            int i1 = l + QuartPos.fromBlock(this.getHeight()) - 1;
+            int j1 = Mth.clamp(j, l, i1);
+            int k1 = this.getSectionIndex(QuartPos.toBlock(j1));
+
+            this.sections[k1].setBiome(i & 3, j1 & 3, k & 3, biome);
+        } catch (Throwable throwable) {
+            CrashReport crashreport = CrashReport.forThrowable(throwable, "Setting biome");
+            CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Biome being set");
+
+            crashreportsystemdetails.setDetail("Location", () -> {
+                return CrashReportCategory.formatLocation(this, i, j, k);
+            });
+            throw new ReportedException(crashreport);
+        }
+    }
+    // CraftBukkit end
+
     public void fillBiomesFromNoise(BiomeResolver biomeSupplier, Climate.Sampler sampler) {
-        ChunkPos chunkPos = this.getPos();
-        int i = QuartPos.fromBlock(chunkPos.getMinBlockX());
-        int j = QuartPos.fromBlock(chunkPos.getMinBlockZ());
-        LevelHeightAccessor levelHeightAccessor = this.getHeightAccessorForGeneration();
+        ChunkPos chunkcoordintpair = this.getPos();
+        int i = QuartPos.fromBlock(chunkcoordintpair.getMinBlockX());
+        int j = QuartPos.fromBlock(chunkcoordintpair.getMinBlockZ());
+        LevelHeightAccessor levelheightaccessor = this.getHeightAccessorForGeneration();
 
-        for (int k = levelHeightAccessor.getMinSection(); k < levelHeightAccessor.getMaxSection(); k++) {
-            LevelChunkSection levelChunkSection = this.getSection(this.getSectionIndexFromSectionY(k));
+        for (int k = levelheightaccessor.getMinSection(); k < levelheightaccessor.getMaxSection(); ++k) {
+            LevelChunkSection chunksection = this.getSection(this.getSectionIndexFromSectionY(k));
             int l = QuartPos.fromSection(k);
-            levelChunkSection.fillBiomesFromNoise(biomeSupplier, sampler, i, l, j);
+
+            chunksection.fillBiomesFromNoise(biomeSupplier, sampler, i, l, j);
         }
+
     }
 
     public boolean hasAnyStructureReferences() {
@@ -462,14 +558,15 @@
     }
 
     public void initializeLightSources() {
-        this.skyLightSources.fillFrom(this);
+        // Paper - starlight - remove skyLightSources
     }
 
     @Override
     public ChunkSkyLightSources getSkyLightSources() {
-        return this.skyLightSources;
+        return null; // Paper - starlight - remove skyLightSources
     }
 
     public static record TicksToSave(SerializableTickContainer<Block> blocks, SerializableTickContainer<Fluid> fluids) {
+
     }
 }
