5c7081fecc
* Updated Upstream (Bukkit/CraftBukkit) Upstream has released updates that appears to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: 45690fe9 SPIGOT-5047: Correct slot types for 1.14 inventories CraftBukkit Changes: 4090d01f SPIGOT-5047: Correct slot types for 1.14 inventories e8c08362 SPIGOT-5046: World#getLoadedChunks returning inaccessible cached chunks. d445af3b SPIGOT-5067: Add item meta for 1.14 spawn eggs * Bring Chunk load checks in-line with spigot As of the last upstream merge spigot now checks ticket level status when returning loaded chunks for a world from api. Now our checks will respect that decision. * Fix spawn ticket levels Vanilla would keep the inner chunks of spawn available for ticking, however my changes made all chunks non-ticking. Resolve by changing ticket levels for spawn chunks inside the border to respect this behavior. * Make World#getChunkIfLoadedImmediately return only entity ticking chunks Mojang appears to be using chunks with level > 33 (non-ticking chunks) as cached chunks and not actually loaded chunks. * Bring all loaded checks in line with spigot Loaded chunks must be at least border chunks, or level <= 33
115 lines
5 KiB
Diff
115 lines
5 KiB
Diff
From 2726a8e5a3268e94dbecc08766492627088ffe98 Mon Sep 17 00:00:00 2001
|
|
From: Aikar <aikar@aikar.co>
|
|
Date: Sat, 11 Aug 2018 00:49:20 -0400
|
|
Subject: [PATCH] Detect and repair corrupt Region Files
|
|
|
|
If the file has partial data written but not the full 8192 bytes,
|
|
then the server will be unable to load that region file...
|
|
|
|
I don't know why mojang only checks for 4096, when anything less than 8192 is a crash.
|
|
|
|
But to be safe, it will attempt to back up the file.
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java
|
|
index 4e6288e8bb..e68f901943 100644
|
|
--- a/src/main/java/net/minecraft/server/RegionFile.java
|
|
+++ b/src/main/java/net/minecraft/server/RegionFile.java
|
|
@@ -27,13 +27,13 @@ public class RegionFile implements AutoCloseable {
|
|
// Spigot end
|
|
private static final byte[] a = new byte[4096];
|
|
private final RandomAccessFile b; private RandomAccessFile getDataFile() { return this.b; } // Paper - OBFHELPER
|
|
- private final int[] c = new int[1024];
|
|
- private final int[] d = new int[1024];
|
|
+ private final int[] c = new int[1024]; private int[] offsets = c; // Paper - OBFHELPER
|
|
+ private final int[] d = new int[1024];private int[] timestamps = d; // Paper - OBFHELPER
|
|
private final List<Boolean> e;
|
|
|
|
public RegionFile(File file) throws IOException {
|
|
this.b = new RandomAccessFile(file, "rw");
|
|
- if (this.b.length() < 4096L) {
|
|
+ if (this.b.length() < 8192L) { // Paper - headers should be 8192
|
|
this.b.write(RegionFile.a);
|
|
this.b.write(RegionFile.a);
|
|
}
|
|
@@ -83,7 +83,7 @@ public class RegionFile implements AutoCloseable {
|
|
this.b.seek(j * 4 + 4); // Go back to where we were
|
|
}
|
|
}
|
|
- if (k != 0 && (k >> 8) + (length) <= this.e.size()) {
|
|
+ if (k > 0 && (k >> 8) > 1 && (k >> 8) + (k & 255) <= this.e.size()) { // Paper >= 1 as 0/1 are the headers, and negative isnt valid
|
|
for (int l = 0; l < (length); ++l) {
|
|
// Spigot end
|
|
this.e.set((k >> 8) + l, false);
|
|
@@ -92,13 +92,14 @@ public class RegionFile implements AutoCloseable {
|
|
// Spigot start
|
|
else if (length > 0) {
|
|
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Length: {3} runs off end file. {4}", new Object[]{j % 32, (int) (j / 32), k >> 8, length, file});
|
|
+ deleteChunk(j); // Paper
|
|
}
|
|
// Spigot end
|
|
}
|
|
|
|
for (j = 0; j < 1024; ++j) {
|
|
k = headerAsInts.get(); // Paper
|
|
- this.d[j] = k;
|
|
+ if (offsets[j] != 0) this.timestamps[j] = k; // Paper - don't set timestamp if it got 0'd above due to corruption
|
|
}
|
|
|
|
this.file = file; // Spigot
|
|
@@ -306,6 +307,53 @@ public class RegionFile implements AutoCloseable {
|
|
this.b.close();
|
|
}
|
|
|
|
+ // Paper start
|
|
+ public synchronized void deleteChunk(int j1) {
|
|
+ backup();
|
|
+ int k = offsets[j1];
|
|
+ int x = j1 & 1024;
|
|
+ int z = j1 >> 2;
|
|
+ int offset = (k >> 8);
|
|
+ int len = (k & 255);
|
|
+ String debug = "idx:" + + j1 + " - " + x + "," + z + " - offset: " + offset + " - len: " + len;
|
|
+ try {
|
|
+ timestamps[j1] = 0;
|
|
+ offsets[j1] = 0;
|
|
+ RandomAccessFile file = getDataFile();
|
|
+ file.seek(j1 * 4);
|
|
+ file.writeInt(0);
|
|
+ // clear the timestamp
|
|
+ file.seek(4096 + j1 * 4);
|
|
+ file.writeInt(0);
|
|
+ org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Deleted corrupt chunk (" + debug + ") " + this.file.getAbsolutePath(), e);
|
|
+ } catch (IOException e) {
|
|
+
|
|
+ org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Error deleting corrupt chunk (" + debug + ") " + this.file.getAbsolutePath(), e);
|
|
+ }
|
|
+ }
|
|
+ private boolean backedUp = false;
|
|
+ private synchronized void backup() {
|
|
+ if (backedUp) {
|
|
+ return;
|
|
+ }
|
|
+ backedUp = true;
|
|
+ java.text.DateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
+ java.util.Date today = new java.util.Date();
|
|
+ File corrupt = new File(file.getParentFile(), file.getName() + "." + formatter.format(today) + ".corrupt");
|
|
+ if (corrupt.exists()) {
|
|
+ return;
|
|
+ }
|
|
+ org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger();
|
|
+ logger.error("Region file " + file.getAbsolutePath() + " was corrupt. Backing up to " + corrupt.getAbsolutePath() + " and repairing");
|
|
+ try {
|
|
+ java.nio.file.Files.copy(file.toPath(), corrupt.toPath());
|
|
+
|
|
+ } catch (IOException e) {
|
|
+ logger.error("Error backing up corrupt file" + file.getAbsolutePath(), e);
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
class ChunkBuffer extends ByteArrayOutputStream {
|
|
|
|
private final ChunkCoordIntPair b;
|
|
--
|
|
2.21.0
|
|
|