mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-01 17:01:56 +01:00
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
This commit is contained in:
parent
04becc8a1c
commit
56d8694bd9
6 changed files with 238 additions and 77 deletions
|
@ -366,7 +366,116 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
+ * @return Future that will resolve when the chunk is loaded
|
+ * @return Future that will resolve when the chunk is loaded
|
||||||
+ */
|
+ */
|
||||||
+ @NotNull
|
+ @NotNull
|
||||||
+ public java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen);
|
+ public default java.util.concurrent.CompletableFuture<Chunk> 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<Chunk> 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<Chunk> 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<Chunk> 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<Chunk> 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<Chunk> getChunkAtAsyncUrgently(int x, int z) {
|
||||||
|
+ return getChunkAtAsync(x, z, true, true);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @NotNull
|
||||||
|
+ java.util.concurrent.CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent);
|
||||||
// Paper end
|
// Paper end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -397,7 +506,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
+ @NotNull
|
+ @NotNull
|
||||||
+ public default java.util.concurrent.CompletableFuture<Boolean> teleportAsync(@NotNull Location loc, @NotNull TeleportCause cause) {
|
+ public default java.util.concurrent.CompletableFuture<Boolean> teleportAsync(@NotNull Location loc, @NotNull TeleportCause cause) {
|
||||||
+ java.util.concurrent.CompletableFuture<Boolean> future = new java.util.concurrent.CompletableFuture<>();
|
+ java.util.concurrent.CompletableFuture<Boolean> 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;
|
+ return future;
|
||||||
+ }
|
+ }
|
||||||
+ // Paper end
|
+ // Paper end
|
||||||
|
|
|
@ -2315,12 +2315,23 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
+
|
+
|
||||||
+ private long asyncLoadSeqCounter;
|
+ private long asyncLoadSeqCounter;
|
||||||
+
|
+
|
||||||
+ public void getChunkAtAsynchronously(int x, int z, boolean gen, java.util.function.Consumer<Chunk> onComplete) {
|
+ public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getChunkAtAsynchronously(int x, int z, boolean gen, boolean isUrgent) {
|
||||||
+ if (Thread.currentThread() != this.serverThread) {
|
+ if (Thread.currentThread() != this.serverThread) {
|
||||||
|
+ CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> future = new CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>>();
|
||||||
+ this.serverThreadQueue.execute(() -> {
|
+ 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);
|
+ long k = ChunkCoordIntPair.pair(x, z);
|
||||||
|
@ -2346,75 +2357,64 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
+ this.cacheStatus[0] = ChunkStatus.FULL;
|
+ this.cacheStatus[0] = ChunkStatus.FULL;
|
||||||
+ this.cacheChunk[0] = ichunkaccess;
|
+ this.cacheChunk[0] = ichunkaccess;
|
||||||
+
|
+
|
||||||
+ onComplete.accept((Chunk)ichunkaccess);
|
+ return CompletableFuture.completedFuture(Either.left(ichunkaccess));
|
||||||
+
|
|
||||||
+ return;
|
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ if (gen) {
|
+ if (gen) {
|
||||||
+ this.bringToFullStatusAsync(x, z, chunkPos, onComplete);
|
+ return this.bringToFullStatusAsync(x, z, chunkPos, isUrgent);
|
||||||
+ return;
|
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ IChunkAccess current = this.getChunkAtImmediately(x, z); // we want to bypass ticket restrictions
|
+ IChunkAccess current = this.getChunkAtImmediately(x, z); // we want to bypass ticket restrictions
|
||||||
+ if (current != null) {
|
+ if (current != null) {
|
||||||
+ if (!(current instanceof ProtoChunkExtension) && !(current instanceof net.minecraft.server.Chunk)) {
|
+ if (!(current instanceof ProtoChunkExtension) && !(current instanceof net.minecraft.server.Chunk)) {
|
||||||
+ onComplete.accept(null); // the chunk is not gen'd
|
+ return CompletableFuture.completedFuture(Either.left(null));
|
||||||
+ return;
|
|
||||||
+ }
|
+ }
|
||||||
+ // we know the chunk is at full status here (either in read-only mode or the real thing)
|
+ // 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 this.bringToFullStatusAsync(x, z, chunkPos, isUrgent);
|
||||||
+ return;
|
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ ChunkStatus status = world.getChunkProvider().playerChunkMap.getStatusOnDiskNoLoad(x, z);
|
+ ChunkStatus status = world.getChunkProvider().playerChunkMap.getStatusOnDiskNoLoad(x, z);
|
||||||
+
|
+
|
||||||
+ if (status != null && status != ChunkStatus.FULL) {
|
+ if (status != null && status != ChunkStatus.FULL) {
|
||||||
+ // does not exist on disk
|
+ // does not exist on disk
|
||||||
+ onComplete.accept(null);
|
+ return CompletableFuture.completedFuture(Either.left(null));
|
||||||
+ return;
|
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ if (status == ChunkStatus.FULL) {
|
+ if (status == ChunkStatus.FULL) {
|
||||||
+ this.bringToFullStatusAsync(x, z, chunkPos, onComplete);
|
+ return this.bringToFullStatusAsync(x, z, chunkPos, isUrgent);
|
||||||
+ return;
|
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ // status is null here
|
+ // status is null here
|
||||||
+
|
+
|
||||||
+ // here we don't know what status it is and we're not supposed to generate
|
+ // here we don't know what status it is and we're not supposed to generate
|
||||||
+ // so we asynchronously load empty status
|
+ // so we asynchronously load empty status
|
||||||
+
|
+ return this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.EMPTY, isUrgent).thenCompose((either) -> {
|
||||||
+ this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.EMPTY, (IChunkAccess chunk) -> {
|
+ IChunkAccess chunk = either.left().orElse(null);
|
||||||
+ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) {
|
+ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof Chunk)) {
|
||||||
+ // the chunk on disk was not a full status chunk
|
+ // the chunk on disk was not a full status chunk
|
||||||
+ onComplete.accept(null);
|
+ return CompletableFuture.completedFuture(Either.left(null));
|
||||||
+ return;
|
|
||||||
+ }
|
+ }
|
||||||
+ 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<Chunk> onComplete) {
|
+ private CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> bringToFullStatusAsync(int x, int z, ChunkCoordIntPair chunkPos, boolean isUrgent) {
|
||||||
+ this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.FULL, (java.util.function.Consumer)onComplete);
|
+ return this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.FULL, isUrgent);
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ private void bringToStatusAsync(int x, int z, ChunkCoordIntPair chunkPos, ChunkStatus status, java.util.function.Consumer<IChunkAccess> onComplete) {
|
+ private CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> bringToStatusAsync(int x, int z, ChunkCoordIntPair chunkPos, ChunkStatus status, boolean isUrgent) {
|
||||||
+ CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> future = this.getChunkFutureMainThread(x, z, status, true);
|
+ CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> future = this.getChunkFutureMainThread(x, z, status, true);
|
||||||
+ Long identifier = Long.valueOf(this.asyncLoadSeqCounter++);
|
+ Long identifier = Long.valueOf(this.asyncLoadSeqCounter++);
|
||||||
+ int ticketLevel = MCUtil.getTicketLevelFor(status);
|
+ int ticketLevel = MCUtil.getTicketLevelFor(status);
|
||||||
+ this.addTicketAtLevel(TicketType.ASYNC_LOAD, chunkPos, ticketLevel, identifier);
|
+ this.addTicketAtLevel(TicketType.ASYNC_LOAD, chunkPos, ticketLevel, identifier);
|
||||||
+
|
+
|
||||||
+ future.whenCompleteAsync((Either<IChunkAccess, PlayerChunk.Failure> either, Throwable throwable) -> {
|
+ return future.thenComposeAsync((Either<IChunkAccess, PlayerChunk.Failure> either) -> {
|
||||||
+ // either left -> success
|
+ // either left -> success
|
||||||
+ // either right -> failure
|
+ // either right -> failure
|
||||||
+
|
+
|
||||||
+ if (throwable != null) {
|
|
||||||
+ throw new RuntimeException(throwable);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ this.removeTicketAtLevel(TicketType.ASYNC_LOAD, chunkPos, ticketLevel, identifier);
|
+ this.removeTicketAtLevel(TicketType.ASYNC_LOAD, chunkPos, ticketLevel, identifier);
|
||||||
+ this.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, ticketLevel, chunkPos); // allow unloading
|
+ 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());
|
+ throw new IllegalStateException("Chunk failed to load: " + failure.get().toString());
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ onComplete.accept(either.left().get());
|
+ return CompletableFuture.completedFuture(either);
|
||||||
+
|
|
||||||
+ }, this.serverThreadQueue);
|
+ }, this.serverThreadQueue);
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
|
@ -4020,11 +4019,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
|
|
||||||
MCUtil.getSpiralOutChunks(spawn, radiusInBlocks >> 4).forEach(pair -> {
|
MCUtil.getSpiralOutChunks(spawn, radiusInBlocks >> 4).forEach(pair -> {
|
||||||
- getChunkProvider().getChunkAtMainThread(pair.x, pair.z);
|
- getChunkProvider().getChunkAtMainThread(pair.x, pair.z);
|
||||||
+ if (com.destroystokyo.paper.PaperConfig.asyncChunks) {
|
+ getChunkProvider().getChunkAtAsynchronously(pair.x, pair.z, true, false).exceptionally((ex) -> {
|
||||||
+ getChunkProvider().getChunkAtAsynchronously(pair.x, pair.z, true, (c) -> {});
|
+ ex.printStackTrace();
|
||||||
+ } else {
|
+ return null;
|
||||||
+ getChunkProvider().getChunkAtMainThread(pair.x, pair.z);
|
+ });
|
||||||
+ }
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public void removeTicketsForSpawn(int radiusInBlocks, BlockPosition spawn) {
|
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
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||||
+++ b/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 {
|
@@ -0,0 +0,0 @@ public class CraftWorld implements World {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -4070,20 +4076,18 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
}
|
}
|
||||||
+ // Paper start
|
+ // Paper start
|
||||||
+ @Override
|
+ @Override
|
||||||
+ public CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen) {
|
+ public CompletableFuture<Chunk> getChunkAtAsync(int x, int z, boolean gen, boolean urgent) {
|
||||||
+ if (Bukkit.isPrimaryThread()) {
|
+ if (Bukkit.isPrimaryThread()) {
|
||||||
+ net.minecraft.server.Chunk immediate = this.world.getChunkProvider().getChunkAtIfLoadedImmediately(x, z);
|
+ net.minecraft.server.Chunk immediate = this.world.getChunkProvider().getChunkAtIfLoadedImmediately(x, z);
|
||||||
+ if (immediate != null) {
|
+ if (immediate != null) {
|
||||||
+ return CompletableFuture.completedFuture(immediate.bukkitChunk);
|
+ return CompletableFuture.completedFuture(immediate.getBukkitChunk());
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ CompletableFuture<Chunk> ret = new CompletableFuture<>();
|
+ return this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, urgent).thenComposeAsync((either) -> {
|
||||||
+ this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, (net.minecraft.server.Chunk chunk) -> {
|
+ net.minecraft.server.Chunk chunk = (net.minecraft.server.Chunk) either.left().orElse(null);
|
||||||
+ ret.complete(chunk == null ? null : chunk.bukkitChunk);
|
+ return CompletableFuture.completedFuture(chunk == null ? null : chunk.getBukkitChunk());
|
||||||
+ });
|
+ }, MinecraftServer.getServer());
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+ }
|
+ }
|
||||||
+ // Paper end
|
+ // Paper end
|
||||||
|
|
||||||
|
|
|
@ -43,16 +43,3 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
this.getMinecraftServer().midTickLoadChunks(); // Paper
|
this.getMinecraftServer().midTickLoadChunks(); // Paper
|
||||||
|
|
||||||
try (co.aikar.timings.Timing ignored = this.timings.newEntities.startTiming()) { // Paper - timings
|
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<Chunk> 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;
|
|
||||||
|
|
|
@ -81,7 +81,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
+ sender.sendMessage("All Chunks Light updated");
|
+ sender.sendMessage("All Chunks Light updated");
|
||||||
+ return;
|
+ 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
|
+ lightengine.a(world.paperConfig.lightQueueSize + 16 * 256); // ensure full chunk can fit into queue
|
||||||
+ sender.sendMessage("Updating Light " + coord);
|
+ sender.sendMessage("Updating Light " + coord);
|
||||||
+ for (int y = 0; y < world.getHeight(); y++) {
|
+ for (int y = 0; y < world.getHeight(); y++) {
|
||||||
|
@ -105,7 +115,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
+ updateLight(sender, world, lightengine, queue);
|
+ updateLight(sender, world, lightengine, queue);
|
||||||
+ }
|
+ }
|
||||||
+ lightengine.a(world.paperConfig.lightQueueSize);
|
+ lightengine.a(world.paperConfig.lightQueueSize);
|
||||||
+ });
|
+ }, MinecraftServer.getServer());
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
private void doSyncLoadInfo(CommandSender sender, String[] args) {
|
private void doSyncLoadInfo(CommandSender sender, String[] args) {
|
||||||
|
|
|
@ -19,23 +19,12 @@ diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/m
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||||
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
|
||||||
+++ b/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<Chunk> onComplete) {
|
|
||||||
if (Thread.currentThread() != this.serverThread) {
|
|
||||||
@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider {
|
@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
gameprofilerfiller.c("getChunkCacheMiss");
|
gameprofilerfiller.c("getChunkCacheMiss");
|
||||||
+ // Paper start - Chunk Load/Gen Priority
|
- CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = this.getChunkFutureMainThread(i, j, chunkstatus, flag);
|
||||||
+ boolean prevBlocking = IS_CHUNK_LOAD_BLOCKING_MAIN;
|
+ CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = this.getChunkFutureMainThread(i, j, chunkstatus, flag, true); // Paper
|
||||||
+ IS_CHUNK_LOAD_BLOCKING_MAIN = true;
|
|
||||||
+ // Paper end
|
|
||||||
CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = this.getChunkFutureMainThread(i, j, chunkstatus, flag);
|
|
||||||
|
|
||||||
if (!completablefuture.isDone()) { // Paper
|
if (!completablefuture.isDone()) { // Paper
|
||||||
// Paper start - async chunk io/loading
|
// Paper start - async chunk io/loading
|
||||||
|
@ -54,10 +43,33 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
+ if (playerChunk != null) {
|
+ if (playerChunk != null) {
|
||||||
+ playerChunk.clearChunkUrgent();
|
+ playerChunk.clearChunkUrgent();
|
||||||
+ }
|
+ }
|
||||||
+ IS_CHUNK_LOAD_BLOCKING_MAIN = prevBlocking;// Paper
|
|
||||||
ichunkaccess = (IChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
|
ichunkaccess = (IChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> {
|
||||||
return ichunkaccess1;
|
return ichunkaccess1;
|
||||||
}, (playerchunk_failure) -> {
|
}, (playerchunk_failure) -> {
|
||||||
|
@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider {
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getChunkFutureMainThread(int i, int j, ChunkStatus chunkstatus, boolean flag) {
|
||||||
|
+ // Paper start
|
||||||
|
+ return getChunkFutureMainThread(i, j, chunkstatus, flag, false);
|
||||||
|
+ }
|
||||||
|
+ private CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> 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
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||||
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
|
||||||
|
@ -196,6 +208,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
}
|
}
|
||||||
|
|
||||||
private void d(int i) {
|
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
|
diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
||||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||||
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
|
||||||
|
@ -279,3 +299,31 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
CompletableFuture<Either<Chunk, PlayerChunk.Failure>> completablefuture1 = completablefuture.thenApplyAsync((either) -> {
|
CompletableFuture<Either<Chunk, PlayerChunk.Failure>> completablefuture1 = completablefuture.thenApplyAsync((either) -> {
|
||||||
return either.flatMap((list) -> {
|
return either.flatMap((list) -> {
|
||||||
Chunk chunk = (Chunk) list.get(list.size() / 2);
|
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<Chunk> 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
|
||||||
|
|
||||||
|
|
|
@ -222,8 +222,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
||||||
import net.minecraft.server.IChunkAccess;
|
import net.minecraft.server.IChunkAccess;
|
||||||
+import net.minecraft.server.MCUtil;
|
+import net.minecraft.server.MCUtil;
|
||||||
import net.minecraft.server.MinecraftKey;
|
import net.minecraft.server.MinecraftKey;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.MovingObjectPosition;
|
import net.minecraft.server.MovingObjectPosition;
|
||||||
import net.minecraft.server.PacketPlayOutCustomSoundEffect;
|
|
||||||
@@ -0,0 +0,0 @@ public class CraftWorld implements World {
|
@@ -0,0 +0,0 @@ public class CraftWorld implements World {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue