SPIGOT-5228: Entities that are removed during chunk unloads are not

properly removed from the chunk.

This could lead to dead entities accumulating in memory over time if the
chunk never gets fully unloaded (as it is the case for chunks around the
spawn region).

The issue is that Minecraft processes the removal of these entities
during the next tick, when the chunk has already switched to state
INACCESSIBLE and can no longer be retrieved as usual.

For the purpose of removing dead entities from their still loaded but no
longer accessible chunk, this adds and uses a new method with which a
chunk can be accessed without checking its current state first.

By: blablubbabc <lukas@wirsindwir.de>
This commit is contained in:
CraftBukkit/Spigot
2021-02-14 09:24:23 +11:00
parent 98e0c5c715
commit 373ed1ddd5
3 changed files with 38 additions and 17 deletions

View File

@@ -9,23 +9,27 @@
this.dirtyBlocks = new ShortSet[16];
this.location = chunkcoordintpair;
this.lightEngine = lightengine;
@@ -56,6 +56,15 @@
@@ -56,6 +56,19 @@
this.a(i);
}
+ // CraftBukkit start
+ public Chunk getFullChunk() {
+ if (!getChunkState(this.oldTicketLevel).isAtLeast(PlayerChunk.State.BORDER)) return null; // note: using oldTicketLevel for isLoaded checks
+ return this.getFullChunkUnchecked();
+ }
+
+ public Chunk getFullChunkUnchecked() {
+ CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> statusFuture = this.getStatusFutureUnchecked(ChunkStatus.FULL);
+ Either<IChunkAccess, PlayerChunk.Failure> either = (Either<IChunkAccess, PlayerChunk.Failure>) statusFuture.getNow(null);
+ return either == null ? null : (Chunk) either.left().orElse(null);
+ return (either == null) ? null : (Chunk) either.left().orElse(null);
+ }
+ // CraftBukkit end
+
public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUnchecked(ChunkStatus chunkstatus) {
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = (CompletableFuture) this.statusFutures.get(chunkstatus.c());
@@ -81,9 +90,9 @@
@@ -81,9 +94,9 @@
@Nullable
public Chunk getChunk() {
CompletableFuture<Either<Chunk, PlayerChunk.Failure>> completablefuture = this.a();
@@ -37,7 +41,7 @@
}
@Nullable
@@ -114,6 +123,7 @@
@@ -114,6 +127,7 @@
if (chunk != null) {
byte b0 = (byte) SectionPosition.a(blockposition.getY());
@@ -45,7 +49,7 @@
if (this.dirtyBlocks[b0] == null) {
this.p = true;
this.dirtyBlocks[b0] = new ShortArraySet();
@@ -216,7 +226,7 @@
@@ -216,7 +230,7 @@
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = (CompletableFuture) this.statusFutures.get(i);
if (completablefuture != null) {
@@ -54,7 +58,7 @@
if (either == null || either.left().isPresent()) {
return completablefuture;
@@ -271,6 +281,30 @@
@@ -271,6 +285,30 @@
boolean flag1 = this.ticketLevel <= PlayerChunkMap.GOLDEN_TICKET;
PlayerChunk.State playerchunk_state = getChunkState(this.oldTicketLevel);
PlayerChunk.State playerchunk_state1 = getChunkState(this.ticketLevel);
@@ -85,7 +89,7 @@
CompletableFuture completablefuture;
if (flag) {
@@ -302,7 +336,7 @@
@@ -302,7 +340,7 @@
if (flag2 && !flag3) {
completablefuture = this.fullChunkFuture;
this.fullChunkFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
@@ -94,7 +98,7 @@
playerchunkmap.getClass();
return either1.ifLeft(playerchunkmap::a);
}));
@@ -340,6 +374,26 @@
@@ -340,6 +378,26 @@
this.u.a(this.location, this::k, this.ticketLevel, this::d);
this.oldTicketLevel = this.ticketLevel;