From 043559513c96a049fda21074c547286730ffa345 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 15 Jul 2024 12:20:47 -0700
Subject: [PATCH] Apply automatic regionfile header recalculation patch

---
 ...culate-regionfile-header-if-it-is-co.patch | 129 +++++-------------
 1 file changed, 32 insertions(+), 97 deletions(-)
 rename patches/{unapplied => }/server/Attempt-to-recalculate-regionfile-header-if-it-is-co.patch (87%)

diff --git a/patches/unapplied/server/Attempt-to-recalculate-regionfile-header-if-it-is-co.patch b/patches/server/Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
similarity index 87%
rename from patches/unapplied/server/Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
rename to patches/server/Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
index 4bd3c7a192..557f8c35ca 100644
--- a/patches/unapplied/server/Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
+++ b/patches/server/Attempt-to-recalculate-regionfile-header-if-it-is-co.patch
@@ -36,24 +36,11 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
          nbttagcompound.putInt("xPos", chunkcoordintpair.x);
          nbttagcompound.putInt("yPos", chunk.getMinSection());
          nbttagcompound.putInt("zPos", chunkcoordintpair.z);
--        nbttagcompound.putLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime : world.getGameTime()); // Paper - async chunk unloading
-+        nbttagcompound.putLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime : world.getGameTime()); // Paper - async chunk unloading // Paper - diff on change
+-        nbttagcompound.putLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime() : world.getGameTime()); // Paper - async chunk saving
++        nbttagcompound.putLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime() : world.getGameTime()); // Paper - async chunk saving // Paper - diff on change
          nbttagcompound.putLong("InhabitedTime", chunk.getInhabitedTime());
-         nbttagcompound.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(chunk.getStatus()).toString());
+         nbttagcompound.putString("Status", BuiltInRegistries.CHUNK_STATUS.getKey(chunk.getPersistedStatus()).toString());
          BlendingData blendingdata = chunk.getBlendingData();
-diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
-index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
---- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
-+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
-@@ -0,0 +0,0 @@ public class ChunkStorage implements AutoCloseable {
- 
-     public ChunkStorage(RegionStorageInfo storageKey, Path directory, DataFixer dataFixer, boolean dsync) {
-         this.fixerUpper = dataFixer;
--        this.regionFileCache = new RegionFileStorage(storageKey, directory, dsync); // Paper - rewrite chunk system; async chunk IO
-+        this.regionFileCache = new RegionFileStorage(storageKey, directory, dsync, true); // Paper - rewrite chunk system; async chunk IO & Attempt to recalculate regionfile header if it is corrupt
-     }
- 
-     public boolean isOldChunkAround(ChunkPos chunkPos, int checkRadius) {
 diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionBitmap.java
@@ -91,9 +78,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 --- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
 +++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
 @@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable {
+     private final IntBuffer timestamps;
      @VisibleForTesting
      protected final RegionBitmap usedSectors;
-     public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(); // Paper
 +    // Paper start - Attempt to recalculate regionfile header if it is corrupt
 +    private static long roundToSectors(long bytes) {
 +        long sectors = bytes >>> 12; // 4096 = 2^12
@@ -442,28 +429,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +
 +    final boolean canRecalcHeader; // final forces compile fail on new constructor
 +    // Paper end - Attempt to recalculate regionfile header if it is corrupt
-     // Paper start - Cache chunk status
-     private final net.minecraft.world.level.chunk.status.ChunkStatus[] statuses = new net.minecraft.world.level.chunk.status.ChunkStatus[32 * 32];
  
-@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable {
      public RegionFile(RegionStorageInfo storageKey, Path directory, Path path, boolean dsync) throws IOException {
          this(storageKey, directory, path, RegionFileVersion.getCompressionFormat(), dsync); // Paper - Configurable region compression format
-     }
-+    // Paper start - add can recalc flag
-+    public RegionFile(RegionStorageInfo storageKey, Path directory, Path path, boolean dsync, boolean canRecalcHeader) throws IOException {
-+        this(storageKey, directory, path, RegionFileVersion.getCompressionFormat(), dsync, canRecalcHeader);
-+    }
- 
-     public RegionFile(RegionStorageInfo storageKey, Path path, Path directory, RegionFileVersion compressionFormat, boolean dsync) throws IOException {
-+        this(storageKey, path, directory, compressionFormat, dsync, true);
-+    }
-+
-+    public RegionFile(RegionStorageInfo storageKey, Path path, Path directory, RegionFileVersion compressionFormat, boolean dsync, boolean canRecalcHeader) throws IOException {
-+        this.canRecalcHeader = canRecalcHeader;
-+        // Paper end - add can recalc flag
-         this.header = ByteBuffer.allocateDirect(8192);
-         this.usedSectors = new RegionBitmap();
-         this.info = storageKey;
+@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable {
+             throw new IllegalArgumentException("Expected directory, got " + String.valueOf(directory.toAbsolutePath()));
+         } else {
+             this.externalFileDir = directory;
++            this.canRecalcHeader = RegionFileStorage.isChunkDataFolder(this.externalFileDir); // Paper - add can recalc flag
+             this.offsets = this.header.asIntBuffer();
+             ((java.nio.Buffer) this.offsets).limit(1024); // CraftBukkit - decompile error
+             ((java.nio.Buffer) this.header).position(4096); // CraftBukkit - decompile error
 @@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable {
                      RegionFile.LOGGER.warn("Region file {} has truncated header: {}", path, i);
                  }
@@ -688,30 +664,17 @@ diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSto
 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
 --- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
 +++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
-@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
-     private final RegionStorageInfo info;
-     private final Path folder;
-     private final boolean sync;
-+    private final boolean isChunkData; // Paper
- 
-     // Paper start - cache regionfile does not exist state
-     static final int MAX_NON_EXISTING_CACHE = 1024 * 64;
-@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
-     // Paper end - cache regionfile does not exist state
- 
-     protected RegionFileStorage(RegionStorageInfo storageKey, Path directory, boolean dsync) { // Paper - protected constructor
-+        // Paper start - add isChunkData param
-+        this(storageKey, directory, dsync, false);
-+    }
-+    RegionFileStorage(RegionStorageInfo storageKey, Path directory, boolean dsync, boolean isChunkData) {
-+        this.isChunkData = isChunkData;
-+        // Paper end - add isChunkData param
-         this.folder = directory;
-         this.sync = dsync;
-         this.info = storageKey;
+@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
+         return ret;
      }
- 
-+    // Paper start
+     // Paper end - rewrite chunk system
++    // Paper start - recalculate region file headers
++    private final boolean isChunkData;
++
++    public static boolean isChunkDataFolder(Path path) {
++        return path.toFile().getName().equalsIgnoreCase("region");
++    }
++
 +    @Nullable
 +    public static ChunkPos getRegionFileCoordinates(Path file) {
 +        String fileName = file.getFileName().toString();
@@ -735,43 +698,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +        }
 +    }
 +    // Paper end
-+
-     // Paper start
-     public synchronized RegionFile getRegionFileIfLoaded(ChunkPos chunkcoordintpair) {
-         return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()));
-@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
-             // Paper - only create directory if not existing only - moved down
-             Path path = this.folder;
-             int j = chunkcoordintpair.getRegionX();
--            Path path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca");
-+            Path path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca"); // Paper - diff on change
-             if (existingOnly && !java.nio.file.Files.exists(path1)) { // Paper start - cache regionfile does not exist state
-                 this.markNonExisting(regionPos);
-                 return null; // CraftBukkit
-@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
-             }
-             // Paper end - cache regionfile does not exist state
-             FileUtil.createDirectoriesSafe(this.folder); // Paper - only create directory if not existing only - moved from above
--            RegionFile regionfile1 = new RegionFile(this.info, path1, this.folder, this.sync);
-+            RegionFile regionfile1 = new RegionFile(this.info, path1, this.folder, this.sync, this.isChunkData); // Paper - allow for chunk regionfiles to regen header
  
-             this.regionCache.putAndMoveToFirst(i, regionfile1);
-             // Paper start
-@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
-         if (regionfile == null) {
-             return null;
-         }
-+        // Paper start - Add regionfile parameter
-+        return this.read(pos, regionfile);
-+    }
-+    public CompoundTag read(ChunkPos pos, RegionFile regionfile) throws IOException {
-+        // We add the regionfile parameter to avoid the potential deadlock (on fileLock) if we went back to obtain a regionfile
-+        // if we decide to re-read
-+        // Paper end
-         // CraftBukkit end
-         try { // Paper
-         DataInputStream datainputstream = regionfile.getChunkDataInputStream(pos);
-@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable {
+     protected RegionFileStorage(RegionStorageInfo storageKey, Path directory, boolean dsync) { // Paper - protected
+         this.folder = directory;
+         this.sync = dsync;
+         this.info = storageKey;
++        this.isChunkData = isChunkDataFolder(this.folder); // Paper - recalculate region file headers
+     }
+ 
+     public RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - public
+@@ -0,0 +0,0 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise
              try {
                  if (datainputstream != null) {
                      nbttagcompound = NbtIo.read((DataInput) datainputstream);
@@ -781,8 +717,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +                        if (!chunkPos.equals(pos)) {
 +                            net.minecraft.server.MinecraftServer.LOGGER.error("Attempting to read chunk data at " + pos + " but got chunk data for " + chunkPos + " instead! Attempting regionfile recalculation for regionfile " + regionfile.getPath().toAbsolutePath());
 +                            if (regionfile.recalculateHeader()) {
-+                                regionfile.fileLock.lock(); // otherwise we will unlock twice and only lock once.
-+                                return this.read(pos, regionfile);
++                                return this.read(pos);
 +                            }
 +                            net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + pos + " for " + regionfile.getPath().toAbsolutePath());
 +                            return null;