From 7e948a6179de0d4f5f61c55aebaaa1725af0cdb0 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sun, 19 Mar 2023 16:43:34 -0700 Subject: [PATCH] Add global region scheduler This will allow plugins to safely execute commands or perform other global tick thread data modification. --- patches/api/0002-Region-scheduler-API.patch | 63 +++++++++++++++++-- ...king-ownership-of-region-by-position.patch | 16 ++--- patches/server/0004-Threaded-Regions.patch | 49 +++++++++++++-- 3 files changed, 110 insertions(+), 18 deletions(-) diff --git a/patches/api/0002-Region-scheduler-API.patch b/patches/api/0002-Region-scheduler-API.patch index a0485d4..36b40a9 100644 --- a/patches/api/0002-Region-scheduler-API.patch +++ b/patches/api/0002-Region-scheduler-API.patch @@ -3,7 +3,8 @@ From: Spottedleaf Date: Sat, 4 Mar 2023 12:48:43 -0800 Subject: [PATCH] Region scheduler API -Add both a location based scheduler and an entity based scheduler +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 @@ -58,6 +59,34 @@ index 0000000000000000000000000000000000000000..4193b13f1f51c2fb8da76f3e03187d85 + 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. ++ *

++ * 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. ++ *

++ */ ++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 @@ -91,10 +120,10 @@ index 0000000000000000000000000000000000000000..210a3dce74959efd7ac0ca9a92a2ad88 + 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..3b8a6d7336b0cb20594de9c6b56f215f1370ce89 100644 +index ac9b690fcccb60b587e5345f12f1383afd0a73a1..4ae25cb199c6e20b51c4a1e7ac4ff55b23ae724f 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java -@@ -2459,6 +2459,22 @@ public final class Bukkit { +@@ -2459,6 +2459,35 @@ public final class Bukkit { return server.getPotionBrewer(); } // Paper end @@ -113,15 +142,28 @@ index ac9b690fcccb60b587e5345f12f1383afd0a73a1..3b8a6d7336b0cb20594de9c6b56f215f + 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. ++ *

++ * 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. ++ *

++ * @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..135467092eec33a21b171df39267d15bad26003e 100644 +index 2204336d8800311b65e894739ab1b27273e7c6f2..1092ceef77bad421df647d349d997d02b2ba80a9 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java -@@ -2139,4 +2139,18 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi +@@ -2139,4 +2139,29 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi */ @NotNull org.bukkit.potion.PotionBrewer getPotionBrewer(); // Paper end @@ -138,6 +180,17 @@ index 2204336d8800311b65e894739ab1b27273e7c6f2..135467092eec33a21b171df39267d15b + * @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. ++ *

++ * 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. ++ *

++ * @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 diff --git a/patches/api/0005-Add-API-for-checking-ownership-of-region-by-position.patch b/patches/api/0005-Add-API-for-checking-ownership-of-region-by-position.patch index 68407b6..6d10adc 100644 --- a/patches/api/0005-Add-API-for-checking-ownership-of-region-by-position.patch +++ b/patches/api/0005-Add-API-for-checking-ownership-of-region-by-position.patch @@ -11,12 +11,12 @@ the schedulers depending on the result of the ownership check. diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index 3b8a6d7336b0cb20594de9c6b56f215f1370ce89..aee6508cec0631fc7b5fe6bf1b978a5c01437422 100644 +index 4ae25cb199c6e20b51c4a1e7ac4ff55b23ae724f..b8c62f431bf802a80fa097f1e5f8e6247badd0ea 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java -@@ -2474,6 +2474,100 @@ public final class Bukkit { - public static @NotNull io.papermc.paper.threadedregions.scheduler.RegionisedScheduler getRegionScheduler() { - return server.getRegionScheduler(); +@@ -2487,6 +2487,100 @@ public final class Bukkit { + public static @NotNull io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler getGlobalRegionScheduler() { + return server.getGlobalRegionScheduler(); } + + /** @@ -116,13 +116,13 @@ index 3b8a6d7336b0cb20594de9c6b56f215f1370ce89..aee6508cec0631fc7b5fe6bf1b978a5c @NotNull diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index 135467092eec33a21b171df39267d15bad26003e..48902e9aec6af7a4252db5b567d08a80f5709cbf 100644 +index 1092ceef77bad421df647d349d997d02b2ba80a9..3a17ccd041ca64c8ab0cd3edf4ce6fc1c6f2d459 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java -@@ -2152,5 +2152,83 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi - * @return the region task scheduler +@@ -2163,5 +2163,83 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi + * @return the global region scheduler */ - @NotNull io.papermc.paper.threadedregions.scheduler.RegionisedScheduler getRegionScheduler(); + public @NotNull io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler getGlobalRegionScheduler(); + + /** + * Returns whether the current thread is ticking a region and that the region being ticked diff --git a/patches/server/0004-Threaded-Regions.patch b/patches/server/0004-Threaded-Regions.patch index 882ab58..a589642 100644 --- a/patches/server/0004-Threaded-Regions.patch +++ b/patches/server/0004-Threaded-Regions.patch @@ -8921,6 +8921,39 @@ index 0000000000000000000000000000000000000000..c0b9f43fc53a5c9b3d9788f080e4fd3f + return this.entity.taskScheduler.schedule(runNMS, runRetired, delay); + } +} +diff --git a/src/main/java/io/papermc/paper/threadedregions/scheduler/FoliaGlobalRegionScheduler.java b/src/main/java/io/papermc/paper/threadedregions/scheduler/FoliaGlobalRegionScheduler.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cada55accf6c6b882918fe59f82cabe029f7b511 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/threadedregions/scheduler/FoliaGlobalRegionScheduler.java +@@ -0,0 +1,27 @@ ++package io.papermc.paper.threadedregions.scheduler; ++ ++import ca.spottedleaf.concurrentutil.util.Validate; ++import io.papermc.paper.threadedregions.RegionisedServer; ++import org.bukkit.plugin.Plugin; ++import java.util.logging.Level; ++ ++public final class FoliaGlobalRegionScheduler implements GlobalRegionScheduler { ++ ++ private static Runnable wrap(final Plugin plugin, final Runnable run) { ++ return () -> { ++ try { ++ run.run(); ++ } catch (final Throwable throwable) { ++ plugin.getLogger().log(Level.WARNING, "Global region task for " + plugin.getDescription().getFullName() + " generated an exception", throwable); ++ } ++ }; ++ } ++ ++ @Override ++ public void execute(final Plugin plugin, final Runnable run) { ++ Validate.notNull(plugin, "Plugin may not be null"); ++ Validate.notNull(run, "Runnable may not be null"); ++ ++ RegionisedServer.getInstance().addTaskWithoutNotify(wrap(plugin, run)); ++ } ++} diff --git a/src/main/java/io/papermc/paper/threadedregions/scheduler/FoliaRegionisedScheduler.java b/src/main/java/io/papermc/paper/threadedregions/scheduler/FoliaRegionisedScheduler.java new file mode 100644 index 0000000000000000000000000000000000000000..e4637086ba0095341da9c2bfe20083375c82bc01 @@ -21688,15 +21721,16 @@ index 7f1ac2cb29eb84833c0895442d611dfa0504527e..c79cfebc65fd04994735dabcf5bb6e6c LevelChunkTicks levelChunkTicks = this.allContainers.get(l); if (levelChunkTicks == null) { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 2ea3778ee1348e5d06b15a2c5dc5d9bd4136dbe3..c970a9165657636f1a442df8aa3f566eea46a290 100644 +index 2ea3778ee1348e5d06b15a2c5dc5d9bd4136dbe3..38d508b7a198df39de6d8ddc1b2aa6b8284f2d1b 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -307,6 +307,69 @@ public final class CraftServer implements Server { +@@ -307,6 +307,75 @@ public final class CraftServer implements Server { CraftItemFactory.instance(); } + // Folia start - region threading API + private final io.papermc.paper.threadedregions.scheduler.FoliaRegionisedScheduler regionisedScheduler = new io.papermc.paper.threadedregions.scheduler.FoliaRegionisedScheduler(); ++ private final io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler globalRegionScheduler = new io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler(); + + @Override + public final io.papermc.paper.threadedregions.scheduler.RegionisedScheduler getRegionScheduler() { @@ -21704,6 +21738,11 @@ index 2ea3778ee1348e5d06b15a2c5dc5d9bd4136dbe3..c970a9165657636f1a442df8aa3f566e + } + + @Override ++ public final io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler getGlobalRegionScheduler() { ++ return this.globalRegionScheduler; ++ } ++ ++ @Override + public final boolean isOwnedByCurrentRegion(World world, org.bukkit.util.Vector position) { + return io.papermc.paper.util.TickThread.isTickThreadFor( + ((CraftWorld)world).getHandle(), position.getBlockX() >> 4, position.getBlockZ() >> 4 @@ -21761,7 +21800,7 @@ index 2ea3778ee1348e5d06b15a2c5dc5d9bd4136dbe3..c970a9165657636f1a442df8aa3f566e public CraftServer(DedicatedServer console, PlayerList playerList) { this.console = console; this.playerList = (DedicatedPlayerList) playerList; -@@ -879,6 +942,9 @@ public final class CraftServer implements Server { +@@ -879,6 +948,9 @@ public final class CraftServer implements Server { // NOTE: Should only be called from DedicatedServer.ah() public boolean dispatchServerCommand(CommandSender sender, ConsoleInput serverCommand) { @@ -21771,7 +21810,7 @@ index 2ea3778ee1348e5d06b15a2c5dc5d9bd4136dbe3..c970a9165657636f1a442df8aa3f566e if (sender instanceof Conversable) { Conversable conversable = (Conversable) sender; -@@ -898,12 +964,44 @@ public final class CraftServer implements Server { +@@ -898,12 +970,44 @@ public final class CraftServer implements Server { } } @@ -21816,7 +21855,7 @@ index 2ea3778ee1348e5d06b15a2c5dc5d9bd4136dbe3..c970a9165657636f1a442df8aa3f566e // Paper Start if (!org.spigotmc.AsyncCatcher.shuttingDown && !Bukkit.isPrimaryThread()) { final CommandSender fSender = sender; -@@ -2913,7 +3011,7 @@ public final class CraftServer implements Server { +@@ -2913,7 +3017,7 @@ public final class CraftServer implements Server { @Override public int getCurrentTick() {