diff options
Diffstat (limited to 'patches/unapplied/server/1001-Fix-World-isChunkGenerated-calls.patch')
-rw-r--r-- | patches/unapplied/server/1001-Fix-World-isChunkGenerated-calls.patch | 243 |
1 files changed, 0 insertions, 243 deletions
diff --git a/patches/unapplied/server/1001-Fix-World-isChunkGenerated-calls.patch b/patches/unapplied/server/1001-Fix-World-isChunkGenerated-calls.patch deleted file mode 100644 index 37c3afba66..0000000000 --- a/patches/unapplied/server/1001-Fix-World-isChunkGenerated-calls.patch +++ /dev/null @@ -1,243 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf <[email protected]> -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/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index d6ecee1db17cb9eaeffa94b3d8dd150238fdefe5..1d2d4d38ff3414896d07f7f4e40d0edb9a8c3993 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -735,9 +735,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - // Paper end - - private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos chunkPos) { -- return this.read(chunkPos).thenApplyAsync((optional) -> { -- return optional.map((nbttagcompound) -> this.upgradeChunkTag(nbttagcompound, chunkPos)); // CraftBukkit -- }, Util.backgroundExecutor()); -+ // Paper start - Cache chunk status on disk -+ try { -+ return CompletableFuture.completedFuture(Optional.ofNullable(this.readConvertChunkSync(chunkPos))); -+ } catch (Throwable thr) { -+ return CompletableFuture.failedFuture(thr); -+ } -+ // Paper end - Cache chunk status on disk - } - - // CraftBukkit start -@@ -746,6 +750,60 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - // CraftBukkit end - } - -+ // Paper start - Cache chunk status on disk -+ @Nullable -+ public CompoundTag readConvertChunkSync(ChunkPos pos) throws IOException { -+ CompoundTag nbttagcompound = this.readSync(pos); -+ if (nbttagcompound == null) { -+ return null; -+ } -+ -+ nbttagcompound = this.upgradeChunkTag(nbttagcompound, pos); // CraftBukkit -+ if (nbttagcompound == null) { -+ return null; -+ } -+ -+ this.updateChunkStatusOnDisk(pos, nbttagcompound); -+ -+ return nbttagcompound; -+ } -+ -+ public ChunkStatus getChunkStatusOnDiskIfCached(ChunkPos chunkPos) { -+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFileIfLoaded(chunkPos); -+ -+ return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); -+ } -+ -+ public ChunkStatus getChunkStatusOnDisk(ChunkPos chunkPos) throws IOException { -+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, true); -+ -+ if (regionFile == null || !regionFileCache.chunkExists(chunkPos)) { -+ return null; -+ } -+ -+ ChunkStatus status = regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); -+ -+ if (status != null) { -+ return status; -+ } -+ -+ this.readChunk(chunkPos); -+ -+ return regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); -+ } -+ -+ public void updateChunkStatusOnDisk(ChunkPos chunkPos, @Nullable CompoundTag compound) throws IOException { -+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, false); -+ -+ regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkSerializer.getStatus(compound)); -+ } -+ -+ public ChunkAccess getUnloadingChunk(int chunkX, int chunkZ) { -+ ChunkHolder chunkHolder = io.papermc.paper.chunk.system.ChunkSystem.getUnloadingChunkHolder(this.level, chunkX, chunkZ); -+ return chunkHolder == null ? null : chunkHolder.getAvailableChunkNow(); -+ } -+ // Paper end - Cache chunk status on disk -+ - public boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) { // Paper - public - // Spigot start - return this.anyPlayerCloseEnoughForSpawning(pos, false); -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java -index f994f91dd4b4a0a6d540a5605af0b6623e229f84..cf43daa019b239464401784938d01af83f9da47c 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java -@@ -52,6 +52,29 @@ public class RegionFile implements AutoCloseable { - @VisibleForTesting - protected final RegionBitmap usedSectors; - public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(); // Paper -+ // 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]; -+ -+ private boolean closed; -+ -+ // invoked on write/read -+ public void setStatus(int x, int z, net.minecraft.world.level.chunk.status.ChunkStatus status) { -+ if (this.closed) { -+ // We've used an invalid region file. -+ throw new IllegalStateException("RegionFile is closed"); -+ } -+ this.statuses[getChunkLocation(x, z)] = status; -+ } -+ -+ public net.minecraft.world.level.chunk.status.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 = getChunkLocation(x, z); -+ return this.statuses[location]; -+ } -+ // Paper end - Cache chunk status - - public RegionFile(RegionStorageInfo storageKey, Path directory, Path path, boolean dsync) throws IOException { - this(storageKey, directory, path, RegionFileVersion.getCompressionFormat(), dsync); // Paper - Configurable region compression format -@@ -417,6 +440,7 @@ public class RegionFile implements AutoCloseable { - return this.getOffset(pos) != 0; - } - -+ private static int getChunkLocation(int x, int z) { return (x & 31) + (z & 31) * 32; } // Paper - Cache chunk status; OBFHELPER - sort of, mirror of logic below - private static int getOffsetIndex(ChunkPos pos) { - return pos.getRegionLocalX() + pos.getRegionLocalZ() * 32; - } -@@ -427,6 +451,7 @@ public class RegionFile implements AutoCloseable { - synchronized (this) { - try { - // Paper end -+ this.closed = true; // Paper - Cache chunk status - try { - this.padToFullSector(); - } finally { -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -index c2838ae91b7f078369b63503df57a1eb5d2b0df5..c33640859aab837c85f3e860fe2241a0e78bb09a 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 -@@ -268,6 +268,7 @@ public class RegionFileStorage implements AutoCloseable { - - try { - NbtIo.write(nbt, (DataOutput) dataoutputstream); -+ regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - Cache chunk status - regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone - // Paper start - don't write garbage data to disk if writing serialization fails - dataoutputstream.close(); // Only write if successful -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 9c06c3729b09726e1da6ff8fb975cef2aeba9643..8f50d893f8f9dea306756b640abd2373ee028a86 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -391,9 +391,23 @@ public class CraftWorld extends CraftRegionAccessor implements World { - - @Override - public boolean isChunkGenerated(int x, int z) { -+ // Paper start - Fix this method -+ if (!Bukkit.isPrimaryThread()) { -+ return java.util.concurrent.CompletableFuture.supplyAsync(() -> { -+ return CraftWorld.this.isChunkGenerated(x, z); -+ }, world.getChunkSource().mainThreadProcessor).join(); -+ } -+ ChunkAccess chunk = world.getChunkSource().getChunkAtImmediately(x, z); -+ if (chunk == null) { -+ chunk = world.getChunkSource().chunkMap.getUnloadingChunk(x, z); -+ } -+ if (chunk != null) { -+ return chunk instanceof ImposterProtoChunk || chunk instanceof net.minecraft.world.level.chunk.LevelChunk; -+ } - try { -- return this.isChunkLoaded(x, z) || this.world.getChunkSource().chunkMap.read(new ChunkPos(x, z)).get().isPresent(); -- } catch (InterruptedException | ExecutionException ex) { -+ return world.getChunkSource().chunkMap.getChunkStatusOnDisk(new ChunkPos(x, z)) == ChunkStatus.FULL; -+ } catch (java.io.IOException ex) { -+ // Paper end - Fix this method - throw new RuntimeException(ex); - } - } -@@ -572,20 +586,48 @@ public class CraftWorld extends CraftRegionAccessor implements World { - public boolean loadChunk(int x, int z, boolean generate) { - org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot - warnUnsafeChunk("loading a faraway chunk", x, z); // Paper -- ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper -- -- // If generate = false, but the chunk already exists, we will get this back. -- if (chunk instanceof ImposterProtoChunk) { -- // We then cycle through again to get the full chunk immediately, rather than after the ticket addition -- chunk = this.world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true); -- } -- -- if (chunk instanceof net.minecraft.world.level.chunk.LevelChunk) { -- this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE); // Paper -+ // Paper start - Optimize this method -+ ChunkPos chunkPos = new ChunkPos(x, z); -+ ChunkAccess immediate = world.getChunkSource().getChunkAtIfLoadedImmediately(x, z); -+ if (immediate != null) { -+ // Plugins should use plugin tickets instead of this method to keep a chunk perpetually loaded - return true; - } - -- return false; -+ if (!generate) { -+ immediate = world.getChunkSource().chunkMap.getUnloadingChunk(x, z); -+ if (immediate != null) { -+ if (!(immediate instanceof ImposterProtoChunk) && !(immediate instanceof net.minecraft.world.level.chunk.LevelChunk)) { -+ return false; // not full status -+ } -+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 0, Unit.INSTANCE); // Paper -+ world.getChunk(x, z); // make sure we're at ticket level 32 or lower -+ return true; -+ } -+ net.minecraft.world.level.chunk.storage.RegionFile file; -+ try { -+ file = world.getChunkSource().chunkMap.regionFileCache.getRegionFile(chunkPos, false); -+ } catch (java.io.IOException ex) { -+ throw new RuntimeException(ex); -+ } -+ -+ ChunkStatus status = file.getStatusIfCached(x, z); -+ if (!file.hasChunk(chunkPos) || (status != null && status != ChunkStatus.FULL)) { -+ return false; -+ } -+ -+ ChunkAccess chunk = world.getChunkSource().getChunk(x, z, ChunkStatus.EMPTY, true); -+ if (!(chunk instanceof ImposterProtoChunk) && !(chunk instanceof net.minecraft.world.level.chunk.LevelChunk)) { -+ return false; -+ } -+ -+ // fall through to load -+ // we do this so we do not re-read the chunk data on disk -+ } -+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 0, Unit.INSTANCE); // Paper -+ world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true); -+ return true; -+ // Paper end - Optimize this method - } - - @Override |