Add methods taking world, chunkX, chunkZ to RegionScheduler

This commit is contained in:
Nassim Jahnke 2023-03-25 18:40:20 +01:00
parent dfc157075a
commit 8d15f3e23d
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
3 changed files with 150 additions and 92 deletions

View File

@ -8,7 +8,7 @@ and a global region scheduler.
diff --git a/src/main/java/io/papermc/paper/threadedregions/scheduler/AsyncScheduler.java b/src/main/java/io/papermc/paper/threadedregions/scheduler/AsyncScheduler.java
new file mode 100644
index 0000000000000000000000000000000000000000..64d1fe385d30f1f5ab82d35fe66e268da13346b1
index 0000000000000000000000000000000000000000..66232a9f5cea31dc8046817c3c2a91695930e53f
--- /dev/null
+++ b/src/main/java/io/papermc/paper/threadedregions/scheduler/AsyncScheduler.java
@@ -0,0 +1,50 @@
@ -30,7 +30,7 @@ index 0000000000000000000000000000000000000000..64d1fe385d30f1f5ab82d35fe66e268d
+ * @param task Specified task.
+ * @return The {@link ScheduledTask} that represents the scheduled task.
+ */
+ public @NotNull ScheduledTask runNow(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task);
+ @NotNull ScheduledTask runNow(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task);
+
+ /**
+ * Schedules the specified task to be executed asynchronously after the time delay has passed.
@ -40,7 +40,7 @@ index 0000000000000000000000000000000000000000..64d1fe385d30f1f5ab82d35fe66e268d
+ * @param unit The time unit for the time delay.
+ * @return The {@link ScheduledTask} that represents the scheduled task.
+ */
+ public @NotNull ScheduledTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task, long delay,
+ @NotNull ScheduledTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task, long delay,
+ @NotNull TimeUnit unit);
+
+ /**
@ -53,18 +53,18 @@ index 0000000000000000000000000000000000000000..64d1fe385d30f1f5ab82d35fe66e268d
+ * @param unit The time unit for the initial delay and period.
+ * @return The {@link ScheduledTask} that represents the scheduled task.
+ */
+ public @NotNull ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task,
+ @NotNull ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task,
+ long initialDelay, long period, @NotNull TimeUnit unit);
+
+ /**
+ * Attempts to cancel all tasks scheduled by the specified plugin.
+ * @param plugin Specified plugin.
+ */
+ public void cancelTasks(@NotNull Plugin plugin);
+ void cancelTasks(@NotNull Plugin plugin);
+}
diff --git a/src/main/java/io/papermc/paper/threadedregions/scheduler/EntityScheduler.java b/src/main/java/io/papermc/paper/threadedregions/scheduler/EntityScheduler.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c4ee07a86104f3601ba6d8a911197dbe1a17102
index 0000000000000000000000000000000000000000..017dbb27f66cbf744a67189e91c476f8d971d1e7
--- /dev/null
+++ b/src/main/java/io/papermc/paper/threadedregions/scheduler/EntityScheduler.java
@@ -0,0 +1,103 @@
@ -112,7 +112,7 @@ index 0000000000000000000000000000000000000000..9c4ee07a86104f3601ba6d8a911197db
+ * will be invoked (but never both), or {@code false} indicating neither the run nor retired function will be invoked
+ * since the scheduler has been retired.
+ */
+ public boolean execute(@NotNull Plugin plugin, @NotNull Runnable run, @Nullable Runnable retired, long delay);
+ boolean execute(@NotNull Plugin plugin, @NotNull Runnable run, @Nullable Runnable retired, long delay);
+
+ /**
+ * Schedules a task to execute on the next tick. If the task failed to schedule because the scheduler is retired (entity
@ -129,7 +129,7 @@ index 0000000000000000000000000000000000000000..9c4ee07a86104f3601ba6d8a911197db
+ * @param retired Retire callback to run if the entity is retired before the run callback can be invoked, may be null.
+ * @return The {@link ScheduledTask} that represents the scheduled task, or {@code null} if the entity has been removed.
+ */
+ public @Nullable ScheduledTask run(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task,
+ @Nullable ScheduledTask run(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task,
+ @Nullable Runnable retired);
+
+ /**
@ -148,7 +148,7 @@ index 0000000000000000000000000000000000000000..9c4ee07a86104f3601ba6d8a911197db
+ * @param delayTicks The delay, in ticks.
+ * @return The {@link ScheduledTask} that represents the scheduled task, or {@code null} if the entity has been removed.
+ */
+ public @Nullable ScheduledTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task,
+ @Nullable ScheduledTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task,
+ @Nullable Runnable retired, int delayTicks);
+
+ /**
@ -168,12 +168,12 @@ index 0000000000000000000000000000000000000000..9c4ee07a86104f3601ba6d8a911197db
+ * @param periodTicks The period, in ticks.
+ * @return The {@link ScheduledTask} that represents the scheduled task, or {@code null} if the entity has been removed.
+ */
+ public @Nullable ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task,
+ @Nullable ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task,
+ @Nullable Runnable retired, int initialDelayTicks, int periodTicks);
+}
diff --git a/src/main/java/io/papermc/paper/threadedregions/scheduler/GlobalRegionScheduler.java b/src/main/java/io/papermc/paper/threadedregions/scheduler/GlobalRegionScheduler.java
new file mode 100644
index 0000000000000000000000000000000000000000..f2d2565d903af90f6909319c811a49162f972e27
index 0000000000000000000000000000000000000000..45713139678cd2bcb9a370c947a124d8f5e2e36b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/threadedregions/scheduler/GlobalRegionScheduler.java
@@ -0,0 +1,57 @@
@ -197,7 +197,7 @@ index 0000000000000000000000000000000000000000..f2d2565d903af90f6909319c811a4916
+ * @param plugin The plugin that owns the task
+ * @param run The task to execute
+ */
+ public void execute(@NotNull Plugin plugin, @NotNull Runnable run);
+ void execute(@NotNull Plugin plugin, @NotNull Runnable run);
+
+ /**
+ * Schedules a task to be executed on the global region on the next tick.
@ -205,7 +205,7 @@ index 0000000000000000000000000000000000000000..f2d2565d903af90f6909319c811a4916
+ * @param task The task to execute
+ * @return The {@link ScheduledTask} that represents the scheduled task.
+ */
+ public @NotNull ScheduledTask run(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task);
+ @NotNull ScheduledTask run(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task);
+
+ /**
+ * Schedules a task to be executed on the global region after the specified delay in ticks.
@ -214,7 +214,7 @@ index 0000000000000000000000000000000000000000..f2d2565d903af90f6909319c811a4916
+ * @param delayTicks The delay, in ticks.
+ * @return The {@link ScheduledTask} that represents the scheduled task.
+ */
+ public @NotNull ScheduledTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task, int delayTicks);
+ @NotNull ScheduledTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task, int delayTicks);
+
+ /**
+ * Schedules a repeating task to be executed on the global region after the initial delay with the
@ -225,24 +225,25 @@ index 0000000000000000000000000000000000000000..f2d2565d903af90f6909319c811a4916
+ * @param periodTicks The period, in ticks.
+ * @return The {@link ScheduledTask} that represents the scheduled task.
+ */
+ public @NotNull ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task,
+ @NotNull ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer<ScheduledTask> task,
+ int initialDelayTicks, int periodTicks);
+
+ /**
+ * Attempts to cancel all tasks scheduled by the specified plugin.
+ * @param plugin Specified plugin.
+ */
+ public void cancelTasks(@NotNull Plugin plugin);
+ void cancelTasks(@NotNull Plugin plugin);
+}
diff --git a/src/main/java/io/papermc/paper/threadedregions/scheduler/RegionScheduler.java b/src/main/java/io/papermc/paper/threadedregions/scheduler/RegionScheduler.java
new file mode 100644
index 0000000000000000000000000000000000000000..80baf45735fbf52a69872712cf3ae6c71739db27
index 0000000000000000000000000000000000000000..87a267ca2cd7a751acdd1fbbfbf9c664640a9805
--- /dev/null
+++ b/src/main/java/io/papermc/paper/threadedregions/scheduler/RegionScheduler.java
@@ -0,0 +1,60 @@
@@ -0,0 +1,126 @@
+package io.papermc.paper.threadedregions.scheduler;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
@ -261,48 +262,113 @@ index 0000000000000000000000000000000000000000..80baf45735fbf52a69872712cf3ae6c7
+
+ /**
+ * Schedules a task to be executed on the region which owns the location.
+ *
+ * @param plugin The plugin that owns the task
+ * @param location The location at which the region executing should own
+ * @param run The task to execute
+ * @param world The world of the region that owns the task
+ * @param chunkX The chunk X coordinate of the region that owns the task
+ * @param chunkZ The chunk Z coordinate of the region that owns the task
+ * @param run The task to execute
+ */
+ public void execute(@NotNull Plugin plugin, @NotNull Location location, @NotNull Runnable run);
+ void execute(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Runnable run);
+
+ /**
+ * Schedules a task to be executed on the region which owns the location.
+ *
+ * @param plugin The plugin that owns the task
+ * @param location The location at which the region executing should own
+ * @param run The task to execute
+ */
+ default void execute(@NotNull Plugin plugin, @NotNull Location location, @NotNull Runnable run) {
+ this.execute(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, run);
+ }
+
+ /**
+ * Schedules a task to be executed on the region which owns the location on the next tick.
+ *
+ * @param plugin The plugin that owns the task
+ * @param location The location at which the region executing should own
+ * @param task The task to execute
+ * @param world The world of the region that owns the task
+ * @param chunkX The chunk X coordinate of the region that owns the task
+ * @param chunkZ The chunk Z coordinate of the region that owns the task
+ * @param task The task to execute
+ * @return The {@link ScheduledTask} that represents the scheduled task.
+ */
+ public @NotNull ScheduledTask run(@NotNull Plugin plugin, @NotNull Location location, @NotNull Consumer<ScheduledTask> task);
+ @NotNull ScheduledTask run(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Consumer<ScheduledTask> task);
+
+ /**
+ * Schedules a task to be executed on the region which owns the location on the next tick.
+ *
+ * @param plugin The plugin that owns the task
+ * @param location The location at which the region executing should own
+ * @param task The task to execute
+ * @return The {@link ScheduledTask} that represents the scheduled task.
+ */
+ default @NotNull ScheduledTask run(@NotNull Plugin plugin, @NotNull Location location, @NotNull Consumer<ScheduledTask> task) {
+ return this.run(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task);
+ }
+
+ /**
+ * Schedules a task to be executed on the region which owns the location after the specified delay in ticks.
+ * @param plugin The plugin that owns the task
+ * @param location The location at which the region executing should own
+ * @param task The task to execute
+ *
+ * @param plugin The plugin that owns the task
+ * @param world The world of the region that owns the task
+ * @param chunkX The chunk X coordinate of the region that owns the task
+ * @param chunkZ The chunk Z coordinate of the region that owns the task
+ * @param task The task to execute
+ * @param delayTicks The delay, in ticks.
+ * @return The {@link ScheduledTask} that represents the scheduled task.
+ */
+ public @NotNull ScheduledTask runDelayed(@NotNull Plugin plugin, @NotNull Location location, @NotNull Consumer<ScheduledTask> task,
+ int delayTicks);
+ @NotNull ScheduledTask runDelayed(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Consumer<ScheduledTask> task,
+ int delayTicks);
+
+ /**
+ * Schedules a task to be executed on the region which owns the location after the specified delay in ticks.
+ *
+ * @param plugin The plugin that owns the task
+ * @param location The location at which the region executing should own
+ * @param task The task to execute
+ * @param delayTicks The delay, in ticks.
+ * @return The {@link ScheduledTask} that represents the scheduled task.
+ */
+ default @NotNull ScheduledTask runDelayed(@NotNull Plugin plugin, @NotNull Location location, @NotNull Consumer<ScheduledTask> task,
+ int delayTicks) {
+ return this.runDelayed(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task, delayTicks);
+ }
+
+ /**
+ * Schedules a repeating task to be executed on the region which owns the location after the initial delay with the
+ * specified period.
+ * @param plugin The plugin that owns the task
+ * @param location The location at which the region executing should own
+ * @param task The task to execute
+ *
+ * @param plugin The plugin that owns the task
+ * @param world The world of the region that owns the task
+ * @param chunkX The chunk X coordinate of the region that owns the task
+ * @param chunkZ The chunk Z coordinate of the region that owns the task
+ * @param task The task to execute
+ * @param initialDelayTicks The initial delay, in ticks.
+ * @param periodTicks The period, in ticks.
+ * @param periodTicks The period, in ticks.
+ * @return The {@link ScheduledTask} that represents the scheduled task.
+ */
+ public @NotNull ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Location location, @NotNull Consumer<ScheduledTask> task,
+ int initialDelayTicks, int periodTicks);
+ @NotNull ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Consumer<ScheduledTask> task,
+ int initialDelayTicks, int periodTicks);
+
+ /**
+ * Schedules a repeating task to be executed on the region which owns the location after the initial delay with the
+ * specified period.
+ *
+ * @param plugin The plugin that owns the task
+ * @param location The location at which the region executing should own
+ * @param task The task to execute
+ * @param initialDelayTicks The initial delay, in ticks.
+ * @param periodTicks The period, in ticks.
+ * @return The {@link ScheduledTask} that represents the scheduled task.
+ */
+ default @NotNull ScheduledTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Location location, @NotNull Consumer<ScheduledTask> task,
+ int initialDelayTicks, int periodTicks) {
+ return this.runAtFixedRate(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task, initialDelayTicks, periodTicks);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/threadedregions/scheduler/ScheduledTask.java b/src/main/java/io/papermc/paper/threadedregions/scheduler/ScheduledTask.java
new file mode 100644
index 0000000000000000000000000000000000000000..fa4ac300d3721b2d6d84b95618d3305874cb803d
index 0000000000000000000000000000000000000000..a6b50c9d8af589cc4747e14d343d2045216c249c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/threadedregions/scheduler/ScheduledTask.java
@@ -0,0 +1,112 @@
@ -320,32 +386,32 @@ index 0000000000000000000000000000000000000000..fa4ac300d3721b2d6d84b95618d33058
+ * Returns the plugin that scheduled this task.
+ * @return the plugin that scheduled this task.
+ */
+ public @NotNull Plugin getOwningPlugin();
+ @NotNull Plugin getOwningPlugin();
+
+ /**
+ * Returns whether this task executes on a fixed period, as opposed to executing only once.
+ * @return whether this task executes on a fixed period, as opposed to executing only once.
+ */
+ public boolean isRepeatingTask();
+ boolean isRepeatingTask();
+
+ /**
+ * Attempts to cancel this task, returning the result of the attempt. In all cases, if the task is currently
+ * being executed no attempt is made to halt the task, however any executions in the future are halted.
+ * @return the result of the cancellation attempt.
+ */
+ public @NotNull CancelledState cancel();
+ @NotNull CancelledState cancel();
+
+ /**
+ * Returns the current execution state of this task.
+ * @return the current execution state of this task.
+ */
+ public @NotNull ExecutionState getExecutionState();
+ @NotNull ExecutionState getExecutionState();
+
+ /**
+ * Returns whether the current execution state is {@link ExecutionState#CANCELLED} or {@link ExecutionState#CANCELLED_RUNNING}.
+ * @return whether the current execution state is {@link ExecutionState#CANCELLED} or {@link ExecutionState#CANCELLED_RUNNING}.
+ */
+ public default boolean isCancelled() {
+ default boolean isCancelled() {
+ final ExecutionState state = this.getExecutionState();
+ return state == ExecutionState.CANCELLED || state == ExecutionState.CANCELLED_RUNNING;
+ }
@ -353,7 +419,7 @@ index 0000000000000000000000000000000000000000..fa4ac300d3721b2d6d84b95618d33058
+ /**
+ * Represents the result of attempting to cancel a task.
+ */
+ public enum CancelledState {
+ enum CancelledState {
+ /**
+ * The task (repeating or not) has been successfully cancelled by the caller thread. The task is not executing
+ * currently, and it will not begin execution in the future.
@ -390,7 +456,7 @@ index 0000000000000000000000000000000000000000..fa4ac300d3721b2d6d84b95618d33058
+ /**
+ * Represents the current execution state of the task.
+ */
+ public enum ExecutionState {
+ enum ExecutionState {
+ /**
+ * The task is currently not executing, but may begin execution in the future.
+ */
@ -468,7 +534,7 @@ index ac9b690fcccb60b587e5345f12f1383afd0a73a1..7986b9fcaf256e9042f6d9ddc38e8bd7
@NotNull
public static Server.Spigot spigot() {
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index 2204336d8800311b65e894739ab1b27273e7c6f2..bf02c948a50d934e94c44f4844254a45ae7cb2a5 100644
index 2204336d8800311b65e894739ab1b27273e7c6f2..5caa00a413450dee18739f6430ffaf5095ea3036 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -2139,4 +2139,36 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi

View File

@ -116,7 +116,7 @@ index 7986b9fcaf256e9042f6d9ddc38e8bd76645cbb7..16043d6f7894182e8ff75a8faf57dedf
@NotNull
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index bf02c948a50d934e94c44f4844254a45ae7cb2a5..fea84620b25ac393be6d3253c100ddeac1983105 100644
index 5caa00a413450dee18739f6430ffaf5095ea3036..c72eee72401e275af57d4fd67a04df1eac13954c 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -2170,5 +2170,83 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi

View File

@ -9818,10 +9818,10 @@ index 0000000000000000000000000000000000000000..505329d601d56e42daa0b794092590cb
+}
diff --git a/src/main/java/io/papermc/paper/threadedregions/scheduler/FoliaRegionScheduler.java b/src/main/java/io/papermc/paper/threadedregions/scheduler/FoliaRegionScheduler.java
new file mode 100644
index 0000000000000000000000000000000000000000..b699e20df7a98b0dd8f783b293f4c4b47b868f2c
index 0000000000000000000000000000000000000000..7ee75fe44507f79362f15d6445e32954dcc4308c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/threadedregions/scheduler/FoliaRegionScheduler.java
@@ -0,0 +1,430 @@
@@ -0,0 +1,422 @@
+package io.papermc.paper.threadedregions.scheduler;
+
+import ca.spottedleaf.concurrentutil.util.ConcurrentUtil;
@ -9840,7 +9840,6 @@ index 0000000000000000000000000000000000000000..b699e20df7a98b0dd8f783b293f4c4b4
+import net.minecraft.server.level.TicketType;
+import net.minecraft.util.Unit;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.plugin.IllegalPluginAccessException;
@ -9854,12 +9853,13 @@ index 0000000000000000000000000000000000000000..b699e20df7a98b0dd8f783b293f4c4b4
+
+public final class FoliaRegionScheduler implements RegionScheduler {
+
+ private static Runnable wrap(final Plugin plugin, final Location location, final Runnable run) {
+ private static Runnable wrap(final Plugin plugin, final World world, final int chunkX, final int chunkZ, final Runnable run) {
+ return () -> {
+ try {
+ run.run();
+ } catch (final Throwable throwable) {
+ plugin.getLogger().log(Level.WARNING, "Location task for " + plugin.getDescription().getFullName() + " at location " + location + " generated an exception", throwable);
+ plugin.getLogger().log(Level.WARNING, "Location task for " + plugin.getDescription().getFullName()
+ + " in world " + world + " at " + chunkX + ", " + chunkZ + " generated an exception", throwable);
+ }
+ };
+ }
@ -9871,50 +9871,40 @@ index 0000000000000000000000000000000000000000..b699e20df7a98b0dd8f783b293f4c4b4
+ }
+
+ private static void scheduleInternalOffRegion(final LocationScheduledTask task, final int delay) {
+ final Location location = task.location;
+ if (location == null) {
+ final World world = task.world;
+ if (world == null) {
+ // cancelled
+ return;
+ }
+
+ final World world = Validate.notNull(location.getWorld(), "Location world may not be null");
+
+ final int chunkX = location.getBlockX() >> 4;
+ final int chunkZ = location.getBlockZ() >> 4;
+
+ RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
+ ((CraftWorld)world).getHandle(), chunkX, chunkZ, () -> {
+ ((CraftWorld) world).getHandle(), task.chunkX, task.chunkZ, () -> {
+ scheduleInternalOnRegion(task, delay);
+ }
+ );
+ }
+
+ @Override
+ public void execute(final Plugin plugin, final Location location, final Runnable run) {
+ public void execute(final Plugin plugin, final World world, final int chunkX, final int chunkZ, final Runnable run) {
+ Validate.notNull(plugin, "Plugin may not be null");
+ Validate.notNull(location, "Location may not be null");
+ Validate.notNull(world, "World may not be null");
+ Validate.notNull(run, "Runnable may not be null");
+
+ final World world = Validate.notNull(location.getWorld(), "Location world may not be null");
+
+ final int chunkX = location.getBlockX() >> 4;
+ final int chunkZ = location.getBlockZ() >> 4;
+
+ RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
+ ((CraftWorld)world).getHandle(), chunkX, chunkZ, wrap(plugin, location.clone(), run)
+ ((CraftWorld) world).getHandle(), chunkX, chunkZ, wrap(plugin, world, chunkX, chunkZ, run)
+ );
+ }
+
+ @Override
+ public ScheduledTask run(final Plugin plugin, final Location location, final Consumer<ScheduledTask> task) {
+ return this.runDelayed(plugin, location, task, 1);
+ public ScheduledTask run(final Plugin plugin, final World world, final int chunkX, final int chunkZ, final Consumer<ScheduledTask> task) {
+ return this.runDelayed(plugin, world, chunkX, chunkZ, task, 1);
+ }
+
+ @Override
+ public ScheduledTask runDelayed(final Plugin plugin, Location location, final Consumer<ScheduledTask> task,
+ final int delayTicks) {
+ public ScheduledTask runDelayed(final Plugin plugin, final World world, final int chunkX, final int chunkZ,
+ final Consumer<ScheduledTask> task, final int delayTicks) {
+ Validate.notNull(plugin, "Plugin may not be null");
+ Validate.notNull(location, "Location may not be null");
+ Validate.notNull(world, "World may not be null");
+ Validate.notNull(task, "Task may not be null");
+ if (delayTicks <= 0) {
+ throw new IllegalArgumentException("Delay ticks may not be <= 0");
@ -9924,11 +9914,9 @@ index 0000000000000000000000000000000000000000..b699e20df7a98b0dd8f783b293f4c4b4
+ throw new IllegalPluginAccessException("Plugin attempted to register task while disabled");
+ }
+
+ location = location.clone();
+ final LocationScheduledTask ret = new LocationScheduledTask(plugin, world, chunkX, chunkZ, -1, task);
+
+ final LocationScheduledTask ret = new LocationScheduledTask(plugin, location, -1, task);
+
+ if (Bukkit.isOwnedByCurrentRegion(location)) {
+ if (Bukkit.isOwnedByCurrentRegion(world, chunkX, chunkZ)) {
+ scheduleInternalOnRegion(ret, delayTicks);
+ } else {
+ scheduleInternalOffRegion(ret, delayTicks);
@ -9943,10 +9931,10 @@ index 0000000000000000000000000000000000000000..b699e20df7a98b0dd8f783b293f4c4b4
+ }
+
+ @Override
+ public ScheduledTask runAtFixedRate(final Plugin plugin, Location location, final Consumer<ScheduledTask> task,
+ final int initialDelayTicks, final int periodTicks) {
+ public ScheduledTask runAtFixedRate(final Plugin plugin, final World world, final int chunkX, final int chunkZ,
+ final Consumer<ScheduledTask> task, final int initialDelayTicks, final int periodTicks) {
+ Validate.notNull(plugin, "Plugin may not be null");
+ Validate.notNull(location, "Location may not be null");
+ Validate.notNull(world, "World may not be null");
+ Validate.notNull(task, "Task may not be null");
+ if (initialDelayTicks <= 0) {
+ throw new IllegalArgumentException("Initial delay ticks may not be <= 0");
@ -9959,11 +9947,9 @@ index 0000000000000000000000000000000000000000..b699e20df7a98b0dd8f783b293f4c4b4
+ throw new IllegalPluginAccessException("Plugin attempted to register task while disabled");
+ }
+
+ location = location.clone();
+ final LocationScheduledTask ret = new LocationScheduledTask(plugin, world, chunkX, chunkZ, periodTicks, task);
+
+ final LocationScheduledTask ret = new LocationScheduledTask(plugin, location, periodTicks, task);
+
+ if (Bukkit.isOwnedByCurrentRegion(location)) {
+ if (Bukkit.isOwnedByCurrentRegion(world, chunkX, chunkZ)) {
+ scheduleInternalOnRegion(ret, initialDelayTicks);
+ } else {
+ scheduleInternalOffRegion(ret, initialDelayTicks);
@ -10054,14 +10040,14 @@ index 0000000000000000000000000000000000000000..b699e20df7a98b0dd8f783b293f4c4b4
+ // note: must be on the thread that owns this scheduler
+ // note: delay > 0
+
+ final Location location = task.location;
+ if (location == null) {
+ final World world = task.world;
+ if (world == null) {
+ // cancelled
+ return;
+ }
+
+ final int sectionX = (location.getBlockX() >> 4) >> TickRegions.getRegionChunkShift();
+ final int sectionZ = (location.getBlockZ() >> 4) >> TickRegions.getRegionChunkShift();
+ final int sectionX = task.chunkX >> TickRegions.getRegionChunkShift();
+ final int sectionZ = task.chunkZ >> TickRegions.getRegionChunkShift();
+
+ final Long2ObjectOpenHashMap<List<LocationScheduledTask>> section =
+ this.tasksByDeadlineBySection.computeIfAbsent(CoordinateUtils.getChunkKey(sectionX, sectionZ), (final long keyInMap) -> {
@ -10119,16 +10105,21 @@ index 0000000000000000000000000000000000000000..b699e20df7a98b0dd8f783b293f4c4b4
+ private static final int STATE_CANCELLED = 4;
+
+ private final Plugin plugin;
+ private Location location;
+ private final int chunkX;
+ private final int chunkZ;
+ private final int repeatDelay; // in ticks
+ private World world;
+ private Consumer<ScheduledTask> run;
+
+ private volatile int state;
+ private static final VarHandle STATE_HANDLE = ConcurrentUtil.getVarHandle(LocationScheduledTask.class, "state", int.class);
+
+ private LocationScheduledTask(final Plugin plugin, final Location location, final int repeatDelay, final Consumer<ScheduledTask> run) {
+ private LocationScheduledTask(final Plugin plugin, final World world, final int chunkX, final int chunkZ,
+ final int repeatDelay, final Consumer<ScheduledTask> run) {
+ this.plugin = plugin;
+ this.location = location;
+ this.world = world;
+ this.chunkX = chunkX;
+ this.chunkZ = chunkZ;
+ this.repeatDelay = repeatDelay;
+ this.run = run;
+ }
@ -10161,7 +10152,8 @@ index 0000000000000000000000000000000000000000..b699e20df7a98b0dd8f783b293f4c4b4
+ try {
+ this.run.accept(this);
+ } catch (final Throwable throwable) {
+ this.plugin.getLogger().log(Level.WARNING, "Location task for " + this.plugin.getDescription().getFullName() + " at location " + this.location + " generated an exception", throwable);
+ this.plugin.getLogger().log(Level.WARNING, "Location task for " + this.plugin.getDescription().getFullName()
+ + " in world " + world + " at " + chunkX + ", " + chunkZ + " generated an exception", throwable);
+ } finally {
+ boolean reschedule = false;
+ if (!repeating) {
@ -10174,7 +10166,7 @@ index 0000000000000000000000000000000000000000..b699e20df7a98b0dd8f783b293f4c4b4
+
+ if (!reschedule) {
+ this.run = null;
+ this.location = null;
+ this.world = null;
+ } else {
+ FoliaRegionScheduler.scheduleInternalOnRegion(this, this.repeatDelay);
+ }
@ -10199,7 +10191,7 @@ index 0000000000000000000000000000000000000000..b699e20df7a98b0dd8f783b293f4c4b4
+ if (STATE_IDLE == (curr = this.compareAndExchangeStateVolatile(STATE_IDLE, STATE_CANCELLED))) {
+ this.state = STATE_CANCELLED;
+ this.run = null;
+ this.location = null;
+ this.world = null;
+ return CancelledState.CANCELLED_BY_CALLER;
+ }
+ // try again