From 3c00b8eaca9587ad2ec841656fa80ba45b2511b3 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sun, 7 Jul 2019 23:52:08 -0700 Subject: [PATCH 1/6] Fix loadChunk(x, z, false) I was not correctly checking if the status was even cached. --- .../0401-Fix-World-isChunkGenerated-calls.patch | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch b/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch index 8e38277cb9a1..551b210a6abc 100644 --- a/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch +++ b/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch @@ -1,4 +1,4 @@ -From 8ec38f6eb4de25ddb05562621e41e3abb073b949 Mon Sep 17 00:00:00 2001 +From 9f8d67e1b05822dd5a4a5901de2d9eb8e43e68b7 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sat, 15 Jun 2019 08:54:33 -0700 Subject: [PATCH] Fix World#isChunkGenerated calls @@ -265,7 +265,7 @@ index 6f34d8aea0..d2b3289450 100644 printOversizedLog("ChunkTooLarge even after reduction. Trying in overzealous mode.", regionfile.file, chunkX, chunkZ); // Eek, major fail. We have retry logic, so reduce threshholds and fall back diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 3444c19b0f..3e55033d3c 100644 +index 3444c19b0f..9ac160ea75 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -386,8 +386,20 @@ public class CraftWorld implements World { @@ -290,7 +290,7 @@ index 3444c19b0f..3e55033d3c 100644 } catch (IOException ex) { throw new RuntimeException(ex); } -@@ -499,20 +511,24 @@ public class CraftWorld implements World { +@@ -499,20 +511,25 @@ public class CraftWorld implements World { @Override public boolean loadChunk(int x, int z, boolean generate) { org.spigotmc.AsyncCatcher.catchOp( "chunk load"); // Spigot @@ -304,7 +304,8 @@ index 3444c19b0f..3e55033d3c 100644 + // Paper - Optimize this method + if (!generate) { + net.minecraft.server.RegionFile file = world.getChunkProvider().playerChunkMap.getRegionFileIfLoaded(new ChunkCoordIntPair(x, z)); -+ if (file != null && file.getStatusIfCached(x, z) != ChunkStatus.FULL) { ++ ChunkStatus status; ++ if (file != null && (status = file.getStatusIfCached(x, z)) != null && status != ChunkStatus.FULL) { + return false; + } @@ -327,7 +328,7 @@ index 3444c19b0f..3e55033d3c 100644 } @Override -@@ -2163,21 +2179,20 @@ public class CraftWorld implements World { +@@ -2163,21 +2180,20 @@ public class CraftWorld implements World { // Paper start private Chunk getChunkAtGen(int x, int z, boolean gen) { From fd544c8eb582ec7c6d4706a59958e219686a2cab Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Mon, 8 Jul 2019 03:49:22 -0700 Subject: [PATCH 2/6] Actually fix it this time --- ...401-Fix-World-isChunkGenerated-calls.patch | 73 +++++++++++++------ 1 file changed, 50 insertions(+), 23 deletions(-) diff --git a/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch b/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch index 551b210a6abc..a6e73dfce35a 100644 --- a/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch +++ b/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch @@ -1,4 +1,4 @@ -From 9f8d67e1b05822dd5a4a5901de2d9eb8e43e68b7 Mon Sep 17 00:00:00 2001 +From ad47bdf32e883902230efa85bb9577dc7ae885f3 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sat, 15 Jun 2019 08:54:33 -0700 Subject: [PATCH] Fix World#isChunkGenerated calls @@ -164,7 +164,7 @@ index c4ad039ffd..3f41072f72 100644 // Spigot Start diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java -index 2e14d84657..d610253b95 100644 +index 2e14d84657..135eed4deb 100644 --- a/src/main/java/net/minecraft/server/RegionFile.java +++ b/src/main/java/net/minecraft/server/RegionFile.java @@ -31,6 +31,47 @@ public class RegionFile implements AutoCloseable { @@ -215,7 +215,15 @@ index 2e14d84657..d610253b95 100644 public RegionFile(File file) throws IOException { this.b = new RandomAccessFile(file, "rw"); this.file = file; // Spigot // Paper - We need this earlier -@@ -299,6 +340,7 @@ public class RegionFile implements AutoCloseable { +@@ -286,6 +327,7 @@ public class RegionFile implements AutoCloseable { + return this.c[this.f(chunkcoordintpair)]; + } + ++ public final boolean chunkExists(ChunkCoordIntPair chunkPos) { return this.d(chunkPos); } // Paper - OBFHELPER + public boolean d(ChunkCoordIntPair chunkcoordintpair) { + return this.getOffset(chunkcoordintpair) != 0; + } +@@ -299,6 +341,7 @@ public class RegionFile implements AutoCloseable { this.writeInt(i); // Paper - Avoid 3 io write calls } @@ -223,7 +231,7 @@ index 2e14d84657..d610253b95 100644 private int f(ChunkCoordIntPair chunkcoordintpair) { return chunkcoordintpair.j() + chunkcoordintpair.k() * 32; } -@@ -312,6 +354,7 @@ public class RegionFile implements AutoCloseable { +@@ -312,6 +355,7 @@ public class RegionFile implements AutoCloseable { } public void close() throws IOException { @@ -265,7 +273,7 @@ index 6f34d8aea0..d2b3289450 100644 printOversizedLog("ChunkTooLarge even after reduction. Trying in overzealous mode.", regionfile.file, chunkX, chunkZ); // Eek, major fail. We have retry logic, so reduce threshholds and fall back diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 3444c19b0f..9ac160ea75 100644 +index 3444c19b0f..cb532c7aea 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -386,8 +386,20 @@ public class CraftWorld implements World { @@ -290,7 +298,7 @@ index 3444c19b0f..9ac160ea75 100644 } catch (IOException ex) { throw new RuntimeException(ex); } -@@ -499,20 +511,25 @@ public class CraftWorld implements World { +@@ -499,20 +511,46 @@ public class CraftWorld implements World { @Override public boolean loadChunk(int x, int z, boolean generate) { org.spigotmc.AsyncCatcher.catchOp( "chunk load"); // Spigot @@ -301,34 +309,53 @@ index 3444c19b0f..9ac160ea75 100644 - // We then cycle through again to get the full chunk immediately, rather than after the ticket addition - chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true); - } -+ // Paper - Optimize this method -+ if (!generate) { -+ net.minecraft.server.RegionFile file = world.getChunkProvider().playerChunkMap.getRegionFileIfLoaded(new ChunkCoordIntPair(x, z)); -+ ChunkStatus status; -+ if (file != null && (status = file.getStatusIfCached(x, z)) != null && status != ChunkStatus.FULL) { -+ return false; -+ } ++ // Paper start - Optimize this method ++ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(x, z); - if (chunk instanceof net.minecraft.server.Chunk) { - world.getChunkProvider().addTicket(TicketType.PLUGIN, new ChunkCoordIntPair(x, z), 1, Unit.INSTANCE); -- return true; -+ IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.EMPTY, true); -+ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) { -+ return false; -+ } -+ // fall through to load -+ // we do this so we do not re-read the chunk data on disk ++ net.minecraft.server.Chunk immediate = world.getChunkProvider().getChunkAtIfLoadedImmediately(x, z); ++ if (immediate != null) { ++ world.getChunkProvider().addTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE); + return true; } -- + - return false; -+ world.getChunkProvider().addTicket(TicketType.PLUGIN, new ChunkCoordIntPair(x, z), 1, Unit.INSTANCE); ++ if (!generate) { ++ net.minecraft.server.RegionFile file = world.getChunkProvider().playerChunkMap.getRegionFileIfLoaded(chunkPos); ++ ChunkStatus status; ++ if (file != null) { ++ if (!file.chunkExists(chunkPos) || ((status = file.getStatusIfCached(x, z)) != null && status != ChunkStatus.FULL)) { ++ return false; // not on disk or cached status says not full status ++ } ++ // full status on disk, fall through to load ++ } else { ++ ++ try { ++ if (!world.getChunkProvider().playerChunkMap.getRegionFile(chunkPos, false).chunkExists(chunkPos)) { ++ return false; // does not exist on disk ++ } ++ } catch (IOException ex) { ++ throw new RuntimeException(ex); ++ } ++ ++ IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.EMPTY, true); ++ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) { ++ return false; ++ } ++ ++ // fall through to load ++ // we do this so we do not re-read the chunk data on disk ++ } ++ } ++ world.getChunkProvider().addTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE); + world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true); + return true; + // Paper end } @Override -@@ -2163,21 +2180,20 @@ public class CraftWorld implements World { +@@ -2163,21 +2201,20 @@ public class CraftWorld implements World { // Paper start private Chunk getChunkAtGen(int x, int z, boolean gen) { From 20a4d7ed0eadda176d039ca04701e6cb503b2e98 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Mon, 8 Jul 2019 03:55:00 -0700 Subject: [PATCH 3/6] Do not forget about the async chunk placeholder --- ...401-Fix-World-isChunkGenerated-calls.patch | 65 ++++++++++++------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch b/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch index a6e73dfce35a..7e40cbd84448 100644 --- a/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch +++ b/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch @@ -1,4 +1,4 @@ -From ad47bdf32e883902230efa85bb9577dc7ae885f3 Mon Sep 17 00:00:00 2001 +From 81375cad01fd586ca8f3f6b4b4d162ece8e16f17 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sat, 15 Jun 2019 08:54:33 -0700 Subject: [PATCH] Fix World#isChunkGenerated calls @@ -273,7 +273,7 @@ index 6f34d8aea0..d2b3289450 100644 printOversizedLog("ChunkTooLarge even after reduction. Trying in overzealous mode.", regionfile.file, chunkX, chunkZ); // Eek, major fail. We have retry logic, so reduce threshholds and fall back diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 3444c19b0f..cb532c7aea 100644 +index 3444c19b0f..347092a41d 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -386,8 +386,20 @@ public class CraftWorld implements World { @@ -303,15 +303,15 @@ index 3444c19b0f..cb532c7aea 100644 public boolean loadChunk(int x, int z, boolean generate) { org.spigotmc.AsyncCatcher.catchOp( "chunk load"); // Spigot - IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); -- ++ // Paper start - Optimize this method ++ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(x, z); + - // If generate = false, but the chunk already exists, we will get this back. - if (chunk instanceof ProtoChunkExtension) { - // We then cycle through again to get the full chunk immediately, rather than after the ticket addition - chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true); - } -+ // Paper start - Optimize this method -+ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(x, z); - +- - if (chunk instanceof net.minecraft.server.Chunk) { - world.getChunkProvider().addTicket(TicketType.PLUGIN, new ChunkCoordIntPair(x, z), 1, Unit.INSTANCE); + net.minecraft.server.Chunk immediate = world.getChunkProvider().getChunkAtIfLoadedImmediately(x, z); @@ -355,37 +355,56 @@ index 3444c19b0f..cb532c7aea 100644 } @Override -@@ -2163,21 +2201,20 @@ public class CraftWorld implements World { +@@ -2163,21 +2201,42 @@ public class CraftWorld implements World { // Paper start private Chunk getChunkAtGen(int x, int z, boolean gen) { - // copied from loadChunk() - // this function is identical except we do not add a plugin ticket - IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, gen || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); -- ++ // Note: Copied from loadChunk() ++ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(x, z); + - // If generate = false, but the chunk already exists, we will get this back. - if (chunk instanceof ProtoChunkExtension) { - // We then cycle through again to get the full chunk immediately, rather than after the ticket addition - chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true); -- } -- ++ net.minecraft.server.Chunk immediate = world.getChunkProvider().getChunkAtIfLoadedImmediately(x, z); ++ if (immediate != null) { ++ return immediate.bukkitChunk; + } + - if (chunk instanceof net.minecraft.server.Chunk) { - return ((net.minecraft.server.Chunk)chunk).bukkitChunk; -+ // Note: Copied from loadChunk() +- } + if (!gen) { -+ net.minecraft.server.RegionFile file = world.getChunkProvider().playerChunkMap.getRegionFileIfLoaded(new ChunkCoordIntPair(x, z)); -+ if (file != null && file.getStatusIfCached(x, z) != ChunkStatus.FULL) { -+ return null; -+ } -+ IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.EMPTY, true); -+ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) { -+ return null; -+ } -+ // fall through to load -+ // we do this so we do not re-read the chunk data on disk - } -- ++ net.minecraft.server.RegionFile file = world.getChunkProvider().playerChunkMap.getRegionFileIfLoaded(chunkPos); ++ ChunkStatus status; ++ if (file != null) { ++ if (!file.chunkExists(chunkPos) || ((status = file.getStatusIfCached(x, z)) != null && status != ChunkStatus.FULL)) { ++ return null; // not on disk or cached status says not full status ++ } ++ // full status on disk, fall through to load ++ } else { + - return null; ++ try { ++ if (!world.getChunkProvider().playerChunkMap.getRegionFile(chunkPos, false).chunkExists(chunkPos)) { ++ return null; // does not exist on disk ++ } ++ } catch (IOException ex) { ++ throw new RuntimeException(ex); ++ } ++ ++ IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.EMPTY, true); ++ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) { ++ return null; ++ } ++ ++ // fall through to load ++ // we do this so we do not re-read the chunk data on disk ++ } ++ } + return ((net.minecraft.server.Chunk)world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true)).bukkitChunk; } From 18cbfd2f7341013b2595afc46ca46ecfeb09564d Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Mon, 8 Jul 2019 13:12:53 -0700 Subject: [PATCH 4/6] Actually fix it this time I hope --- ...401-Fix-World-isChunkGenerated-calls.patch | 121 +++++++++--------- 1 file changed, 59 insertions(+), 62 deletions(-) diff --git a/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch b/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch index 7e40cbd84448..103e3292be8f 100644 --- a/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch +++ b/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch @@ -1,4 +1,4 @@ -From 81375cad01fd586ca8f3f6b4b4d162ece8e16f17 Mon Sep 17 00:00:00 2001 +From 260a947d0326dd33295c4a244a5e1299be6538f0 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sat, 15 Jun 2019 08:54:33 -0700 Subject: [PATCH] Fix World#isChunkGenerated calls @@ -273,7 +273,7 @@ index 6f34d8aea0..d2b3289450 100644 printOversizedLog("ChunkTooLarge even after reduction. Trying in overzealous mode.", regionfile.file, chunkX, chunkZ); // Eek, major fail. We have retry logic, so reduce threshholds and fall back diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 3444c19b0f..347092a41d 100644 +index 3444c19b0f..633785f58b 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -386,8 +386,20 @@ public class CraftWorld implements World { @@ -298,7 +298,7 @@ index 3444c19b0f..347092a41d 100644 } catch (IOException ex) { throw new RuntimeException(ex); } -@@ -499,20 +511,46 @@ public class CraftWorld implements World { +@@ -499,20 +511,45 @@ public class CraftWorld implements World { @Override public boolean loadChunk(int x, int z, boolean generate) { org.spigotmc.AsyncCatcher.catchOp( "chunk load"); // Spigot @@ -310,44 +310,42 @@ index 3444c19b0f..347092a41d 100644 - if (chunk instanceof ProtoChunkExtension) { - // We then cycle through again to get the full chunk immediately, rather than after the ticket addition - chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true); -- } -- -- if (chunk instanceof net.minecraft.server.Chunk) { -- world.getChunkProvider().addTicket(TicketType.PLUGIN, new ChunkCoordIntPair(x, z), 1, Unit.INSTANCE); -+ net.minecraft.server.Chunk immediate = world.getChunkProvider().getChunkAtIfLoadedImmediately(x, z); ++ IChunkAccess immediate = world.getChunkProvider().getChunkAtImmediately(x, z); + if (immediate != null) { ++ if (!(immediate instanceof ProtoChunkExtension) && !(immediate instanceof net.minecraft.server.Chunk)) { ++ return false; // not full status ++ } + world.getChunkProvider().addTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE); - return true; ++ world.getChunkAt(x, z); // make sure we're at ticket level 32 or lower ++ return true; } -- return false; +- if (chunk instanceof net.minecraft.server.Chunk) { +- world.getChunkProvider().addTicket(TicketType.PLUGIN, new ChunkCoordIntPair(x, z), 1, Unit.INSTANCE); +- return true; + if (!generate) { -+ net.minecraft.server.RegionFile file = world.getChunkProvider().playerChunkMap.getRegionFileIfLoaded(chunkPos); -+ ChunkStatus status; -+ if (file != null) { -+ if (!file.chunkExists(chunkPos) || ((status = file.getStatusIfCached(x, z)) != null && status != ChunkStatus.FULL)) { -+ return false; // not on disk or cached status says not full status -+ } -+ // full status on disk, fall through to load -+ } else { -+ -+ try { -+ if (!world.getChunkProvider().playerChunkMap.getRegionFile(chunkPos, false).chunkExists(chunkPos)) { -+ return false; // does not exist on disk -+ } -+ } catch (IOException ex) { -+ throw new RuntimeException(ex); -+ } ++ net.minecraft.server.RegionFile file; ++ try { ++ file = world.getChunkProvider().playerChunkMap.getRegionFile(chunkPos, false); ++ } catch (IOException ex) { ++ throw new RuntimeException(ex); ++ } + -+ IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.EMPTY, true); -+ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) { -+ return false; -+ } ++ ChunkStatus status = file.getStatusIfCached(x, z); ++ if (!file.chunkExists(chunkPos) || (status != null && status != ChunkStatus.FULL)) { ++ return false; ++ } + -+ // fall through to load -+ // we do this so we do not re-read the chunk data on disk ++ IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.EMPTY, true); ++ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) { ++ return false; + } -+ } ++ ++ // fall through to load ++ // we do this so we do not re-read the chunk data on disk + } + +- return false; + world.getChunkProvider().addTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE); + world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true); + return true; @@ -355,7 +353,7 @@ index 3444c19b0f..347092a41d 100644 } @Override -@@ -2163,21 +2201,42 @@ public class CraftWorld implements World { +@@ -2163,21 +2200,42 @@ public class CraftWorld implements World { // Paper start private Chunk getChunkAtGen(int x, int z, boolean gen) { @@ -369,43 +367,42 @@ index 3444c19b0f..347092a41d 100644 - if (chunk instanceof ProtoChunkExtension) { - // We then cycle through again to get the full chunk immediately, rather than after the ticket addition - chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true); -+ net.minecraft.server.Chunk immediate = world.getChunkProvider().getChunkAtIfLoadedImmediately(x, z); ++ IChunkAccess immediate = world.getChunkProvider().getChunkAtImmediately(x, z); + if (immediate != null) { -+ return immediate.bukkitChunk; ++ if (!(immediate instanceof ProtoChunkExtension) && !(immediate instanceof net.minecraft.server.Chunk)) { ++ return null; // not full status ++ } ++ world.getChunkProvider().addTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE); ++ return world.getChunkAt(x, z).bukkitChunk; // make sure we're at ticket level 32 or lower } - if (chunk instanceof net.minecraft.server.Chunk) { - return ((net.minecraft.server.Chunk)chunk).bukkitChunk; -- } + if (!gen) { -+ net.minecraft.server.RegionFile file = world.getChunkProvider().playerChunkMap.getRegionFileIfLoaded(chunkPos); -+ ChunkStatus status; -+ if (file != null) { -+ if (!file.chunkExists(chunkPos) || ((status = file.getStatusIfCached(x, z)) != null && status != ChunkStatus.FULL)) { -+ return null; // not on disk or cached status says not full status -+ } -+ // full status on disk, fall through to load -+ } else { - -- return null; -+ try { -+ if (!world.getChunkProvider().playerChunkMap.getRegionFile(chunkPos, false).chunkExists(chunkPos)) { -+ return null; // does not exist on disk -+ } -+ } catch (IOException ex) { -+ throw new RuntimeException(ex); -+ } ++ net.minecraft.server.RegionFile file; ++ try { ++ file = world.getChunkProvider().playerChunkMap.getRegionFile(chunkPos, false); ++ } catch (IOException ex) { ++ throw new RuntimeException(ex); ++ } + -+ IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.EMPTY, true); -+ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) { -+ return null; -+ } ++ ChunkStatus status = file.getStatusIfCached(x, z); ++ if (!file.chunkExists(chunkPos) || (status != null && status != ChunkStatus.FULL)) { ++ return null; ++ } + -+ // fall through to load -+ // we do this so we do not re-read the chunk data on disk ++ IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.EMPTY, true); ++ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) { ++ return null; + } -+ } -+ return ((net.minecraft.server.Chunk)world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true)).bukkitChunk; ++ ++ // fall through to load ++ // we load at empty so we don't double-load chunk data in this case + } + +- return null; ++ world.getChunkProvider().addTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE); ++ return world.getChunkAt(x, z).bukkitChunk; } @Override From 95c9b76db6a9f3b36f8c19e7a0e4fd4c42de77e1 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Mon, 8 Jul 2019 13:15:01 -0700 Subject: [PATCH 5/6] No plugin tickets for getChunkAtGen(x, z, boolean) --- .../0401-Fix-World-isChunkGenerated-calls.patch | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch b/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch index 103e3292be8f..0cb0548ff08d 100644 --- a/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch +++ b/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch @@ -1,4 +1,4 @@ -From 260a947d0326dd33295c4a244a5e1299be6538f0 Mon Sep 17 00:00:00 2001 +From 65927e10d4d55bc1c2549081a7ed84d2dbc9af99 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sat, 15 Jun 2019 08:54:33 -0700 Subject: [PATCH] Fix World#isChunkGenerated calls @@ -273,7 +273,7 @@ index 6f34d8aea0..d2b3289450 100644 printOversizedLog("ChunkTooLarge even after reduction. Trying in overzealous mode.", regionfile.file, chunkX, chunkZ); // Eek, major fail. We have retry logic, so reduce threshholds and fall back diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 3444c19b0f..633785f58b 100644 +index 3444c19b0f..a833d0dd6f 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -386,8 +386,20 @@ public class CraftWorld implements World { @@ -353,7 +353,7 @@ index 3444c19b0f..633785f58b 100644 } @Override -@@ -2163,21 +2200,42 @@ public class CraftWorld implements World { +@@ -2163,21 +2200,40 @@ public class CraftWorld implements World { // Paper start private Chunk getChunkAtGen(int x, int z, boolean gen) { @@ -372,7 +372,6 @@ index 3444c19b0f..633785f58b 100644 + if (!(immediate instanceof ProtoChunkExtension) && !(immediate instanceof net.minecraft.server.Chunk)) { + return null; // not full status + } -+ world.getChunkProvider().addTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE); + return world.getChunkAt(x, z).bukkitChunk; // make sure we're at ticket level 32 or lower } @@ -401,7 +400,6 @@ index 3444c19b0f..633785f58b 100644 } - return null; -+ world.getChunkProvider().addTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE); + return world.getChunkAt(x, z).bukkitChunk; } From 5f5f06515df7bd7d902f273a17e827070ac6baa4 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Wed, 10 Jul 2019 19:29:50 -0700 Subject: [PATCH 6/6] Change ChunkStatus ABI This is required for asynchronous IO. async io will require calls to getChunkStatusIfCached to return the chunk status for a chunk currently queued to save - this cannot be reasonably done with current ABI --- ...401-Fix-World-isChunkGenerated-calls.patch | 85 ++++++++++++------- ...Status-cache-when-saving-protochunks.patch | 6 +- .../0406-Fix-tracker-desync-issue.patch | 6 +- Spigot-Server-Patches/0407-Anti-Xray.patch | 14 +-- 4 files changed, 67 insertions(+), 44 deletions(-) diff --git a/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch b/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch index 8e38277cb9a1..be803cee11be 100644 --- a/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch +++ b/Spigot-Server-Patches/0401-Fix-World-isChunkGenerated-calls.patch @@ -1,4 +1,4 @@ -From 8ec38f6eb4de25ddb05562621e41e3abb073b949 Mon Sep 17 00:00:00 2001 +From af257098287059ea49551060784af7887283ad90 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sat, 15 Jun 2019 08:54:33 -0700 Subject: [PATCH] Fix World#isChunkGenerated calls @@ -134,10 +134,10 @@ index 0daf5f99e3..761cd1355b 100644 public CompletableFuture> getStatusFutureUnchecked(ChunkStatus chunkstatus) { diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index c4ad039ffd..3f41072f72 100644 +index c4ad039ffd..9382745834 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java -@@ -802,10 +802,23 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -802,11 +802,56 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { } @Nullable @@ -156,18 +156,51 @@ index c4ad039ffd..3f41072f72 100644 + return null; + } + -+ this.getRegionFile(chunkcoordintpair, false).setStatus(chunkcoordintpair.x, chunkcoordintpair.z, ChunkRegionLoader.getStatus(nbttagcompound)); ++ this.updateChunkStatusOnDisk(chunkcoordintpair, nbttagcompound); + + return nbttagcompound; + // Paper end ++ } ++ ++ // Paper start - chunk status cache "api" ++ public ChunkStatus getChunkStatusOnDiskIfCached(ChunkCoordIntPair chunkPos) { ++ RegionFile regionFile = this.getRegionFileIfLoaded(chunkPos); ++ ++ return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); ++ } ++ ++ public ChunkStatus getChunkStatusOnDisk(ChunkCoordIntPair chunkPos) throws IOException { ++ RegionFile regionFile = this.getRegionFile(chunkPos, false); ++ ++ if (!regionFile.chunkExists(chunkPos)) { ++ return null; ++ } ++ ++ ChunkStatus status = regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); ++ ++ if (status != null) { ++ return status; ++ } ++ ++ this.readChunkData(chunkPos); ++ ++ return regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); ++ } ++ ++ public void updateChunkStatusOnDisk(ChunkCoordIntPair chunkPos, @Nullable NBTTagCompound compound) throws IOException { ++ RegionFile regionFile = this.getRegionFile(chunkPos, false); ++ ++ regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkRegionLoader.getStatus(compound)); } ++ // Paper end // Spigot Start + boolean isOutsideOfRange(ChunkCoordIntPair chunkcoordintpair, boolean reducedRange) { diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java -index 2e14d84657..d610253b95 100644 +index 2e14d84657..66c8b0307f 100644 --- a/src/main/java/net/minecraft/server/RegionFile.java +++ b/src/main/java/net/minecraft/server/RegionFile.java -@@ -31,6 +31,47 @@ public class RegionFile implements AutoCloseable { +@@ -31,6 +31,30 @@ public class RegionFile implements AutoCloseable { private final int[] d = new int[1024];private int[] timestamps = d; // Paper - OBFHELPER private final List e; private List getFreeSectors() { return this.e; } // Paper - OBFHELPER @@ -193,29 +226,20 @@ index 2e14d84657..d610253b95 100644 + final int location = this.getChunkLocation(new ChunkCoordIntPair(x, z)); + return this.statuses[location]; + } -+ -+ public ChunkStatus getStatus(int x, int z, PlayerChunkMap playerChunkMap) throws IOException { -+ if (this.closed) { -+ // We've used an invalid region file. -+ throw new java.io.EOFException("RegionFile is closed"); -+ } -+ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(x, z); -+ int location = this.getChunkLocation(chunkPos); -+ ChunkStatus cached = this.statuses[location]; -+ if (cached != null) { -+ return cached; -+ } -+ -+ playerChunkMap.readChunkData(chunkPos); // This will set our status (yes it's disgusting) -+ -+ return this.statuses[location]; -+ } + // Paper end + public RegionFile(File file) throws IOException { this.b = new RandomAccessFile(file, "rw"); this.file = file; // Spigot // Paper - We need this earlier -@@ -299,6 +340,7 @@ public class RegionFile implements AutoCloseable { +@@ -286,6 +310,7 @@ public class RegionFile implements AutoCloseable { + return this.c[this.f(chunkcoordintpair)]; + } + ++ public final boolean chunkExists(ChunkCoordIntPair chunkPos) { return this.d(chunkPos); } // Paper - OBFHELPER + public boolean d(ChunkCoordIntPair chunkcoordintpair) { + return this.getOffset(chunkcoordintpair) != 0; + } +@@ -299,6 +324,7 @@ public class RegionFile implements AutoCloseable { this.writeInt(i); // Paper - Avoid 3 io write calls } @@ -223,7 +247,7 @@ index 2e14d84657..d610253b95 100644 private int f(ChunkCoordIntPair chunkcoordintpair) { return chunkcoordintpair.j() + chunkcoordintpair.k() * 32; } -@@ -312,6 +354,7 @@ public class RegionFile implements AutoCloseable { +@@ -312,6 +338,7 @@ public class RegionFile implements AutoCloseable { } public void close() throws IOException { @@ -265,10 +289,10 @@ index 6f34d8aea0..d2b3289450 100644 printOversizedLog("ChunkTooLarge even after reduction. Trying in overzealous mode.", regionfile.file, chunkX, chunkZ); // Eek, major fail. We have retry logic, so reduce threshholds and fall back diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 3444c19b0f..3e55033d3c 100644 +index 3444c19b0f..abef19838f 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -386,8 +386,20 @@ public class CraftWorld implements World { +@@ -386,8 +386,19 @@ public class CraftWorld implements World { @Override public boolean isChunkGenerated(int x, int z) { @@ -284,13 +308,12 @@ index 3444c19b0f..3e55033d3c 100644 + } try { - return world.getChunkProvider().getChunkAtIfCachedImmediately(x, z) != null || world.getChunkProvider().playerChunkMap.chunkExists(new ChunkCoordIntPair(x, z)); // Paper -+ return world.getChunkProvider().playerChunkMap.getRegionFile(new ChunkCoordIntPair(x, z), false) -+ .getStatus(x, z, world.getChunkProvider().playerChunkMap) == ChunkStatus.FULL; ++ return world.getChunkProvider().playerChunkMap.getChunkStatusOnDisk(new ChunkCoordIntPair(x, z)) == ChunkStatus.FULL; + // Paper end } catch (IOException ex) { throw new RuntimeException(ex); } -@@ -499,20 +511,24 @@ public class CraftWorld implements World { +@@ -499,20 +510,24 @@ public class CraftWorld implements World { @Override public boolean loadChunk(int x, int z, boolean generate) { org.spigotmc.AsyncCatcher.catchOp( "chunk load"); // Spigot @@ -327,7 +350,7 @@ index 3444c19b0f..3e55033d3c 100644 } @Override -@@ -2163,21 +2179,20 @@ public class CraftWorld implements World { +@@ -2163,21 +2178,20 @@ public class CraftWorld implements World { // Paper start private Chunk getChunkAtGen(int x, int z, boolean gen) { diff --git a/Spigot-Server-Patches/0405-Use-ChunkStatus-cache-when-saving-protochunks.patch b/Spigot-Server-Patches/0405-Use-ChunkStatus-cache-when-saving-protochunks.patch index 9e5a7149be07..d16618773d1e 100644 --- a/Spigot-Server-Patches/0405-Use-ChunkStatus-cache-when-saving-protochunks.patch +++ b/Spigot-Server-Patches/0405-Use-ChunkStatus-cache-when-saving-protochunks.patch @@ -1,4 +1,4 @@ -From 3a6cd1b34c71328e9f0d7d47a4ba1d4cabf6d4ca Mon Sep 17 00:00:00 2001 +From 20006eddef9943095f5568261558bc72a2d5c423 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sat, 22 Jun 2019 04:20:47 -0700 Subject: [PATCH] Use ChunkStatus cache when saving protochunks @@ -7,7 +7,7 @@ The cache should contain the chunk status when saving. If not it will load it. diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 3f41072f72..f34d7d0dad 100644 +index 9382745834..65959a8d18 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -718,8 +718,10 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -17,7 +17,7 @@ index 3f41072f72..f34d7d0dad 100644 - nbttagcompound = this.readChunkData(chunkcoordintpair); - if (nbttagcompound != null && ChunkRegionLoader.a(nbttagcompound) == ChunkStatus.Type.LEVELCHUNK) { + // Paper start - Optimize save by using status cache -+ ChunkStatus statusOnDisk = this.getRegionFile(ichunkaccess.getPos(), false).getStatus(ichunkaccess.getPos().x, ichunkaccess.getPos().z, this); ++ ChunkStatus statusOnDisk = this.getChunkStatusOnDisk(chunkcoordintpair); + if (statusOnDisk != null && statusOnDisk.getType() == ChunkStatus.Type.LEVELCHUNK) { + // Paper end return false; diff --git a/Spigot-Server-Patches/0406-Fix-tracker-desync-issue.patch b/Spigot-Server-Patches/0406-Fix-tracker-desync-issue.patch index ca9dc2211a9f..36f74c3875b7 100644 --- a/Spigot-Server-Patches/0406-Fix-tracker-desync-issue.patch +++ b/Spigot-Server-Patches/0406-Fix-tracker-desync-issue.patch @@ -1,4 +1,4 @@ -From 8e3c5764f6a63ca9302d36608291a1f7afd77bc1 Mon Sep 17 00:00:00 2001 +From 3c77d914b97d8287f7a357c2570446b174dad3ad Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sun, 23 Jun 2019 19:11:27 -0700 Subject: [PATCH] Fix tracker desync issue @@ -17,10 +17,10 @@ index b2a2090e79..0a2c9a9f85 100644 return new Vec3D(this.locX, this.locY, this.locZ); } diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index f34d7d0dad..8e16d6ac87 100644 +index 65959a8d18..e6c35923e0 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java -@@ -1238,7 +1238,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1270,7 +1270,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { public void updatePlayer(EntityPlayer entityplayer) { org.spigotmc.AsyncCatcher.catchOp( "player tracker update"); // Spigot if (entityplayer != this.tracker) { diff --git a/Spigot-Server-Patches/0407-Anti-Xray.patch b/Spigot-Server-Patches/0407-Anti-Xray.patch index 4c33ef842392..5962b55521e5 100644 --- a/Spigot-Server-Patches/0407-Anti-Xray.patch +++ b/Spigot-Server-Patches/0407-Anti-Xray.patch @@ -1,4 +1,4 @@ -From 5b18d35d10af4ba8c655f65dc378abf8c1d81c98 Mon Sep 17 00:00:00 2001 +From a0d5a113e8c1cf7ad2ebb906348eb6d0d5366b57 Mon Sep 17 00:00:00 2001 From: stonar96 Date: Mon, 20 Aug 2018 03:03:58 +0200 Subject: [PATCH] Anti-Xray @@ -20,7 +20,7 @@ index 81987e4ad9..5942c3438e 100644 } diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 58109e1308..b03d3ee84b 100644 +index 4d3c6c6b47..929f5c3031 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -1,7 +1,11 @@ @@ -35,7 +35,7 @@ index 58109e1308..b03d3ee84b 100644 import org.bukkit.Bukkit; import org.bukkit.configuration.file.YamlConfiguration; import org.spigotmc.SpigotWorldConfig; -@@ -509,4 +513,43 @@ public class PaperWorldConfig { +@@ -504,4 +508,43 @@ public class PaperWorldConfig { private void maxAutoSaveChunksPerTick() { maxAutoSaveChunksPerTick = getInt("max-auto-save-chunks-per-tick", 24); } @@ -1565,7 +1565,7 @@ index 761cd1355b..956a47132f 100644 this.a(new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, chunk), false); diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 8e16d6ac87..f486331118 100644 +index e6c35923e0..c23f640320 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -491,7 +491,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -1577,7 +1577,7 @@ index 8e16d6ac87..f486331118 100644 }, this.executor); } -@@ -1111,7 +1111,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -1143,7 +1143,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { private void a(EntityPlayer entityplayer, Packet[] apacket, Chunk chunk) { if (apacket[0] == null) { @@ -1643,7 +1643,7 @@ index 6bdd7dda04..7bad12eb00 100644 return this.j[i]; diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java -index d2bf158a91..2eeae60d52 100644 +index 0096762f22..1ba85edd17 100644 --- a/src/main/java/net/minecraft/server/TicketType.java +++ b/src/main/java/net/minecraft/server/TicketType.java @@ -20,6 +20,7 @@ public class TicketType { @@ -1655,7 +1655,7 @@ index d2bf158a91..2eeae60d52 100644 public static TicketType a(String s, Comparator comparator) { return new TicketType<>(s, comparator, 0L); diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index 11113c9614..ae286aaf29 100644 +index f0e8acdd1a..ee6474b188 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -2,6 +2,8 @@ package net.minecraft.server;