--- a/net/minecraft/server/PlayerChunk.java +++ b/net/minecraft/server/PlayerChunk.java @@ -23,7 +23,7 @@ private volatile CompletableFuture> tickingFuture; private volatile CompletableFuture> entityTickingFuture; private CompletableFuture 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; this.entityTickingFuture = PlayerChunk.UNLOADED_CHUNK_FUTURE; - this.chunkSave = CompletableFuture.completedFuture((Object) null); + this.chunkSave = CompletableFuture.completedFuture(null); // CraftBukkit - decompile error this.dirtyBlocks = new short[64]; this.location = chunkcoordintpair; this.lightEngine = lightengine; @@ -55,6 +55,14 @@ this.a(i); } + // CraftBukkit start + public Chunk getFullChunk() { + CompletableFuture> statusFuture = this.getStatusFuture(ChunkStatus.FULL); + Either either = (Either) statusFuture.getNow(null); + return either == null ? null : (Chunk) either.left().orElse(null); + } + // CraftBukkit end + public CompletableFuture> getStatusFutureUnchecked(ChunkStatus chunkstatus) { CompletableFuture> completablefuture = (CompletableFuture) this.statusFutures.get(chunkstatus.c()); @@ -62,7 +70,7 @@ } public CompletableFuture> 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> a() { @@ -76,9 +84,9 @@ @Nullable public Chunk getChunk() { CompletableFuture> completablefuture = this.a(); - Either either = (Either) completablefuture.getNow((Object) null); + Either either = (Either) completablefuture.getNow(null); // CraftBukkit - decompile error - return either == null ? null : (Chunk) either.left().orElse((Object) null); + return either == null ? null : (Chunk) either.left().orElse(null); // CraftBukkit - decompile error } public CompletableFuture getChunkSave() { @@ -201,7 +209,7 @@ CompletableFuture> completablefuture = (CompletableFuture) this.statusFutures.get(i); if (completablefuture != null) { - Either either = (Either) completablefuture.getNow((Object) null); + Either either = (Either) completablefuture.getNow(null); // CraftBukkit - decompile error if (either == null || either.left().isPresent()) { return completablefuture; @@ -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 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; - this.a(completablefuture.thenApply((either1) -> { + this.a(((CompletableFuture>) completablefuture).thenApply((either1) -> { // CraftBukkit - decompile error 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) {