mirror of
https://github.com/PaperMC/Paper.git
synced 2025-03-31 11:49:53 +02:00
Spigot made structure start not load chunks, but forgot to null check the result... This likely never blew up before due to the chunk leak issue, but now that leaky chunks are cleaned up, it was identified.
127 lines
7.9 KiB
Diff
127 lines
7.9 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Aikar <aikar@aikar.co>
|
|
Date: Mon, 25 May 2020 11:02:42 -0400
|
|
Subject: [PATCH] Unload leaked Cached Chunks
|
|
|
|
Due to some complexity in mojangs complicated chain of juggling
|
|
whether or not a chunk should be unloaded when the last ticket is
|
|
removed, many chunks are remaining around in the cache.
|
|
|
|
These chunks are never being targetted for unload because they are
|
|
vastly out of view distance range and have no reason to be looked at.
|
|
|
|
This is a huge issue for performance because we have to iterate these
|
|
chunks EVERY TICK... This is what's been leading to high SELF time in
|
|
Ticking Chunks timings/profiler results.
|
|
|
|
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
|
@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
}
|
|
}
|
|
// Paper start
|
|
+ if (playerchunk != null) playerchunk.lastActivity = world.getTime(); // Paper
|
|
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> future = this.a(playerchunk, l) ? PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE : playerchunk.a(chunkstatus, this.playerChunkMap);
|
|
if (isUrgent) {
|
|
future.thenAccept(either -> this.chunkMapDistance.clearUrgent(chunkcoordintpair));
|
|
@@ -0,0 +0,0 @@ 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.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<Ticket<?>> tickets = playerChunkMap.chunkDistanceManager.tickets.get(key);
|
|
+ if (tickets == null || tickets.isEmpty()) {
|
|
+ playerChunkMap.unloadQueue.add(key);
|
|
+ }
|
|
+ // Paper end
|
|
+ }
|
|
});
|
|
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
|
|
@@ -0,0 +0,0 @@ public class PlayerChunk {
|
|
|
|
long lastAutoSaveTime; // Paper - incremental autosave
|
|
long inactiveTimeStart; // Paper - incremental autosave
|
|
+ long lastActivity; // Paper - fix chunk leak
|
|
|
|
// Paper start - optimise isOutsideOfRange
|
|
// cached here to avoid a map lookup
|
|
@@ -0,0 +0,0 @@ public class PlayerChunk {
|
|
protected void a(PlayerChunkMap playerchunkmap) {
|
|
ChunkStatus chunkstatus = getChunkStatus(this.oldTicketLevel);
|
|
ChunkStatus chunkstatus1 = getChunkStatus(this.ticketLevel);
|
|
+ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
|
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
}
|
|
}));
|
|
}
|
|
+ playerchunk.lastActivity = world.getTime(); // Paper - chunk leak
|
|
|
|
ChunkStatus chunkstatus = (ChunkStatus) intfunction.apply(j1);
|
|
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = playerchunk.a(chunkstatus, this);
|
|
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
if (requestingNeighbor != null && requestingNeighbor != playerchunk && !completablefuture.isDone()) {
|
|
requestingNeighbor.onNeighborRequest(playerchunk, chunkstatus);
|
|
completablefuture.thenAccept(either -> {
|
|
+ playerchunk.lastActivity = world.getTime(); // Paper - chunk leak
|
|
requestingNeighbor.onNeighborDone(playerchunk, chunkstatus, either.left().orElse(null));
|
|
});
|
|
}
|
|
@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
while (longiterator.hasNext()) { // Spigot
|
|
long j = longiterator.nextLong();
|
|
longiterator.remove(); // Spigot
|
|
+ ArraySetSorted<Ticket<?>> 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) {
|
|
diff --git a/src/main/java/net/minecraft/server/StructureGenerator.java b/src/main/java/net/minecraft/server/StructureGenerator.java
|
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
|
--- a/src/main/java/net/minecraft/server/StructureGenerator.java
|
|
+++ b/src/main/java/net/minecraft/server/StructureGenerator.java
|
|
@@ -0,0 +0,0 @@ public abstract class StructureGenerator<C extends WorldGenFeatureConfiguration>
|
|
while (longiterator.hasNext()) {
|
|
long k = longiterator.nextLong();
|
|
IChunkAccess ichunkaccess1 = generatoraccess.getChunkAt(ChunkCoordIntPair.getX(k), ChunkCoordIntPair.getZ(k), ChunkStatus.STRUCTURE_STARTS, false); // CraftBukkit - don't load chunks
|
|
- StructureStart structurestart = ichunkaccess1.a(this.b());
|
|
+ StructureStart structurestart = ichunkaccess1 != null ? ichunkaccess1.a(this.b()) : null; // Paper - make sure not null
|
|
|
|
if (structurestart != null) {
|
|
list.add(structurestart);
|