From 88a688293098703ddd5895d9b3ce94b6ebb01568 Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 25 May 2020 12:34:03 -0400 Subject: [PATCH] Fix unloading inaccessible chunks too fast regressing gen speed A chunk was loaded but not yet finished in use and was unloaded too early. This caused it to be reloaded again or caused crashes. Now also check if the chunk pops out of the unload queue that it also doesn't now have a ticket either. --- .../0532-Unload-leaked-Cached-Chunks.patch | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/Spigot-Server-Patches/0532-Unload-leaked-Cached-Chunks.patch b/Spigot-Server-Patches/0532-Unload-leaked-Cached-Chunks.patch index 0b6d4b4a8..fcb194b96 100644 --- a/Spigot-Server-Patches/0532-Unload-leaked-Cached-Chunks.patch +++ b/Spigot-Server-Patches/0532-Unload-leaked-Cached-Chunks.patch @@ -18,17 +18,17 @@ 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..c9516d2d6724e84095e0e3c27c14870519fc05f9 100644 +index 54e89c9cc6c47ff2c4f4dd5d4c22a391f8a3d6e0..899a49974acf60eb7a160a4b55d1ab389249d3b0 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -898,6 +898,37 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -898,6 +898,29 @@ public class ChunkProviderServer extends IChunkProvider { 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.lastStatusChange == 0 || world.getTime() - playerchunk.lastStatusChange > 20) && ++ (playerchunk.lastActivity == 0 || world.getTime() - playerchunk.lastActivity > 20*15) && + PlayerChunk.getChunkState(playerchunk.getTicketLevel()) == PlayerChunk.State.INACCESSIBLE + ) { + ChunkStatus chunkHolderStatus = playerchunk.getChunkHolderStatus(); @@ -36,23 +36,15 @@ index 54e89c9cc6c47ff2c4f4dd5d4c22a391f8a3d6e0..c9516d2d6724e84095e0e3c27c148705 + if (chunkHolderStatus != null && !chunkHolderStatus.isAtLeastStatus(desiredStatus)) { + return; + } -+ -+ if (playerchunk.lastStatusChange == 0) { -+ playerchunk.lastStatusChange = world.getTime(); -+ } else { -+ Chunk chunk = playerchunk.getChunk(); -+ if (chunk != null && chunk.isAnyNeighborsLoaded()) { -+ playerchunk.lastStatusChange = world.getTime()+(20*5); -+ return; -+ } -+ long key = playerchunk.location.pair(); -+ ArraySetSorted> tickets = playerChunkMap.chunkDistanceManager.tickets.get(key); -+ if (tickets == null || tickets.isEmpty()) { -+ playerchunk.lastStatusChange = world.getTime()+(20*30); -+ playerChunkMap.unloadQueue.add(key); -+ } else { -+ playerchunk.lastStatusChange = world.getTime()+(20*5); -+ } ++ 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 + } @@ -60,14 +52,14 @@ index 54e89c9cc6c47ff2c4f4dd5d4c22a391f8a3d6e0..c9516d2d6724e84095e0e3c27c148705 this.world.getMethodProfiler().enter("customSpawners"); if (flag1) { diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index b8fe42e8123e972b1ec97b048c35d90118076e66..7e4d37128738ad0ef44d14ef3b48d8c5aca0c2f5 100644 +index b8fe42e8123e972b1ec97b048c35d90118076e66..ecac7b72759a3884020b9c19c58d3db3338e0fc3 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -44,6 +44,7 @@ public class PlayerChunk { long lastAutoSaveTime; // Paper - incremental autosave long inactiveTimeStart; // Paper - incremental autosave -+ long lastStatusChange; // Paper - fix chunk leak ++ long lastActivity; // Paper - fix chunk leak // Paper start - optimise isOutsideOfRange // cached here to avoid a map lookup @@ -75,7 +67,28 @@ index b8fe42e8123e972b1ec97b048c35d90118076e66..7e4d37128738ad0ef44d14ef3b48d8c5 protected void a(PlayerChunkMap playerchunkmap) { ChunkStatus chunkstatus = getChunkStatus(this.oldTicketLevel); ChunkStatus chunkstatus1 = getChunkStatus(this.ticketLevel); -+ if (oldTicketLevel != ticketLevel) lastStatusChange = chunkMap.world.getTime(); // Paper - chunk leak ++ if (oldTicketLevel != ticketLevel) lastActivity = chunkMap.world.getTime(); // Paper - chunk leak boolean flag = this.oldTicketLevel <= PlayerChunkMap.GOLDEN_TICKET; boolean flag1 = this.ticketLevel <= PlayerChunkMap.GOLDEN_TICKET; // Paper - diff on change: (flag1 = new ticket level is in loadable range) PlayerChunk.State playerchunk_state = getChunkState(this.oldTicketLevel); +diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java +index 2b09437642ec846d025b226692f2290f9bb5b556..f0bb519450d5ff2a5c8f7364fa8d8ecac03a6c59 100644 +--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java ++++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java +@@ -639,6 +639,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + } + })); + } ++ playerchunk.lastActivity = world.getTime(); // Paper - chunk leak + + ChunkStatus chunkstatus = (ChunkStatus) intfunction.apply(j1); + CompletableFuture> completablefuture = playerchunk.a(chunkstatus, this); +@@ -871,6 +872,8 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + while (longiterator.hasNext()) { // Spigot + long j = longiterator.nextLong(); + longiterator.remove(); // Spigot ++ ArraySetSorted> tickets = chunkDistanceManager.tickets.get(j); // Paper - chunk leak ++ if (tickets != null && !tickets.isEmpty()) continue; // Paper - ticket got added, don't remove + PlayerChunk playerchunk = (PlayerChunk) this.updatingChunks.remove(j); + + if (playerchunk != null) {