--- a/net/minecraft/world/level/chunk/storage/ChunkStorage.java
+++ b/net/minecraft/world/level/chunk/storage/ChunkStorage.java
@@ -22,43 +_,82 @@
 import net.minecraft.world.level.levelgen.structure.LegacyStructureDataHandler;
 import net.minecraft.world.level.storage.DimensionDataStorage;
 
-public class ChunkStorage implements AutoCloseable {
+public class ChunkStorage implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemChunkStorage { // Paper - rewrite chunk system
     public static final int LAST_MONOLYTH_STRUCTURE_DATA_VERSION = 1493;
-    private final IOWorker worker;
+    // Paper - rewrite chunk system
     protected final DataFixer fixerUpper;
     @Nullable
     private volatile LegacyStructureDataHandler legacyStructureHandler;
 
+    // Paper start - rewrite chunk system
+    private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger();
+    private final RegionFileStorage storage;
+
+    @Override
+    public final RegionFileStorage moonrise$getRegionStorage() {
+        return this.storage;
+    }
+    // Paper end - rewrite chunk system
+
     public ChunkStorage(RegionStorageInfo info, Path folder, DataFixer fixerUpper, boolean sync) {
         this.fixerUpper = fixerUpper;
-        this.worker = new IOWorker(info, folder, sync);
+        this.storage = new IOWorker(info, folder, sync).storage; // Paper - rewrite chunk system
     }
 
     public boolean isOldChunkAround(ChunkPos pos, int radius) {
-        return this.worker.isOldChunkAround(pos, radius);
+        return true; // Paper - rewrite chunk system
     }
 
+    // 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().getDataVersion().getVersion()) {
             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)) {
                         LegacyStructureDataHandler legacyStructureHandler = this.getLegacyStructureHandler(levelKey, storage);
+                        synchronized (legacyStructureHandler) { // Paper - rewrite chunk system
                         chunkData = legacyStructureHandler.updateFromLegacy(chunkData);
+                        }
                     }
                 }
+
+                // 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());
@@ -103,34 +_,76 @@
     }
 
     public CompletableFuture<Optional<CompoundTag>> read(ChunkPos chunkPos) {
-        return this.worker.loadAsync(chunkPos);
+        // Paper start - rewrite chunk system
+        try {
+            return CompletableFuture.completedFuture(Optional.ofNullable(this.storage.read(chunkPos)));
+        } catch (final Throwable throwable) {
+            return CompletableFuture.failedFuture(throwable);
+        }
+        // Paper end - rewrite chunk system
     }
 
     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);
+        // Paper start - rewrite chunk system
+        try {
+            this.storage.write(pos, guardedPosCheck.get());
+            return CompletableFuture.completedFuture(null);
+        } catch (final Throwable throwable) {
+            return CompletableFuture.failedFuture(throwable);
+        }
+        // Paper end - rewrite chunk system
     }
 
     protected void handleLegacyStructureIndex(ChunkPos chunkPos) {
         if (this.legacyStructureHandler != null) {
+            synchronized (this.legacyStructureHandler) { // Paper - rewrite chunk system
             this.legacyStructureHandler.removeIndex(chunkPos.toLong());
+            } // Paper - rewrite chunk system
         }
     }
 
     public void flushWorker() {
-        this.worker.synchronize(true).join();
+        // Paper start - rewrite chunk system
+        try {
+            this.storage.flush();
+        } catch (final IOException ex) {
+            LOGGER.error("Failed to flush chunk storage", ex);
+        }
+        // Paper end - rewrite chunk system
     }
 
     @Override
     public void close() throws IOException {
-        this.worker.close();
+        this.storage.close(); // Paper - rewrite chunk system
     }
 
     public ChunkScanAccess chunkScanner() {
-        return this.worker;
+        // Paper start - rewrite chunk system
+        // TODO ChunkMap implementation?
+        return (chunkPos, streamTagVisitor) -> {
+            try {
+                this.storage.scanChunk(chunkPos, streamTagVisitor);
+                return java.util.concurrent.CompletableFuture.completedFuture(null);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        };
+        // Paper end - rewrite chunk system
     }
 
-    protected RegionStorageInfo storageInfo() {
-        return this.worker.storageInfo();
+    public RegionStorageInfo storageInfo() { // Paper - public
+        return this.storage.info(); // Paper - rewrite chunk system
     }
 }
