diff --git a/Spigot-API-Patches/Async-Chunks-API.patch b/Spigot-API-Patches/Async-Chunks-API.patch index 19361dd914..776df034cd 100644 --- a/Spigot-API-Patches/Async-Chunks-API.patch +++ b/Spigot-API-Patches/Async-Chunks-API.patch @@ -366,7 +366,116 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + * @return Future that will resolve when the chunk is loaded + */ + @NotNull -+ public java.util.concurrent.CompletableFuture getChunkAtAsync(int x, int z, boolean gen); ++ public default java.util.concurrent.CompletableFuture getChunkAtAsync(int x, int z, boolean gen) { ++ return getChunkAtAsync(x, z, gen, false); ++ } ++ ++ /** ++ * Requests a {@link Chunk} to be loaded at the given coordinates ++ * ++ * This method makes no guarantee on how fast the chunk will load, ++ * and will return the chunk to the callback at a later time. ++ * ++ * You should use this method if you need a chunk but do not need it ++ * immediately, and you wish to let the server control the speed ++ * of chunk loads, keeping performance in mind. ++ * ++ * The future will always be executed synchronously ++ * on the main Server Thread. ++ * @param loc Location to load the corresponding chunk from ++ * @return Future that will resolve when the chunk is loaded ++ */ ++ @NotNull ++ public default java.util.concurrent.CompletableFuture getChunkAtAsyncUrgently(@NotNull Location loc) { ++ return getChunkAtAsync((int)Math.floor(loc.getX()) >> 4, (int)Math.floor(loc.getZ()) >> 4, true, true); ++ } ++ ++ /** ++ * Requests a {@link Chunk} to be loaded at the given coordinates ++ * ++ * This method makes no guarantee on how fast the chunk will load, ++ * and will return the chunk to the callback at a later time. ++ * ++ * You should use this method if you need a chunk but do not need it ++ * immediately, and you wish to let the server control the speed ++ * of chunk loads, keeping performance in mind. ++ * ++ * The future will always be executed synchronously ++ * on the main Server Thread. ++ * @param loc Location to load the corresponding chunk from ++ * @param gen Should the chunk generate ++ * @return Future that will resolve when the chunk is loaded ++ */ ++ @NotNull ++ public default java.util.concurrent.CompletableFuture getChunkAtAsyncUrgently(@NotNull Location loc, boolean gen) { ++ return getChunkAtAsync((int)Math.floor(loc.getX()) >> 4, (int)Math.floor(loc.getZ()) >> 4, gen, true); ++ } ++ ++ /** ++ * Requests a {@link Chunk} to be loaded at the given coordinates ++ * ++ * This method makes no guarantee on how fast the chunk will load, ++ * and will return the chunk to the callback at a later time. ++ * ++ * You should use this method if you need a chunk but do not need it ++ * immediately, and you wish to let the server control the speed ++ * of chunk loads, keeping performance in mind. ++ * ++ * The future will always be executed synchronously ++ * on the main Server Thread. ++ * @param block Block to load the corresponding chunk from ++ * @return Future that will resolve when the chunk is loaded ++ */ ++ @NotNull ++ public default java.util.concurrent.CompletableFuture getChunkAtAsyncUrgently(@NotNull Block block) { ++ return getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, true, true); ++ } ++ ++ /** ++ * Requests a {@link Chunk} to be loaded at the given coordinates ++ * ++ * This method makes no guarantee on how fast the chunk will load, ++ * and will return the chunk to the callback at a later time. ++ * ++ * You should use this method if you need a chunk but do not need it ++ * immediately, and you wish to let the server control the speed ++ * of chunk loads, keeping performance in mind. ++ * ++ * The future will always be executed synchronously ++ * on the main Server Thread. ++ * @param block Block to load the corresponding chunk from ++ * @param gen Should the chunk generate ++ * @return Future that will resolve when the chunk is loaded ++ */ ++ @NotNull ++ public default java.util.concurrent.CompletableFuture getChunkAtAsyncUrgently(@NotNull Block block, boolean gen) { ++ return getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, gen, true); ++ } ++ ++ /** ++ * Requests a {@link Chunk} to be loaded at the given coordinates ++ * ++ * This method makes no guarantee on how fast the chunk will load, ++ * and will return the chunk to the callback at a later time. ++ * ++ * You should use this method if you need a chunk but do not need it ++ * immediately, and you wish to let the server control the speed ++ * of chunk loads, keeping performance in mind. ++ * ++ * The future will always be executed synchronously ++ * on the main Server Thread. ++ * ++ * @param x X Coord ++ * @param z Z Coord ++ * @return Future that will resolve when the chunk is loaded ++ */ ++ @NotNull ++ public default java.util.concurrent.CompletableFuture getChunkAtAsyncUrgently(int x, int z) { ++ return getChunkAtAsync(x, z, true, true); ++ } ++ ++ @NotNull ++ java.util.concurrent.CompletableFuture getChunkAtAsync(int x, int z, boolean gen, boolean urgent); // Paper end /** @@ -397,7 +506,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + @NotNull + public default java.util.concurrent.CompletableFuture teleportAsync(@NotNull Location loc, @NotNull TeleportCause cause) { + java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); -+ loc.getWorld().getChunkAtAsync(loc).thenAccept((chunk) -> future.complete(teleport(loc, cause))); ++ loc.getWorld().getChunkAtAsyncUrgently(loc).thenAccept((chunk) -> future.complete(teleport(loc, cause))).exceptionally(ex -> { ++ future.completeExceptionally(ex); ++ return null; ++ }); + return future; + } + // Paper end diff --git a/Spigot-Server-Patches/Asynchronous-chunk-IO-and-loading.patch b/Spigot-Server-Patches/Asynchronous-chunk-IO-and-loading.patch index 8af41e1696..eb73e3b4a8 100644 --- a/Spigot-Server-Patches/Asynchronous-chunk-IO-and-loading.patch +++ b/Spigot-Server-Patches/Asynchronous-chunk-IO-and-loading.patch @@ -2315,12 +2315,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + private long asyncLoadSeqCounter; + -+ public void getChunkAtAsynchronously(int x, int z, boolean gen, java.util.function.Consumer onComplete) { ++ public CompletableFuture> getChunkAtAsynchronously(int x, int z, boolean gen, boolean isUrgent) { + if (Thread.currentThread() != this.serverThread) { ++ CompletableFuture> future = new CompletableFuture>(); + this.serverThreadQueue.execute(() -> { -+ this.getChunkAtAsynchronously(x, z, gen, onComplete); ++ this.getChunkAtAsynchronously(x, z, gen, isUrgent).whenComplete((chunk, ex) -> { ++ if (ex != null) { ++ future.completeExceptionally(ex); ++ } else { ++ future.complete(chunk); ++ } ++ }); + }); -+ return; ++ return future; ++ } ++ ++ if (!com.destroystokyo.paper.PaperConfig.asyncChunks) { ++ return CompletableFuture.completedFuture(Either.left(getChunkAt(x, z, gen))); + } + + long k = ChunkCoordIntPair.pair(x, z); @@ -2346,75 +2357,64 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + this.cacheStatus[0] = ChunkStatus.FULL; + this.cacheChunk[0] = ichunkaccess; + -+ onComplete.accept((Chunk)ichunkaccess); -+ -+ return; ++ return CompletableFuture.completedFuture(Either.left(ichunkaccess)); + } + } + } + + if (gen) { -+ this.bringToFullStatusAsync(x, z, chunkPos, onComplete); -+ return; ++ return this.bringToFullStatusAsync(x, z, chunkPos, isUrgent); + } + + IChunkAccess current = this.getChunkAtImmediately(x, z); // we want to bypass ticket restrictions + if (current != null) { + if (!(current instanceof ProtoChunkExtension) && !(current instanceof net.minecraft.server.Chunk)) { -+ onComplete.accept(null); // the chunk is not gen'd -+ return; ++ return CompletableFuture.completedFuture(Either.left(null)); + } + // we know the chunk is at full status here (either in read-only mode or the real thing) -+ this.bringToFullStatusAsync(x, z, chunkPos, onComplete); -+ return; ++ return this.bringToFullStatusAsync(x, z, chunkPos, isUrgent); + } + + ChunkStatus status = world.getChunkProvider().playerChunkMap.getStatusOnDiskNoLoad(x, z); + + if (status != null && status != ChunkStatus.FULL) { + // does not exist on disk -+ onComplete.accept(null); -+ return; ++ return CompletableFuture.completedFuture(Either.left(null)); + } + + if (status == ChunkStatus.FULL) { -+ this.bringToFullStatusAsync(x, z, chunkPos, onComplete); -+ return; ++ return this.bringToFullStatusAsync(x, z, chunkPos, isUrgent); + } + + // status is null here + + // here we don't know what status it is and we're not supposed to generate + // so we asynchronously load empty status -+ -+ this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.EMPTY, (IChunkAccess chunk) -> { -+ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) { ++ return this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.EMPTY, isUrgent).thenCompose((either) -> { ++ IChunkAccess chunk = either.left().orElse(null); ++ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof Chunk)) { + // the chunk on disk was not a full status chunk -+ onComplete.accept(null); -+ return; ++ return CompletableFuture.completedFuture(Either.left(null)); + } -+ this.bringToFullStatusAsync(x, z, chunkPos, onComplete); // bring to full status if required ++ ; // bring to full status if required ++ return this.bringToFullStatusAsync(x, z, chunkPos, isUrgent); + }); + } + -+ private void bringToFullStatusAsync(int x, int z, ChunkCoordIntPair chunkPos, java.util.function.Consumer onComplete) { -+ this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.FULL, (java.util.function.Consumer)onComplete); ++ private CompletableFuture> bringToFullStatusAsync(int x, int z, ChunkCoordIntPair chunkPos, boolean isUrgent) { ++ return this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.FULL, isUrgent); + } + -+ private void bringToStatusAsync(int x, int z, ChunkCoordIntPair chunkPos, ChunkStatus status, java.util.function.Consumer onComplete) { ++ private CompletableFuture> bringToStatusAsync(int x, int z, ChunkCoordIntPair chunkPos, ChunkStatus status, boolean isUrgent) { + CompletableFuture> future = this.getChunkFutureMainThread(x, z, status, true); + Long identifier = Long.valueOf(this.asyncLoadSeqCounter++); + int ticketLevel = MCUtil.getTicketLevelFor(status); + this.addTicketAtLevel(TicketType.ASYNC_LOAD, chunkPos, ticketLevel, identifier); + -+ future.whenCompleteAsync((Either either, Throwable throwable) -> { ++ return future.thenComposeAsync((Either either) -> { + // either left -> success + // either right -> failure + -+ if (throwable != null) { -+ throw new RuntimeException(throwable); -+ } -+ + this.removeTicketAtLevel(TicketType.ASYNC_LOAD, chunkPos, ticketLevel, identifier); + this.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, ticketLevel, chunkPos); // allow unloading + @@ -2425,8 +2425,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + throw new IllegalStateException("Chunk failed to load: " + failure.get().toString()); + } + -+ onComplete.accept(either.left().get()); -+ ++ return CompletableFuture.completedFuture(either); + }, this.serverThreadQueue); + } + @@ -4020,11 +4019,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 MCUtil.getSpiralOutChunks(spawn, radiusInBlocks >> 4).forEach(pair -> { - getChunkProvider().getChunkAtMainThread(pair.x, pair.z); -+ if (com.destroystokyo.paper.PaperConfig.asyncChunks) { -+ getChunkProvider().getChunkAtAsynchronously(pair.x, pair.z, true, (c) -> {}); -+ } else { -+ getChunkProvider().getChunkAtMainThread(pair.x, pair.z); -+ } ++ getChunkProvider().getChunkAtAsynchronously(pair.x, pair.z, true, false).exceptionally((ex) -> { ++ ex.printStackTrace(); ++ return null; ++ }); }); } public void removeTicketsForSpawn(int radiusInBlocks, BlockPosition spawn) { @@ -4032,6 +4030,14 @@ diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/jav index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -0,0 +0,0 @@ import net.minecraft.server.GroupDataEntity; + import net.minecraft.server.IBlockData; + import net.minecraft.server.IChunkAccess; + import net.minecraft.server.MinecraftKey; ++import net.minecraft.server.MinecraftServer; + import net.minecraft.server.MovingObjectPosition; + import net.minecraft.server.PacketPlayOutCustomSoundEffect; + import net.minecraft.server.PacketPlayOutUpdateTime; @@ -0,0 +0,0 @@ public class CraftWorld implements World { return true; } @@ -4070,20 +4076,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } + // Paper start + @Override -+ public CompletableFuture getChunkAtAsync(int x, int z, boolean gen) { ++ public CompletableFuture getChunkAtAsync(int x, int z, boolean gen, boolean urgent) { + if (Bukkit.isPrimaryThread()) { + net.minecraft.server.Chunk immediate = this.world.getChunkProvider().getChunkAtIfLoadedImmediately(x, z); + if (immediate != null) { -+ return CompletableFuture.completedFuture(immediate.bukkitChunk); ++ return CompletableFuture.completedFuture(immediate.getBukkitChunk()); + } + } + -+ CompletableFuture ret = new CompletableFuture<>(); -+ this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, (net.minecraft.server.Chunk chunk) -> { -+ ret.complete(chunk == null ? null : chunk.bukkitChunk); -+ }); -+ -+ return ret; ++ return this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { ++ net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) either.left().orElse(null); ++ return CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk()); ++ }, MinecraftServer.getServer()); + } + // Paper end diff --git a/Spigot-Server-Patches/Delay-unsafe-actions-until-after-entity-ticking-is-d.patch b/Spigot-Server-Patches/Delay-unsafe-actions-until-after-entity-ticking-is-d.patch index 98b0e87300..4abf658465 100644 --- a/Spigot-Server-Patches/Delay-unsafe-actions-until-after-entity-ticking-is-d.patch +++ b/Spigot-Server-Patches/Delay-unsafe-actions-until-after-entity-ticking-is-d.patch @@ -43,16 +43,3 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 this.getMinecraftServer().midTickLoadChunks(); // Paper try (co.aikar.timings.Timing ignored = this.timings.newEntities.startTiming()) { // Paper - timings -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -0,0 +0,0 @@ public class CraftWorld implements World { - - CompletableFuture ret = new CompletableFuture<>(); - this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, (net.minecraft.server.Chunk chunk) -> { -- ret.complete(chunk == null ? null : chunk.bukkitChunk); -+ this.world.doIfNotEntityTicking(() -> ret.complete(chunk == null ? null : chunk.bukkitChunk)); - }); - - return ret; diff --git a/Spigot-Server-Patches/Fix-Light-Command.patch b/Spigot-Server-Patches/Fix-Light-Command.patch index 8654c33ac0..d8a39727d7 100644 --- a/Spigot-Server-Patches/Fix-Light-Command.patch +++ b/Spigot-Server-Patches/Fix-Light-Command.patch @@ -81,7 +81,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + sender.sendMessage("All Chunks Light updated"); + return; + } -+ world.getChunkProvider().getChunkAtAsynchronously(coord.x, coord.z, false, chunk -> { ++ world.getChunkProvider().getChunkAtAsynchronously(coord.x, coord.z, false, false).whenCompleteAsync((either, ex) -> { ++ if (ex != null) { ++ sender.sendMessage("Error loading chunk " + coord); ++ updateLight(sender, world, lightengine, queue); ++ return; ++ } ++ Chunk chunk = (Chunk) either.left().orElse(null); ++ if (chunk == null) { ++ updateLight(sender, world, lightengine, queue); ++ return; ++ } + lightengine.a(world.paperConfig.lightQueueSize + 16 * 256); // ensure full chunk can fit into queue + sender.sendMessage("Updating Light " + coord); + for (int y = 0; y < world.getHeight(); y++) { @@ -105,7 +115,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + updateLight(sender, world, lightengine, queue); + } + lightengine.a(world.paperConfig.lightQueueSize); -+ }); ++ }, MinecraftServer.getServer()); + } + private void doSyncLoadInfo(CommandSender sender, String[] args) { diff --git a/Spigot-Server-Patches/Implement-Chunk-Priority-Urgency-System-for-World-Ge.patch b/Spigot-Server-Patches/Implement-Chunk-Priority-Urgency-System-for-World-Ge.patch index b29e749315..980f27e6b4 100644 --- a/Spigot-Server-Patches/Implement-Chunk-Priority-Urgency-System-for-World-Ge.patch +++ b/Spigot-Server-Patches/Implement-Chunk-Priority-Urgency-System-for-World-Ge.patch @@ -19,23 +19,12 @@ diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/m index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider { - } - - private long asyncLoadSeqCounter; -+ public static boolean IS_CHUNK_LOAD_BLOCKING_MAIN = false; - - public void getChunkAtAsynchronously(int x, int z, boolean gen, java.util.function.Consumer onComplete) { - if (Thread.currentThread() != this.serverThread) { @@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider { } gameprofilerfiller.c("getChunkCacheMiss"); -+ // Paper start - Chunk Load/Gen Priority -+ boolean prevBlocking = IS_CHUNK_LOAD_BLOCKING_MAIN; -+ IS_CHUNK_LOAD_BLOCKING_MAIN = true; -+ // Paper end - CompletableFuture> completablefuture = this.getChunkFutureMainThread(i, j, chunkstatus, flag); +- CompletableFuture> completablefuture = this.getChunkFutureMainThread(i, j, chunkstatus, flag); ++ CompletableFuture> completablefuture = this.getChunkFutureMainThread(i, j, chunkstatus, flag, true); // Paper if (!completablefuture.isDone()) { // Paper // Paper start - async chunk io/loading @@ -54,10 +43,33 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + if (playerChunk != null) { + playerChunk.clearChunkUrgent(); + } -+ IS_CHUNK_LOAD_BLOCKING_MAIN = prevBlocking;// Paper ichunkaccess = (IChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { return ichunkaccess1; }, (playerchunk_failure) -> { +@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider { + } + + private CompletableFuture> getChunkFutureMainThread(int i, int j, ChunkStatus chunkstatus, boolean flag) { ++ // Paper start ++ return getChunkFutureMainThread(i, j, chunkstatus, flag, false); ++ } ++ private CompletableFuture> getChunkFutureMainThread(int i, int j, ChunkStatus chunkstatus, boolean flag, boolean isUrgent) { ++ // Paper end + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j); + long k = chunkcoordintpair.pair(); + int l = 33 + ChunkStatus.a(chunkstatus); +@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider { + } + } + } ++ // Paper start ++ if (playerchunk != null && isUrgent) { ++ playerchunk.markChunkUrgent(chunkstatus); ++ } ++ // Paper end + + return this.a(playerchunk, l) ? PlayerChunk.UNLOADED_CHUNK_ACCESS_FUTURE : playerchunk.a(chunkstatus, this.playerChunkMap); + } diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java @@ -196,6 +208,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } private void d(int i) { +@@ -0,0 +0,0 @@ public class PlayerChunk { + Chunk fullChunk = either.left().get(); + PlayerChunk.this.isFullChunkReady = true; + fullChunk.playerChunk = PlayerChunk.this; ++ this.clearChunkUrgent(); + + + } diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -279,3 +299,31 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 CompletableFuture> completablefuture1 = completablefuture.thenApplyAsync((either) -> { return either.flatMap((list) -> { Chunk chunk = (Chunk) list.get(list.size() / 2); +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -0,0 +0,0 @@ + package org.bukkit.craftbukkit; + ++import com.destroystokyo.paper.io.PrioritizedTaskQueue; + import com.google.common.base.Preconditions; + import com.google.common.collect.ImmutableList; + import com.google.common.collect.ImmutableMap; +@@ -0,0 +0,0 @@ public class CraftWorld implements World { + } + } + +- return this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { ++ CompletableFuture future = this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> { + net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) either.left().orElse(null); + return CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk()); + }, MinecraftServer.getServer()); ++ if (urgent) { ++ world.asyncChunkTaskManager.raisePriority(x, z, PrioritizedTaskQueue.HIGHEST_PRIORITY); ++ } ++ return future; ++ + } + // Paper end + diff --git a/Spigot-Server-Patches/Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch b/Spigot-Server-Patches/Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch index c40331362d..bec73302ee 100644 --- a/Spigot-Server-Patches/Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch +++ b/Spigot-Server-Patches/Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch @@ -222,8 +222,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 import net.minecraft.server.IChunkAccess; +import net.minecraft.server.MCUtil; import net.minecraft.server.MinecraftKey; + import net.minecraft.server.MinecraftServer; import net.minecraft.server.MovingObjectPosition; - import net.minecraft.server.PacketPlayOutCustomSoundEffect; @@ -0,0 +0,0 @@ public class CraftWorld implements World { return ret; }