--- a/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
+++ b/net/minecraft/world/level/chunk/storage/SerializableChunkData.java
@@ -91,9 +_,10 @@
     List<CompoundTag> entities,
     List<CompoundTag> blockEntities,
     CompoundTag structureData
+    , @Nullable net.minecraft.nbt.Tag persistentDataContainer // CraftBukkit - persistentDataContainer
 ) {
-    private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(
-        Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState()
+    public static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(
+        Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), null // Paper - Anti-Xray
     );
     private static final Codec<List<SavedTick<Block>>> BLOCK_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.BLOCK.byNameCodec()).listOf();
     private static final Codec<List<SavedTick<Fluid>>> FLUID_TICKS_CODEC = SavedTick.codec(BuiltInRegistries.FLUID.byNameCodec()).listOf();
@@ -109,19 +_,58 @@
     public static final String BLOCK_LIGHT_TAG = "BlockLight";
     public static final String SKY_LIGHT_TAG = "SkyLight";
 
+    // Paper start - guard against serializing mismatching coordinates
+    // TODO Note: This needs to be re-checked each update
+    public static ChunkPos getChunkCoordinate(final CompoundTag chunkData) {
+        final int dataVersion = ChunkStorage.getVersion(chunkData);
+        if (dataVersion < 2842) { // Level tag is removed after this version
+            final CompoundTag levelData = chunkData.getCompoundOrEmpty("Level");
+            return new ChunkPos(levelData.getIntOr("xPos", 0), levelData.getIntOr("zPos", 0));
+        } else {
+            return new ChunkPos(chunkData.getIntOr("xPos", 0), chunkData.getIntOr("zPos", 0));
+        }
+    }
+    // Paper end - guard against serializing mismatching coordinates
+    // Paper start - Attempt to recalculate regionfile header if it is corrupt
+    // TODO: Check on update
+    public static long getLastWorldSaveTime(final CompoundTag chunkData) {
+        final int dataVersion = ChunkStorage.getVersion(chunkData);
+        if (dataVersion < 2842) { // Level tag is removed after this version
+            final CompoundTag levelData = chunkData.getCompoundOrEmpty("Level");
+            return levelData.getLongOr("LastUpdate", 0L);
+        } else {
+            return chunkData.getLongOr("LastUpdate", 0L);
+        }
+    }
+    // Paper end - Attempt to recalculate regionfile header if it is corrupt
+
+    // Paper start - Do not let the server load chunks from newer versions
+    private static final int CURRENT_DATA_VERSION = net.minecraft.SharedConstants.getCurrentVersion().getDataVersion().getVersion();
+    private static final boolean JUST_CORRUPT_IT = Boolean.getBoolean("Paper.ignoreWorldDataVersion");
+    // Paper end - Do not let the server load chunks from newer versions
+
     @Nullable
     public static SerializableChunkData parse(LevelHeightAccessor levelHeightAccessor, RegistryAccess registries, CompoundTag tag) {
+        net.minecraft.server.level.ServerLevel serverLevel = (net.minecraft.server.level.ServerLevel) levelHeightAccessor; // Paper - Anti-Xray This is is seemingly only called from ChunkMap, where, we have a server level. We'll fight this later if needed.
         if (tag.getString("Status").isEmpty()) {
             return null;
         } else {
-            ChunkPos chunkPos = new ChunkPos(tag.getIntOr("xPos", 0), tag.getIntOr("zPos", 0));
+            // Paper start - Do not let the server load chunks from newer versions
+            tag.getInt("DataVersion").ifPresent(dataVersion -> {
+                if (!JUST_CORRUPT_IT && dataVersion > CURRENT_DATA_VERSION) {
+                    new RuntimeException("Server attempted to load chunk saved with newer version of minecraft! " + dataVersion + " > " + CURRENT_DATA_VERSION).printStackTrace();
+                    System.exit(1);
+                }
+            });
+            // Paper end - Do not let the server load chunks from newer versions
+            ChunkPos chunkPos = new ChunkPos(tag.getIntOr("xPos", 0), tag.getIntOr("zPos", 0)); // Paper - guard against serializing mismatching coordinates; diff on change, see ChunkSerializer#getChunkCoordinate
             long longOr = tag.getLongOr("LastUpdate", 0L);
             long longOr1 = tag.getLongOr("InhabitedTime", 0L);
             ChunkStatus chunkStatus = tag.read("Status", ChunkStatus.CODEC).orElse(ChunkStatus.EMPTY);
             UpgradeData upgradeData = tag.getCompound("UpgradeData")
                 .map(compoundTag1 -> new UpgradeData(compoundTag1, levelHeightAccessor))
                 .orElse(UpgradeData.EMPTY);
-            boolean booleanOr = tag.getBooleanOr("isLightOn", false);
+            boolean booleanOr = chunkStatus.isOrAfter(ChunkStatus.LIGHT) && (tag.get("isLightOn") != null && tag.getIntOr(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.STARLIGHT_VERSION_TAG, -1) == ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.STARLIGHT_LIGHT_VERSION); // Paper - starlight
             BlendingData.Packed packed = tag.read("blending_data", BlendingData.Packed.CODEC).orElse(null);
             BelowZeroRetrogen belowZeroRetrogen = tag.read("below_zero_retrogen", BelowZeroRetrogen.CODEC).orElse(null);
             long[] longs = tag.getLongArray("carving_mask").orElse(null);
@@ -154,27 +_,29 @@
             ListTag listOrEmpty2 = tag.getListOrEmpty("sections");
             List<SerializableChunkData.SectionData> list5 = new ArrayList<>(listOrEmpty2.size());
             Registry<Biome> registry = registries.lookupOrThrow(Registries.BIOME);
-            Codec<PalettedContainerRO<Holder<Biome>>> codec = makeBiomeCodec(registry);
+            Codec<PalettedContainer<Holder<Biome>>> codec = makeBiomeCodecRW(registry); // CraftBukkit - read/write
 
             for (int i2 = 0; i2 < listOrEmpty2.size(); i2++) {
                 Optional<CompoundTag> compound = listOrEmpty2.getCompound(i2);
                 if (!compound.isEmpty()) {
-                    CompoundTag compoundTag = compound.get();
+                    CompoundTag compoundTag = compound.get(); final CompoundTag sectionData = compoundTag; // Paper - OBFHELPER
                     int byteOr = compoundTag.getByteOr("Y", (byte)0);
                     LevelChunkSection levelChunkSection;
                     if (byteOr >= levelHeightAccessor.getMinSectionY() && byteOr <= levelHeightAccessor.getMaxSectionY()) {
+                        final BlockState[] presetBlockStates = serverLevel.chunkPacketBlockController.getPresetBlockStates(serverLevel, chunkPos, byteOr); // Paper - Anti-Xray - Add preset block states
+                        final Codec<PalettedContainer<BlockState>> blockStateCodec = presetBlockStates == null ? BLOCK_STATE_CODEC : PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState(), presetBlockStates); // Paper - Anti-Xray
                         PalettedContainer<BlockState> palettedContainer = compoundTag.getCompound("block_states")
                             .map(
-                                compoundTag1 -> BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, compoundTag1)
+                                compoundTag1 -> blockStateCodec.parse(NbtOps.INSTANCE, compoundTag1) // Paper - Anti-Xray
                                     .promotePartial(string -> logErrors(chunkPos, byteOr, string))
                                     .getOrThrow(SerializableChunkData.ChunkReadException::new)
                             )
                             .orElseGet(
                                 () -> new PalettedContainer<>(
-                                    Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES
+                                    Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES, presetBlockStates // Paper - Anti-Xray - Add preset biomes
                                 )
                             );
-                        PalettedContainerRO<Holder<Biome>> palettedContainerRo = compoundTag.getCompound("biomes")
+                        PalettedContainer<Holder<Biome>> palettedContainerRo = compoundTag.getCompound("biomes") // CraftBukkit - read/write
                             .map(
                                 compoundTag1 -> codec.parse(NbtOps.INSTANCE, compoundTag1)
                                     .promotePartial(string -> logErrors(chunkPos, byteOr, string))
@@ -182,7 +_,7 @@
                             )
                             .orElseGet(
                                 () -> new PalettedContainer<>(
-                                    registry.asHolderIdMap(), registry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES
+                                    registry.asHolderIdMap(), registry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES, null // Paper - Anti-Xray - Add preset biomes
                                 )
                             );
                         levelChunkSection = new LevelChunkSection(palettedContainer, palettedContainerRo);
@@ -192,7 +_,17 @@
 
                     DataLayer dataLayer = compoundTag.getByteArray("BlockLight").map(DataLayer::new).orElse(null);
                     DataLayer dataLayer1 = compoundTag.getByteArray("SkyLight").map(DataLayer::new).orElse(null);
-                    list5.add(new SerializableChunkData.SectionData(byteOr, levelChunkSection, dataLayer, dataLayer1));
+                    // Paper start - starlight
+                    SerializableChunkData.SectionData serializableChunkData = new SerializableChunkData.SectionData(byteOr, levelChunkSection, dataLayer, dataLayer1);
+                    if (sectionData.contains(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.BLOCKLIGHT_STATE_TAG)) {
+                        ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)serializableChunkData).starlight$setBlockLightState(sectionData.getIntOr(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.BLOCKLIGHT_STATE_TAG, 0));
+                    }
+
+                    if (sectionData.contains(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.SKYLIGHT_STATE_TAG)) {
+                        ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)serializableChunkData).starlight$setSkyLightState(sectionData.getIntOr(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.SKYLIGHT_STATE_TAG, 0));
+                    }
+                    list5.add(serializableChunkData);
+                    // Paper end - starlight
                 }
             }
 
@@ -215,9 +_,63 @@
                 list3,
                 list4,
                 compoundOrEmpty
+                , tag.get("ChunkBukkitValues") // CraftBukkit - ChunkBukkitValues
             );
         }
     }
+
+    // Paper start - starlight
+    private ProtoChunk loadStarlightLightData(final ServerLevel world, final ProtoChunk ret) {
+
+        final boolean hasSkyLight = world.dimensionType().hasSkyLight();
+        final int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinLightSection(world);
+
+        final ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles = ca.spottedleaf.moonrise.patches.starlight.light.StarLightEngine.getFilledEmptyLight(world);
+        final ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] skyNibbles = ca.spottedleaf.moonrise.patches.starlight.light.StarLightEngine.getFilledEmptyLight(world);
+
+        if (!this.lightCorrect) {
+            ((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)ret).starlight$setBlockNibbles(blockNibbles);
+            ((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)ret).starlight$setSkyNibbles(skyNibbles);
+            return ret;
+        }
+
+        try {
+            for (final SerializableChunkData.SectionData sectionData : this.sectionData) {
+                final int y = sectionData.y();
+                final DataLayer blockLight = sectionData.blockLight();
+                final DataLayer skyLight = sectionData.skyLight();
+
+                final int blockState = ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$getBlockLightState();
+                final int skyState = ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$getSkyLightState();
+
+                if (blockState >= 0) {
+                    if (blockLight != null) {
+                        blockNibbles[y - minSection] = new ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray(ca.spottedleaf.moonrise.common.util.MixinWorkarounds.clone(blockLight.getData()), blockState); // clone for data safety
+                    } else {
+                        blockNibbles[y - minSection] = new ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray(null, blockState);
+                    }
+                }
+
+                if (skyState >= 0 && hasSkyLight) {
+                    if (skyLight != null) {
+                        skyNibbles[y - minSection] = new ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray(ca.spottedleaf.moonrise.common.util.MixinWorkarounds.clone(skyLight.getData()), skyState); // clone for data safety
+                    } else {
+                        skyNibbles[y - minSection] = new ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray(null, skyState);
+                    }
+                }
+            }
+
+            ((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)ret).starlight$setBlockNibbles(blockNibbles);
+            ((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)ret).starlight$setSkyNibbles(skyNibbles);
+        } catch (final Throwable thr) {
+            ret.setLightCorrect(false);
+
+            LOGGER.error("Failed to parse light data for chunk " + ret.getPos() + " in world '" + ca.spottedleaf.moonrise.common.util.WorldUtil.getWorldName(world) + "'", thr);
+        }
+
+        return ret;
+    }
+    // Paper end - starlight
 
     public ProtoChunk read(ServerLevel level, PoiManager poiManager, RegionStorageInfo regionStorageInfo, ChunkPos pos) {
         if (!Objects.equals(pos, this.chunkPos)) {
@@ -237,7 +_,7 @@
             SectionPos sectionPos = SectionPos.of(pos, sectionData.y);
             if (sectionData.chunkSection != null) {
                 levelChunkSections[level.getSectionIndexFromSectionY(sectionData.y)] = sectionData.chunkSection;
-                poiManager.checkConsistencyWithBlocks(sectionPos, sectionData.chunkSection);
+                //poiManager.checkConsistencyWithBlocks(sectionPos, sectionData.chunkSection); // Paper - rewrite chunk system
             }
 
             boolean flag1 = sectionData.blockLight != null;
@@ -292,6 +_,12 @@
             }
         }
 
+        // CraftBukkit start - load chunk persistent data from nbt - SPIGOT-6814: Already load PDC here to account for 1.17 to 1.18 chunk upgrading.
+        if (this.persistentDataContainer instanceof CompoundTag compoundTag) {
+            chunkAccess.persistentDataContainer.putAll(compoundTag);
+        }
+        // CraftBukkit end
+
         chunkAccess.setLightCorrect(this.lightCorrect);
         EnumSet<Heightmap.Types> set = EnumSet.noneOf(Heightmap.Types.class);
 
@@ -313,7 +_,7 @@
         }
 
         if (chunkType == ChunkType.LEVELCHUNK) {
-            return new ImposterProtoChunk((LevelChunk)chunkAccess, false);
+            return this.loadStarlightLightData(level, new ImposterProtoChunk((LevelChunk)chunkAccess, false)); // Paper - starlight
         } else {
             ProtoChunk protoChunk1 = (ProtoChunk)chunkAccess;
 
@@ -329,7 +_,7 @@
                 protoChunk1.setCarvingMask(new CarvingMask(this.carvingMask, chunkAccess.getMinY()));
             }
 
-            return protoChunk1;
+            return this.loadStarlightLightData(level, protoChunk1); // Paper - starlight
         }
     }
 
@@ -346,27 +_,59 @@
         );
     }
 
+    // CraftBukkit start - read/write
+    private static Codec<PalettedContainer<Holder<Biome>>> makeBiomeCodecRW(Registry<Biome> biomeRegistry) {
+        return PalettedContainer.codecRW(biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS), null); // Paper - Anti-Xray - Add preset biomes
+    }
+    // CraftBukkit end
+
     public static SerializableChunkData copyOf(ServerLevel level, ChunkAccess chunk) {
         if (!chunk.canBeSerialized()) {
             throw new IllegalArgumentException("Chunk can't be serialized: " + chunk);
         } else {
             ChunkPos pos = chunk.getPos();
-            List<SerializableChunkData.SectionData> list = new ArrayList<>();
+            List<SerializableChunkData.SectionData> list = new ArrayList<>(); final List<SerializableChunkData.SectionData> sectionsList = list; // Paper - starlight - OBFHELPER
             LevelChunkSection[] sections = chunk.getSections();
             LevelLightEngine lightEngine = level.getChunkSource().getLightEngine();
 
-            for (int lightSection = lightEngine.getMinLightSection(); lightSection < lightEngine.getMaxLightSection(); lightSection++) {
-                int sectionIndexFromSectionY = chunk.getSectionIndexFromSectionY(lightSection);
-                boolean flag = sectionIndexFromSectionY >= 0 && sectionIndexFromSectionY < sections.length;
-                DataLayer dataLayerData = lightEngine.getLayerListener(LightLayer.BLOCK).getDataLayerData(SectionPos.of(pos, lightSection));
-                DataLayer dataLayerData1 = lightEngine.getLayerListener(LightLayer.SKY).getDataLayerData(SectionPos.of(pos, lightSection));
-                DataLayer dataLayer = dataLayerData != null && !dataLayerData.isEmpty() ? dataLayerData.copy() : null;
-                DataLayer dataLayer1 = dataLayerData1 != null && !dataLayerData1.isEmpty() ? dataLayerData1.copy() : null;
-                if (flag || dataLayer != null || dataLayer1 != null) {
-                    LevelChunkSection levelChunkSection = flag ? sections[sectionIndexFromSectionY].copy() : null;
-                    list.add(new SerializableChunkData.SectionData(lightSection, levelChunkSection, dataLayer, dataLayer1));
-                }
+            // Paper start - starlight
+            final int minLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinLightSection(level);
+            final int maxLightSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxLightSection(level);
+            final int minBlockSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(level);
+
+            final LevelChunkSection[] chunkSections = chunk.getSections();
+            final ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] blockNibbles = ((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)chunk).starlight$getBlockNibbles();
+            final ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray[] skyNibbles = ((ca.spottedleaf.moonrise.patches.starlight.chunk.StarlightChunk)chunk).starlight$getSkyNibbles();
+
+            for (int lightSection = minLightSection; lightSection <= maxLightSection; ++lightSection) {
+                final int lightSectionIdx = lightSection - minLightSection;
+                final int blockSectionIdx = lightSection - minBlockSection;
+
+                final LevelChunkSection chunkSection = (blockSectionIdx >= 0 && blockSectionIdx < chunkSections.length) ? chunkSections[blockSectionIdx].copy() : null;
+                final ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray.SaveState blockNibble = blockNibbles[lightSectionIdx].getSaveState();
+                final ca.spottedleaf.moonrise.patches.starlight.light.SWMRNibbleArray.SaveState skyNibble = skyNibbles[lightSectionIdx].getSaveState();
+
+                if (chunkSection == null && blockNibble == null && skyNibble == null) {
+                    continue;
+                }
+
+                final SerializableChunkData.SectionData sectionData = new SerializableChunkData.SectionData(
+                    lightSection, chunkSection,
+                    blockNibble == null ? null : (blockNibble.data == null ? null : new DataLayer(blockNibble.data)),
+                    skyNibble == null ? null : (skyNibble.data == null ? null : new DataLayer(skyNibble.data))
+                );
+
+                if (blockNibble != null) {
+                    ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$setBlockLightState(blockNibble.state);
+                }
+
+                if (skyNibble != null) {
+                    ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$setSkyLightState(skyNibble.state);
+                }
+
+                sectionsList.add(sectionData);
             }
+            // Paper end - starlight
 
             List<CompoundTag> list1 = new ArrayList<>(chunk.getBlockEntitiesPos().size());
 
@@ -404,6 +_,12 @@
             CompoundTag compoundTag = packStructureData(
                 StructurePieceSerializationContext.fromLevel(level), pos, chunk.getAllStarts(), chunk.getAllReferences()
             );
+            // CraftBukkit start - store chunk persistent data in nbt
+            CompoundTag persistentDataContainer = null;
+            if (!chunk.persistentDataContainer.isEmpty()) { // SPIGOT-6814: Always save PDC to account for 1.17 to 1.18 chunk upgrading.
+                persistentDataContainer = chunk.persistentDataContainer.toTagCompound();
+            }
+            // CraftBukkit end
             return new SerializableChunkData(
                 level.registryAccess().lookupOrThrow(Registries.BIOME),
                 pos,
@@ -423,6 +_,7 @@
                 list2,
                 list1,
                 compoundTag
+                , persistentDataContainer // CraftBukkit - persistentDataContainer
             );
         }
     }
@@ -432,7 +_,7 @@
         compoundTag.putInt("xPos", this.chunkPos.x);
         compoundTag.putInt("yPos", this.minSectionY);
         compoundTag.putInt("zPos", this.chunkPos.z);
-        compoundTag.putLong("LastUpdate", this.lastUpdateTime);
+        compoundTag.putLong("LastUpdate", this.lastUpdateTime); // Paper - Diff on change
         compoundTag.putLong("InhabitedTime", this.inhabitedTime);
         compoundTag.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(this.chunkStatus).toString());
         compoundTag.storeNullable("blending_data", BlendingData.Packed.CODEC, this.blendingData);
@@ -445,7 +_,7 @@
         Codec<PalettedContainerRO<Holder<Biome>>> codec = makeBiomeCodec(this.biomeRegistry);
 
         for (SerializableChunkData.SectionData sectionData : this.sectionData) {
-            CompoundTag compoundTag1 = new CompoundTag();
+            CompoundTag compoundTag1 = new CompoundTag(); final CompoundTag sectionNBT = compoundTag1; // Paper - starlight - OBFHELPER
             LevelChunkSection levelChunkSection = sectionData.chunkSection;
             if (levelChunkSection != null) {
                 compoundTag1.store("block_states", BLOCK_STATE_CODEC, levelChunkSection.getStates());
@@ -460,6 +_,19 @@
                 compoundTag1.putByteArray("SkyLight", sectionData.skyLight.getData());
             }
 
+            // Paper start - starlight
+            final int blockState = ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$getBlockLightState();
+            final int skyState = ((ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData)(Object)sectionData).starlight$getSkyLightState();
+
+            if (blockState > 0) {
+                sectionNBT.putInt(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.BLOCKLIGHT_STATE_TAG, blockState);
+            }
+
+            if (skyState > 0) {
+                sectionNBT.putInt(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.SKYLIGHT_STATE_TAG, skyState);
+            }
+            // Paper end - starlight
+
             if (!compoundTag1.isEmpty()) {
                 compoundTag1.putByte("Y", (byte)sectionData.y);
                 listTag.add(compoundTag1);
@@ -489,6 +_,19 @@
         this.heightmaps.forEach((types, longs) -> compoundTag2.put(types.getSerializationKey(), new LongArrayTag(longs)));
         compoundTag.put("Heightmaps", compoundTag2);
         compoundTag.put("structures", this.structureData);
+        // CraftBukkit start - store chunk persistent data in nbt
+        if (this.persistentDataContainer != null) { // SPIGOT-6814: Always save PDC to account for 1.17 to 1.18 chunk upgrading.
+            compoundTag.put("ChunkBukkitValues", this.persistentDataContainer);
+        }
+        // CraftBukkit end
+        // Paper start - starlight
+        if (this.lightCorrect && !this.chunkStatus.isBefore(net.minecraft.world.level.chunk.status.ChunkStatus.LIGHT)) {
+            // clobber vanilla value to force vanilla to relight
+            compoundTag.putBoolean("isLightOn", false);
+            // store our light version
+            compoundTag.putInt(ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.STARLIGHT_VERSION_TAG, ca.spottedleaf.moonrise.patches.starlight.util.SaveUtil.STARLIGHT_LIGHT_VERSION);
+        }
+        // Paper end - starlight
         return compoundTag;
     }
 
@@ -562,6 +_,12 @@
             } else {
                 StructureStart structureStart = StructureStart.loadStaticStart(context, compoundOrEmpty.getCompoundOrEmpty(string), seed);
                 if (structureStart != null) {
+                    // CraftBukkit start - load persistent data for structure start
+                    net.minecraft.nbt.Tag persistentBase = compoundOrEmpty.getCompoundOrEmpty(string).get("StructureBukkitValues");
+                    if (persistentBase instanceof CompoundTag compoundTag) {
+                        structureStart.persistentDataContainer.putAll(compoundTag);
+                    }
+                    // CraftBukkit end
                     map.put(structure, structureStart);
                 }
             }
@@ -620,6 +_,66 @@
         }
     }
 
-    public record SectionData(int y, @Nullable LevelChunkSection chunkSection, @Nullable DataLayer blockLight, @Nullable DataLayer skyLight) {
+    // Paper start - starlight - convert from record
+    public static final class SectionData implements ca.spottedleaf.moonrise.patches.starlight.storage.StarlightSectionData { // Paper - starlight - our diff
+        private final int y;
+        @javax.annotation.Nullable
+        private final net.minecraft.world.level.chunk.LevelChunkSection chunkSection;
+        @javax.annotation.Nullable
+        private final net.minecraft.world.level.chunk.DataLayer blockLight;
+        @javax.annotation.Nullable
+        private final net.minecraft.world.level.chunk.DataLayer skyLight;
+
+        // Paper start - starlight - our diff
+        private int blockLightState = -1;
+        private int skyLightState = -1;
+
+        @Override
+        public final int starlight$getBlockLightState() {
+            return this.blockLightState;
+        }
+
+        @Override
+        public final void starlight$setBlockLightState(final int state) {
+            this.blockLightState = state;
+        }
+
+        @Override
+        public final int starlight$getSkyLightState() {
+            return this.skyLightState;
+        }
+
+        @Override
+        public final void starlight$setSkyLightState(final int state) {
+            this.skyLightState = state;
+        }
+        // Paper end - starlight - our diff
+
+        public SectionData(int y, @javax.annotation.Nullable net.minecraft.world.level.chunk.LevelChunkSection chunkSection, @javax.annotation.Nullable net.minecraft.world.level.chunk.DataLayer blockLight, @javax.annotation.Nullable net.minecraft.world.level.chunk.DataLayer skyLight) {
+            this.y = y;
+            this.chunkSection = chunkSection;
+            this.blockLight = blockLight;
+            this.skyLight = skyLight;
+        }
+
+        public int y() {
+            return y;
+        }
+
+        @javax.annotation.Nullable
+        public net.minecraft.world.level.chunk.LevelChunkSection chunkSection() {
+            return chunkSection;
+        }
+
+        @javax.annotation.Nullable
+        public net.minecraft.world.level.chunk.DataLayer blockLight() {
+            return blockLight;
+        }
+
+        @javax.annotation.Nullable
+        public net.minecraft.world.level.chunk.DataLayer skyLight() {
+            return skyLight;
+        }
+        // Paper end - starlight - convert from record
     }
 }
