PaperMC/paper-server/nms-patches/net/minecraft/server/level/PlayerChunk.patch

153 lines
8 KiB
Diff
Raw Normal View History

2021-03-15 23:00:00 +01:00
--- a/net/minecraft/server/level/PlayerChunk.java
+++ b/net/minecraft/server/level/PlayerChunk.java
@@ -38,6 +38,10 @@
2021-03-15 23:00:00 +01:00
import net.minecraft.world.level.chunk.ProtoChunkExtension;
import net.minecraft.world.level.lighting.LightEngine;
+// CraftBukkit start
+import net.minecraft.server.MinecraftServer;
+// CraftBukkit end
+
public class PlayerChunk {
public static final Either<IChunkAccess, PlayerChunk.Failure> UNLOADED_CHUNK = Either.right(PlayerChunk.Failure.UNLOADED);
@@ -75,11 +79,11 @@
this.fullChunkFuture = PlayerChunk.UNLOADED_LEVEL_CHUNK_FUTURE;
this.tickingChunkFuture = PlayerChunk.UNLOADED_LEVEL_CHUNK_FUTURE;
this.entityTickingChunkFuture = PlayerChunk.UNLOADED_LEVEL_CHUNK_FUTURE;
- this.chunkToSave = CompletableFuture.completedFuture((Object) null);
+ this.chunkToSave = CompletableFuture.completedFuture(null); // CraftBukkit - decompile error
this.chunkToSaveHistory = null;
this.blockChangedLightSectionFilter = new BitSet();
this.skyChangedLightSectionFilter = new BitSet();
- this.pendingFullStateConfirmation = CompletableFuture.completedFuture((Object) null);
+ this.pendingFullStateConfirmation = CompletableFuture.completedFuture(null); // CraftBukkit - decompile error
this.pos = chunkcoordintpair;
this.levelHeightAccessor = levelheightaccessor;
this.lightEngine = lightengine;
@@ -92,6 +96,19 @@
this.changedBlocksPerSection = new ShortSet[levelheightaccessor.getSectionsCount()];
}
+ // 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);
+ }
+ // CraftBukkit end
+
public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUnchecked(ChunkStatus chunkstatus) {
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = (CompletableFuture) this.futures.get(chunkstatus.c());
@@ -117,9 +134,9 @@
@Nullable
public Chunk getChunk() {
CompletableFuture<Either<Chunk, PlayerChunk.Failure>> completablefuture = this.a();
- Either<Chunk, PlayerChunk.Failure> either = (Either) completablefuture.getNow((Object) null);
+ Either<Chunk, PlayerChunk.Failure> 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
}
@Nullable
@@ -164,6 +181,7 @@
if (chunk != null) {
int i = this.levelHeightAccessor.getSectionIndex(blockposition.getY());
+ if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296
if (this.changedBlocksPerSection[i] == null) {
this.hasChangedSections = true;
this.changedBlocksPerSection[i] = new ShortArraySet();
@@ -274,7 +292,7 @@
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = (CompletableFuture) this.futures.get(i);
if (completablefuture != null) {
- Either<IChunkAccess, PlayerChunk.Failure> either = (Either) completablefuture.getNow((Object) null);
+ Either<IChunkAccess, PlayerChunk.Failure> either = (Either) completablefuture.getNow(null); // CraftBukkit - decompile error
boolean flag = either != null && either.right().isPresent();
if (!flag) {
@@ -341,7 +359,7 @@
this.pendingFullStateConfirmation = completablefuture1;
completablefuture.thenAccept((either) -> {
either.ifLeft((chunk) -> {
- completablefuture1.complete((Object) null);
+ completablefuture1.complete(null); // CraftBukkit - decompile error
});
});
}
@@ -358,6 +376,30 @@
boolean flag1 = this.ticketLevel <= PlayerChunkMap.MAX_CHUNK_DISTANCE;
PlayerChunk.State playerchunk_state = getChunkState(this.oldTicketLevel);
PlayerChunk.State playerchunk_state1 = getChunkState(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.isAtLeast(PlayerChunk.State.BORDER) && !playerchunk_state1.isAtLeast(PlayerChunk.State.BORDER)) {
+ this.getStatusFutureUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+ Chunk chunk = (Chunk)either.left().orElse(null);
+ if (chunk != null) {
+ playerchunkmap.callbackExecutor.execute(() -> {
+ // 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();
+ });
+ }
+ }).exceptionally((throwable) -> {
+ // ensure exceptions are printed, by default this is not the case
+ MinecraftServer.LOGGER.fatal("Failed to schedule unload callback for chunk " + PlayerChunk.this.pos, throwable);
+ return null;
+ });
+
+ // Run callback right away if the future was already done
+ playerchunkmap.callbackExecutor.run();
+ }
+ // CraftBukkit end
CompletableFuture completablefuture;
if (flag) {
@@ -388,7 +430,7 @@
if (flag2 && !flag3) {
completablefuture = this.fullChunkFuture;
this.fullChunkFuture = PlayerChunk.UNLOADED_LEVEL_CHUNK_FUTURE;
- this.a(completablefuture.thenApply((either1) -> {
+ this.a(((CompletableFuture<Either<Chunk, PlayerChunk.Failure>>) completablefuture).thenApply((either1) -> { // CraftBukkit - decompile error
Objects.requireNonNull(playerchunkmap);
return either1.ifLeft(playerchunkmap::a);
}), "unfull");
@@ -432,6 +474,26 @@
this.onLevelChange.a(this.pos, this::k, 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.isAtLeast(PlayerChunk.State.BORDER) && playerchunk_state1.isAtLeast(PlayerChunk.State.BORDER)) {
+ this.getStatusFutureUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+ Chunk chunk = (Chunk)either.left().orElse(null);
+ if (chunk != null) {
+ playerchunkmap.callbackExecutor.execute(() -> {
+ chunk.loadCallback();
+ });
+ }
+ }).exceptionally((throwable) -> {
+ // ensure exceptions are printed, by default this is not the case
+ MinecraftServer.LOGGER.fatal("Failed to schedule load callback for chunk " + PlayerChunk.this.pos, throwable);
+ return null;
+ });
+
+ // Run callback right away if the future was already done
+ playerchunkmap.callbackExecutor.run();
+ }
+ // CraftBukkit end
}
public static ChunkStatus getChunkStatus(int i) {