diff options
Diffstat (limited to 'patch-remap/mache-vineflower/net/minecraft/server/level/ChunkHolder.java.patch')
-rw-r--r-- | patch-remap/mache-vineflower/net/minecraft/server/level/ChunkHolder.java.patch | 753 |
1 files changed, 753 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower/net/minecraft/server/level/ChunkHolder.java.patch b/patch-remap/mache-vineflower/net/minecraft/server/level/ChunkHolder.java.patch new file mode 100644 index 0000000000..96da1be143 --- /dev/null +++ b/patch-remap/mache-vineflower/net/minecraft/server/level/ChunkHolder.java.patch @@ -0,0 +1,753 @@ +--- a/net/minecraft/server/level/ChunkHolder.java ++++ b/net/minecraft/server/level/ChunkHolder.java +@@ -9,7 +9,6 @@ + import java.util.List; + import java.util.Optional; + import java.util.concurrent.CompletableFuture; +-import java.util.concurrent.CompletionStage; + import java.util.concurrent.Executor; + import java.util.concurrent.atomic.AtomicReferenceArray; + import java.util.function.IntConsumer; +@@ -24,11 +23,11 @@ + import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; + import net.minecraft.util.DebugBuffer; + import net.minecraft.world.level.ChunkPos; ++import net.minecraft.world.level.EnumSkyBlock; + import net.minecraft.world.level.Level; + import net.minecraft.world.level.LevelHeightAccessor; +-import net.minecraft.world.level.LightLayer; + import net.minecraft.world.level.block.entity.BlockEntity; +-import net.minecraft.world.level.block.state.BlockState; ++import net.minecraft.world.level.block.state.IBlockData; + import net.minecraft.world.level.chunk.ChunkAccess; + import net.minecraft.world.level.chunk.ChunkStatus; + import net.minecraft.world.level.chunk.ImposterProtoChunk; +@@ -37,50 +36,52 @@ + import net.minecraft.world.level.chunk.ProtoChunk; + import net.minecraft.world.level.lighting.LevelLightEngine; + ++// CraftBukkit start ++import net.minecraft.server.MinecraftServer; ++// CraftBukkit end ++ + public class ChunkHolder { +- public static final Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> UNLOADED_CHUNK = Either.right(ChunkHolder.ChunkLoadingFailure.UNLOADED); +- public static final CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> UNLOADED_CHUNK_FUTURE = CompletableFuture.completedFuture( +- UNLOADED_CHUNK +- ); +- public static final Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> UNLOADED_LEVEL_CHUNK = Either.right(ChunkHolder.ChunkLoadingFailure.UNLOADED); +- private static final Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> NOT_DONE_YET = Either.right(ChunkHolder.ChunkLoadingFailure.UNLOADED); +- private static final CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> UNLOADED_LEVEL_CHUNK_FUTURE = CompletableFuture.completedFuture( +- UNLOADED_LEVEL_CHUNK +- ); ++ ++ public static final Either<ChunkAccess, ChunkHolder.Failure> UNLOADED_CHUNK = Either.right(ChunkHolder.Failure.UNLOADED); ++ public static final CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> UNLOADED_CHUNK_FUTURE = CompletableFuture.completedFuture(ChunkHolder.UNLOADED_CHUNK); ++ public static final Either<LevelChunk, ChunkHolder.Failure> UNLOADED_LEVEL_CHUNK = Either.right(ChunkHolder.Failure.UNLOADED); ++ private static final Either<ChunkAccess, ChunkHolder.Failure> NOT_DONE_YET = Either.right(ChunkHolder.Failure.UNLOADED); ++ private static final CompletableFuture<Either<LevelChunk, ChunkHolder.Failure>> UNLOADED_LEVEL_CHUNK_FUTURE = CompletableFuture.completedFuture(ChunkHolder.UNLOADED_LEVEL_CHUNK); + private static final List<ChunkStatus> CHUNK_STATUSES = ChunkStatus.getStatusList(); +- private final AtomicReferenceArray<CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>> futures = new AtomicReferenceArray<>( +- CHUNK_STATUSES.size() +- ); ++ private final AtomicReferenceArray<CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>>> futures; + private final LevelHeightAccessor levelHeightAccessor; +- private volatile CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> fullChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE; +- private volatile CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> tickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE; +- private volatile CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> entityTickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE; +- private CompletableFuture<ChunkAccess> chunkToSave = CompletableFuture.completedFuture(null); ++ private volatile CompletableFuture<Either<LevelChunk, ChunkHolder.Failure>> fullChunkFuture; ++ private volatile CompletableFuture<Either<LevelChunk, ChunkHolder.Failure>> tickingChunkFuture; ++ private volatile CompletableFuture<Either<LevelChunk, ChunkHolder.Failure>> entityTickingChunkFuture; ++ private CompletableFuture<ChunkAccess> chunkToSave; + @Nullable +- private final DebugBuffer<ChunkHolder.ChunkSaveDebug> chunkToSaveHistory = null; +- private int oldTicketLevel; ++ private final DebugBuffer<ChunkHolder.ChunkSaveDebug> chunkToSaveHistory; ++ public int oldTicketLevel; + private int ticketLevel; + private int queueLevel; + final ChunkPos pos; + private boolean hasChangedSections; + private final ShortSet[] changedBlocksPerSection; +- private final BitSet blockChangedLightSectionFilter = new BitSet(); +- private final BitSet skyChangedLightSectionFilter = new BitSet(); ++ private final BitSet blockChangedLightSectionFilter; ++ private final BitSet skyChangedLightSectionFilter; + private final LevelLightEngine lightEngine; + private final ChunkHolder.LevelChangeListener onLevelChange; +- private final ChunkHolder.PlayerProvider playerProvider; ++ public final ChunkHolder.PlayerProvider playerProvider; + private boolean wasAccessibleSinceLastSave; +- private CompletableFuture<Void> pendingFullStateConfirmation = CompletableFuture.completedFuture(null); +- private CompletableFuture<?> sendSync = CompletableFuture.completedFuture(null); ++ private CompletableFuture<Void> pendingFullStateConfirmation; ++ private CompletableFuture<?> sendSync; + +- public ChunkHolder( +- ChunkPos pos, +- int ticketLevel, +- LevelHeightAccessor levelHeightAccessor, +- LevelLightEngine lightEngine, +- ChunkHolder.LevelChangeListener onLevelChange, +- ChunkHolder.PlayerProvider playerProvider +- ) { ++ public ChunkHolder(ChunkPos pos, int ticketLevel, LevelHeightAccessor levelHeightAccessor, LevelLightEngine lightEngine, ChunkHolder.LevelChangeListener onLevelChange, ChunkHolder.PlayerProvider playerProvider) { ++ this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size()); ++ this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; ++ this.tickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; ++ this.entityTickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; ++ this.chunkToSave = CompletableFuture.completedFuture(null); // CraftBukkit - decompile error ++ this.chunkToSaveHistory = null; ++ this.blockChangedLightSectionFilter = new BitSet(); ++ this.skyChangedLightSectionFilter = new BitSet(); ++ this.pendingFullStateConfirmation = CompletableFuture.completedFuture(null); // CraftBukkit - decompile error ++ this.sendSync = CompletableFuture.completedFuture(null); // CraftBukkit - decompile error + this.pos = pos; + this.levelHeightAccessor = levelHeightAccessor; + this.lightEngine = lightEngine; +@@ -93,32 +94,48 @@ + this.changedBlocksPerSection = new ShortSet[levelHeightAccessor.getSectionsCount()]; + } + +- public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getFutureIfPresentUnchecked(ChunkStatus chunkStatus) { +- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completableFuture = this.futures.get(chunkStatus.getIndex()); +- return completableFuture == null ? UNLOADED_CHUNK_FUTURE : completableFuture; ++ // CraftBukkit start ++ public LevelChunk getFullChunkNow() { ++ // Note: We use the oldTicketLevel for isLoaded checks. ++ if (!ChunkLevel.fullStatus(this.oldTicketLevel).isOrAfter(FullChunkStatus.FULL)) return null; ++ return this.getFullChunkNowUnchecked(); + } + +- public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getFutureIfPresent(ChunkStatus chunkStatus) { +- return ChunkLevel.generationStatus(this.ticketLevel).isOrAfter(chunkStatus) ? this.getFutureIfPresentUnchecked(chunkStatus) : UNLOADED_CHUNK_FUTURE; ++ public LevelChunk getFullChunkNowUnchecked() { ++ CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> statusFuture = this.getFutureIfPresentUnchecked(ChunkStatus.FULL); ++ Either<ChunkAccess, ChunkHolder.Failure> either = (Either<ChunkAccess, ChunkHolder.Failure>) statusFuture.getNow(null); ++ return (either == null) ? null : (LevelChunk) either.left().orElse(null); + } ++ // CraftBukkit end + +- public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getTickingChunkFuture() { ++ public CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> getFutureIfPresentUnchecked(ChunkStatus chunkStatus) { ++ CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> completablefuture = (CompletableFuture) this.futures.get(chunkStatus.getIndex()); ++ ++ return completablefuture == null ? ChunkHolder.UNLOADED_CHUNK_FUTURE : completablefuture; ++ } ++ ++ public CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> getFutureIfPresent(ChunkStatus chunkStatus) { ++ return ChunkLevel.generationStatus(this.ticketLevel).isOrAfter(chunkStatus) ? this.getFutureIfPresentUnchecked(chunkStatus) : ChunkHolder.UNLOADED_CHUNK_FUTURE; ++ } ++ ++ public CompletableFuture<Either<LevelChunk, ChunkHolder.Failure>> getTickingChunkFuture() { + return this.tickingChunkFuture; + } + +- public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getEntityTickingChunkFuture() { ++ public CompletableFuture<Either<LevelChunk, ChunkHolder.Failure>> getEntityTickingChunkFuture() { + return this.entityTickingChunkFuture; + } + +- public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> getFullChunkFuture() { ++ public CompletableFuture<Either<LevelChunk, ChunkHolder.Failure>> getFullChunkFuture() { + return this.fullChunkFuture; + } + + @Nullable + public LevelChunk getTickingChunk() { +- CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> tickingChunkFuture = this.getTickingChunkFuture(); +- Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = tickingChunkFuture.getNow(null); +- return either == null ? null : either.left().orElse(null); ++ CompletableFuture<Either<LevelChunk, ChunkHolder.Failure>> completablefuture = this.getTickingChunkFuture(); ++ Either<LevelChunk, ChunkHolder.Failure> either = (Either) completablefuture.getNow(null); // CraftBukkit - decompile error ++ ++ return either == null ? null : (LevelChunk) either.left().orElse(null); // CraftBukkit - decompile error + } + + public CompletableFuture<?> getChunkSendSyncFuture() { +@@ -132,18 +149,20 @@ + + @Nullable + public LevelChunk getFullChunk() { +- CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> fullChunkFuture = this.getFullChunkFuture(); +- Either<LevelChunk, ChunkHolder.ChunkLoadingFailure> either = fullChunkFuture.getNow(null); +- return either == null ? null : either.left().orElse(null); ++ CompletableFuture<Either<LevelChunk, ChunkHolder.Failure>> completablefuture = this.getFullChunkFuture(); ++ Either<LevelChunk, ChunkHolder.Failure> either = (Either) completablefuture.getNow(null); // CraftBukkit - decompile error ++ ++ return either == null ? null : (LevelChunk) either.left().orElse(null); // CraftBukkit - decompile error + } + + @Nullable + public ChunkStatus getLastAvailableStatus() { +- for (int i = CHUNK_STATUSES.size() - 1; i >= 0; i--) { +- ChunkStatus chunkStatus = CHUNK_STATUSES.get(i); +- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> futureIfPresentUnchecked = this.getFutureIfPresentUnchecked(chunkStatus); +- if (futureIfPresentUnchecked.getNow(UNLOADED_CHUNK).left().isPresent()) { +- return chunkStatus; ++ for (int i = ChunkHolder.CHUNK_STATUSES.size() - 1; i >= 0; --i) { ++ ChunkStatus chunkstatus = (ChunkStatus) ChunkHolder.CHUNK_STATUSES.get(i); ++ CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> completablefuture = this.getFutureIfPresentUnchecked(chunkstatus); ++ ++ if (((Either) completablefuture.getNow(ChunkHolder.UNLOADED_CHUNK)).left().isPresent()) { ++ return chunkstatus; + } + } + +@@ -152,13 +171,15 @@ + + @Nullable + public ChunkAccess getLastAvailable() { +- for (int i = CHUNK_STATUSES.size() - 1; i >= 0; i--) { +- ChunkStatus chunkStatus = CHUNK_STATUSES.get(i); +- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> futureIfPresentUnchecked = this.getFutureIfPresentUnchecked(chunkStatus); +- if (!futureIfPresentUnchecked.isCompletedExceptionally()) { +- Optional<ChunkAccess> optional = futureIfPresentUnchecked.getNow(UNLOADED_CHUNK).left(); ++ for (int i = ChunkHolder.CHUNK_STATUSES.size() - 1; i >= 0; --i) { ++ ChunkStatus chunkstatus = (ChunkStatus) ChunkHolder.CHUNK_STATUSES.get(i); ++ CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> completablefuture = this.getFutureIfPresentUnchecked(chunkstatus); ++ ++ if (!completablefuture.isCompletedExceptionally()) { ++ Optional<ChunkAccess> optional = ((Either) completablefuture.getNow(ChunkHolder.UNLOADED_CHUNK)).left(); ++ + if (optional.isPresent()) { +- return optional.get(); ++ return (ChunkAccess) optional.get(); + } + } + } +@@ -171,35 +192,44 @@ + } + + public void blockChanged(BlockPos pos) { +- LevelChunk tickingChunk = this.getTickingChunk(); +- if (tickingChunk != null) { +- int sectionIndex = this.levelHeightAccessor.getSectionIndex(pos.getY()); +- if (this.changedBlocksPerSection[sectionIndex] == null) { ++ LevelChunk chunk = this.getTickingChunk(); ++ ++ if (chunk != null) { ++ int i = this.levelHeightAccessor.getSectionIndex(pos.getY()); ++ ++ if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296 ++ if (this.changedBlocksPerSection[i] == null) { + this.hasChangedSections = true; +- this.changedBlocksPerSection[sectionIndex] = new ShortOpenHashSet(); ++ this.changedBlocksPerSection[i] = new ShortOpenHashSet(); + } + +- this.changedBlocksPerSection[sectionIndex].add(SectionPos.sectionRelativePos(pos)); ++ this.changedBlocksPerSection[i].add(SectionPos.sectionRelativePos(pos)); + } + } + +- public void sectionLightChanged(LightLayer type, int sectionY) { +- Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = this.getFutureIfPresent(ChunkStatus.INITIALIZE_LIGHT).getNow(null); ++ public void sectionLightChanged(EnumSkyBlock type, int sectionY) { ++ Either<ChunkAccess, ChunkHolder.Failure> either = (Either) this.getFutureIfPresent(ChunkStatus.INITIALIZE_LIGHT).getNow(null); // CraftBukkit - decompile error ++ + if (either != null) { +- ChunkAccess chunkAccess = either.left().orElse(null); +- if (chunkAccess != null) { +- chunkAccess.setUnsaved(true); +- LevelChunk tickingChunk = this.getTickingChunk(); +- if (tickingChunk != null) { +- int minLightSection = this.lightEngine.getMinLightSection(); +- int maxLightSection = this.lightEngine.getMaxLightSection(); +- if (sectionY >= minLightSection && sectionY <= maxLightSection) { +- int i = sectionY - minLightSection; +- if (type == LightLayer.SKY) { +- this.skyChangedLightSectionFilter.set(i); ++ ChunkAccess ichunkaccess = (ChunkAccess) either.left().orElse(null); // CraftBukkit - decompile error ++ ++ if (ichunkaccess != null) { ++ ichunkaccess.setUnsaved(true); ++ LevelChunk chunk = this.getTickingChunk(); ++ ++ if (chunk != null) { ++ int j = this.lightEngine.getMinLightSection(); ++ int k = this.lightEngine.getMaxLightSection(); ++ ++ if (sectionY >= j && sectionY <= k) { ++ int l = sectionY - j; ++ ++ if (type == EnumSkyBlock.SKY) { ++ this.skyChangedLightSectionFilter.set(l); + } else { +- this.blockChangedLightSectionFilter.set(i); ++ this.blockChangedLightSectionFilter.set(l); + } ++ + } + } + } +@@ -208,14 +238,15 @@ + + public void broadcastChanges(LevelChunk chunk) { + if (this.hasChangedSections || !this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty()) { +- Level level = chunk.getLevel(); ++ Level world = chunk.getLevel(); ++ List list; ++ + if (!this.skyChangedLightSectionFilter.isEmpty() || !this.blockChangedLightSectionFilter.isEmpty()) { +- List<ServerPlayer> players = this.playerProvider.getPlayers(this.pos, true); +- if (!players.isEmpty()) { +- ClientboundLightUpdatePacket clientboundLightUpdatePacket = new ClientboundLightUpdatePacket( +- chunk.getPos(), this.lightEngine, this.skyChangedLightSectionFilter, this.blockChangedLightSectionFilter +- ); +- this.broadcast(players, clientboundLightUpdatePacket); ++ list = this.playerProvider.getPlayers(this.pos, true); ++ if (!list.isEmpty()) { ++ ClientboundLightUpdatePacket packetplayoutlightupdate = new ClientboundLightUpdatePacket(chunk.getPos(), this.lightEngine, this.skyChangedLightSectionFilter, this.blockChangedLightSectionFilter); ++ ++ this.broadcast(list, packetplayoutlightupdate); + } + + this.skyChangedLightSectionFilter.clear(); +@@ -223,29 +254,34 @@ + } + + if (this.hasChangedSections) { +- List<ServerPlayer> players = this.playerProvider.getPlayers(this.pos, false); ++ list = this.playerProvider.getPlayers(this.pos, false); + +- for (int i = 0; i < this.changedBlocksPerSection.length; i++) { +- ShortSet set = this.changedBlocksPerSection[i]; +- if (set != null) { ++ for (int i = 0; i < this.changedBlocksPerSection.length; ++i) { ++ ShortSet shortset = this.changedBlocksPerSection[i]; ++ ++ if (shortset != null) { + this.changedBlocksPerSection[i] = null; +- if (!players.isEmpty()) { +- int sectionYFromSectionIndex = this.levelHeightAccessor.getSectionYFromSectionIndex(i); +- SectionPos sectionPos = SectionPos.of(chunk.getPos(), sectionYFromSectionIndex); +- if (set.size() == 1) { +- BlockPos blockPos = sectionPos.relativeToBlockPos(set.iterator().nextShort()); +- BlockState blockState = level.getBlockState(blockPos); +- this.broadcast(players, new ClientboundBlockUpdatePacket(blockPos, blockState)); +- this.broadcastBlockEntityIfNeeded(players, level, blockPos, blockState); ++ if (!list.isEmpty()) { ++ int j = this.levelHeightAccessor.getSectionYFromSectionIndex(i); ++ SectionPos sectionposition = SectionPos.of(chunk.getPos(), j); ++ ++ if (shortset.size() == 1) { ++ BlockPos blockposition = sectionposition.relativeToBlockPos(shortset.iterator().nextShort()); ++ IBlockData iblockdata = world.getBlockState(blockposition); ++ ++ this.broadcast(list, new ClientboundBlockUpdatePacket(blockposition, iblockdata)); ++ this.broadcastBlockEntityIfNeeded(list, world, blockposition, iblockdata); + } else { +- LevelChunkSection section = chunk.getSection(i); +- ClientboundSectionBlocksUpdatePacket clientboundSectionBlocksUpdatePacket = new ClientboundSectionBlocksUpdatePacket( +- sectionPos, set, section +- ); +- this.broadcast(players, clientboundSectionBlocksUpdatePacket); +- clientboundSectionBlocksUpdatePacket.runUpdates( +- (blockPos1, blockState1) -> this.broadcastBlockEntityIfNeeded(players, level, blockPos1, blockState1) +- ); ++ LevelChunkSection chunksection = chunk.getSection(i); ++ ClientboundSectionBlocksUpdatePacket packetplayoutmultiblockchange = new ClientboundSectionBlocksUpdatePacket(sectionposition, shortset, chunksection); ++ ++ this.broadcast(list, packetplayoutmultiblockchange); ++ // CraftBukkit start ++ List finalList = list; ++ packetplayoutmultiblockchange.runUpdates((blockposition1, iblockdata1) -> { ++ this.broadcastBlockEntityIfNeeded(finalList, world, blockposition1, iblockdata1); ++ // CraftBukkit end ++ }); + } + } + } +@@ -256,48 +292,58 @@ + } + } + +- private void broadcastBlockEntityIfNeeded(List<ServerPlayer> players, Level level, BlockPos pos, BlockState state) { ++ private void broadcastBlockEntityIfNeeded(List<ServerPlayer> players, Level level, BlockPos pos, IBlockData state) { + if (state.hasBlockEntity()) { + this.broadcastBlockEntity(players, level, pos); + } ++ + } + + private void broadcastBlockEntity(List<ServerPlayer> players, Level level, BlockPos pox) { +- BlockEntity blockEntity = level.getBlockEntity(pox); +- if (blockEntity != null) { +- Packet<?> updatePacket = blockEntity.getUpdatePacket(); +- if (updatePacket != null) { +- this.broadcast(players, updatePacket); ++ BlockEntity tileentity = level.getBlockEntity(pox); ++ ++ if (tileentity != null) { ++ Packet<?> packet = tileentity.getUpdatePacket(); ++ ++ if (packet != null) { ++ this.broadcast(players, packet); + } + } ++ + } + + private void broadcast(List<ServerPlayer> players, Packet<?> packet) { +- players.forEach(serverPlayer -> serverPlayer.connection.send(packet)); ++ players.forEach((entityplayer) -> { ++ entityplayer.connection.send(packet); ++ }); + } + +- public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getOrScheduleFuture(ChunkStatus status, ChunkMap map) { +- int index = status.getIndex(); +- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completableFuture = this.futures.get(index); +- if (completableFuture != null) { +- Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = completableFuture.getNow(NOT_DONE_YET); ++ public CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> getOrScheduleFuture(ChunkStatus status, ChunkMap map) { ++ int i = status.getIndex(); ++ CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> completablefuture = (CompletableFuture) this.futures.get(i); ++ ++ if (completablefuture != null) { ++ Either<ChunkAccess, ChunkHolder.Failure> either = (Either) completablefuture.getNow(ChunkHolder.NOT_DONE_YET); ++ + if (either == null) { +- String string = "value in future for status: " + status + " was incorrectly set to null at chunk: " + this.pos; +- throw map.debugFuturesAndCreateReportedException(new IllegalStateException("null value previously set for chunk status"), string); ++ String s = "value in future for status: " + status + " was incorrectly set to null at chunk: " + this.pos; ++ ++ throw map.debugFuturesAndCreateReportedException(new IllegalStateException("null value previously set for chunk status"), s); + } + +- if (either == NOT_DONE_YET || either.right().isEmpty()) { +- return completableFuture; ++ if (either == ChunkHolder.NOT_DONE_YET || either.right().isEmpty()) { ++ return completablefuture; + } + } + + if (ChunkLevel.generationStatus(this.ticketLevel).isOrAfter(status)) { +- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completableFuture1 = map.schedule(this, status); +- this.updateChunkToSave(completableFuture1, "schedule " + status); +- this.futures.set(index, completableFuture1); +- return completableFuture1; ++ CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> completablefuture1 = map.schedule(this, status); ++ ++ this.updateChunkToSave(completablefuture1, "schedule " + status); ++ this.futures.set(i, completablefuture1); ++ return completablefuture1; + } else { +- return completableFuture == null ? UNLOADED_CHUNK_FUTURE : completableFuture; ++ return completablefuture == null ? ChunkHolder.UNLOADED_CHUNK_FUTURE : completablefuture; + } + } + +@@ -306,26 +352,34 @@ + this.chunkToSaveHistory.push(new ChunkHolder.ChunkSaveDebug(Thread.currentThread(), future, source)); + } + +- this.chunkToSave = this.chunkToSave.thenCombine((CompletionStage<? extends Object>)future, (chunkAccess, object) -> (ChunkAccess)chunkAccess); ++ this.chunkToSave = this.chunkToSave.thenCombine(future, (ichunkaccess, object) -> { ++ return ichunkaccess; ++ }); + } + +- private void updateChunkToSave(CompletableFuture<? extends Either<? extends ChunkAccess, ChunkHolder.ChunkLoadingFailure>> feature, String source) { ++ private void updateChunkToSave(CompletableFuture<? extends Either<? extends ChunkAccess, ChunkHolder.Failure>> feature, String source) { + if (this.chunkToSaveHistory != null) { + this.chunkToSaveHistory.push(new ChunkHolder.ChunkSaveDebug(Thread.currentThread(), feature, source)); + } + +- this.chunkToSave = this.chunkToSave +- .thenCombine( +- feature, (chunkAccess, either) -> either.map(chunkAccess1 -> (ChunkAccess)chunkAccess1, chunkLoadingFailure -> (ChunkAccess)chunkAccess) +- ); ++ this.chunkToSave = this.chunkToSave.thenCombine(feature, (ichunkaccess, either) -> { ++ return (ChunkAccess) either.map((ichunkaccess1) -> { ++ return ichunkaccess1; ++ }, (playerchunk_failure) -> { ++ return ichunkaccess; ++ }); ++ }); + } + +- public void addSendDependency(CompletableFuture<?> completableFuture) { ++ public void addSendDependency(CompletableFuture<?> completablefuture) { + if (this.sendSync.isDone()) { +- this.sendSync = completableFuture; ++ this.sendSync = completablefuture; + } else { +- this.sendSync = this.sendSync.thenCombine((CompletionStage<? extends Object>)completableFuture, (object, object1) -> null); ++ this.sendSync = this.sendSync.thenCombine(completablefuture, (object, object1) -> { ++ return null; ++ }); + } ++ + } + + public FullChunkStatus getFullStatus() { +@@ -352,14 +406,19 @@ + this.ticketLevel = level; + } + +- private void scheduleFullChunkPromotion( +- ChunkMap chunkMap, CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> future, Executor executor, FullChunkStatus fullChunkStatus +- ) { ++ private void scheduleFullChunkPromotion(ChunkMap chunkMap, CompletableFuture<Either<LevelChunk, ChunkHolder.Failure>> future, Executor executor, FullChunkStatus fullChunkStatus) { + this.pendingFullStateConfirmation.cancel(false); +- CompletableFuture<Void> completableFuture = new CompletableFuture<>(); +- completableFuture.thenRunAsync(() -> chunkMap.onFullChunkStatusChange(this.pos, fullChunkStatus), executor); +- this.pendingFullStateConfirmation = completableFuture; +- future.thenAccept(either -> either.ifLeft(levelChunk -> completableFuture.complete(null))); ++ CompletableFuture<Void> completablefuture1 = new CompletableFuture(); ++ ++ completablefuture1.thenRunAsync(() -> { ++ chunkMap.onFullChunkStatusChange(this.pos, fullChunkStatus); ++ }, executor); ++ this.pendingFullStateConfirmation = completablefuture1; ++ future.thenAccept((either) -> { ++ either.ifLeft((chunk) -> { ++ completablefuture1.complete(null); // CraftBukkit - decompile error ++ }); ++ }); + } + + private void demoteFullChunk(ChunkMap chunkMap, FullChunkStatus fullChunkStatus) { +@@ -368,60 +427,88 @@ + } + + protected void updateFutures(ChunkMap chunkMap, Executor executor) { +- ChunkStatus chunkStatus = ChunkLevel.generationStatus(this.oldTicketLevel); +- ChunkStatus chunkStatus1 = ChunkLevel.generationStatus(this.ticketLevel); +- boolean isLoaded = ChunkLevel.isLoaded(this.oldTicketLevel); +- boolean isLoaded1 = ChunkLevel.isLoaded(this.ticketLevel); +- FullChunkStatus fullChunkStatus = ChunkLevel.fullStatus(this.oldTicketLevel); +- FullChunkStatus fullChunkStatus1 = ChunkLevel.fullStatus(this.ticketLevel); +- if (isLoaded) { +- Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = Either.right(new ChunkHolder.ChunkLoadingFailure() { +- @Override ++ ChunkStatus chunkstatus = ChunkLevel.generationStatus(this.oldTicketLevel); ++ ChunkStatus chunkstatus1 = ChunkLevel.generationStatus(this.ticketLevel); ++ boolean flag = ChunkLevel.isLoaded(this.oldTicketLevel); ++ boolean flag1 = ChunkLevel.isLoaded(this.ticketLevel); ++ FullChunkStatus fullchunkstatus = ChunkLevel.fullStatus(this.oldTicketLevel); ++ FullChunkStatus fullchunkstatus1 = ChunkLevel.fullStatus(this.ticketLevel); ++ // CraftBukkit start ++ // ChunkUnloadEvent: Called before the chunk is unloaded: isChunkLoaded is still true and chunk can still be modified by plugins. ++ if (fullchunkstatus.isOrAfter(FullChunkStatus.FULL) && !fullchunkstatus1.isOrAfter(FullChunkStatus.FULL)) { ++ this.getFutureIfPresentUnchecked(ChunkStatus.FULL).thenAccept((either) -> { ++ LevelChunk chunk = (LevelChunk)either.left().orElse(null); ++ if (chunk != null) { ++ chunkMap.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.setUnsaved(true); ++ chunk.unloadCallback(); ++ }); ++ } ++ }).exceptionally((throwable) -> { ++ // ensure exceptions are printed, by default this is not the case ++ MinecraftServer.LOGGER.error("Failed to schedule unload callback for chunk " + ChunkHolder.this.pos, throwable); ++ return null; ++ }); ++ ++ // Run callback right away if the future was already done ++ chunkMap.callbackExecutor.run(); ++ } ++ // CraftBukkit end ++ ++ if (flag) { ++ Either<ChunkAccess, ChunkHolder.Failure> either = Either.right(new ChunkHolder.Failure() { + public String toString() { + return "Unloaded ticket level " + ChunkHolder.this.pos; + } + }); + +- for (int i = isLoaded1 ? chunkStatus1.getIndex() + 1 : 0; i <= chunkStatus.getIndex(); i++) { +- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completableFuture = this.futures.get(i); +- if (completableFuture == null) { ++ for (int i = flag1 ? chunkstatus1.getIndex() + 1 : 0; i <= chunkstatus.getIndex(); ++i) { ++ CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> completablefuture = (CompletableFuture) this.futures.get(i); ++ ++ if (completablefuture == null) { + this.futures.set(i, CompletableFuture.completedFuture(either)); + } + } + } + +- boolean isOrAfter = fullChunkStatus.isOrAfter(FullChunkStatus.FULL); +- boolean isOrAfter1 = fullChunkStatus1.isOrAfter(FullChunkStatus.FULL); +- this.wasAccessibleSinceLastSave |= isOrAfter1; +- if (!isOrAfter && isOrAfter1) { ++ boolean flag2 = fullchunkstatus.isOrAfter(FullChunkStatus.FULL); ++ boolean flag3 = fullchunkstatus1.isOrAfter(FullChunkStatus.FULL); ++ ++ this.wasAccessibleSinceLastSave |= flag3; ++ if (!flag2 && flag3) { + this.fullChunkFuture = chunkMap.prepareAccessibleChunk(this); + this.scheduleFullChunkPromotion(chunkMap, this.fullChunkFuture, executor, FullChunkStatus.FULL); + this.updateChunkToSave(this.fullChunkFuture, "full"); + } + +- if (isOrAfter && !isOrAfter1) { +- this.fullChunkFuture.complete(UNLOADED_LEVEL_CHUNK); +- this.fullChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE; ++ if (flag2 && !flag3) { ++ this.fullChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); ++ this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; + } + +- boolean isOrAfter2 = fullChunkStatus.isOrAfter(FullChunkStatus.BLOCK_TICKING); +- boolean isOrAfter3 = fullChunkStatus1.isOrAfter(FullChunkStatus.BLOCK_TICKING); +- if (!isOrAfter2 && isOrAfter3) { ++ boolean flag4 = fullchunkstatus.isOrAfter(FullChunkStatus.BLOCK_TICKING); ++ boolean flag5 = fullchunkstatus1.isOrAfter(FullChunkStatus.BLOCK_TICKING); ++ ++ if (!flag4 && flag5) { + this.tickingChunkFuture = chunkMap.prepareTickingChunk(this); + this.scheduleFullChunkPromotion(chunkMap, this.tickingChunkFuture, executor, FullChunkStatus.BLOCK_TICKING); + this.updateChunkToSave(this.tickingChunkFuture, "ticking"); + } + +- if (isOrAfter2 && !isOrAfter3) { +- this.tickingChunkFuture.complete(UNLOADED_LEVEL_CHUNK); +- this.tickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE; ++ if (flag4 && !flag5) { ++ this.tickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); ++ this.tickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; + } + +- boolean isOrAfter4 = fullChunkStatus.isOrAfter(FullChunkStatus.ENTITY_TICKING); +- boolean isOrAfter5 = fullChunkStatus1.isOrAfter(FullChunkStatus.ENTITY_TICKING); +- if (!isOrAfter4 && isOrAfter5) { +- if (this.entityTickingChunkFuture != UNLOADED_LEVEL_CHUNK_FUTURE) { +- throw (IllegalStateException)Util.pauseInIde(new IllegalStateException()); ++ boolean flag6 = fullchunkstatus.isOrAfter(FullChunkStatus.ENTITY_TICKING); ++ boolean flag7 = fullchunkstatus1.isOrAfter(FullChunkStatus.ENTITY_TICKING); ++ ++ if (!flag6 && flag7) { ++ if (this.entityTickingChunkFuture != ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE) { ++ throw (IllegalStateException) Util.pauseInIde(new IllegalStateException()); + } + + this.entityTickingChunkFuture = chunkMap.prepareEntityTickingChunk(this); +@@ -429,17 +516,37 @@ + this.updateChunkToSave(this.entityTickingChunkFuture, "entity ticking"); + } + +- if (isOrAfter4 && !isOrAfter5) { +- this.entityTickingChunkFuture.complete(UNLOADED_LEVEL_CHUNK); +- this.entityTickingChunkFuture = UNLOADED_LEVEL_CHUNK_FUTURE; ++ if (flag6 && !flag7) { ++ this.entityTickingChunkFuture.complete(ChunkHolder.UNLOADED_LEVEL_CHUNK); ++ this.entityTickingChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE; + } + +- if (!fullChunkStatus1.isOrAfter(fullChunkStatus)) { +- this.demoteFullChunk(chunkMap, fullChunkStatus1); ++ if (!fullchunkstatus1.isOrAfter(fullchunkstatus)) { ++ this.demoteFullChunk(chunkMap, fullchunkstatus1); + } + + this.onLevelChange.onLevelChange(this.pos, this::getQueueLevel, this.ticketLevel, this::setQueueLevel); + 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 (!fullchunkstatus.isOrAfter(FullChunkStatus.FULL) && fullchunkstatus1.isOrAfter(FullChunkStatus.FULL)) { ++ this.getFutureIfPresentUnchecked(ChunkStatus.FULL).thenAccept((either) -> { ++ LevelChunk chunk = (LevelChunk)either.left().orElse(null); ++ if (chunk != null) { ++ chunkMap.callbackExecutor.execute(() -> { ++ chunk.loadCallback(); ++ }); ++ } ++ }).exceptionally((throwable) -> { ++ // ensure exceptions are printed, by default this is not the case ++ MinecraftServer.LOGGER.error("Failed to schedule load callback for chunk " + ChunkHolder.this.pos, throwable); ++ return null; ++ }); ++ ++ // Run callback right away if the future was already done ++ chunkMap.callbackExecutor.run(); ++ } ++ // CraftBukkit end + } + + public boolean wasAccessibleSinceLastSave() { +@@ -451,10 +558,12 @@ + } + + public void replaceProtoChunk(ImposterProtoChunk imposter) { +- for (int i = 0; i < this.futures.length(); i++) { +- CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completableFuture = this.futures.get(i); +- if (completableFuture != null) { +- Optional<ChunkAccess> optional = completableFuture.getNow(UNLOADED_CHUNK).left(); ++ for (int i = 0; i < this.futures.length(); ++i) { ++ CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> completablefuture = (CompletableFuture) this.futures.get(i); ++ ++ if (completablefuture != null) { ++ Optional<ChunkAccess> optional = ((Either) completablefuture.getNow(ChunkHolder.UNLOADED_CHUNK)).left(); ++ + if (!optional.isEmpty() && optional.get() instanceof ProtoChunk) { + this.futures.set(i, CompletableFuture.completedFuture(Either.left(imposter))); + } +@@ -464,26 +573,29 @@ + this.updateChunkToSave(CompletableFuture.completedFuture(Either.left(imposter.getWrapped())), "replaceProto"); + } + +- public List<Pair<ChunkStatus, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>>> getAllFutures() { +- List<Pair<ChunkStatus, CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>>>> list = new ArrayList<>(); ++ public List<Pair<ChunkStatus, CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>>>> getAllFutures() { ++ List<Pair<ChunkStatus, CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>>>> list = new ArrayList(); + +- for (int i = 0; i < CHUNK_STATUSES.size(); i++) { +- list.add(Pair.of(CHUNK_STATUSES.get(i), this.futures.get(i))); ++ for (int i = 0; i < ChunkHolder.CHUNK_STATUSES.size(); ++i) { ++ list.add(Pair.of((ChunkStatus) ChunkHolder.CHUNK_STATUSES.get(i), (CompletableFuture) this.futures.get(i))); + } + + return list; + } + +- public interface ChunkLoadingFailure { +- ChunkHolder.ChunkLoadingFailure UNLOADED = new ChunkHolder.ChunkLoadingFailure() { +- @Override +- public String toString() { +- return "UNLOADED"; +- } +- }; ++ @FunctionalInterface ++ public interface LevelChangeListener { ++ ++ void onLevelChange(ChunkPos chunkPos, IntSupplier intsupplier, int i, IntConsumer intconsumer); + } + +- static final class ChunkSaveDebug { ++ public interface PlayerProvider { ++ ++ List<ServerPlayer> getPlayers(ChunkPos pos, boolean boundaryOnly); ++ } ++ ++ private static final class ChunkSaveDebug { ++ + private final Thread thread; + private final CompletableFuture<?> future; + private final String source; +@@ -495,12 +607,12 @@ + } + } + +- @FunctionalInterface +- public interface LevelChangeListener { +- void onLevelChange(ChunkPos chunkPos, IntSupplier intSupplier, int i, IntConsumer intConsumer); +- } ++ public interface Failure { + +- public interface PlayerProvider { +- List<ServerPlayer> getPlayers(ChunkPos pos, boolean boundaryOnly); ++ ChunkHolder.Failure UNLOADED = new ChunkHolder.Failure() { ++ public String toString() { ++ return "UNLOADED"; ++ } ++ }; + } + } |