Fix premature unloading of neighbor chunks for world gen

We must check the level tracker as ticket levels add "virtual"
tickets to neighbors.

Also added neighbor tracking during generation to be extra safe.

Fixes #3465
Fixes #3451
Fixes #3459
This commit is contained in:
Aikar 2020-05-28 22:59:40 -04:00
parent fde81356bb
commit ff4ca31f0d
No known key found for this signature in database
GPG Key ID: 401ADFC9891FAAFE
1 changed files with 87 additions and 12 deletions

View File

@ -17,8 +17,43 @@ 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/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java
index 9805361e2d49fa1cfecf0c5811187fc503d0ad8e..91b9946f987a397960a08c7a606fcf4e2607e0ae 100644
--- a/src/main/java/net/minecraft/server/ChunkMapDistance.java
+++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java
@@ -33,7 +33,7 @@ public abstract class ChunkMapDistance {
public final Long2ObjectOpenHashMap<ArraySetSorted<Ticket<?>>> tickets = new Long2ObjectOpenHashMap();
private final ChunkMapDistance.a e = new ChunkMapDistance.a();
public static final int MOB_SPAWN_RANGE = 8; //private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); // Paper - no longer used
- private final ChunkMapDistance.c g = new ChunkMapDistance.c(33);
+ private final ChunkMapDistance.c g = new ChunkMapDistance.c(33); public final ChunkMapDistance.c getLevelTracker() { return g; } // Paper
// Paper start use a queue, but still keep unique requirement
public final java.util.Queue<PlayerChunk> pendingChunkUpdates = new java.util.ArrayDeque<PlayerChunk>() {
@Override
@@ -471,6 +471,12 @@ public abstract class ChunkMapDistance {
this.e = i;
}
+ // Paper start - check diff below
+ public boolean isChunkLoaded(long i) {
+ return this.c(this.c(i));
+ }
+ // Paper end
+
private void a(long i, int j, boolean flag, boolean flag1) {
if (flag != flag1) {
Ticket<?> ticket = new Ticket<>(TicketType.PLAYER, 33, new ChunkCoordIntPair(i)); // Paper - no-tick view distance
@@ -478,7 +484,7 @@ public abstract class ChunkMapDistance {
if (flag1) {
ChunkMapDistance.this.j.a(ChunkTaskQueueSorter.a(() -> { // CraftBukkit - decompile error
ChunkMapDistance.this.m.execute(() -> {
- if (this.c(this.c(i))) {
+ if (this.c(this.c(i))) { // Paper - diff above isChunkLoaded
ChunkMapDistance.this.addTicket(i, ticket);
ChunkMapDistance.this.l.add(i);
} else {
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index 54e89c9cc6c47ff2c4f4dd5d4c22a391f8a3d6e0..1209fbf1c906511954bc61ffb1a8882498855ba4 100644
index 54e89c9cc6c47ff2c4f4dd5d4c22a391f8a3d6e0..32b6d28cd28d60937eca14187287645ea2797da7 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 {
@ -46,7 +81,7 @@ index 54e89c9cc6c47ff2c4f4dd5d4c22a391f8a3d6e0..1209fbf1c906511954bc61ffb1a88824
});
this.world.getMethodProfiler().enter("customSpawners");
if (flag1) {
@@ -913,6 +915,37 @@ public class ChunkProviderServer extends IChunkProvider {
@@ -913,6 +915,40 @@ public class ChunkProviderServer extends IChunkProvider {
this.playerChunkMap.g();
}
@ -54,9 +89,8 @@ index 54e89c9cc6c47ff2c4f4dd5d4c22a391f8a3d6e0..1209fbf1c906511954bc61ffb1a88824
+ 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
+ (playerchunk.lastActivity == 0 || time - playerchunk.lastActivity > 20*5) &&
+ playerchunk.location.pair() % 20 == 0 && playerChunkMap.unloadQueue.size() < 100
+ ) {
+ ChunkStatus chunkHolderStatus = playerchunk.getChunkHolderStatus();
+ ChunkStatus desiredStatus = PlayerChunk.getChunkStatus(ticketLevel);
@ -69,12 +103,16 @@ index 54e89c9cc6c47ff2c4f4dd5d4c22a391f8a3d6e0..1209fbf1c906511954bc61ffb1a88824
+ }
+ playerchunk.lastActivity = time;
+ Chunk chunk = playerchunk.getChunk();
+ if ((chunk != null && chunk.isAnyNeighborsLoaded()) || !playerchunk.neighborPriorities.isEmpty()) {
+ if ((chunk != null && chunk.isAnyNeighborsLoaded()) || !playerchunk.neighborPriorities.isEmpty() || !playerchunk.dependendedOnBy.isEmpty()) {
+ return;
+ }
+ long key = playerchunk.location.pair();
+ ArraySetSorted<Ticket<?>> tickets = playerChunkMap.chunkDistanceManager.tickets.get(key);
+ if (tickets == null || tickets.isEmpty()) {
+ if (playerChunkMap.playerViewDistanceNoTickMap.getObjectsInRange(key) != null) {
+ return;
+ }
+ PlayerChunkMap.a distanceManager = playerChunkMap.chunkDistanceManager;
+ ArraySetSorted<Ticket<?>> tickets = distanceManager.tickets.get(key);
+ if ((tickets == null || tickets.isEmpty()) && !distanceManager.getLevelTracker().isChunkLoaded(key)) {
+ playerChunkMap.unloadQueue.add(key);
+ }
+ }
@ -85,18 +123,19 @@ index 54e89c9cc6c47ff2c4f4dd5d4c22a391f8a3d6e0..1209fbf1c906511954bc61ffb1a88824
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
index b8fe42e8123e972b1ec97b048c35d90118076e66..f213a08e42db20c511f7d70434b03ee4040676bc 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 {
@@ -44,6 +44,8 @@ public class PlayerChunk {
long lastAutoSaveTime; // Paper - incremental autosave
long inactiveTimeStart; // Paper - incremental autosave
+ long lastActivity; // Paper - fix chunk leak
+ java.util.concurrent.ConcurrentHashMap<ChunkCoordIntPair, Boolean> dependendedOnBy = new java.util.concurrent.ConcurrentHashMap<>(); // Paper
// Paper start - optimise isOutsideOfRange
// cached here to avoid a map lookup
@@ -562,6 +563,7 @@ public class PlayerChunk {
@@ -562,6 +564,7 @@ public class PlayerChunk {
protected void a(PlayerChunkMap playerchunkmap) {
ChunkStatus chunkstatus = getChunkStatus(this.oldTicketLevel);
ChunkStatus chunkstatus1 = getChunkStatus(this.ticketLevel);
@ -105,7 +144,7 @@ index b8fe42e8123e972b1ec97b048c35d90118076e66..ecac7b72759a3884020b9c19c58d3db3
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..b7d8a7487e303c2c44160ec1093987b3e0d175bc 100644
index f42507f5a17f9388db738218f58ca76f863274ff..d4bcbf8c1030eee55064b246b25391c0a5201d83 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 {
@ -133,6 +172,42 @@ index 2b09437642ec846d025b226692f2290f9bb5b556..b7d8a7487e303c2c44160ec1093987b3
PlayerChunk playerchunk = (PlayerChunk) this.updatingChunks.remove(j);
if (playerchunk != null) {
@@ -1147,9 +1151,27 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
return completablefuture.thenComposeAsync((either) -> {
return either.map((list) -> { // Paper - Shut up.
try {
+ // Paper start
+ list.forEach(chunk -> {
+ PlayerChunk updatingChunk = getUpdatingChunk(chunk.getPos().pair());
+ if (updatingChunk != null) {
+ updatingChunk.dependendedOnBy.put(playerchunk.location, true);
+ updatingChunk.lastActivity = world.getTime();
+ }
+ });
+ // Paper end
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture1 = chunkstatus.a(this.world, this.chunkGenerator, this.definedStructureManager, this.lightEngine, (ichunkaccess) -> {
return this.c(playerchunk);
}, list);
+ // Paper start
+ completablefuture1.whenComplete((unused, unused2) -> list.forEach(chunk -> {
+ PlayerChunk updatingChunk = getUpdatingChunk(chunk.getPos().pair());
+ if (updatingChunk != null) {
+ updatingChunk.dependendedOnBy.remove(playerchunk.location);
+ updatingChunk.lastActivity = world.getTime();
+ }
+ }));
+ // Paper end
this.worldLoadListener.a(chunkcoordintpair, chunkstatus);
return completablefuture1;
@@ -1167,6 +1189,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
return CompletableFuture.completedFuture(Either.right(playerchunk_failure));
});
}, (runnable) -> {
+ playerchunk.lastActivity = world.getTime(); // Paper
this.mailboxWorldGen.a(ChunkTaskQueueSorter.a(playerchunk, runnable)); // CraftBukkit - decompile error
});
}
diff --git a/src/main/java/net/minecraft/server/StructureGenerator.java b/src/main/java/net/minecraft/server/StructureGenerator.java
index acfe732af5b9f63fc2f6b78499defabe2e73ee45..25b19346fc1c702cc37275d0ec16abbbfacfb418 100644
--- a/src/main/java/net/minecraft/server/StructureGenerator.java