--- a/net/minecraft/world/level/chunk/storage/ChunkStorage.java
+++ b/net/minecraft/world/level/chunk/storage/ChunkStorage.java
@@ -38,17 +_,30 @@
         return this.worker.isOldChunkAround(pos, radius);
     }
 
+    // CraftBukkit start
     public CompoundTag upgradeChunkTag(
-        ResourceKey<Level> levelKey,
+        ResourceKey<net.minecraft.world.level.dimension.LevelStem> levelKey,
         Supplier<DimensionDataStorage> storage,
         CompoundTag chunkData,
-        Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> chunkGeneratorKey
+        Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> chunkGeneratorKey,
+        ChunkPos pos,
+        @Nullable net.minecraft.world.level.LevelAccessor levelAccessor
+        // CraftBukkit end
     ) {
         int version = getVersion(chunkData);
         if (version == SharedConstants.getCurrentVersion().dataVersion().version()) {
             return chunkData;
         } else {
             try {
+                // CraftBukkit start
+                if (version < 1466) {
+                    CompoundTag level = chunkData.getCompoundOrEmpty("Level");
+                    if (level.getBooleanOr("TerrainPopulated", false) && !level.getBooleanOr("LightPopulated", false)) {
+                        // Light is purged updating to 1.14+. We need to set light populated to true so the converter recognizes the chunk as being "full"
+                        level.putBoolean("LightPopulated", true);
+                    }
+                }
+                // CraftBukkit end
                 if (version < 1493) {
                     chunkData = DataFixTypes.CHUNK.update(this.fixerUpper, chunkData, version, 1493);
                     if (chunkData.getCompound("Level").flatMap(compoundTag -> compoundTag.getBoolean("hasLegacyStructureData")).orElse(false)) {
@@ -57,8 +_,22 @@
                     }
                 }
 
+                // Spigot start - SPIGOT-6806: Quick and dirty way to prevent below zero generation in old chunks, by setting the status to heightmap instead of empty
+                boolean stopBelowZero = false;
+                boolean belowZeroGenerationInExistingChunks = (levelAccessor != null) ? ((net.minecraft.server.level.ServerLevel) levelAccessor).spigotConfig.belowZeroGenerationInExistingChunks : org.spigotmc.SpigotConfig.belowZeroGenerationInExistingChunks;
+
+                if (version <= 2730 && !belowZeroGenerationInExistingChunks) {
+                    stopBelowZero = "full".equals(chunkData.getCompound("Level").flatMap(l -> l.getString("Status")).orElse(null));
+                }
+                // Spigot end
+
                 injectDatafixingContext(chunkData, levelKey, chunkGeneratorKey);
                 chunkData = DataFixTypes.CHUNK.updateToCurrentVersion(this.fixerUpper, chunkData, Math.max(1493, version));
+                // Spigot start
+                if (stopBelowZero) {
+                    chunkData.putString("Status", net.minecraft.core.registries.BuiltInRegistries.CHUNK_STATUS.getKey(net.minecraft.world.level.chunk.status.ChunkStatus.SPAWN).toString());
+                }
+                // Spigot end
                 removeDatafixingContext(chunkData);
                 NbtUtils.addCurrentDataVersion(chunkData);
                 return chunkData;
@@ -71,7 +_,7 @@
         }
     }
 
-    private LegacyStructureDataHandler getLegacyStructureHandler(ResourceKey<Level> level, Supplier<DimensionDataStorage> storage) {
+    private LegacyStructureDataHandler getLegacyStructureHandler(ResourceKey<net.minecraft.world.level.dimension.LevelStem> level, Supplier<DimensionDataStorage> storage) { // CraftBukkit
         LegacyStructureDataHandler legacyStructureDataHandler = this.legacyStructureHandler;
         if (legacyStructureDataHandler == null) {
             synchronized (this) {
@@ -86,7 +_,7 @@
     }
 
     public static void injectDatafixingContext(
-        CompoundTag chunkData, ResourceKey<Level> levelKey, Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> chunkGeneratorKey
+        CompoundTag chunkData, ResourceKey<net.minecraft.world.level.dimension.LevelStem> levelKey, Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> chunkGeneratorKey // CraftBukkit
     ) {
         CompoundTag compoundTag = new CompoundTag();
         compoundTag.putString("dimension", levelKey.location().toString());
@@ -107,8 +_,19 @@
     }
 
     public CompletableFuture<Void> write(ChunkPos pos, Supplier<CompoundTag> tagSupplier) {
+        // Paper start - guard against possible chunk pos desync
+        final Supplier<CompoundTag> guardedPosCheck = () -> {
+            CompoundTag nbt = tagSupplier.get();
+            if (nbt != null && !pos.equals(SerializableChunkData.getChunkCoordinate(nbt))) {
+                final String world = (ChunkStorage.this instanceof net.minecraft.server.level.ChunkMap) ? ((net.minecraft.server.level.ChunkMap) ChunkStorage.this).level.getWorld().getName() : null;
+                throw new IllegalArgumentException("Chunk coordinate and serialized data do not have matching coordinates, trying to serialize coordinate " + pos
+                    + " but compound says coordinate is " + SerializableChunkData.getChunkCoordinate(nbt) + (world == null ? " for an unknown world" : (" for world: " + world)));
+            }
+            return nbt;
+        };
+        // Paper end - guard against possible chunk pos desync
         this.handleLegacyStructureIndex(pos);
-        return this.worker.store(pos, tagSupplier);
+        return this.worker.store(pos, guardedPosCheck); // Paper - guard against possible chunk pos desync
     }
 
     protected void handleLegacyStructureIndex(ChunkPos chunkPos) {
