From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 29 Feb 2016 17:43:33 -0600 Subject: [PATCH] Async Chunks API 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 94c91385810642dcd120e534b5a20e21fbcd963a..727aa59b9080d53b9e1f6c619d73afcd077b59da 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java @@ -950,6 +950,472 @@ public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient } // Paper end - additional getNearbyEntities API + // Paper start - async chunks API + /** + * This is the Legacy API before Java 8 was supported. Java 8 Consumer is provided, + * as well as future support + * + * Used by {@link World#getChunkAtAsync(Location,ChunkLoadCallback)} methods + * to request a {@link Chunk} to be loaded, with this callback receiving + * the chunk when it is finished. + * + * This callback will be executed on synchronously on the main thread. + * + * Timing and order this callback is fired is intentionally not defined and + * and subject to change. + * + * @deprecated Use either the Future or the Consumer based methods + */ + @Deprecated(since = "1.13.1") + public static interface ChunkLoadCallback extends java.util.function.Consumer { + public void onLoad(@NotNull Chunk chunk); + + // backwards compat to old api + @Override + default void accept(@NotNull Chunk chunk) { + onLoad(chunk); + } + } + + /** + * 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 {@link ChunkLoadCallback} will always be executed synchronously + * on the main Server Thread. + * + * @deprecated Use either the Future or the Consumer based methods + * @param x Chunk X-coordinate of the chunk - floor(world coordinate / 16) + * @param z Chunk Z-coordinate of the chunk - floor(world coordinate / 16) + * @param cb Callback to receive the chunk when it is loaded. + * will be executed synchronously + */ + @Deprecated(since = "1.13.1") + public default void getChunkAtAsync(int x, int z, @NotNull ChunkLoadCallback cb) { + getChunkAtAsync(x, z, true).thenAccept(cb::onLoad).exceptionally((ex) -> { + Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex); + return null; + }); + } + + /** + * Requests a {@link Chunk} to be loaded at the given {@link Location} + * + * 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 {@link ChunkLoadCallback} will always be executed synchronously + * on the main Server Thread. + * + * @deprecated Use either the Future or the Consumer based methods + * @param loc Location of the chunk + * @param cb Callback to receive the chunk when it is loaded. + * will be executed synchronously + */ + @Deprecated(since = "1.13.1") + public default void getChunkAtAsync(@NotNull Location loc, @NotNull ChunkLoadCallback cb) { + getChunkAtAsync(loc, true).thenAccept(cb::onLoad).exceptionally((ex) -> { + Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex); + return null; + }); + } + + /** + * Requests {@link Chunk} to be loaded that contains the given {@link Block} + * + * 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 {@link ChunkLoadCallback} will always be executed synchronously + * on the main Server Thread. + * + * @deprecated Use either the Future or the Consumer based methods + * @param block Block to get the containing chunk from + * @param cb Callback to receive the chunk when it is loaded. + * will be executed synchronously + */ + @Deprecated(since = "1.13.1") + public default void getChunkAtAsync(@NotNull Block block, @NotNull ChunkLoadCallback cb) { + getChunkAtAsync(block, true).thenAccept(cb::onLoad).exceptionally((ex) -> { + Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex); + return null; + }); + } + + /** + * 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 {@link java.util.function.Consumer} will always be executed synchronously + * on the main Server Thread. + * + * @param x Chunk X-coordinate of the chunk - floor(world coordinate / 16) + * @param z Chunk Z-coordinate of the chunk - floor(world coordinate / 16) + * @param cb Callback to receive the chunk when it is loaded. + * will be executed synchronously + */ + default void getChunkAtAsync(final int x, final int z, final @NotNull Consumer cb) { + this.getChunkAtAsync(x, z, true).thenAccept(cb).exceptionally((ex) -> { + Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex); + return null; + }); + } + + /** + * 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 {@link java.util.function.Consumer} will always be executed synchronously + * on the main Server Thread. + * + * @param x Chunk X-coordinate of the chunk - floor(world coordinate / 16) + * @param z Chunk Z-coordinate of the chunk - floor(world coordinate / 16) + * @param gen Should we generate a chunk if it doesn't exist or not + * @param cb Callback to receive the chunk when it is loaded. + * will be executed synchronously + */ + default void getChunkAtAsync(final int x, final int z, final boolean gen, final @NotNull Consumer cb) { + this.getChunkAtAsync(x, z, gen).thenAccept(cb).exceptionally((ex) -> { + Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Exception in chunk load callback", ex); + return null; + }); + } + + /** + * Requests a {@link Chunk} to be loaded at the given {@link Location} + * + * 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 {@link java.util.function.Consumer} will always be executed synchronously + * on the main Server Thread. + * + * @param loc Location of the chunk + * @param cb Callback to receive the chunk when it is loaded. + * will be executed synchronously + */ + default void getChunkAtAsync(final @NotNull Location loc, final @NotNull Consumer cb) { + this.getChunkAtAsync((int) Math.floor(loc.getX()) >> 4, (int) Math.floor(loc.getZ()) >> 4, true, cb); + } + + /** + * Requests a {@link Chunk} to be loaded at the given {@link Location} + * + * 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 {@link java.util.function.Consumer} will always be executed synchronously + * on the main Server Thread. + * + * @param loc Location of the chunk + * @param gen Should the chunk generate if it doesn't exist + * @param cb Callback to receive the chunk when it is loaded. + * will be executed synchronously + */ + default void getChunkAtAsync(final @NotNull Location loc, final boolean gen, final @NotNull Consumer cb) { + this.getChunkAtAsync((int) Math.floor(loc.getX()) >> 4, (int) Math.floor(loc.getZ()) >> 4, gen, cb); + } + + /** + * Requests {@link Chunk} to be loaded that contains the given {@link Block} + * + * 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 {@link java.util.function.Consumer} will always be executed synchronously + * on the main Server Thread. + * + * @param block Block to get the containing chunk from + * @param cb Callback to receive the chunk when it is loaded. + * will be executed synchronously + */ + default void getChunkAtAsync(final @NotNull Block block, final @NotNull Consumer cb) { + this.getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, true, cb); + } + + /** + * Requests {@link Chunk} to be loaded that contains the given {@link Block} + * + * 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 {@link java.util.function.Consumer} will always be executed synchronously + * on the main Server Thread. + * + * @param block Block to get the containing chunk from + * @param gen Should the chunk generate if it doesn't exist + * @param cb Callback to receive the chunk when it is loaded. + * will be executed synchronously + */ + default void getChunkAtAsync(final @NotNull Block block, final boolean gen, final @NotNull Consumer cb) { + this.getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, gen, cb); + } + + /** + * 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 + */ + default @NotNull java.util.concurrent.CompletableFuture getChunkAtAsync(final @NotNull Location loc) { + return this.getChunkAtAsync((int) Math.floor(loc.getX()) >> 4, (int) Math.floor(loc.getZ()) >> 4, 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 if it doesn't exist + * @return Future that will resolve when the chunk is loaded + */ + default @NotNull java.util.concurrent.CompletableFuture getChunkAtAsync(final @NotNull Location loc, final boolean gen) { + return this.getChunkAtAsync((int) Math.floor(loc.getX()) >> 4, (int) Math.floor(loc.getZ()) >> 4, gen); + } + + /** + * 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 + */ + default @NotNull java.util.concurrent.CompletableFuture getChunkAtAsync(final @NotNull Block block) { + return this.getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, 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 if it doesn't exist + * @return Future that will resolve when the chunk is loaded + */ + default @NotNull java.util.concurrent.CompletableFuture getChunkAtAsync(final @NotNull Block block, final boolean gen) { + return this.getChunkAtAsync(block.getX() >> 4, block.getZ() >> 4, gen); + } + + /** + * 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 Chunk X-coordinate of the chunk - floor(world coordinate / 16) + * @param z Chunk Z-coordinate of the chunk - floor(world coordinate / 16) + * @return Future that will resolve when the chunk is loaded + */ + default @NotNull java.util.concurrent.CompletableFuture getChunkAtAsync(final int x, final int z) { + return this.getChunkAtAsync(x, z, 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 Chunk X-coordinate of the chunk - floor(world coordinate / 16) + * @param z Chunk Z-coordinate of the chunk - floor(world coordinate / 16) + * @param gen Should we generate a chunk if it doesn't exist or not + * @return Future that will resolve when the chunk is loaded + */ + default @NotNull java.util.concurrent.CompletableFuture getChunkAtAsync(final int x, final int z, final boolean gen) { + return this.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 + */ + default @NotNull java.util.concurrent.CompletableFuture getChunkAtAsyncUrgently(final @NotNull Location loc) { + return this.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 if it doesn't exist + * @return Future that will resolve when the chunk is loaded + */ + default @NotNull java.util.concurrent.CompletableFuture getChunkAtAsyncUrgently(final @NotNull Location loc, final boolean gen) { + return this.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 + */ + default @NotNull java.util.concurrent.CompletableFuture getChunkAtAsyncUrgently(final @NotNull Block block) { + return this.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 if it doesn't exist + * @return Future that will resolve when the chunk is loaded + */ + default @NotNull java.util.concurrent.CompletableFuture getChunkAtAsyncUrgently(final @NotNull Block block, final boolean gen) { + return this.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 + */ + default @NotNull java.util.concurrent.CompletableFuture getChunkAtAsyncUrgently(final int x, final int z) { + return this.getChunkAtAsync(x, z, true, true); + } + + java.util.concurrent.@NotNull CompletableFuture getChunkAtAsync(int x, int z, boolean gen, boolean urgent); + // Paper end - async chunks API + /** * Get a list of all players in this World * diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java index 1e43deadce5a1a0e97521b1f69fee3106f5a0b9e..f1fc42ad24648ee481b9a5d4c4cc58ae8c0a93c1 100644 --- a/src/main/java/org/bukkit/entity/Entity.java +++ b/src/main/java/org/bukkit/entity/Entity.java @@ -168,6 +168,39 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent */ public boolean teleport(@NotNull Entity destination, @NotNull TeleportCause cause); + // Paper start + /** + * Loads/Generates(in 1.13+) the Chunk asynchronously, and then teleports the entity when the chunk is ready. + * @param loc Location to teleport to + * @return A future that will be completed with the result of the teleport + */ + default java.util.concurrent.@NotNull CompletableFuture teleportAsync(final @NotNull Location loc) { + return this.teleportAsync(loc, TeleportCause.PLUGIN); + } + + /** + * Loads/Generates(in 1.13+) the Chunk asynchronously, and then teleports the entity when the chunk is ready. + * @param loc Location to teleport to + * @param cause Reason for teleport + * @return A future that will be completed with the result of the teleport + */ + default java.util.concurrent.@NotNull CompletableFuture teleportAsync(final @NotNull Location loc, final @NotNull TeleportCause cause) { + final class Holder { + static final io.papermc.paper.entity.TeleportFlag[] EMPTY_FLAGS = new io.papermc.paper.entity.TeleportFlag[0]; + } + return this.teleportAsync(loc, cause, Holder.EMPTY_FLAGS); + } + + /** + * Loads/Generates(in 1.13+) the Chunk asynchronously, and then teleports the entity when the chunk is ready. + * @param loc Location to teleport to + * @param cause Reason for teleport + * @param teleportFlags Flags to be used in this teleportation + * @return A future that will be completed with the result of the teleport + */ + java.util.concurrent.@NotNull CompletableFuture teleportAsync(@NotNull Location loc, @NotNull TeleportCause cause, @NotNull io.papermc.paper.entity.TeleportFlag @NotNull... teleportFlags); + // Paper end + /** * Returns a list of entities within a bounding box centered around this * entity