diff --git a/Spigot-Server-Patches/0394-Asynchronous-chunk-IO-and-loading.patch b/Spigot-Server-Patches/0394-Asynchronous-chunk-IO-and-loading.patch index 262c5c2587..4e8f9c9aa7 100644 --- a/Spigot-Server-Patches/0394-Asynchronous-chunk-IO-and-loading.patch +++ b/Spigot-Server-Patches/0394-Asynchronous-chunk-IO-and-loading.patch @@ -1,11 +1,8 @@ -From 867219c4f28a3b9b5ea9dfe010569d45b620f1e1 Mon Sep 17 00:00:00 2001 +From 783755ca4f609df8e98ca724b6efa576e29bd117 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sat, 13 Jul 2019 09:23:10 -0700 Subject: [PATCH] Asynchronous chunk IO and loading -THIS PATCH NEEDS RE-EVALUTING AND WILL LIKELY NOT WORK AS-IS RIGHT THIS SECOND -- Pending investigation of IOWorker changes (Will do this when not too tired) - This patch re-adds a file IO thread as well as shoving de-serializing chunk NBT data onto worker threads. This patch also will shove chunk data serialization onto the same worker threads when the chunk @@ -2826,10 +2823,10 @@ index 721021791..f7156acb8 100644 ; } diff --git a/src/main/java/net/minecraft/server/IChunkLoader.java b/src/main/java/net/minecraft/server/IChunkLoader.java -index 2f95174fc..1025e01d5 100644 +index 2f95174fc..134c76065 100644 --- a/src/main/java/net/minecraft/server/IChunkLoader.java +++ b/src/main/java/net/minecraft/server/IChunkLoader.java -@@ -3,6 +3,10 @@ package net.minecraft.server; +@@ -3,37 +3,49 @@ package net.minecraft.server; import com.mojang.datafixers.DataFixer; import java.io.File; import java.io.IOException; @@ -2840,8 +2837,11 @@ index 2f95174fc..1025e01d5 100644 import java.util.function.Supplier; import javax.annotation.Nullable; -@@ -11,7 +15,9 @@ public class IChunkLoader implements AutoCloseable { - private final IOWorker a; public IOWorker getIOWorker() { return a; } // Paper - OBFHELPER +-public class IChunkLoader implements AutoCloseable { ++public class IChunkLoader extends RegionFileCache implements AutoCloseable { + +- private final IOWorker a; public IOWorker getIOWorker() { return a; } // Paper - OBFHELPER ++// private final IOWorker a; public IOWorker getIOWorker() { return a; } // Paper - OBFHELPER - nuke IOWorker protected final DataFixer b; @Nullable - private PersistentStructureLegacy c; @@ -2850,8 +2850,13 @@ index 2f95174fc..1025e01d5 100644 + private final Object persistentDataLock = new Object(); // Paper public IChunkLoader(File file, DataFixer datafixer) { ++ super(file); this.b = datafixer; -@@ -22,18 +28,23 @@ public class IChunkLoader implements AutoCloseable { +- this.a = new IOWorker(new RegionFileCache(file), "chunk"); ++// this.a = new IOWorker(new RegionFileCache(file), "chunk"); // Paper - nuke IOWorker + } + + // CraftBukkit start private boolean check(ChunkProviderServer cps, int x, int z) throws IOException { ChunkCoordIntPair pos = new ChunkCoordIntPair(x, z); if (cps != null) { @@ -2883,7 +2888,7 @@ index 2f95174fc..1025e01d5 100644 ChunkStatus status = ChunkStatus.a(level.getString("Status")); if (status != null && status.b(ChunkStatus.FEATURES)) { -@@ -64,11 +75,13 @@ public class IChunkLoader implements AutoCloseable { +@@ -64,11 +76,13 @@ public class IChunkLoader implements AutoCloseable { if (i < 1493) { nbttagcompound = GameProfileSerializer.a(this.b, DataFixTypes.CHUNK, nbttagcompound, i, 1493); if (nbttagcompound.getCompound("Level").getBoolean("hasLegacyStructureData")) { @@ -2897,14 +2902,26 @@ index 2f95174fc..1025e01d5 100644 } } -@@ -89,10 +102,12 @@ public class IChunkLoader implements AutoCloseable { - return this.a.a(chunkcoordintpair); +@@ -84,24 +98,28 @@ public class IChunkLoader implements AutoCloseable { + return nbttagcompound.hasKeyOfType("DataVersion", 99) ? nbttagcompound.getInt("DataVersion") : -1; } +- @Nullable +- public NBTTagCompound read(ChunkCoordIntPair chunkcoordintpair) throws IOException { +- return this.a.a(chunkcoordintpair); +- } +- - public void a(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) { +- this.a.a(chunkcoordintpair, nbttagcompound); ++// Paper start - nuke IOWorker ++// @Nullable ++// public NBTTagCompound read(ChunkCoordIntPair chunkcoordintpair) throws IOException { ++// return this.a.a(chunkcoordintpair); ++// } ++// + public void a(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) throws IOException { write(chunkcoordintpair, nbttagcompound); } // Paper OBFHELPER + public void write(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) throws IOException { // Paper - OBFHELPER - (Switched around for safety) - this.a.a(chunkcoordintpair, nbttagcompound); ++ super.write(chunkcoordintpair, nbttagcompound); if (this.c != null) { - this.c.a(chunkcoordintpair.pair()); + synchronized (this.persistentDataLock) { // Paper - Async chunk loading @@ -2912,6 +2929,24 @@ index 2f95174fc..1025e01d5 100644 } } +- +- public void i() { +- this.a.a().join(); +- } +- +- public void close() throws IOException { +- this.a.close(); +- } ++// ++// public void i() { ++// this.a.a().join(); ++// } ++// ++// public void close() throws IOException { ++// this.a.close(); ++// } ++// Paper end + } diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java index 25a87c2d3..c02c53b50 100644 --- a/src/main/java/net/minecraft/server/MCUtil.java @@ -2939,7 +2974,7 @@ index 8e15aba7f..edb3a6035 100644 public String getServerIp() { diff --git a/src/main/java/net/minecraft/server/NextTickListEntry.java b/src/main/java/net/minecraft/server/NextTickListEntry.java -index 8471920b8..04429a274 100644 +index e9c405fb5..33cfeabde 100644 --- a/src/main/java/net/minecraft/server/NextTickListEntry.java +++ b/src/main/java/net/minecraft/server/NextTickListEntry.java @@ -4,7 +4,7 @@ import java.util.Comparator; @@ -2999,7 +3034,7 @@ index 7a1578afa..0fb9c1e44 100644 completablefuture = (CompletableFuture) this.statusFutures.get(i); if (completablefuture != null) { diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 6a54ccb86..66bd402e9 100644 +index 6a54ccb86..fce37d0d6 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -63,7 +63,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -3064,15 +3099,16 @@ index 6a54ccb86..66bd402e9 100644 mutableboolean.setTrue(); }); } while (mutableboolean.isTrue()); -@@ -354,6 +355,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -354,18 +355,20 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { this.b(() -> { return true; }); +- this.i(); + this.world.asyncChunkTaskManager.flush(); // Paper - flush to preserve behavior compat with pre-async behaviour - this.i(); ++// this.i(); // Paper - nuke IOWorker PlayerChunkMap.LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", this.w.getName()); } else { -@@ -361,11 +363,12 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + this.visibleChunks.values().stream().filter(PlayerChunk::hasBeenLoaded).forEach((playerchunk) -> { IChunkAccess ichunkaccess = (IChunkAccess) playerchunk.getChunkSave().getNow(null); // CraftBukkit - decompile error if (ichunkaccess instanceof ProtoChunkExtension || ichunkaccess instanceof Chunk) { @@ -3392,7 +3428,14 @@ index 6a54ccb86..66bd402e9 100644 @Nullable public NBTTagCompound readChunkData(ChunkCoordIntPair chunkcoordintpair) throws IOException { // Paper - private -> public NBTTagCompound nbttagcompound = this.read(chunkcoordintpair); -@@ -927,27 +1087,47 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { +@@ -921,33 +1081,53 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + + // Paper start - chunk status cache "api" + public ChunkStatus getChunkStatusOnDiskIfCached(ChunkCoordIntPair chunkPos) { +- RegionFile regionFile = this.getIOWorker().getRegionFileCache().getRegionFileIfLoaded(chunkPos); ++ RegionFile regionFile = this.getRegionFileIfLoaded(chunkPos); + + return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); } public ChunkStatus getChunkStatusOnDisk(ChunkCoordIntPair chunkPos) throws IOException { @@ -3414,7 +3457,7 @@ index 6a54ccb86..66bd402e9 100644 } + // Paper end + synchronized (this) { // Paper - async io -+ RegionFile regionFile = this.getIOWorker().getRegionFileCache().getFile(chunkPos, false); ++ RegionFile regionFile = this.getFile(chunkPos, false); + + if (!regionFile.chunkExists(chunkPos)) { + return null; @@ -3442,7 +3485,7 @@ index 6a54ccb86..66bd402e9 100644 public void updateChunkStatusOnDisk(ChunkCoordIntPair chunkPos, @Nullable NBTTagCompound compound) throws IOException { - RegionFile regionFile = this.getIOWorker().getRegionFileCache().getFile(chunkPos, false); + synchronized (this) { -+ RegionFile regionFile = this.getIOWorker().getRegionFileCache().getFile(chunkPos, false); ++ RegionFile regionFile = this.getFile(chunkPos, false); - regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkRegionLoader.getStatus(compound)); + regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkRegionLoader.getStatus(compound)); @@ -3478,7 +3521,7 @@ index 6a54ccb86..66bd402e9 100644 + synchronized (world.getChunkProvider().playerChunkMap) { + net.minecraft.server.RegionFile file; + try { -+ file = world.getChunkProvider().playerChunkMap.getIOWorker().getRegionFileCache().getFile(chunkPos, false); ++ file = world.getChunkProvider().playerChunkMap.getFile(chunkPos, false); + } catch (IOException ex) { + throw new RuntimeException(ex); + } @@ -3499,9 +3542,18 @@ index 6a54ccb86..66bd402e9 100644 return this.m; } diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java -index 7eb87c517..551e91869 100644 +index 7eb87c517..b252684c2 100644 --- a/src/main/java/net/minecraft/server/RegionFile.java +++ b/src/main/java/net/minecraft/server/RegionFile.java +@@ -218,7 +218,7 @@ public class RegionFile implements AutoCloseable { + return (i + 4096 - 1) / 4096; + } + +- public boolean b(ChunkCoordIntPair chunkcoordintpair) { ++ public synchronized boolean b(ChunkCoordIntPair chunkcoordintpair) { // Paper - synchronized + int i = this.getOffset(chunkcoordintpair); + + if (i == 0) { @@ -373,7 +373,7 @@ public class RegionFile implements AutoCloseable { return chunkcoordintpair.j() + chunkcoordintpair.k() * 32; } @@ -3512,10 +3564,19 @@ index 7eb87c517..551e91869 100644 try { this.c(); diff --git a/src/main/java/net/minecraft/server/RegionFileCache.java b/src/main/java/net/minecraft/server/RegionFileCache.java -index 1a6be7c6d..9a0fdec47 100644 +index 1a6be7c6d..386f47dc8 100644 --- a/src/main/java/net/minecraft/server/RegionFileCache.java +++ b/src/main/java/net/minecraft/server/RegionFileCache.java -@@ -20,7 +20,7 @@ public final class RegionFileCache implements AutoCloseable { +@@ -9,7 +9,7 @@ import java.io.File; + import java.io.IOException; + import javax.annotation.Nullable; + +-public final class RegionFileCache implements AutoCloseable { ++public class RegionFileCache implements AutoCloseable { // Paper - no final + + public final Long2ObjectLinkedOpenHashMap cache = new Long2ObjectLinkedOpenHashMap(); + private final File b; +@@ -20,12 +20,12 @@ public final class RegionFileCache implements AutoCloseable { // Paper start @@ -3524,6 +3585,21 @@ index 1a6be7c6d..9a0fdec47 100644 return this.cache.getAndMoveToFirst(ChunkCoordIntPair.pair(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ())); } + // Paper end +- public RegionFile getFile(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - private > public ++ public synchronized RegionFile getFile(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - private > public, synchronize + long i = ChunkCoordIntPair.pair(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()); + RegionFile regionfile = (RegionFile) this.cache.getAndMoveToFirst(i); + +@@ -126,7 +126,7 @@ public final class RegionFileCache implements AutoCloseable { + // Paper end + } + +- public void close() throws IOException { ++ public synchronized void close() throws IOException { // Paper -> synchronized + ObjectIterator objectiterator = this.cache.values().iterator(); + + while (objectiterator.hasNext()) { @@ -138,7 +138,7 @@ public final class RegionFileCache implements AutoCloseable { } @@ -3534,19 +3610,35 @@ index 1a6be7c6d..9a0fdec47 100644 return regionfile != null ? regionfile.chunkExists(pos) : false; diff --git a/src/main/java/net/minecraft/server/RegionFileSection.java b/src/main/java/net/minecraft/server/RegionFileSection.java -index db9f0196b..e7ea04861 100644 +index db9f0196b..a6d8ef5eb 100644 --- a/src/main/java/net/minecraft/server/RegionFileSection.java +++ b/src/main/java/net/minecraft/server/RegionFileSection.java -@@ -25,7 +25,7 @@ public class RegionFileSection implements AutoC +@@ -20,28 +20,29 @@ import javax.annotation.Nullable; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + +-public class RegionFileSection implements AutoCloseable { ++public class RegionFileSection extends RegionFileCache implements AutoCloseable { // Paper - nuke IOWorker + private static final Logger LOGGER = LogManager.getLogger(); - private final IOWorker b; +- private final IOWorker b; ++// private final IOWorker b; private final Long2ObjectMap> c = new Long2ObjectOpenHashMap(); - private final LongLinkedOpenHashSet d = new LongLinkedOpenHashSet(); + protected final LongLinkedOpenHashSet d = new LongLinkedOpenHashSet(); // Paper - private -> protected private final BiFunction, R> e; private final Function f; private final DataFixer g; -@@ -40,8 +40,8 @@ public class RegionFileSection implements AutoC + private final DataFixTypes h; + + public RegionFileSection(File file, BiFunction, R> bifunction, Function function, DataFixer datafixer, DataFixTypes datafixtypes) { ++ super(file); // Paper - nuke IOWorker + this.e = bifunction; + this.f = function; + this.g = datafixer; + this.h = datafixtypes; +- this.b = new IOWorker(new RegionFileCache(file), file.getName()); ++// this.b = new IOWorker(new RegionFileCache(file), file.getName()); // Paper - nuke IOWorker } protected void a(BooleanSupplier booleansupplier) { @@ -3557,7 +3649,7 @@ index db9f0196b..e7ea04861 100644 this.d(chunkcoordintpair); } -@@ -95,7 +95,12 @@ public class RegionFileSection implements AutoC +@@ -95,13 +96,18 @@ public class RegionFileSection implements AutoC } private void b(ChunkCoordIntPair chunkcoordintpair) { @@ -3571,7 +3663,14 @@ index db9f0196b..e7ea04861 100644 } @Nullable -@@ -143,7 +148,7 @@ public class RegionFileSection implements AutoC + private NBTTagCompound c(ChunkCoordIntPair chunkcoordintpair) { + try { +- return this.b.a(chunkcoordintpair); ++ return this.read(chunkcoordintpair); // Paper - nuke IOWorker + } catch (IOException ioexception) { + RegionFileSection.LOGGER.error("Error reading chunk {} data from disk", chunkcoordintpair, ioexception); + return null; +@@ -143,17 +149,31 @@ public class RegionFileSection implements AutoC } private void d(ChunkCoordIntPair chunkcoordintpair) { @@ -3580,7 +3679,11 @@ index db9f0196b..e7ea04861 100644 NBTBase nbtbase = (NBTBase) dynamic.getValue(); if (nbtbase instanceof NBTTagCompound) { -@@ -154,6 +159,20 @@ public class RegionFileSection implements AutoC +- this.b.a(chunkcoordintpair, (NBTTagCompound) nbtbase); ++ try { this.write(chunkcoordintpair, (NBTTagCompound) nbtbase); } catch (IOException ioexception) { RegionFileSection.LOGGER.error("Error writing data to disk", ioexception); } // Paper - nuke IOWorker // TODO make this write async + } else { + RegionFileSection.LOGGER.error("Expected compound tag, got {}", nbtbase); + } } @@ -3601,7 +3704,7 @@ index db9f0196b..e7ea04861 100644 private Dynamic a(ChunkCoordIntPair chunkcoordintpair, DynamicOps dynamicops) { Map map = Maps.newHashMap(); -@@ -190,9 +209,9 @@ public class RegionFileSection implements AutoC +@@ -190,9 +210,9 @@ public class RegionFileSection implements AutoC public void a(ChunkCoordIntPair chunkcoordintpair) { if (!this.d.isEmpty()) { for (int i = 0; i < 16; ++i) { @@ -3613,10 +3716,17 @@ index db9f0196b..e7ea04861 100644 this.d(chunkcoordintpair); return; } -@@ -204,4 +223,21 @@ public class RegionFileSection implements AutoC - public void close() throws IOException { - this.b.close(); +@@ -201,7 +221,26 @@ public class RegionFileSection implements AutoC + } + +- public void close() throws IOException { +- this.b.close(); ++// Paper start - nuke IOWorker ++// public void close() throws IOException { ++// this.b.close(); ++// } ++// Paper end + + // Paper start - get data function + public NBTTagCompound getData(ChunkCoordIntPair chunkcoordintpair) { @@ -3632,7 +3742,7 @@ index db9f0196b..e7ea04861 100644 + } + } + return null; -+ } + } + // Paper end } diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java @@ -3737,7 +3847,7 @@ index c999f8c9b..b59ef1a63 100644 HAS_SPACE(VillagePlaceRecord::d), IS_OCCUPIED(VillagePlaceRecord::e), ANY((villageplacerecord) -> { diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 8ea9b34a1..fecbe7914 100644 +index 049d4ef4e..59b2fc629 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -81,6 +81,79 @@ public class WorldServer extends World { @@ -3762,7 +3872,7 @@ index 8ea9b34a1..fecbe7914 100644 + RegionFile file; + + try { -+ file = WorldServer.this.getChunkProvider().playerChunkMap.getVillagePlace().getRegionFile(new ChunkCoordIntPair(chunkX, chunkZ), false); ++ file = WorldServer.this.getChunkProvider().playerChunkMap.getVillagePlace().getFile(new ChunkCoordIntPair(chunkX, chunkZ), false); + } catch (java.io.IOException ex) { + throw new RuntimeException(ex); + } @@ -3797,7 +3907,7 @@ index 8ea9b34a1..fecbe7914 100644 + RegionFile file; + + try { -+ file = WorldServer.this.getChunkProvider().playerChunkMap.getRegionFile(new ChunkCoordIntPair(chunkX, chunkZ), false); ++ file = WorldServer.this.getChunkProvider().playerChunkMap.getFile(new ChunkCoordIntPair(chunkX, chunkZ), false); + } catch (java.io.IOException ex) { + throw new RuntimeException(ex); + } @@ -3920,5 +4030,5 @@ index a1d93200e..6ca0ebfde 100644 log.log( Level.SEVERE, "------------------------------" ); // -- -2.24.1 +2.17.1 diff --git a/removed/1.15/0393-Fix-World-isChunkGenerated-calls.patch b/removed/1.15/0393-Fix-World-isChunkGenerated-calls.patch deleted file mode 100644 index 5877458fa3..0000000000 --- a/removed/1.15/0393-Fix-World-isChunkGenerated-calls.patch +++ /dev/null @@ -1,379 +0,0 @@ -From 14f4011c2f16754e3f39826237f4822c3b6446b1 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Sat, 15 Jun 2019 08:54:33 -0700 -Subject: [PATCH] Fix World#isChunkGenerated calls - -Optimize World#loadChunk() too -This patch also adds a chunk status cache on region files (note that -its only purpose is to cache the status on DISK) - -diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 8689e0f9f..56761afdf 100644 ---- a/src/main/java/net/minecraft/server/ChunkProviderServer.java -+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -28,7 +28,7 @@ public class ChunkProviderServer extends IChunkProvider { - private final WorldServer world; - private final Thread serverThread; - private final LightEngineThreaded lightEngine; -- private final ChunkProviderServer.a serverThreadQueue; -+ public final ChunkProviderServer.a serverThreadQueue; // Paper private -> public - public final PlayerChunkMap playerChunkMap; - private final WorldPersistentData worldPersistentData; - private long lastTickTime; -@@ -109,6 +109,21 @@ public class ChunkProviderServer extends IChunkProvider { - - return playerChunk.getFullChunk(); - } -+ -+ @Nullable -+ public IChunkAccess getChunkAtImmediately(int x, int z) { -+ long k = ChunkCoordIntPair.pair(x, z); -+ -+ // Note: Bypass cache to make this MT-Safe -+ -+ PlayerChunk playerChunk = this.getChunk(k); -+ if (playerChunk == null) { -+ return null; -+ } -+ -+ return playerChunk.getAvailableChunkNow(); -+ -+ } - // Paper end - - @Nullable -diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index e778c2e85..73f93e494 100644 ---- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java -+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -@@ -410,6 +410,17 @@ public class ChunkRegionLoader { - return nbttagcompound; - } - -+ // Paper start -+ public static ChunkStatus getStatus(NBTTagCompound compound) { -+ if (compound == null) { -+ return null; -+ } -+ -+ // Note: Copied from below -+ return ChunkStatus.getStatus(compound.getCompound("Level").getString("Status")); -+ } -+ // Paper end -+ - public static ChunkStatus.Type a(@Nullable NBTTagCompound nbttagcompound) { - if (nbttagcompound != null) { - ChunkStatus chunkstatus = ChunkStatus.a(nbttagcompound.getCompound("Level").getString("Status")); -diff --git a/src/main/java/net/minecraft/server/ChunkStatus.java b/src/main/java/net/minecraft/server/ChunkStatus.java -index dd1822d6f..e324989b4 100644 ---- a/src/main/java/net/minecraft/server/ChunkStatus.java -+++ b/src/main/java/net/minecraft/server/ChunkStatus.java -@@ -176,6 +176,7 @@ public class ChunkStatus { - return this.s; - } - -+ public ChunkStatus getPreviousStatus() { return this.e(); } // Paper - OBFHELPER - public ChunkStatus e() { - return this.u; - } -@@ -196,6 +197,17 @@ public class ChunkStatus { - return this.y; - } - -+ // Paper start -+ public static ChunkStatus getStatus(String name) { -+ try { -+ // We need this otherwise we return EMPTY for invalid names -+ MinecraftKey key = new MinecraftKey(name); -+ return IRegistry.CHUNK_STATUS.getOptional(key).orElse(null); -+ } catch (Exception ex) { -+ return null; // invalid name -+ } -+ } -+ // Paper end - public static ChunkStatus a(String s) { - return (ChunkStatus) IRegistry.CHUNK_STATUS.get(MinecraftKey.a(s)); - } -diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 14a176d61..98590e233 100644 ---- a/src/main/java/net/minecraft/server/PlayerChunk.java -+++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -70,6 +70,19 @@ public class PlayerChunk { - Either either = (Either) statusFuture.getNow(null); - return either == null ? null : (Chunk) either.left().orElse(null); - } -+ -+ public IChunkAccess getAvailableChunkNow() { -+ // TODO can we just getStatusFuture(EMPTY)? -+ for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getPreviousStatus(); curr != next; curr = next, next = next.getPreviousStatus()) { -+ CompletableFuture> future = this.getStatusFutureUnchecked(curr); -+ Either either = future.getNow(null); -+ if (either == null || !either.left().isPresent()) { -+ continue; -+ } -+ return either.left().get(); -+ } -+ return null; -+ } - // Paper end - - 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 6f1e48ba4..eb49e9021 100644 ---- a/src/main/java/net/minecraft/server/PlayerChunkMap.java -+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java -@@ -897,11 +897,61 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - } - - @Nullable -- private NBTTagCompound readChunkData(ChunkCoordIntPair chunkcoordintpair) throws IOException { -+ public NBTTagCompound readChunkData(ChunkCoordIntPair chunkcoordintpair) throws IOException { // Paper - private -> public - NBTTagCompound nbttagcompound = this.read(chunkcoordintpair); - -- return nbttagcompound == null ? null : this.getChunkData(this.world.getWorldProvider().getDimensionManager(), this.m, nbttagcompound, chunkcoordintpair, world); // CraftBukkit -+ // Paper start - Cache chunk status on disk -+ if (nbttagcompound == null) { -+ return null; -+ } -+ -+ nbttagcompound = this.getChunkData(this.world.getWorldProvider().getDimensionManager(), this.m, nbttagcompound, chunkcoordintpair, world); // CraftBukkit -+ if (nbttagcompound == null) { -+ return null; -+ } -+ -+ 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)); -+ } -+ -+ public IChunkAccess getUnloadingChunk(int chunkX, int chunkZ) { -+ PlayerChunk chunkHolder = this.pendingUnload.get(ChunkCoordIntPair.pair(chunkX, chunkZ)); -+ return chunkHolder == null ? null : chunkHolder.getAvailableChunkNow(); - } -+ // Paper end - - boolean isOutsideOfRange(ChunkCoordIntPair chunkcoordintpair) { - // Spigot start -diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java -index ccc3d6c7a..b487e8060 100644 ---- a/src/main/java/net/minecraft/server/RegionFile.java -+++ b/src/main/java/net/minecraft/server/RegionFile.java -@@ -31,6 +31,30 @@ public class RegionFile implements AutoCloseable { - private final int[] d = new int[1024]; private final int[] timestamps = d; // Paper - OBFHELPER - private final List e; // PAIL freeSectors - -+ // Paper start - Cache chunk status -+ private final ChunkStatus[] statuses = new ChunkStatus[32 * 32]; -+ -+ private boolean closed; -+ -+ // invoked on write/read -+ public void setStatus(int x, int z, ChunkStatus status) { -+ if (this.closed) { -+ // We've used an invalid region file. -+ throw new IllegalStateException("RegionFile is closed"); -+ } -+ this.statuses[this.getChunkLocation(new ChunkCoordIntPair(x, z))] = status; -+ } -+ -+ public ChunkStatus getStatusIfCached(int x, int z) { -+ if (this.closed) { -+ // We've used an invalid region file. -+ throw new IllegalStateException("RegionFile is closed"); -+ } -+ final int location = this.getChunkLocation(new ChunkCoordIntPair(x, z)); -+ 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 -@@ -291,6 +315,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; - } -@@ -304,6 +329,7 @@ public class RegionFile implements AutoCloseable { - this.c[j] = i; // Spigot - move this to after the write - } - -+ private final int getChunkLocation(ChunkCoordIntPair chunkcoordintpair) { return this.f(chunkcoordintpair); } // Paper - OBFHELPER - private int f(ChunkCoordIntPair chunkcoordintpair) { - return chunkcoordintpair.j() + chunkcoordintpair.k() * 32; - } -@@ -318,6 +344,7 @@ public class RegionFile implements AutoCloseable { - } - - public void close() throws IOException { -+ this.closed = true; // Paper - this.b.close(); - } - -diff --git a/src/main/java/net/minecraft/server/RegionFileCache.java b/src/main/java/net/minecraft/server/RegionFileCache.java -index 6f34d8aea..d2b328945 100644 ---- a/src/main/java/net/minecraft/server/RegionFileCache.java -+++ b/src/main/java/net/minecraft/server/RegionFileCache.java -@@ -47,6 +47,12 @@ public abstract class RegionFileCache implements AutoCloseable { - // Paper start - } - -+ // Paper start -+ public RegionFile getRegionFileIfLoaded(ChunkCoordIntPair chunkcoordintpair) { -+ return this.cache.getAndMoveToFirst(ChunkCoordIntPair.pair(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ())); -+ } -+ // Paper end -+ - public RegionFile getRegionFile(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { return this.a(chunkcoordintpair, existingOnly); } // Paper - OBFHELPER - private RegionFile a(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit - long i = ChunkCoordIntPair.pair(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()); -@@ -110,6 +116,7 @@ public abstract class RegionFileCache implements AutoCloseable { - try { - NBTCompressedStreamTools.writeNBT(nbttagcompound, out); - out.close(); -+ regionfile.setStatus(chunk.x, chunk.z, ChunkRegionLoader.getStatus(nbttagcompound)); // Paper - cache status on disk - regionfile.setOversized(chunkX, chunkZ, false); - } catch (RegionFile.ChunkTooLargeException ignored) { - printOversizedLog("ChunkTooLarge! Someone is trying to duplicate.", regionfile.file, chunkX, chunkZ); -@@ -127,6 +134,7 @@ public abstract class RegionFileCache implements AutoCloseable { - if (SIZE_THRESHOLD == OVERZEALOUS_THRESHOLD) { - resetFilterThresholds(); - } -+ regionfile.setStatus(chunk.x, chunk.z, ChunkRegionLoader.getStatus(nbttagcompound)); // Paper - cache status on disk - } catch (RegionFile.ChunkTooLargeException e) { - 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 e42bd2638..2227de3bf 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -18,6 +18,7 @@ import java.util.Objects; - import java.util.Random; - import java.util.Set; - import java.util.UUID; -+import java.util.concurrent.CompletableFuture; - import java.util.function.Predicate; - import java.util.stream.Collectors; - import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -@@ -408,8 +409,22 @@ public class CraftWorld implements World { - - @Override - public boolean isChunkGenerated(int x, int z) { -+ // Paper start - Fix this method -+ if (!Bukkit.isPrimaryThread()) { -+ return CompletableFuture.supplyAsync(() -> { -+ return CraftWorld.this.isChunkGenerated(x, z); -+ }, world.getChunkProvider().serverThreadQueue).join(); -+ } -+ IChunkAccess chunk = world.getChunkProvider().getChunkAtImmediately(x, z); -+ if (chunk == null) { -+ chunk = world.getChunkProvider().playerChunkMap.getUnloadingChunk(x, z); -+ } -+ if (chunk != null) { -+ return chunk instanceof ProtoChunkExtension || chunk instanceof net.minecraft.server.Chunk; -+ } - try { -- return world.getChunkProvider().getChunkAtIfCachedImmediately(x, z) != null || world.getChunkProvider().playerChunkMap.chunkExists(new ChunkCoordIntPair(x, z)); // Paper -+ return world.getChunkProvider().playerChunkMap.getChunkStatusOnDisk(new ChunkCoordIntPair(x, z)) == ChunkStatus.FULL; -+ // Paper end - } catch (IOException ex) { - throw new RuntimeException(ex); - } -@@ -521,20 +536,49 @@ public class CraftWorld implements World { - @Override - 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 -+ // 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); -- } -+ if (!generate) { - -- if (chunk instanceof net.minecraft.server.Chunk) { -- world.getChunkProvider().addTicket(TicketType.PLUGIN, new ChunkCoordIntPair(x, z), 1, Unit.INSTANCE); -- return true; -+ IChunkAccess immediate = world.getChunkProvider().getChunkAtImmediately(x, z); -+ if (immediate == null) { -+ immediate = world.getChunkProvider().playerChunkMap.getUnloadingChunk(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); -+ world.getChunkAt(x, z); // make sure we're at ticket level 32 or lower -+ return true; -+ } -+ -+ net.minecraft.server.RegionFile file; -+ try { -+ file = world.getChunkProvider().playerChunkMap.getRegionFile(chunkPos, false); -+ } catch (IOException ex) { -+ throw new RuntimeException(ex); -+ } -+ -+ ChunkStatus status = file.getStatusIfCached(x, z); -+ if (!file.chunkExists(chunkPos) || (status != null && status != ChunkStatus.FULL)) { -+ return false; -+ } -+ -+ 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; -+ // Paper end - } - - @Override --- -2.24.0 -