diff --git a/Spigot-Server-Patches/0004-MC-Utils.patch b/Spigot-Server-Patches/0004-MC-Utils.patch index 3e3588fea1..6ebbdd83d1 100644 --- a/Spigot-Server-Patches/0004-MC-Utils.patch +++ b/Spigot-Server-Patches/0004-MC-Utils.patch @@ -2677,11 +2677,36 @@ index 11c4d23ba988dac5f3fd85142083c77fb603e673..a03e4c5b8693395f4a145ca565b074d6 @Override public TileEntity getTileEntity(BlockPosition blockposition) { diff --git a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java -index 260644bf0be4c5b2d96033f11382f88231048ce3..f2a19acd84561e746bfc8da0331b5d4055e95327 100644 +index 260644bf0be4c5b2d96033f11382f88231048ce3..5a975f6bc60922ac872ec9c00c9150ce7dcad046 100644 --- a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java +++ b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java -@@ -31,7 +31,9 @@ public class ChunkCoordIntPair { - return pair(this.x, this.z); +@@ -11,27 +11,33 @@ public class ChunkCoordIntPair { + public static final long a = pair(1875016, 1875016); + public final int x; + public final int z; ++ public final long longKey; // Paper + + public ChunkCoordIntPair(int i, int j) { + this.x = i; + this.z = j; ++ this.longKey = pair(this.x, this.z); // Paper + } + + public ChunkCoordIntPair(BlockPosition blockposition) { + this.x = blockposition.getX() >> 4; + this.z = blockposition.getZ() >> 4; ++ this.longKey = pair(this.x, this.z); // Paper + } + + public ChunkCoordIntPair(long i) { + this.x = (int) i; + this.z = (int) (i >> 32); ++ this.longKey = pair(this.x, this.z); // Paper + } + + public long pair() { +- return pair(this.x, this.z); ++ return longKey; // Paper } - public static long pair(int i, int j) { diff --git a/Spigot-Server-Patches/0429-Seed-based-feature-search.patch b/Spigot-Server-Patches/0429-Seed-based-feature-search.patch index f068ce7dbb..2fcf7d28e0 100644 --- a/Spigot-Server-Patches/0429-Seed-based-feature-search.patch +++ b/Spigot-Server-Patches/0429-Seed-based-feature-search.patch @@ -49,10 +49,10 @@ index e96f544f126371f6f629a20ba3c99ba42d31e04a..68423645df3aa08d4c5126ff068d5e56 return this.c.a(this.b, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this.a); } diff --git a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java -index f2a19acd84561e746bfc8da0331b5d4055e95327..09f1308b0d092877fae64ce3ab4cce7e460e3c4d 100644 +index 5a975f6bc60922ac872ec9c00c9150ce7dcad046..f617636a22167b06ac8073aa25efd8c7099155f0 100644 --- a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java +++ b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java -@@ -64,10 +64,12 @@ public class ChunkCoordIntPair { +@@ -68,10 +68,12 @@ public class ChunkCoordIntPair { } } diff --git a/Spigot-Server-Patches/0532-Unload-leaked-Cached-Chunks.patch b/Spigot-Server-Patches/0532-Unload-leaked-Cached-Chunks.patch index 0035a12fc7..0d6332fe86 100644 --- a/Spigot-Server-Patches/0532-Unload-leaked-Cached-Chunks.patch +++ b/Spigot-Server-Patches/0532-Unload-leaked-Cached-Chunks.patch @@ -18,7 +18,7 @@ We will now detect these chunks in that iteration, and automatically add it to the unload queue when the chunk is found without any tickets. diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 54e89c9cc6c47ff2c4f4dd5d4c22a391f8a3d6e0..59f19c38b06dd3370f9e1604af59535934715abc 100644 +index 54e89c9cc6c47ff2c4f4dd5d4c22a391f8a3d6e0..1209fbf1c906511954bc61ffb1a8882498855ba4 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -560,6 +560,7 @@ public class ChunkProviderServer extends IChunkProvider { @@ -29,40 +29,61 @@ index 54e89c9cc6c47ff2c4f4dd5d4c22a391f8a3d6e0..59f19c38b06dd3370f9e1604af595359 CompletableFuture> future = this.a(playerchunk, l) ? PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE : playerchunk.a(chunkstatus, this.playerChunkMap); if (isUrgent) { future.thenAccept(either -> this.chunkMapDistance.clearUrgent(chunkcoordintpair)); -@@ -898,6 +899,33 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -812,6 +813,7 @@ public class ChunkProviderServer extends IChunkProvider { + this.world.timings.countNaturalMobs.stopTiming(); // Paper - timings + this.world.getMethodProfiler().exit(); + // Paper - replaced by above ++ final long time = world.getTime(); // Paper + final int[] chunksTicked = {0}; this.playerChunkMap.forEachVisibleChunk((playerchunk) -> { // Paper - safe iterator incase chunk loads, also no wrapping + Optional optional = ((Either) playerchunk.b().getNow(PlayerChunk.UNLOADED_CHUNK)).left(); + +@@ -897,7 +899,7 @@ public class ChunkProviderServer extends IChunkProvider { + this.world.timings.chunkTicks.stopTiming(); // Spigot // Paper if (chunksTicked[0]++ % 10 == 0) this.world.getMinecraftServer().midTickLoadChunks(); // Paper } - } -+ // Paper start - remove inaccessible chunks leaked -+ else if (playerchunk.getTicketLevel() == playerchunk.oldTicketLevel && -+ playerChunkMap.unloadQueue.size() < 100 && -+ (playerchunk.lastActivity == 0 || world.getTime() - playerchunk.lastActivity > 20*180) && -+ PlayerChunk.getChunkState(playerchunk.getTicketLevel()) == PlayerChunk.State.INACCESSIBLE -+ ) { -+ ChunkStatus chunkHolderStatus = playerchunk.getChunkHolderStatus(); -+ ChunkStatus desiredStatus = PlayerChunk.getChunkStatus(playerchunk.getTicketLevel()); -+ if (chunkHolderStatus != null && !chunkHolderStatus.isAtLeastStatus(desiredStatus)) { -+ return; -+ } -+ if (playerchunk.lastActivity == 0) { -+ playerchunk.lastActivity = world.getTime(); -+ return; -+ } -+ playerchunk.lastActivity = world.getTime(); -+ Chunk chunk = playerchunk.getChunk(); -+ if ((chunk != null && chunk.isAnyNeighborsLoaded()) || !playerchunk.neighborPriorities.isEmpty()) { -+ return; -+ } -+ long key = playerchunk.location.pair(); -+ ArraySetSorted> tickets = playerChunkMap.chunkDistanceManager.tickets.get(key); -+ if (tickets == null || tickets.isEmpty()) { -+ playerChunkMap.unloadQueue.add(key); -+ } -+ // Paper end -+ } +- } ++ } else { checkInactiveChunk(playerchunk, time); } // Paper - check inaccessible chunks }); this.world.getMethodProfiler().enter("customSpawners"); if (flag1) { +@@ -913,6 +915,37 @@ public class ChunkProviderServer extends IChunkProvider { + this.playerChunkMap.g(); + } + ++ // Paper start - remove inaccessible chunks leaked ++ private void checkInactiveChunk(PlayerChunk playerchunk, long time) { ++ int ticketLevel = playerchunk.getTicketLevel(); ++ if (ticketLevel > 33 && ticketLevel == playerchunk.oldTicketLevel && ++ (playerchunk.lastActivity == 0 || time - playerchunk.lastActivity > 20*180) && ++ playerchunk.location.pair() % 20 == 0 && playerChunkMap.unloadQueue.size() < 100 && ++ PlayerChunk.getChunkState(ticketLevel) == PlayerChunk.State.INACCESSIBLE ++ ) { ++ ChunkStatus chunkHolderStatus = playerchunk.getChunkHolderStatus(); ++ ChunkStatus desiredStatus = PlayerChunk.getChunkStatus(ticketLevel); ++ if (chunkHolderStatus != null && !chunkHolderStatus.isAtLeastStatus(desiredStatus)) { ++ return; ++ } ++ if (playerchunk.lastActivity == 0) { ++ playerchunk.lastActivity = time; ++ return; ++ } ++ playerchunk.lastActivity = time; ++ Chunk chunk = playerchunk.getChunk(); ++ if ((chunk != null && chunk.isAnyNeighborsLoaded()) || !playerchunk.neighborPriorities.isEmpty()) { ++ return; ++ } ++ long key = playerchunk.location.pair(); ++ ArraySetSorted> tickets = playerChunkMap.chunkDistanceManager.tickets.get(key); ++ if (tickets == null || tickets.isEmpty()) { ++ playerChunkMap.unloadQueue.add(key); ++ } ++ } ++ } ++ // Paper end ++ + @Override + public String getName() { + return "ServerChunkCache: " + this.h(); diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java index b8fe42e8123e972b1ec97b048c35d90118076e66..ecac7b72759a3884020b9c19c58d3db3338e0fc3 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java