diff --git a/patches/server-remapped/0360-Fix-World-isChunkGenerated-calls.patch b/patches/server/0330-Fix-World-isChunkGenerated-calls.patch similarity index 67% rename from patches/server-remapped/0360-Fix-World-isChunkGenerated-calls.patch rename to patches/server/0330-Fix-World-isChunkGenerated-calls.patch index 6f00b2b7a..953566fa3 100644 --- a/patches/server-remapped/0360-Fix-World-isChunkGenerated-calls.patch +++ b/patches/server/0330-Fix-World-isChunkGenerated-calls.patch @@ -8,12 +8,12 @@ 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/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java -index a89b9dab043ad4536014141d5a942670b4152a95..7010e0a970462d2b2e1b5696a1a49dba9ea60935 100644 +index fa6d91886d433c0b7d77c1d059a23ee194ae14a4..56e292e6550b19d0cae9ebad369da730ca1cabd8 100644 --- a/src/main/java/net/minecraft/server/level/ChunkHolder.java +++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java -@@ -141,6 +141,19 @@ public class ChunkHolder { +@@ -111,6 +111,19 @@ public class ChunkHolder { Either either = (Either) statusFuture.getNow(null); - return either == null ? null : (LevelChunk) either.left().orElse(null); + return (either == null) ? null : (LevelChunk) either.left().orElse(null); } + + public ChunkAccess getAvailableChunkNow() { @@ -28,15 +28,23 @@ index a89b9dab043ad4536014141d5a942670b4152a95..7010e0a970462d2b2e1b5696a1a49dba + } + return null; + } - // Paper end + // CraftBukkit end public CompletableFuture> getFutureIfPresentUnchecked(ChunkStatus leastStatus) { diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 7585b6f87b72f53deccbcb8627a13503921fc682..0aac29de933c84c34cb24e204e8fcc7010060d8f 100644 +index 25826b41812f2d9ac29806ad7a9242e5674376fc..dd7de2c3db10d9d606d47c52eba40e71034fc11a 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -991,12 +991,61 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } +@@ -84,6 +84,7 @@ import net.minecraft.world.level.chunk.ProtoChunk; + import net.minecraft.world.level.chunk.UpgradeData; + import net.minecraft.world.level.chunk.storage.ChunkSerializer; + import net.minecraft.world.level.chunk.storage.ChunkStorage; ++import net.minecraft.world.level.chunk.storage.RegionFile; + import net.minecraft.world.level.entity.ChunkStatusUpdateListener; + import net.minecraft.world.level.levelgen.structure.StructureStart; + import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; +@@ -1083,12 +1084,61 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Paper end @Nullable - private CompoundTag readChunk(ChunkPos pos) throws IOException { @@ -60,15 +68,16 @@ index 7585b6f87b72f53deccbcb8627a13503921fc682..0aac29de933c84c34cb24e204e8fcc70 + + // Paper start - chunk status cache "api" + public ChunkStatus getChunkStatusOnDiskIfCached(ChunkPos chunkPos) { -+ RegionFile regionFile = this.getIOWorker().getRegionFileCache().getRegionFileIfLoaded(chunkPos); -+ ++ RegionFile regionFile = regionFileCache.getRegionFileIfLoaded(chunkPos); + +- return nbttagcompound == null ? null : this.getChunkData(this.level.getTypeKey(), this.overworldDataStorage, nbttagcompound, pos, level); // CraftBukkit + return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); -+ } -+ + } + + public ChunkStatus getChunkStatusOnDisk(ChunkPos chunkPos) throws IOException { -+ RegionFile regionFile = this.getIOWorker().getRegionFileCache().getFile(chunkPos, true); ++ RegionFile regionFile = regionFileCache.getFile(chunkPos, true); + -+ if (regionFile == null || !regionFile.chunkExists(chunkPos)) { ++ if (regionFile == null || !regionFileCache.chunkExists(chunkPos)) { + return null; + } + @@ -79,13 +88,12 @@ index 7585b6f87b72f53deccbcb8627a13503921fc682..0aac29de933c84c34cb24e204e8fcc70 + } + + this.readChunk(chunkPos); - -- return nbttagcompound == null ? null : this.getChunkData(this.level.getTypeKey(), this.overworldDataStorage, nbttagcompound, pos, level); // CraftBukkit ++ + return regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); - } - ++ } ++ + public void updateChunkStatusOnDisk(ChunkPos chunkPos, @Nullable CompoundTag compound) throws IOException { -+ RegionFile regionFile = this.getIOWorker().getRegionFileCache().getFile(chunkPos, false); ++ RegionFile regionFile = regionFileCache.getFile(chunkPos, false); + + regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkSerializer.getStatus(compound)); + } @@ -96,58 +104,50 @@ index 7585b6f87b72f53deccbcb8627a13503921fc682..0aac29de933c84c34cb24e204e8fcc70 + } + // Paper end + - boolean noPlayersCloseForSpawning(ChunkPos chunkcoordintpair) { + boolean noPlayersCloseForSpawning(ChunkPos chunkPos) { // Spigot start - return isOutsideOfRange(chunkcoordintpair, false); + return this.isOutsideOfRange(chunkPos, false); diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 6d33c1ee44bc732b58d18a8f6b0fd4bbdcb2dcd6..1e8ac0110badbf2d1c2336168c3e11991667c782 100644 +index 77bb2fb280293da4a38f9acc6503e84997ecb399..c47d1772044913475a60292162ef4be594bed4c6 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -52,7 +52,7 @@ public class ServerChunkCache extends ChunkSource { - private final ServerLevel level; - public final Thread mainThread; // Paper - private -> public - private final ThreadedLevelLightEngine lightEngine; -- private final ServerChunkCache.MainThreadExecutor mainThreadProcessor; +@@ -52,10 +52,10 @@ public class ServerChunkCache extends ChunkSource { + private static final List CHUNK_STATUSES = ChunkStatus.getStatusList(); public static final List getPossibleChunkStatuses() { return ServerChunkCache.CHUNK_STATUSES; } // Paper - OBFHELPER + private final DistanceManager distanceManager; + public final ChunkGenerator generator; +- final ServerLevel level; +- public final Thread mainThread; // Paper - package-private -> public +- final ThreadedLevelLightEngine lightEngine; +- public final ServerChunkCache.MainThreadExecutor mainThreadProcessor; // Paper - private -> public ++ private final ServerLevel level; ++ public final Thread mainThread; // Paper - private -> public ++ private final ThreadedLevelLightEngine lightEngine; + public final ServerChunkCache.MainThreadExecutor mainThreadProcessor; // Paper private -> public public final ChunkMap chunkMap; private final DimensionDataStorage dataStorage; private long lastInhabitedUpdate; -@@ -317,6 +317,21 @@ public class ServerChunkCache extends ChunkSource { - - return ret; +@@ -324,6 +324,7 @@ public class ServerChunkCache extends ChunkSource { } -+ -+ @Nullable -+ public ChunkAccess getChunkAtImmediately(int x, int z) { -+ long k = ChunkPos.asLong(x, z); -+ -+ // Note: Bypass cache to make this MT-Safe -+ -+ ChunkHolder playerChunk = this.getVisibleChunkIfPresent(k); -+ if (playerChunk == null) { -+ return null; -+ } -+ -+ return playerChunk.getAvailableChunkNow(); -+ -+ } // Paper end - - @Nullable -@@ -771,7 +786,7 @@ public class ServerChunkCache extends ChunkSource { + // Paper start - async chunk io ++ @Nullable + public ChunkAccess getChunkAtImmediately(int x, int z) { + ChunkHolder holder = this.chunkMap.getVisibleChunkIfPresent(ChunkPos.asLong(x, z)); + if (holder == null) { +@@ -922,7 +923,7 @@ public class ServerChunkCache extends ChunkSource { return this.lastSpawnState; } -- final class MainThreadExecutor extends BlockableEventLoop { +- private final class MainThreadExecutor extends BlockableEventLoop { + public final class MainThreadExecutor extends BlockableEventLoop { // Paper - package -> public - private MainThreadExecutor(Level world) { + MainThreadExecutor(Level world) { super("Chunk source main thread executor for " + world.dimension().location()); diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java -index ae84dc310c076e3212d3cdbca77a1ab06a11d479..46d5a24332c1fd3c164b760ec2a2d5bf859b1ab6 100644 +index 6e0cf8ee76143301c939fc4af5eeb091abdcbc5c..066f03ee7b4feda9ec2b0984ee7cf63fa0b9e4fc 100644 --- a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java +++ b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java -@@ -193,6 +193,7 @@ public class ChunkStatus { +@@ -204,6 +204,7 @@ public class ChunkStatus { return this.name; } @@ -155,7 +155,7 @@ index ae84dc310c076e3212d3cdbca77a1ab06a11d479..46d5a24332c1fd3c164b760ec2a2d5bf public ChunkStatus getParent() { return this.parent; } -@@ -213,6 +214,17 @@ public class ChunkStatus { +@@ -224,6 +225,17 @@ public class ChunkStatus { return this.chunkType; } @@ -174,12 +174,12 @@ index ae84dc310c076e3212d3cdbca77a1ab06a11d479..46d5a24332c1fd3c164b760ec2a2d5bf return (ChunkStatus) Registry.CHUNK_STATUS.get(ResourceLocation.tryParse(id)); } diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java -index 542d6f322df5f44ad9f504c8e14c88e3fa540657..969130442b529eaac6f708107ff129f89cc0af90 100644 +index 2621739b8dd11860084ea574c243cb8ba167ac40..fc320450878279a6aa48019fbde35bb183f5f06e 100644 --- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java +++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkSerializer.java -@@ -462,6 +462,17 @@ public class ChunkSerializer { +@@ -545,6 +545,17 @@ public class ChunkSerializer { + return nbttagcompound; } - // Paper end + // Paper start + public static ChunkStatus getStatus(CompoundTag compound) { @@ -192,37 +192,24 @@ index 542d6f322df5f44ad9f504c8e14c88e3fa540657..969130442b529eaac6f708107ff129f8 + } + // Paper end + - public static ChunkStatus.ChunkType getChunkTypeFromTag(@Nullable CompoundTag tag) { - if (tag != null) { - ChunkStatus chunkstatus = ChunkStatus.byName(tag.getCompound("Level").getString("Status")); -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 637274532b01bb7b4cdb7d7b1b58181b98ac7e98..9cffef2098fbfba89ddd88a45bde33c07660497a 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 -@@ -21,7 +21,7 @@ import net.minecraft.world.level.storage.DimensionDataStorage; - - public class ChunkStorage implements AutoCloseable { - -- private final IOWorker worker; -+ private final IOWorker worker; public IOWorker getIOWorker() { return worker; } // Paper - OBFHELPER - protected final DataFixer fixerUpper; - @Nullable - private LegacyStructureDataHandler legacyStructureHandler; + public static ChunkStatus.ChunkType getChunkTypeFromTag(@Nullable CompoundTag nbt) { + if (nbt != null) { + ChunkStatus chunkstatus = ChunkStatus.byName(nbt.getCompound("Level").getString("Status")); 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 424628c9588c02454558bc7e7c5bad3a3e75ec9f..4d96e5ed28c910387c0a4238c9036c7a12458f57 100644 +index 357da4846344d1182ab7149c4d352d5019384715..832392cc2adc94e1fcb1055d91eb465529da1e92 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 -@@ -27,6 +27,7 @@ import net.minecraft.Util; - import net.minecraft.nbt.CompoundTag; - import net.minecraft.nbt.NbtIo; +@@ -23,6 +23,7 @@ import java.nio.file.StandardOpenOption; + import javax.annotation.Nullable; + import net.minecraft.Util; import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.ChunkStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -@@ -44,6 +45,30 @@ public class RegionFile implements AutoCloseable { +@@ -49,6 +50,30 @@ public class RegionFile implements AutoCloseable { protected final RegionBitmap usedSectors; - public final File file; // Paper + public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(true); // Paper + // Paper start - Cache chunk status + private final ChunkStatus[] statuses = new ChunkStatus[32 * 32]; @@ -251,7 +238,7 @@ index 424628c9588c02454558bc7e7c5bad3a3e75ec9f..4d96e5ed28c910387c0a4238c9036c7a public RegionFile(File file, File directory, boolean dsync) throws IOException { this(file.toPath(), directory.toPath(), RegionFileVersion.VERSION_DEFLATE, dsync); } -@@ -380,11 +405,13 @@ public class RegionFile implements AutoCloseable { +@@ -395,6 +420,7 @@ public class RegionFile implements AutoCloseable { return this.getOffset(pos) != 0; } @@ -259,45 +246,36 @@ index 424628c9588c02454558bc7e7c5bad3a3e75ec9f..4d96e5ed28c910387c0a4238c9036c7a private static int getOffsetIndex(ChunkPos pos) { return pos.getRegionLocalX() + pos.getRegionLocalZ() * 32; } - - public void close() throws IOException { +@@ -405,6 +431,7 @@ public class RegionFile implements AutoCloseable { + synchronized (this) { + try { + // Paper end + this.closed = true; // Paper 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 6d3e1bb20d1ab8ce5c9ea613322042d80550761a..6f1c96e4325caf6b4762700ad2286d9ea41515c9 100644 +index 211ab6cffe78c61fcff12ef7ffba904c4cae57b2..1bee455235ece8aa299a2baeede027d251e6ff57 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 -@@ -28,7 +28,14 @@ public final class RegionFileStorage implements AutoCloseable { - this.sync = dsync; +@@ -143,6 +143,7 @@ public class RegionFileStorage implements AutoCloseable { // Paper - no final + + try { + NbtIo.write(nbt, (DataOutput) dataoutputstream); ++ regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - cache status on disk + } catch (Throwable throwable) { + if (dataoutputstream != null) { + try { +@@ -205,3 +206,4 @@ public class RegionFileStorage implements AutoCloseable { // Paper - no final + } - -- private RegionFile getFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit + } + -+ // Paper start -+ public RegionFile getRegionFileIfLoaded(ChunkPos chunkcoordintpair) { -+ return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ())); -+ } -+ -+ // Paper end -+ public RegionFile getFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - private > public - long i = ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()); - RegionFile regionfile = (RegionFile) this.regionCache.getAndMoveToFirst(i); - -@@ -175,6 +182,7 @@ public final class RegionFileStorage implements AutoCloseable { - - try { - NbtIo.write(tag, (DataOutput) dataoutputstream); -+ regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(tag)); // Paper - cache status on disk - 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 - } catch (Throwable throwable1) { - throwable = throwable1; diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 0cb0021fac211996c5bdbb2cfc8f54addc3b49f6..a0615e4ba015cca4fe074de63b87d0bff84b1a14 100644 +index 30305736b7dc023ad5eb3a177914560b3fec64ee..4841591539fdd5a01f9ded0ee510991602c266a4 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -19,6 +19,7 @@ import java.util.Objects; +@@ -20,6 +20,7 @@ import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.UUID; @@ -305,7 +283,7 @@ index 0cb0021fac211996c5bdbb2cfc8f54addc3b49f6..a0615e4ba015cca4fe074de63b87d0bf import java.util.function.Predicate; import java.util.stream.Collectors; import net.minecraft.core.BlockPos; -@@ -401,8 +402,22 @@ public class CraftWorld implements World { +@@ -417,8 +418,22 @@ public class CraftWorld implements World { @Override public boolean isChunkGenerated(int x, int z) { @@ -323,24 +301,24 @@ index 0cb0021fac211996c5bdbb2cfc8f54addc3b49f6..a0615e4ba015cca4fe074de63b87d0bf + return chunk instanceof ImposterProtoChunk || chunk instanceof net.minecraft.world.level.chunk.LevelChunk; + } try { -- return world.getChunkSource().getChunkAtIfCachedImmediately(x, z) != null || world.getChunkSource().chunkMap.read(new ChunkPos(x, z)) != null; // Paper (TODO check if the first part can be removed) +- return this.world.getChunkSource().getChunkAtIfCachedImmediately(x, z) != null || this.world.getChunkSource().chunkMap.read(new ChunkPos(x, z)) != null; // Paper (TODO check if the first part can be removed) + return world.getChunkSource().chunkMap.getChunkStatusOnDisk(new ChunkPos(x, z)) == ChunkStatus.FULL; + // Paper end } catch (IOException ex) { throw new RuntimeException(ex); } -@@ -513,20 +528,48 @@ public class CraftWorld implements World { +@@ -529,20 +544,48 @@ public class CraftWorld implements World { @Override public boolean loadChunk(int x, int z, boolean generate) { org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot -- ChunkAccess chunk = world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper +- ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper + // Paper start - Optimize this method + ChunkPos chunkPos = new ChunkPos(x, z); - // 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 = world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true); +- chunk = this.world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true); - } + if (!generate) { + ChunkAccess immediate = world.getChunkSource().getChunkAtImmediately(x, z); @@ -357,11 +335,11 @@ index 0cb0021fac211996c5bdbb2cfc8f54addc3b49f6..a0615e4ba015cca4fe074de63b87d0bf + } - if (chunk instanceof net.minecraft.world.level.chunk.LevelChunk) { -- world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 1, Unit.INSTANCE); +- this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 1, Unit.INSTANCE); - return true; + net.minecraft.world.level.chunk.storage.RegionFile file; + try { -+ file = world.getChunkSource().chunkMap.getIOWorker().getRegionFileCache().getFile(chunkPos, false); ++ file = world.getChunkSource().chunkMap.regionFileCache.getFile(chunkPos, false); + } catch (IOException ex) { + throw new RuntimeException(ex); + }