Folia/patches/api/0002-Region-scheduler-API.patch
Spottedleaf 7e948a6179 Add global region scheduler
This will allow plugins to safely execute commands or perform
other global tick thread data modification.
2023-03-19 16:43:34 -07:00

227 lines
12 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 4 Mar 2023 12:48:43 -0800
Subject: [PATCH] Region scheduler API
Add both a location based scheduler, an entity based scheduler,
and a global region scheduler.
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..4193b13f1f51c2fb8da76f3e03187d859eaa8e10
--- /dev/null
+++ b/src/main/java/io/papermc/paper/threadedregions/scheduler/EntityScheduler.java
@@ -0,0 +1,47 @@
+package io.papermc.paper.threadedregions.scheduler;
+
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * An entity can move between worlds with an arbitrary tick delay, be temporarily removed
+ * for players (i.e end credits), be partially removed from world state (i.e inactive but not removed),
+ * teleport between ticking regions, teleport between worlds, and even be removed entirely from the server.
+ * The uncertainty of an entity's state can make it difficult to schedule tasks without worrying about undefined
+ * behaviors resulting from any of the states listed previously.
+ *
+ * <p>
+ * This class is designed to eliminate those states by providing an interface to run tasks only when an entity
+ * is contained in a world, on the owning thread for the region, and by providing the current Entity object.
+ * The scheduler also allows a task to provide a callback, the "retired" callback, that will be invoked
+ * if the entity is removed before a task that was scheduled could be executed. The scheduler is also
+ * completely thread-safe, allowing tasks to be scheduled from any thread context. The scheduler also indicates
+ * properly whether a task was scheduled successfully (i.e scheduler not retired), thus the code scheduling any task
+ * knows whether the given callbacks will be invoked eventually or not - which may be critical for off-thread
+ * contexts.
+ * </p>
+ */
+public interface EntityScheduler {
+
+ /**
+ * Schedules a task with the given delay. If the task failed to schedule because the scheduler is retired (entity
+ * removed), then returns {@code false}. Otherwise, either the run callback will be invoked after the specified delay,
+ * or the retired callback will be invoked if the scheduler is retired.
+ * Note that the retired callback is invoked in critical code, so it should not attempt to remove the entity, remove
+ * other entities, load chunks, load worlds, modify ticket levels, etc.
+ *
+ * <p>
+ * It is guaranteed that the run and retired callback are invoked on the region which owns the entity.
+ * </p>
+ * @param run The callback to run after the specified delay, may not be null.
+ * @param retired Retire callback to run if the entity is retired before the run callback can be invoked, may be null.
+ * @param delay The delay in ticks before the run callback is invoked. Any value less-than 1 is treated as 1.
+ * @return {@code true} if the task was scheduled, which means that either the run function or the retired function
+ * 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 final Plugin plugin, @NotNull final Runnable run, @Nullable final Runnable retired,
+ final long delay);
+
+}
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..c13c89c87a4e6fd441cac8d3c8cd5b283915467f
--- /dev/null
+++ b/src/main/java/io/papermc/paper/threadedregions/scheduler/GlobalRegionScheduler.java
@@ -0,0 +1,22 @@
+package io.papermc.paper.threadedregions.scheduler;
+
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The global region task scheduler may be used to schedule tasks that will execute on the global region.
+ * <p>
+ * The global region is responsible for maintaining world day time, world game time, weather cycle,
+ * sleep night skipping, executing commands for console, and other misc. tasks that do not belong to any specific region.
+ * </p>
+ */
+public interface GlobalRegionScheduler {
+
+ /**
+ * Schedules a task to be executed on the global region.
+ * @param plugin The plugin that owns the task
+ * @param run The task to execute
+ */
+ public void execute(@NotNull final Plugin plugin, @NotNull final Runnable run);
+
+}
diff --git a/src/main/java/io/papermc/paper/threadedregions/scheduler/RegionisedScheduler.java b/src/main/java/io/papermc/paper/threadedregions/scheduler/RegionisedScheduler.java
new file mode 100644
index 0000000000000000000000000000000000000000..210a3dce74959efd7ac0ca9a92a2ad8815844246
--- /dev/null
+++ b/src/main/java/io/papermc/paper/threadedregions/scheduler/RegionisedScheduler.java
@@ -0,0 +1,26 @@
+package io.papermc.paper.threadedregions.scheduler;
+
+import org.bukkit.Location;
+import org.bukkit.entity.Entity;
+import org.bukkit.plugin.Plugin;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * The region task scheduler can be used to schedule tasks by location to be executed on the region which owns the location.
+ * <p>
+ * <b>Note</b>: It is entirely inappropriate to use the region scheduler to schedule tasks for entities.
+ * If you wish to schedule tasks to perform actions on entities, you should be using {@link Entity#getScheduler()}
+ * as the entity scheduler will "follow" an entity if it is teleported, whereas the region task scheduler
+ * will not.
+ * </p>
+ */
+public interface RegionisedScheduler {
+
+ /**
+ * 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
+ */
+ public void execute(@NotNull final Plugin plugin, @NotNull final Location location, @NotNull final Runnable run);
+}
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index ac9b690fcccb60b587e5345f12f1383afd0a73a1..4ae25cb199c6e20b51c4a1e7ac4ff55b23ae724f 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -2459,6 +2459,35 @@ public final class Bukkit {
return server.getPotionBrewer();
}
// Paper end
+ // Folia start - region threading API
+ /**
+ * Returns the region task scheduler. The region task scheduler can be used to schedule
+ * tasks by location to be executed on the region which owns the location.
+ * <p>
+ * <b>Note</b>: It is entirely inappropriate to use the region scheduler to schedule tasks for entities.
+ * If you wish to schedule tasks to perform actions on entities, you should be using {@link Entity#getScheduler()}
+ * as the entity scheduler will "follow" an entity if it is teleported, whereas the region task scheduler
+ * will not.
+ * </p>
+ * @return the region task scheduler
+ */
+ public static @NotNull io.papermc.paper.threadedregions.scheduler.RegionisedScheduler getRegionScheduler() {
+ return server.getRegionScheduler();
+ }
+
+ /**
+ * Returns the global region task scheduler. The global task scheduler can be used to schedule
+ * tasks to execute on the global region.
+ * <p>
+ * The global region is responsible for maintaining world day time, world game time, weather cycle,
+ * sleep night skipping, executing commands for console, and other misc. tasks that do not belong to any specific region.
+ * </p>
+ * @return the global region scheduler
+ */
+ public static @NotNull io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler getGlobalRegionScheduler() {
+ return server.getGlobalRegionScheduler();
+ }
+ // Folia end - region threading API
@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..1092ceef77bad421df647d349d997d02b2ba80a9 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -2139,4 +2139,29 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
*/
@NotNull org.bukkit.potion.PotionBrewer getPotionBrewer();
// Paper end
+ // Folia start - region threading API
+ /**
+ * Returns the region task scheduler. The region task scheduler can be used to schedule
+ * tasks by location to be executed on the region which owns the location.
+ * <p>
+ * <b>Note</b>: It is entirely inappropriate to use the region scheduler to schedule tasks for entities.
+ * If you wish to schedule tasks to perform actions on entities, you should be using {@link Entity#getScheduler()}
+ * as the entity scheduler will "follow" an entity if it is teleported, whereas the region task scheduler
+ * will not.
+ * </p>
+ * @return the region task scheduler
+ */
+ @NotNull io.papermc.paper.threadedregions.scheduler.RegionisedScheduler getRegionScheduler();
+
+ /**
+ * Returns the global region task scheduler. The global task scheduler can be used to schedule
+ * tasks to execute on the global region.
+ * <p>
+ * The global region is responsible for maintaining world day time, world game time, weather cycle,
+ * sleep night skipping, executing commands for console, and other misc. tasks that do not belong to any specific region.
+ * </p>
+ * @return the global region scheduler
+ */
+ public @NotNull io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler getGlobalRegionScheduler();
+ // Folia end - region threading API
}
diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java
index cdbc7329cf5f67d66e31eb31e83b9e7997040f72..90451ed12b2c95bb372ac2e3cbb57b8b83cc6a82 100644
--- a/src/main/java/org/bukkit/entity/Entity.java
+++ b/src/main/java/org/bukkit/entity/Entity.java
@@ -970,4 +970,13 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent
*/
boolean wouldCollideUsing(@NotNull BoundingBox boundingBox);
// Paper End - Collision API
+ // Folia start - region threading API
+ /**
+ * Returns the task scheduler for this entity. The entity scheduler can be used to schedule tasks
+ * that are guaranteed to always execute on the tick thread that owns the entity.
+ * @return the task scheduler for this entity.
+ * @see io.papermc.paper.threadedregions.scheduler.EntityScheduler
+ */
+ @NotNull io.papermc.paper.threadedregions.scheduler.EntityScheduler getScheduler();
+ // Folia end - region threading API
}
diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
index b012ce40d82389c29d1b841ff685425ac10a7f9e..499dc8309a16b33d16b57b433c3c5b4330323717 100644
--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java
+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java
@@ -585,7 +585,7 @@ public final class SimplePluginManager implements PluginManager {
}
try {
- server.getScheduler().cancelTasks(plugin);
+ //server.getScheduler().cancelTasks(plugin); // Folia - Bukkit scheduler not supported
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while cancelling tasks for "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper