From e5f648960256336a952724b00150eb192a0840df Mon Sep 17 00:00:00 2001 From: Aikar Date: Sat, 9 May 2020 22:30:28 -0400 Subject: [PATCH] Add Urgent API for Async Chunks API and use it for Async Teleport This also cleans up the implementation of Async Chunks to get rid of most Consumer callbacks and instead return futures. This lets us propogate errors correctly up the future chain (barring one isn't lost even deeper in the chain...) So exceptions can now bubble to plugins using getChunkAtAsync --- .../0132-Async-Chunks-API.patch | 124 +++++++++++++++++- .../0157-Add-sun-related-API.patch | 4 +- .../0173-Entity-getEntitySpawnReason.patch | 4 +- .../0203-World-view-distance-api.patch | 4 +- .../0205-Spawn-Reason-API.patch | 6 +- ...90-Asynchronous-chunk-IO-and-loading.patch | 112 ++++++++-------- .../0392-Reduce-sync-loads.patch | 6 +- ...ement-optional-per-player-mob-spawns.patch | 8 +- ...hanging-entities-that-are-not-ItemFr.patch | 4 +- ...e-getChunkAt-calls-for-loaded-chunks.patch | 6 +- ...hunkMap-memory-use-for-visibleChunks.patch | 16 +-- ...ks-when-attempting-to-unload-a-chunk.patch | 6 +- ...asks-Speed-up-processing-of-chunk-lo.patch | 14 +- ...Priority-Urgency-System-for-World-Ge.patch | 84 +++++++++--- ...ions-until-after-entity-ticking-is-d.patch | 13 -- ...oviderServer-s-chunk-level-checking-.patch | 4 +- ...-Chunk-Post-Processing-deadlock-risk.patch | 4 +- ...-isOutsideRange-to-use-distance-maps.patch | 10 +- ...No-Tick-view-distance-implementation.patch | 6 +- .../0507-Fix-Light-Command.patch | 20 ++- 20 files changed, 308 insertions(+), 147 deletions(-) diff --git a/Spigot-API-Patches/0132-Async-Chunks-API.patch b/Spigot-API-Patches/0132-Async-Chunks-API.patch index 67fcae0293..16273413b5 100644 --- a/Spigot-API-Patches/0132-Async-Chunks-API.patch +++ b/Spigot-API-Patches/0132-Async-Chunks-API.patch @@ -8,10 +8,10 @@ Adds API's to load or generate chunks asynchronously. Also adds utility methods to Entity to teleport asynchronously. diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java -index e5b76d59fdcc85344cf3932b38ab096155d2eec3..62aa9bd67cf652cc9fcd4ef2ced3bc48e120763d 100644 +index e5b76d59fdcc85344cf3932b38ab096155d2eec3..8fe496ad8f566183e2280582d00c3b7751f06d75 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java -@@ -221,6 +221,358 @@ public interface World extends PluginMessageRecipient, Metadatable { +@@ -221,6 +221,467 @@ public interface World extends PluginMessageRecipient, Metadatable { public default Chunk getChunkAt(long chunkKey) { return getChunkAt((int) chunkKey, (int) (chunkKey >> 32)); } @@ -366,15 +366,124 @@ index e5b76d59fdcc85344cf3932b38ab096155d2eec3..62aa9bd67cf652cc9fcd4ef2ced3bc48 + * @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 /** diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java -index b4069dbf31587786da39f5e387a71b7bc6a5d0ae..45e0dffe60ce1f4d54b481b6b2cbee511659a5cc 100644 +index b4069dbf31587786da39f5e387a71b7bc6a5d0ae..b8e8a425a84a562ccd8431d30e39145a662c4180 100644 --- a/src/main/java/org/bukkit/entity/Entity.java +++ b/src/main/java/org/bukkit/entity/Entity.java -@@ -155,6 +155,30 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent +@@ -155,6 +155,33 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent */ public boolean teleport(@NotNull Entity destination, @NotNull TeleportCause cause); @@ -397,7 +506,10 @@ index b4069dbf31587786da39f5e387a71b7bc6a5d0ae..45e0dffe60ce1f4d54b481b6b2cbee51 + @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-API-Patches/0157-Add-sun-related-API.patch b/Spigot-API-Patches/0157-Add-sun-related-API.patch index d81cff7e9f..b4c7ae2399 100644 --- a/Spigot-API-Patches/0157-Add-sun-related-API.patch +++ b/Spigot-API-Patches/0157-Add-sun-related-API.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add sun related API diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java -index b7ad4f566497914573ccfb595e660226dd34c273..c88f17244617b75e251811595ffc189dc81e12fb 100644 +index 42b91634a131f8705cbde96b6068d5881a393fb7..f9a5ea4fcdb87e741cf04b47d469e05f8a786155 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java -@@ -1646,6 +1646,16 @@ public interface World extends PluginMessageRecipient, Metadatable { +@@ -1755,6 +1755,16 @@ public interface World extends PluginMessageRecipient, Metadatable { */ public void setFullTime(long time); diff --git a/Spigot-API-Patches/0173-Entity-getEntitySpawnReason.patch b/Spigot-API-Patches/0173-Entity-getEntitySpawnReason.patch index 67ae211cd5..7e528fc7e4 100644 --- a/Spigot-API-Patches/0173-Entity-getEntitySpawnReason.patch +++ b/Spigot-API-Patches/0173-Entity-getEntitySpawnReason.patch @@ -10,10 +10,10 @@ persistenting Living Entity, SPAWNER for spawners, or DEFAULT since data was not stored. diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java -index 45e0dffe60ce1f4d54b481b6b2cbee511659a5cc..5518ecd21435334d9148cd3e095ec06031bdd0a5 100644 +index b8e8a425a84a562ccd8431d30e39145a662c4180..3f0f38031000a4eda25584d069939f40fbfb026b 100644 --- a/src/main/java/org/bukkit/entity/Entity.java +++ b/src/main/java/org/bukkit/entity/Entity.java -@@ -649,5 +649,11 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent +@@ -652,5 +652,11 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent */ @NotNull Chunk getChunk(); diff --git a/Spigot-API-Patches/0203-World-view-distance-api.patch b/Spigot-API-Patches/0203-World-view-distance-api.patch index 1a2d6e705c..6676931bf2 100644 --- a/Spigot-API-Patches/0203-World-view-distance-api.patch +++ b/Spigot-API-Patches/0203-World-view-distance-api.patch @@ -5,10 +5,10 @@ Subject: [PATCH] World view distance api diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java -index db18f70ec37253232fb2cfd08ccd07d13c7c457d..421ad6a91ceb38fd62684d18f109d7cf8526605a 100644 +index 0a5865d85dfc28cb68f753878608b12afd74bc99..be4d6ca8e8ec4c2e6643eaecc19f11466e14658c 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java -@@ -3166,6 +3166,34 @@ public interface World extends PluginMessageRecipient, Metadatable { +@@ -3275,6 +3275,34 @@ public interface World extends PluginMessageRecipient, Metadatable { int getViewDistance(); // Spigot end diff --git a/Spigot-API-Patches/0205-Spawn-Reason-API.patch b/Spigot-API-Patches/0205-Spawn-Reason-API.patch index b9780cf4e8..de7272882a 100644 --- a/Spigot-API-Patches/0205-Spawn-Reason-API.patch +++ b/Spigot-API-Patches/0205-Spawn-Reason-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Spawn Reason API diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java -index 421ad6a91ceb38fd62684d18f109d7cf8526605a..906ee6827dc6956b3c1657bde75ef8481e16dce2 100644 +index be4d6ca8e8ec4c2e6643eaecc19f11466e14658c..9518da825ed752c5a477ca9132de50f923f9192d 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java @@ -1,6 +1,8 @@ @@ -17,7 +17,7 @@ index 421ad6a91ceb38fd62684d18f109d7cf8526605a..906ee6827dc6956b3c1657bde75ef848 import org.bukkit.generator.ChunkGenerator; import java.util.ArrayList; -@@ -2037,6 +2039,12 @@ public interface World extends PluginMessageRecipient, Metadatable { +@@ -2146,6 +2148,12 @@ public interface World extends PluginMessageRecipient, Metadatable { @NotNull public T spawn(@NotNull Location location, @NotNull Class clazz) throws IllegalArgumentException; @@ -30,7 +30,7 @@ index 421ad6a91ceb38fd62684d18f109d7cf8526605a..906ee6827dc6956b3c1657bde75ef848 /** * Spawn an entity of a specific class at the given {@link Location}, with * the supplied function run before the entity is added to the world. -@@ -2054,7 +2062,28 @@ public interface World extends PluginMessageRecipient, Metadatable { +@@ -2163,7 +2171,28 @@ public interface World extends PluginMessageRecipient, Metadatable { * {@link Entity} requested cannot be spawned */ @NotNull diff --git a/Spigot-Server-Patches/0390-Asynchronous-chunk-IO-and-loading.patch b/Spigot-Server-Patches/0390-Asynchronous-chunk-IO-and-loading.patch index 99252250c4..879140cf09 100644 --- a/Spigot-Server-Patches/0390-Asynchronous-chunk-IO-and-loading.patch +++ b/Spigot-Server-Patches/0390-Asynchronous-chunk-IO-and-loading.patch @@ -2305,22 +2305,33 @@ index 0000000000000000000000000000000000000000..2b20c159f6bb425be70201cf33159aa9 + +} diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 4c9c8e483974f8869d6711626620cfd7d814d956..259af7095c453e52e7c3a8662968d96de0efbbc8 100644 +index 4c9c8e483974f8869d6711626620cfd7d814d956..cabe2a5908dd9ee721c13c1825e65a37f72361d4 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -299,11 +299,137 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -299,11 +299,136 @@ public class ChunkProviderServer extends IChunkProvider { return playerChunk.getAvailableChunkNow(); } + + 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 4c9c8e483974f8869d6711626620cfd7d814d956..259af7095c453e52e7c3a8662968d96d + 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 4c9c8e483974f8869d6711626620cfd7d814d956..259af7095c453e52e7c3a8662968d96d + throw new IllegalStateException("Chunk failed to load: " + failure.get().toString()); + } + -+ onComplete.accept(either.left().get()); -+ ++ return CompletableFuture.completedFuture(either); + }, this.serverThreadQueue); + } + @@ -2446,7 +2445,7 @@ index 4c9c8e483974f8869d6711626620cfd7d814d956..259af7095c453e52e7c3a8662968d96d if (Thread.currentThread() != this.serverThread) { return (IChunkAccess) CompletableFuture.supplyAsync(() -> { return this.getChunkAt(i, j, chunkstatus, flag); -@@ -329,8 +455,13 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -329,8 +454,13 @@ public class ChunkProviderServer extends IChunkProvider { CompletableFuture> completablefuture = this.getChunkFutureMainThread(i, j, chunkstatus, flag); if (!completablefuture.isDone()) { // Paper @@ -2460,7 +2459,7 @@ index 4c9c8e483974f8869d6711626620cfd7d814d956..259af7095c453e52e7c3a8662968d96d this.world.timings.syncChunkLoad.stopTiming(); // Paper } // Paper ichunkaccess = (IChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { -@@ -835,11 +966,12 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -835,11 +965,12 @@ public class ChunkProviderServer extends IChunkProvider { protected boolean executeNext() { // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task try { @@ -3923,7 +3922,7 @@ index c999f8c9bf8a59e19b3d6d1b7ad8b5fb6e48b928..b59ef1a63338aa150d39e8014e12b227 HAS_SPACE(VillagePlaceRecord::d), IS_OCCUPIED(VillagePlaceRecord::e), ANY((villageplacerecord) -> { diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 8561f96b9a10cc2faa3ec1087a05ddb4c6164938..c0476f69e4a3f079f9160a4d5d384556597ec279 100644 +index 8561f96b9a10cc2faa3ec1087a05ddb4c6164938..30dbf95a02bd8e68471400245b46189f8e0560cc 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -82,6 +82,79 @@ public class WorldServer extends World { @@ -4015,24 +4014,31 @@ index 8561f96b9a10cc2faa3ec1087a05ddb4c6164938..c0476f69e4a3f079f9160a4d5d384556 } // CraftBukkit start -@@ -1673,7 +1748,11 @@ public class WorldServer extends World { +@@ -1673,7 +1748,10 @@ public class WorldServer extends World { } 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) { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 50467656df0b54c2dcba8696b5677a2fc975b178..d3ac0ffe4682e56b7654611f32beefb6bc779823 100644 +index 50467656df0b54c2dcba8696b5677a2fc975b178..711bb5c5f4a37357007e8c6441b61c474952ba4d 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -556,22 +556,23 @@ public class CraftWorld implements World { +@@ -75,6 +75,7 @@ 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; +@@ -556,22 +557,23 @@ public class CraftWorld implements World { return true; } @@ -4064,26 +4070,24 @@ index 50467656df0b54c2dcba8696b5677a2fc975b178..d3ac0ffe4682e56b7654611f32beefb6 // fall through to load // we do this so we do not re-read the chunk data on disk -@@ -2443,6 +2444,24 @@ public class CraftWorld implements World { +@@ -2443,6 +2445,22 @@ public class CraftWorld implements World { return new CraftDragonBattle(((WorldProviderTheEnd) worldProvider).o()); // PAIL rename getDragonBattle } + // 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/0392-Reduce-sync-loads.patch b/Spigot-Server-Patches/0392-Reduce-sync-loads.patch index 8da5cfc0f2..61c27ff914 100644 --- a/Spigot-Server-Patches/0392-Reduce-sync-loads.patch +++ b/Spigot-Server-Patches/0392-Reduce-sync-loads.patch @@ -285,10 +285,10 @@ index 0000000000000000000000000000000000000000..59aec103295f747793fdc0a52eb45f41 + } +} diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 259af7095c453e52e7c3a8662968d96de0efbbc8..ea1117dc86e9621c37ab4ba0f7c42e7b172648b3 100644 +index cabe2a5908dd9ee721c13c1825e65a37f72361d4..61640d814426732a03d8bb3394c2cd3414a27a6c 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -459,6 +459,7 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -458,6 +458,7 @@ public class ChunkProviderServer extends IChunkProvider { this.world.asyncChunkTaskManager.raisePriority(x, z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.world, x, z); // Paper end @@ -328,7 +328,7 @@ index 311685180fe720706dfb6c82b1b54f9876187b73..568e04faa314552e14286efdfcdfb79e if (chunk != null) { chunk.a(oclass, axisalignedbb, list, predicate); diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index c0476f69e4a3f079f9160a4d5d384556597ec279..2fa1b86adf89bb9b2398806bf30b0a6e436a4577 100644 +index 30dbf95a02bd8e68471400245b46189f8e0560cc..4fea35783caa4d93de83e256b565f0e80212e9a5 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -154,6 +154,12 @@ public class WorldServer extends World { diff --git a/Spigot-Server-Patches/0396-implement-optional-per-player-mob-spawns.patch b/Spigot-Server-Patches/0396-implement-optional-per-player-mob-spawns.patch index 51468e3575..a4aef1fd1b 100644 --- a/Spigot-Server-Patches/0396-implement-optional-per-player-mob-spawns.patch +++ b/Spigot-Server-Patches/0396-implement-optional-per-player-mob-spawns.patch @@ -545,10 +545,10 @@ index 0000000000000000000000000000000000000000..4f13d3ff8391793a99f067189f854078 + } +} diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index ea1117dc86e9621c37ab4ba0f7c42e7b172648b3..fe894a68bc28ed01819bb538079856712f813713 100644 +index 61640d814426732a03d8bb3394c2cd3414a27a6c..cbaf14b24b3d941f0912788c87c3eab5aad7f5f0 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -741,7 +741,22 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -740,7 +740,22 @@ public class ChunkProviderServer extends IChunkProvider { this.world.timings.countNaturalMobs.startTiming(); // Paper - timings int l = this.chunkMapDistance.b(); EnumCreatureType[] aenumcreaturetype = EnumCreatureType.values(); @@ -572,7 +572,7 @@ index ea1117dc86e9621c37ab4ba0f7c42e7b172648b3..fe894a68bc28ed01819bb53807985671 this.world.timings.countNaturalMobs.stopTiming(); // Paper - timings this.world.getMethodProfiler().exit(); -@@ -809,8 +824,23 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -808,8 +823,23 @@ public class ChunkProviderServer extends IChunkProvider { if (enumcreaturetype != EnumCreatureType.MISC && (!enumcreaturetype.c() || this.allowAnimals) && (enumcreaturetype.c() || this.allowMonsters) && (!enumcreaturetype.d() || flag2)) { int k1 = limit * l / ChunkProviderServer.b; // CraftBukkit - use per-world limits @@ -755,7 +755,7 @@ index fdac5bb3a2d4a73035e1d914979b87fc224b6b20..58bbf2f9d2ec91715051d40e108e1606 @Nullable diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 731f6a83200b4f7608fe1f1f3f0e04d827913e72..38c5b721bf145c0083cf560763f1c6327210aebf 100644 +index e7747396c19c432de59213a440ba0dd692daadae..95c3ee7387977efe87f2c7eba017489823ba1390 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -1028,7 +1028,20 @@ public class WorldServer extends World { diff --git a/Spigot-Server-Patches/0404-Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch b/Spigot-Server-Patches/0404-Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch index 25b6b75383..b3dd514bc4 100644 --- a/Spigot-Server-Patches/0404-Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch +++ b/Spigot-Server-Patches/0404-Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Fix spawning of hanging entities that are not ItemFrames and diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index d3ac0ffe4682e56b7654611f32beefb6bc779823..07ebd78acc7498616887f90a82300b4d5b71fab1 100644 +index 9a383d7d606f914de896ea07af0fea579ad5682b..8a1aca1990ca801c86065439bdd4d4a987ee3250 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -1850,7 +1850,12 @@ public class CraftWorld implements World { +@@ -1851,7 +1851,12 @@ public class CraftWorld implements World { height = 9; } diff --git a/Spigot-Server-Patches/0431-Optimise-getChunkAt-calls-for-loaded-chunks.patch b/Spigot-Server-Patches/0431-Optimise-getChunkAt-calls-for-loaded-chunks.patch index 1f97d6f40e..62dd00bf9f 100644 --- a/Spigot-Server-Patches/0431-Optimise-getChunkAt-calls-for-loaded-chunks.patch +++ b/Spigot-Server-Patches/0431-Optimise-getChunkAt-calls-for-loaded-chunks.patch @@ -7,10 +7,10 @@ bypass the need to get a player chunk, then get the either, then unwrap it... diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index fe894a68bc28ed01819bb538079856712f813713..e67e00653575c3e57fe16980d2ff074d8413a95e 100644 +index cbaf14b24b3d941f0912788c87c3eab5aad7f5f0..ea85f40f4d40f687f3feaf161d2248f2bcc39af2 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -435,6 +435,12 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -434,6 +434,12 @@ public class ChunkProviderServer extends IChunkProvider { return this.getChunkAt(i, j, chunkstatus, flag); }, this.serverThreadQueue).join(); } else { @@ -23,7 +23,7 @@ index fe894a68bc28ed01819bb538079856712f813713..e67e00653575c3e57fe16980d2ff074d GameProfilerFiller gameprofilerfiller = this.world.getMethodProfiler(); gameprofilerfiller.c("getChunk"); -@@ -485,39 +491,7 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -484,39 +490,7 @@ public class ChunkProviderServer extends IChunkProvider { if (Thread.currentThread() != this.serverThread) { return null; } else { diff --git a/Spigot-Server-Patches/0456-Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch b/Spigot-Server-Patches/0456-Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch index b4d7c74e33..a26715bc15 100644 --- a/Spigot-Server-Patches/0456-Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch +++ b/Spigot-Server-Patches/0456-Optimize-PlayerChunkMap-memory-use-for-visibleChunks.patch @@ -50,10 +50,10 @@ index 0000000000000000000000000000000000000000..e0ad725b2e63ffd329fc4725d15290cb + } +} diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index ca1b5b3b094b847f96742d8466fc42c5bdedbff5..c457e3b772ec9a77dc5028853512b7e50853315d 100644 +index b4cf530bdf1027ef27a1aec40ac4fade8d8275de..e03e6465655e209563eed2313502786fa2361024 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -750,7 +750,7 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -749,7 +749,7 @@ public class ChunkProviderServer extends IChunkProvider { entityPlayer.playerNaturallySpawnedEvent.callEvent(); }; // Paper end @@ -213,7 +213,7 @@ index 4beae504c875767ff00e26461fe7240498750e27..00f26ae23da65453073fc06ffec8a349 while (objectbidirectionaliterator.hasNext()) { Entry entry = (Entry) objectbidirectionaliterator.next(); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 07ebd78acc7498616887f90a82300b4d5b71fab1..b70c0fd977ce71cc8cb484249cb234dc86b59f1f 100644 +index 5e84bbad1ba75cb2b541a29d38d3f96832cff853..2393c8715603478142a1980a9a769de58f8bb3c4 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -74,6 +74,7 @@ import net.minecraft.server.GameRules; @@ -222,9 +222,9 @@ index 07ebd78acc7498616887f90a82300b4d5b71fab1..b70c0fd977ce71cc8cb484249cb234dc 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; -@@ -290,6 +291,7 @@ public class CraftWorld implements World { +@@ -291,6 +292,7 @@ public class CraftWorld implements World { return ret; } public int getTileEntityCount() { @@ -232,7 +232,7 @@ index 07ebd78acc7498616887f90a82300b4d5b71fab1..b70c0fd977ce71cc8cb484249cb234dc // We don't use the full world tile entity list, so we must iterate chunks Long2ObjectLinkedOpenHashMap chunks = world.getChunkProvider().playerChunkMap.visibleChunks; int size = 0; -@@ -301,11 +303,13 @@ public class CraftWorld implements World { +@@ -302,11 +304,13 @@ public class CraftWorld implements World { size += chunk.tileEntities.size(); } return size; @@ -246,7 +246,7 @@ index 07ebd78acc7498616887f90a82300b4d5b71fab1..b70c0fd977ce71cc8cb484249cb234dc int ret = 0; for (PlayerChunk chunkHolder : world.getChunkProvider().playerChunkMap.visibleChunks.values()) { -@@ -314,7 +318,7 @@ public class CraftWorld implements World { +@@ -315,7 +319,7 @@ public class CraftWorld implements World { } } @@ -255,7 +255,7 @@ index 07ebd78acc7498616887f90a82300b4d5b71fab1..b70c0fd977ce71cc8cb484249cb234dc } public int getPlayerCount() { return world.players.size(); -@@ -434,6 +438,14 @@ public class CraftWorld implements World { +@@ -435,6 +439,14 @@ public class CraftWorld implements World { @Override public Chunk[] getLoadedChunks() { diff --git a/Spigot-Server-Patches/0457-Don-t-load-chunks-when-attempting-to-unload-a-chunk.patch b/Spigot-Server-Patches/0457-Don-t-load-chunks-when-attempting-to-unload-a-chunk.patch index 57c4c44d1e..b9fe78219c 100644 --- a/Spigot-Server-Patches/0457-Don-t-load-chunks-when-attempting-to-unload-a-chunk.patch +++ b/Spigot-Server-Patches/0457-Don-t-load-chunks-when-attempting-to-unload-a-chunk.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Don't load chunks when attempting to unload a chunk Big Brain Logic diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index b70c0fd977ce71cc8cb484249cb234dc86b59f1f..eb7b48422e06d25974f19a582c31fecb3b80271c 100644 +index e0e1f88bd4555c721f6faa9e39d1cb9e2680e969..3f70d99af9ed63d704cd58c1614b092b17f69408 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -473,7 +473,7 @@ public class CraftWorld implements World { +@@ -474,7 +474,7 @@ public class CraftWorld implements World { @Override public boolean unloadChunkRequest(int x, int z) { org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot @@ -18,7 +18,7 @@ index b70c0fd977ce71cc8cb484249cb234dc86b59f1f..eb7b48422e06d25974f19a582c31fecb if (chunk != null) { world.getChunkProvider().removeTicket(TicketType.PLUGIN, chunk.getPos(), 1, Unit.INSTANCE); } -@@ -483,7 +483,7 @@ public class CraftWorld implements World { +@@ -484,7 +484,7 @@ public class CraftWorld implements World { private boolean unloadChunk0(int x, int z, boolean save) { org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot diff --git a/Spigot-Server-Patches/0459-Mid-Tick-Chunk-Tasks-Speed-up-processing-of-chunk-lo.patch b/Spigot-Server-Patches/0459-Mid-Tick-Chunk-Tasks-Speed-up-processing-of-chunk-lo.patch index c80faa0f29..1032ebca72 100644 --- a/Spigot-Server-Patches/0459-Mid-Tick-Chunk-Tasks-Speed-up-processing-of-chunk-lo.patch +++ b/Spigot-Server-Patches/0459-Mid-Tick-Chunk-Tasks-Speed-up-processing-of-chunk-lo.patch @@ -56,10 +56,10 @@ index 647f6fc8efb350fbd0bc4c40358a998f8b89b96a..9f1662ece533f5ea744662b718e2d89a + } } diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index c457e3b772ec9a77dc5028853512b7e50853315d..747305619b5050e7dad3ae0ccd62200ee2246b5a 100644 +index e03e6465655e209563eed2313502786fa2361024..21cf5ac6c38968c45bdd0f09f60743ad216cd925 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -683,6 +683,7 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -682,6 +682,7 @@ public class ChunkProviderServer extends IChunkProvider { this.world.getMethodProfiler().enter("purge"); this.world.timings.doChunkMap.startTiming(); // Spigot this.chunkMapDistance.purgeTickets(); @@ -67,7 +67,7 @@ index c457e3b772ec9a77dc5028853512b7e50853315d..747305619b5050e7dad3ae0ccd62200e this.tickDistanceManager(); this.world.timings.doChunkMap.stopTiming(); // Spigot this.world.getMethodProfiler().exitEnter("chunks"); -@@ -692,6 +693,7 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -691,6 +692,7 @@ public class ChunkProviderServer extends IChunkProvider { this.world.timings.doChunkUnload.startTiming(); // Spigot this.world.getMethodProfiler().exitEnter("unload"); this.playerChunkMap.unloadChunks(booleansupplier); @@ -75,7 +75,7 @@ index c457e3b772ec9a77dc5028853512b7e50853315d..747305619b5050e7dad3ae0ccd62200e this.world.timings.doChunkUnload.stopTiming(); // Spigot this.world.getMethodProfiler().exit(); this.clearCache(); -@@ -750,7 +752,7 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -749,7 +751,7 @@ public class ChunkProviderServer extends IChunkProvider { entityPlayer.playerNaturallySpawnedEvent.callEvent(); }; // Paper end @@ -84,7 +84,7 @@ index c457e3b772ec9a77dc5028853512b7e50853315d..747305619b5050e7dad3ae0ccd62200e Optional optional = ((Either) playerchunk.b().getNow(PlayerChunk.UNLOADED_CHUNK)).left(); if (optional.isPresent()) { -@@ -833,6 +835,7 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -832,6 +834,7 @@ public class ChunkProviderServer extends IChunkProvider { this.world.timings.chunkTicks.startTiming(); // Spigot // Paper this.world.a(chunk, k); this.world.timings.chunkTicks.stopTiming(); // Spigot // Paper @@ -92,7 +92,7 @@ index c457e3b772ec9a77dc5028853512b7e50853315d..747305619b5050e7dad3ae0ccd62200e } } }); -@@ -974,6 +977,41 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -973,6 +976,41 @@ public class ChunkProviderServer extends IChunkProvider { super.executeTask(runnable); } @@ -226,7 +226,7 @@ index 77adc64e30cbc1d4542eb8f4a446788c1fdc61be..3c25436f158316d2e09cbf4673365edd // Spigot Start CrashReport crashreport; diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 2a7a47c6707c349af55ccd70eb3b8c48387c391a..9b5f24c262edb82a424385f8f3cb6aa506c0dcd9 100644 +index e37900348d57ff7253224238d9f9975626db9fa0..2676093bf1084ff2b905759aa09e1f1f22fd5660 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -432,6 +432,7 @@ public class WorldServer extends World { diff --git a/Spigot-Server-Patches/0463-Implement-Chunk-Priority-Urgency-System-for-World-Ge.patch b/Spigot-Server-Patches/0463-Implement-Chunk-Priority-Urgency-System-for-World-Ge.patch index 57bf9bc864..52d27f8ef4 100644 --- a/Spigot-Server-Patches/0463-Implement-Chunk-Priority-Urgency-System-for-World-Ge.patch +++ b/Spigot-Server-Patches/0463-Implement-Chunk-Priority-Urgency-System-for-World-Ge.patch @@ -16,26 +16,15 @@ lots of chunks already. This massively reduces the lag spikes from sync chunk gens. diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 747305619b5050e7dad3ae0ccd62200ee2246b5a..746b5b55896eaf39edf92073f61796d7096c48c2 100644 +index 21cf5ac6c38968c45bdd0f09f60743ad216cd925..fb315503b0aad2cb52cb70b5b033d33fcecd1d22 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -308,6 +308,7 @@ 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) { -@@ -465,10 +466,18 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -464,10 +464,14 @@ 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 @@ -46,7 +35,7 @@ index 747305619b5050e7dad3ae0ccd62200ee2246b5a..746b5b55896eaf39edf92073f61796d7 this.world.asyncChunkTaskManager.raisePriority(x, z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.world, x, z); // Paper end -@@ -478,6 +487,11 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -477,6 +481,10 @@ public class ChunkProviderServer extends IChunkProvider { com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug this.world.timings.syncChunkLoad.stopTiming(); // Paper } // Paper @@ -54,12 +43,35 @@ index 747305619b5050e7dad3ae0ccd62200ee2246b5a..746b5b55896eaf39edf92073f61796d7 + if (playerChunk != null) { + playerChunk.clearChunkUrgent(); + } -+ IS_CHUNK_LOAD_BLOCKING_MAIN = prevBlocking;// Paper ichunkaccess = (IChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { return ichunkaccess1; }, (playerchunk_failure) -> { +@@ -508,6 +516,11 @@ 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); +@@ -535,6 +548,11 @@ 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 3d610e41969768da0d2848fa1ae195035ccfd660..4b341c81fc97076a69aede729008c54674fa7adf 100644 +index 3d610e41969768da0d2848fa1ae195035ccfd660..a6e7bcf79df9f4ad5b0a779f3eecf85f121bedc8 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -43,6 +43,111 @@ public class PlayerChunk { @@ -196,6 +208,14 @@ index 3d610e41969768da0d2848fa1ae195035ccfd660..4b341c81fc97076a69aede729008c546 } private void d(int i) { +@@ -441,6 +552,7 @@ 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 00f26ae23da65453073fc06ffec8a349ef28dd7e..6c178492b75134ba25b7730273bb550b693a7e4a 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -279,3 +299,31 @@ index 00f26ae23da65453073fc06ffec8a349ef28dd7e..6c178492b75134ba25b7730273bb550b 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 b184f8ed8ce86965c3ef9aef179126a4d69275e8..64c643aa15d6ea68f9dad3104cc41e412255cee3 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -1,5 +1,6 @@ + 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; +@@ -2472,10 +2473,15 @@ 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/0465-Delay-unsafe-actions-until-after-entity-ticking-is-d.patch b/Spigot-Server-Patches/0465-Delay-unsafe-actions-until-after-entity-ticking-is-d.patch index 40db4c52b5..c8dd5fa06b 100644 --- a/Spigot-Server-Patches/0465-Delay-unsafe-actions-until-after-entity-ticking-is-d.patch +++ b/Spigot-Server-Patches/0465-Delay-unsafe-actions-until-after-entity-ticking-is-d.patch @@ -43,16 +43,3 @@ index 9b5f24c262edb82a424385f8f3cb6aa506c0dcd9..b3785775ecd8e3c13e7829f641f2c1b5 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 eb7b48422e06d25974f19a582c31fecb3b80271c..ac257d50dea8f42a515f19bbae12ab5680e26bb4 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -2473,7 +2473,7 @@ 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/0478-Optimize-ChunkProviderServer-s-chunk-level-checking-.patch b/Spigot-Server-Patches/0478-Optimize-ChunkProviderServer-s-chunk-level-checking-.patch index 117ed8fb63..d399056cdb 100644 --- a/Spigot-Server-Patches/0478-Optimize-ChunkProviderServer-s-chunk-level-checking-.patch +++ b/Spigot-Server-Patches/0478-Optimize-ChunkProviderServer-s-chunk-level-checking-.patch @@ -9,10 +9,10 @@ so inline where possible, and avoid the abstraction of the Either class. diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 746b5b55896eaf39edf92073f61796d7096c48c2..c2e4e4f6f1895850bcac3552eee5bfedfb98c933 100644 +index fb315503b0aad2cb52cb70b5b033d33fcecd1d22..edf0cf3680e5d2212f35c9d4464b22e0f3e87342 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -615,27 +615,37 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -618,27 +618,37 @@ public class ChunkProviderServer extends IChunkProvider { public final boolean isInEntityTickingChunk(Entity entity) { return this.a(entity); } // Paper - OBFHELPER @Override public boolean a(Entity entity) { diff --git a/Spigot-Server-Patches/0481-Fix-Chunk-Post-Processing-deadlock-risk.patch b/Spigot-Server-Patches/0481-Fix-Chunk-Post-Processing-deadlock-risk.patch index bb58a29a9c..02000ab735 100644 --- a/Spigot-Server-Patches/0481-Fix-Chunk-Post-Processing-deadlock-risk.patch +++ b/Spigot-Server-Patches/0481-Fix-Chunk-Post-Processing-deadlock-risk.patch @@ -25,10 +25,10 @@ This successfully fixed a reoccurring and highly reproduceable crash for heightmaps. diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index c2e4e4f6f1895850bcac3552eee5bfedfb98c933..78a8a3cc68f21ec3ade95c13ccacafd3a3a6c4e6 100644 +index edf0cf3680e5d2212f35c9d4464b22e0f3e87342..c3528212ae1bfbdbe6910bcd775990b9b638afaf 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -1048,6 +1048,7 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -1051,6 +1051,7 @@ public class ChunkProviderServer extends IChunkProvider { return super.executeNext() || execChunkTask; // Paper } } finally { diff --git a/Spigot-Server-Patches/0503-Optimize-isOutsideRange-to-use-distance-maps.patch b/Spigot-Server-Patches/0503-Optimize-isOutsideRange-to-use-distance-maps.patch index 0a9f297904..614314c4ce 100644 --- a/Spigot-Server-Patches/0503-Optimize-isOutsideRange-to-use-distance-maps.patch +++ b/Spigot-Server-Patches/0503-Optimize-isOutsideRange-to-use-distance-maps.patch @@ -77,10 +77,10 @@ index 279c7a85fb5b4bff91fba1c9797c902bd68d8539..7cd4e2912351eae35b46dba1c8a471af public String c() { diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 78a8a3cc68f21ec3ade95c13ccacafd3a3a6c4e6..e2abda1bc37b9be0bae2506f0f26360693f3d30a 100644 +index c3528212ae1bfbdbe6910bcd775990b9b638afaf..89017e002f4381ffdae1678349d674474088fb4f 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -733,6 +733,36 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -736,6 +736,36 @@ public class ChunkProviderServer extends IChunkProvider { boolean flag1 = this.world.getGameRules().getBoolean(GameRules.DO_MOB_SPAWNING) && !world.getPlayers().isEmpty(); // CraftBukkit if (!flag) { @@ -117,7 +117,7 @@ index 78a8a3cc68f21ec3ade95c13ccacafd3a3a6c4e6..e2abda1bc37b9be0bae2506f0f263606 this.world.getMethodProfiler().enter("pollingChunks"); int k = this.world.getGameRules().getInt(GameRules.RANDOM_TICK_SPEED); BlockPosition blockposition = this.world.getSpawn(); -@@ -767,15 +797,7 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -770,15 +800,7 @@ public class ChunkProviderServer extends IChunkProvider { this.world.timings.countNaturalMobs.stopTiming(); // Paper - timings this.world.getMethodProfiler().exit(); @@ -134,7 +134,7 @@ index 78a8a3cc68f21ec3ade95c13ccacafd3a3a6c4e6..e2abda1bc37b9be0bae2506f0f263606 final int[] chunksTicked = {0}; this.playerChunkMap.forEachVisibleChunk((playerchunk) -> { // Paper - safe iterator incase chunk loads, also no wrapping Optional optional = ((Either) playerchunk.b().getNow(PlayerChunk.UNLOADED_CHUNK)).left(); -@@ -789,10 +811,10 @@ public class ChunkProviderServer extends IChunkProvider { +@@ -792,10 +814,10 @@ public class ChunkProviderServer extends IChunkProvider { this.world.getMethodProfiler().exit(); ChunkCoordIntPair chunkcoordintpair = playerchunk.i(); @@ -161,7 +161,7 @@ index 48bbaec4b64bede8d280bd866436f5528578013e..6e8179b4651fca214b8957992ec6c943 super((World) worldserver, gameprofile); playerinteractmanager.player = this; diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 4b341c81fc97076a69aede729008c54674fa7adf..bae9371a1e220f4fc78a3905cad24a2e7f88771c 100644 +index a6e7bcf79df9f4ad5b0a779f3eecf85f121bedc8..0f303be3c3257548d1888ddbb575ba69ba12d0b8 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -148,6 +148,18 @@ public class PlayerChunk { diff --git a/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch b/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch index 2e3098d94c..56209181a2 100644 --- a/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch +++ b/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch @@ -114,7 +114,7 @@ index 6e8179b4651fca214b8957992ec6c9438c0da799..e32c458dfe5f22572a365cbcdf45140b super((World) worldserver, gameprofile); playerinteractmanager.player = this; diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index bae9371a1e220f4fc78a3905cad24a2e7f88771c..9d71c4c455d68bcc82dc56b0706c7305e4897e46 100644 +index 0f303be3c3257548d1888ddbb575ba69ba12d0b8..7b2a3287ce8d296d29cbef45322a469921cf9944 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -160,6 +160,18 @@ public class PlayerChunk { @@ -600,10 +600,10 @@ index 899c535c4056cd2375ab8f834f03267d405f4bda..0e6368d0fb3beccb492ae3867fb4e228 if (!this.isClientSide && (i & 1) != 0) { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index ac257d50dea8f42a515f19bbae12ab5680e26bb4..4aafad0762df44990ed9d610a59f3862bd3fe3b0 100644 +index 64c643aa15d6ea68f9dad3104cc41e412255cee3..874240d9dddc3150d65d56d95c459b59f07b8815 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -2483,10 +2483,39 @@ public class CraftWorld implements World { +@@ -2488,10 +2488,39 @@ public class CraftWorld implements World { // Spigot start @Override public int getViewDistance() { diff --git a/Spigot-Server-Patches/0507-Fix-Light-Command.patch b/Spigot-Server-Patches/0507-Fix-Light-Command.patch index 9e1cce1248..afa04c6a8f 100644 --- a/Spigot-Server-Patches/0507-Fix-Light-Command.patch +++ b/Spigot-Server-Patches/0507-Fix-Light-Command.patch @@ -7,7 +7,7 @@ This lets you run /paper fixlight (max 5) to automatically fix all light data in the chunks. diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java -index ddb60e9a48e5e7225ad575240b94fda24b6b78ca..bd9c56dd277c58b5f2c423f6e3e65a5086099619 100644 +index ddb60e9a48e5e7225ad575240b94fda24b6b78ca..beb50f78e8b2f79d45e2e7fdc932e947138be7aa 100644 --- a/src/main/java/com/destroystokyo/paper/PaperCommand.java +++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java @@ -19,6 +19,7 @@ import org.bukkit.command.Command; @@ -45,7 +45,7 @@ index ddb60e9a48e5e7225ad575240b94fda24b6b78ca..bd9c56dd277c58b5f2c423f6e3e65a50 case "ver": case "version": Command ver = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version"); -@@ -156,6 +160,65 @@ public class PaperCommand extends Command { +@@ -156,6 +160,75 @@ public class PaperCommand extends Command { return true; } @@ -81,7 +81,17 @@ index ddb60e9a48e5e7225ad575240b94fda24b6b78ca..bd9c56dd277c58b5f2c423f6e3e65a50 + 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,14 +115,14 @@ index ddb60e9a48e5e7225ad575240b94fda24b6b78ca..bd9c56dd277c58b5f2c423f6e3e65a50 + updateLight(sender, world, lightengine, queue); + } + lightengine.a(world.paperConfig.lightQueueSize); -+ }); ++ }, MinecraftServer.getServer()); + } + private void doSyncLoadInfo(CommandSender sender, String[] args) { if (!SyncLoadFinder.ENABLED) { sender.sendMessage(ChatColor.RED + "This command requires the server startup flag '-Dpaper.debug-sync-loads=true' to be set."); diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 9d71c4c455d68bcc82dc56b0706c7305e4897e46..709e35cd4864eb666b68c1b6666a9d3a5a5a3c29 100644 +index 7b2a3287ce8d296d29cbef45322a469921cf9944..d0085b7459293e3e3460cfda34c67bda6e7bc324 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -437,6 +437,7 @@ public class PlayerChunk {