From f8035d909f281335e74937eb2b5ba5e7f7004104 Mon Sep 17 00:00:00 2001 From: HarvelsX <90945793+HarvelsX@users.noreply.github.com> Date: Thu, 3 Aug 2023 06:33:39 +0300 Subject: [PATCH 1/4] Add scheduler adapters to support Folia schedulers; --- .../scheduler/BukkitSchedulerAdapters.java | 30 +++++++++ .../adapters/BukkitSchedulerAdapter.java | 43 ++++++++++++ .../adapters/FoliaSchedulerAdapter.java | 38 +++++++++++ .../com/sk89q/worldedit/entity/Entity.java | 21 ++++++ .../platform/scheduler/SchedulerAdapter.java | 67 +++++++++++++++++++ 5 files changed, 199 insertions(+) create mode 100644 worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/scheduler/BukkitSchedulerAdapters.java create mode 100644 worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/scheduler/adapters/BukkitSchedulerAdapter.java create mode 100644 worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/scheduler/adapters/FoliaSchedulerAdapter.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/scheduler/SchedulerAdapter.java diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/scheduler/BukkitSchedulerAdapters.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/scheduler/BukkitSchedulerAdapters.java new file mode 100644 index 0000000000..2f12adc217 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/scheduler/BukkitSchedulerAdapters.java @@ -0,0 +1,30 @@ +package com.sk89q.worldedit.bukkit.scheduler; + +import com.sk89q.worldedit.bukkit.scheduler.adapters.BukkitSchedulerAdapter; +import com.sk89q.worldedit.bukkit.scheduler.adapters.FoliaSchedulerAdapter; +import com.sk89q.worldedit.extension.platform.scheduler.SchedulerAdapter; +import org.bukkit.plugin.Plugin; + +public final class BukkitSchedulerAdapters { + private final static boolean FOLIA_SUPPORT = foliaSupport(); + + private BukkitSchedulerAdapters() { + // Call only through a method + } + + public static SchedulerAdapter create(Plugin plugin) { + if (FOLIA_SUPPORT) { + return new FoliaSchedulerAdapter(plugin); + } + return new BukkitSchedulerAdapter(plugin); + } + + private static boolean foliaSupport() { + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } +} diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/scheduler/adapters/BukkitSchedulerAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/scheduler/adapters/BukkitSchedulerAdapter.java new file mode 100644 index 0000000000..eea3262e30 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/scheduler/adapters/BukkitSchedulerAdapter.java @@ -0,0 +1,43 @@ +package com.sk89q.worldedit.bukkit.scheduler.adapters; + +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.extension.platform.scheduler.SchedulerAdapter; +import com.sk89q.worldedit.util.Location; +import org.bukkit.plugin.Plugin; + +public class BukkitSchedulerAdapter implements SchedulerAdapter { + + private final Plugin plugin; + @SuppressWarnings("deprecation") + private final org.bukkit.scheduler.BukkitScheduler scheduler; + + public BukkitSchedulerAdapter(final Plugin plugin) { + this.plugin = plugin; + this.scheduler = plugin.getServer().getScheduler(); + } + + @Override + public void runAsyncRate(Runnable runnable, long delay, long period) { + scheduler.runTaskTimerAsynchronously(plugin, runnable, delay, period); + } + + @Override + public void executeAtEntity(Entity entity, Runnable runnable) { + scheduler.runTask(plugin, runnable); + } + + @Override + public void runAtEntityDelayed(final Entity entity, final Runnable runnable, final long delay) { + scheduler.runTaskLater(plugin, runnable, delay); + } + + @Override + public void executeAtRegion(Location location, Runnable runnable) { + scheduler.runTask(plugin, runnable); + } + + @Override + public void cancelTasks() { + scheduler.cancelTasks(plugin); + } +} \ No newline at end of file diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/scheduler/adapters/FoliaSchedulerAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/scheduler/adapters/FoliaSchedulerAdapter.java new file mode 100644 index 0000000000..4c9783d333 --- /dev/null +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/scheduler/adapters/FoliaSchedulerAdapter.java @@ -0,0 +1,38 @@ +package com.sk89q.worldedit.bukkit.scheduler.adapters; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.extension.platform.scheduler.SchedulerAdapter; +import com.sk89q.worldedit.util.Location; +import io.papermc.paper.threadedregions.scheduler.AsyncScheduler; +import io.papermc.paper.threadedregions.scheduler.RegionScheduler; +import org.bukkit.plugin.Plugin; + +import java.util.concurrent.TimeUnit; + +public class FoliaSchedulerAdapter implements SchedulerAdapter { + private final Plugin plugin; + private final AsyncScheduler asyncScheduler; + private final RegionScheduler regionScheduler; + + public FoliaSchedulerAdapter(final Plugin plugin) { + this.plugin = plugin; + this.asyncScheduler = plugin.getServer().getAsyncScheduler(); + this.regionScheduler = plugin.getServer().getRegionScheduler(); + } + + @Override + public void runAsyncRate(final Runnable runnable, final long delay, final long period) { + asyncScheduler.runAtFixedRate(plugin, task -> runnable.run(), delay * 50, period * 50, TimeUnit.MILLISECONDS); + } + + @Override + public void executeAtRegion(final Location location, final Runnable runnable) { + regionScheduler.execute(plugin, BukkitAdapter.adapt(location), runnable); + } + + @Override + public void cancelTasks() { + asyncScheduler.cancelTasks(plugin); + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Entity.java b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Entity.java index ffa5dfd0fa..2f54ebc033 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Entity.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/entity/Entity.java @@ -54,4 +54,25 @@ public interface Entity extends Faceted, Locatable { */ boolean remove(); + /** + * Schedules a task. + * + * @see com.sk89q.worldedit.extension.platform.scheduler.SchedulerAdapter + * @param runnable The task to execute. + */ + default void executeAtEntity(Runnable runnable) { + throw new UnsupportedOperationException("Not implemented in this platform"); + } + + /** + * Schedules a task with the given delay. + * + * @see com.sk89q.worldedit.extension.platform.scheduler.SchedulerAdapter + * @param runnable The task to execute. + * @param delay The time delay to pass before the task should be executed, in ticks. + */ + default void runAtEntityDelayed(Runnable runnable, long delay) { + throw new UnsupportedOperationException("Not implemented in this platform"); + } + } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/scheduler/SchedulerAdapter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/scheduler/SchedulerAdapter.java new file mode 100644 index 0000000000..4548f754cc --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/scheduler/SchedulerAdapter.java @@ -0,0 +1,67 @@ +package com.sk89q.worldedit.extension.platform.scheduler; + +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.util.Location; + +public interface SchedulerAdapter { + + /** + * Schedules the specified task to be executed asynchronously after the delay has passed, + * and then periodically executed with the specified period. + * + * @param runnable The task to execute. + * @param delay The time delay to pass before the task should be executed. + * @param period The time between task executions after the first execution of the task. + */ + void runAsyncRate(Runnable runnable, long delay, long period); + + /** + * Schedules a task. 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. + * + *
+ * It is guaranteed that the task and retired callback are invoked on the region which owns the entity. + *
+ * + * @param entity The entity relative to which the scheduler is obtained. + * @param runnable The task to execute. + */ + default void executeAtEntity(Entity entity, Runnable runnable) { + entity.executeAtEntity(runnable); + } + + /** + * 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. + * + *+ * It is guaranteed that the task and retired callback are invoked on the region which owns the entity. + *
+ * + * @param entity The entity relative to which the scheduler is obtained. + * @param runnable The task to execute. + * @param delay The time delay to pass before the task should be executed, in ticks. + */ + default void runAtEntityDelayed(Entity entity, Runnable runnable, long delay) { + entity.runAtEntityDelayed(runnable, delay); + } + + /** + * Schedules a task to be executed on the region which owns the location. + * + * @param location The location at which the region executing should own. + * @param runnable The task to execute. + */ + void executeAtRegion(Location location, Runnable runnable); + + /** + * Attempts to cancel all tasks scheduled by the plugin. + */ + void cancelTasks(); +} \ No newline at end of file From 1ed010a9e0c1c305e916c0e656b8568e72885fdd Mon Sep 17 00:00:00 2001 From: HarvelsX <90945793+HarvelsX@users.noreply.github.com> Date: Thu, 3 Aug 2023 06:37:36 +0300 Subject: [PATCH 2/4] Use scheduler adapters instead of scheduler in Bukkit; Implement scheduler methods in Bukkit Entity; --- .../bukkit/BukkitBlockCommandSender.java | 8 +++----- .../com/sk89q/worldedit/bukkit/BukkitEntity.java | 16 ++++++++++++++++ .../worldedit/bukkit/BukkitServerInterface.java | 4 +++- .../sk89q/worldedit/bukkit/WorldEditPlugin.java | 9 ++++++++- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCommandSender.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCommandSender.java index 12840c4dd7..ccd162ff8a 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCommandSender.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitBlockCommandSender.java @@ -156,11 +156,9 @@ public boolean isActive() { updateActive(); } else { // we should update it eventually - Bukkit.getScheduler().callSyncMethod(plugin, - () -> { - updateActive(); - return null; - }); + plugin.getScheduler().executeAtRegion( + BukkitAdapter.adapt(sender.getBlock().getLocation()), + this::updateActive); } return active; } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitEntity.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitEntity.java index e719902862..a71d8bab62 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitEntity.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitEntity.java @@ -125,4 +125,20 @@ public