SPIGOT-4752: Fixed inconsistency between isChunkLoaded and chunk load/unload events

By: blablubbabc <lukas@wirsindwir.de>
This commit is contained in:
CraftBukkit/Spigot
2019-05-16 01:11:20 +02:00
parent 0e59838abf
commit e2b8949bf3
8 changed files with 209 additions and 143 deletions

View File

@@ -1,5 +1,14 @@
--- a/net/minecraft/server/PlayerChunk.java
+++ b/net/minecraft/server/PlayerChunk.java
@@ -23,7 +23,7 @@
private volatile CompletableFuture<Either<Chunk, PlayerChunk.Failure>> tickingFuture;
private volatile CompletableFuture<Either<Chunk, PlayerChunk.Failure>> entityTickingFuture;
private CompletableFuture<IChunkAccess> chunkSave;
- private int oldTicketLevel;
+ public int oldTicketLevel; // CraftBukkit - public
private int ticketLevel;
private int n;
private final ChunkCoordIntPair location;
@@ -43,7 +43,7 @@
this.fullChunkFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
this.tickingFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
@@ -9,7 +18,31 @@
this.dirtyBlocks = new short[64];
this.location = chunkcoordintpair;
this.lightEngine = lightengine;
@@ -76,9 +76,9 @@
@@ -55,6 +55,14 @@
this.a(i);
}
+ // CraftBukkit start
+ public Chunk getFullChunk() {
+ CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> statusFuture = this.getStatusFuture(ChunkStatus.FULL);
+ Either<IChunkAccess, PlayerChunk.Failure> either = (Either<IChunkAccess, PlayerChunk.Failure>) statusFuture.getNow(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());
@@ -62,7 +70,7 @@
}
public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFuture(ChunkStatus chunkstatus) {
- return b(this.ticketLevel).b(chunkstatus) ? this.getStatusFutureUnchecked(chunkstatus) : PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE;
+ return b(this.oldTicketLevel).b(chunkstatus) ? this.getStatusFutureUnchecked(chunkstatus) : PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE; // CraftBukkit using oldTicketLevel for isLoaded checks
}
public CompletableFuture<Either<Chunk, PlayerChunk.Failure>> a() {
@@ -76,9 +84,9 @@
@Nullable
public Chunk getChunk() {
CompletableFuture<Either<Chunk, PlayerChunk.Failure>> completablefuture = this.a();
@@ -21,7 +54,7 @@
}
public CompletableFuture<IChunkAccess> getChunkSave() {
@@ -201,7 +201,7 @@
@@ -201,7 +209,7 @@
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = (CompletableFuture) this.statusFutures.get(i);
if (completablefuture != null) {
@@ -30,23 +63,29 @@
if (either == null || either.left().isPresent()) {
return completablefuture;
@@ -213,6 +213,15 @@
this.a(completablefuture1);
this.statusFutures.set(i, completablefuture1);
+ // CraftBukkit start
+ if (chunkstatus == ChunkStatus.FULL) {
+ completablefuture1.thenAccept((either) -> {
+ Chunk chunk = (Chunk) either.left().get();
+
+ chunk.loadCallback();
@@ -256,6 +264,21 @@
boolean flag1 = this.ticketLevel <= PlayerChunkMap.GOLDEN_TICKET;
PlayerChunk.State playerchunk_state = c(this.oldTicketLevel);
PlayerChunk.State playerchunk_state1 = c(this.ticketLevel);
+ // CraftBukkit start
+ // ChunkUnloadEvent: Called before the chunk is unloaded: isChunkLoaded is still true and chunk can still be modified by plugins.
+ if (playerchunk_state.a(PlayerChunk.State.BORDER) && !playerchunk_state1.a(PlayerChunk.State.BORDER)) { // PAIL oldChunkState, newChunkState, isAtLeast
+ this.getStatusFutureUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+ either.ifLeft((chunkAccess) -> {
+ Chunk chunk = (Chunk) chunkAccess;
+ // Minecraft will apply the chunks tick lists to the world once the chunk got loaded, and then store the tick
+ // lists again inside the chunk once the chunk becomes inaccessible and set the chunk's needsSaving flag.
+ // These actions may however happen deferred, so we manually set the needsSaving flag already here.
+ chunk.setNeedsSaving(true);
+ chunk.unloadCallback();
+ });
+ }
+ // CraftBukkit end
return completablefuture1;
} else {
return completablefuture == null ? PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE : completablefuture;
@@ -294,7 +303,7 @@
+ });
+ }
+ // CraftBukkit end
if (flag1) {
for (int i = flag ? chunkstatus.c() + 1 : 0; i <= chunkstatus1.c(); ++i) {
@@ -294,7 +317,7 @@
if (flag2 && !flag3) {
completablefuture = this.fullChunkFuture;
this.fullChunkFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE;
@@ -55,3 +94,21 @@
playerchunkmap.getClass();
return either1.ifLeft(playerchunkmap::a);
}));
@@ -332,6 +355,17 @@
this.w.a(this.location, this::j, this.ticketLevel, this::d);
this.oldTicketLevel = this.ticketLevel;
+ // CraftBukkit start
+ // ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins.
+ if (!playerchunk_state.a(PlayerChunk.State.BORDER) && playerchunk_state1.a(PlayerChunk.State.BORDER)) { // PAIL oldChunkState, newChunkState, isAtLeast
+ this.getStatusFutureUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+ either.ifLeft((chunkAccess) -> {
+ Chunk chunk = (Chunk) chunkAccess;
+ chunk.loadCallback();
+ });
+ });
+ }
+ // CraftBukkit end
}
public static ChunkStatus b(int i) {