teleportAsync(Entity entity, Location location, PlayerTeleportEvent.TeleportCause cause) {
+ return ASYNC_TELEPORT ? entity.teleportAsync(location, cause) : CompletableFuture.completedFuture(entity.teleport(location, cause));
+ }
+
+ public static boolean isFoliaServer() {
+ return FOLIA_SERVER;
+ }
+
private static ChronoUnit toChronoUnit(TimeUnit tu) {
switch (tu) {
case NANOSECONDS:
@@ -306,6 +317,8 @@ private static ChronoUnit toChronoUnit(TimeUnit tu) {
private static boolean SUPPORT_WORLD_HEIGHT = true;
private static boolean SUPPORTS_KEYED;
private static Boolean using1_13API;
+ private static boolean FOLIA_SERVER = false;
+ private static boolean ASYNC_TELEPORT = false;
static {
try {
@@ -313,5 +326,17 @@ private static ChronoUnit toChronoUnit(TimeUnit tu) {
SUPPORTS_KEYED = true;
} catch (ClassNotFoundException e) {
}
+ try {
+ Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
+ FOLIA_SERVER = true;
+ } catch (ClassNotFoundException ignored) {
+ FOLIA_SERVER = false;
+ }
+ try {
+ Entity.class.getMethod("teleportAsync", Location.class, PlayerTeleportEvent.TeleportCause.class);
+ ASYNC_TELEPORT = true;
+ } catch (NoSuchMethodException exception) {
+ ASYNC_TELEPORT = false;
+ }
}
}
diff --git a/src/main/java/net/citizensnpcs/api/util/schedulers/SchedulerAdapter.java b/src/main/java/net/citizensnpcs/api/util/schedulers/SchedulerAdapter.java
new file mode 100644
index 00000000..75b497ee
--- /dev/null
+++ b/src/main/java/net/citizensnpcs/api/util/schedulers/SchedulerAdapter.java
@@ -0,0 +1,124 @@
+package net.citizensnpcs.api.util.schedulers;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+import org.bukkit.plugin.Plugin;
+
+/**
+ * An abstraction layer for scheduling tasks across different Minecraft server implementations,
+ * such as Folia and Spigot.
+ *
+ * Allows seamless compatibility by abstracting scheduling methods.
+ */
+public interface SchedulerAdapter {
+
+ /**
+ * Executes a task on the global server thread.
+ */
+ SchedulerTask runTask(Runnable runnable);
+
+ /**
+ * Executes a task on the global server thread after a specified delay.
+ *
+ * @param delayTicks Delay before execution, measured in server ticks (1 tick = 50ms).
+ */
+ SchedulerTask runTaskLater(Runnable runnable, long delayTicks);
+
+ /**
+ * Executes a repeating task on the global server thread.
+ *
+ * @param delayTicks Initial delay before the first execution (in ticks).
+ * @param periodTicks Period between each subsequent execution (in ticks).
+ */
+ SchedulerTask runTaskTimer(Runnable runnable, long delayTicks, long periodTicks);
+
+ /**
+ * Executes a task associated with a specific region or chunk.
+ * On Spigot, this defaults to the global thread.
+ */
+ SchedulerTask runRegionTask(Location location, Runnable runnable);
+
+ /**
+ * Executes a task associated with a specific region or chunk after a delay.
+ * On Spigot, this defaults to the global thread.
+ *
+ * @param delayTicks Delay before execution, measured in ticks.
+ */
+ SchedulerTask runRegionTaskLater(Location location, Runnable runnable, long delayTicks);
+
+ /**
+ * Executes a repeating task associated with a specific region or chunk.
+ * On Spigot, this defaults to the global thread.
+ *
+ * @param delayTicks Initial delay before the first execution (in ticks).
+ * @param periodTicks Period between each subsequent execution (in ticks).
+ */
+ SchedulerTask runRegionTaskTimer(Location location, Runnable runnable, long delayTicks, long periodTicks);
+
+ /**
+ * Executes a task associated with a specific region or chunk.
+ * On Spigot, this defaults to the global thread.
+ */
+ SchedulerTask runRegionTask(World world, int chunkX, int chunkZ, Runnable runnable);
+
+ /**
+ * Executes a task associated with a specific region or chunk after a delay.
+ * On Spigot, this defaults to the global thread.
+ *
+ * @param delayTicks Delay before execution, measured in ticks.
+ */
+ SchedulerTask runRegionTaskLater(World world, int chunkX, int chunkZ, Runnable runnable, long delayTicks);
+
+ /**
+ * Executes a repeating task associated with a specific region or chunk.
+ * On Spigot, this defaults to the global thread.
+ *
+ * @param delayTicks Initial delay before the first execution (in ticks).
+ * @param periodTicks Period between each subsequent execution (in ticks).
+ */
+ SchedulerTask runRegionTaskTimer(World world, int chunkX, int chunkZ, Runnable runnable, long delayTicks, long periodTicks);
+
+ /**
+ * Executes a task specifically linked to an entity immediately.
+ * On Spigot, this defaults to the global thread.
+ */
+ SchedulerTask runEntityTask(Entity entity, Runnable runnable);
+
+ /**
+ * Executes a task specifically linked to an entity after a delay.
+ * On Spigot, this defaults to the global thread.
+ *
+ * @param delayTicks Delay before execution, measured in ticks.
+ */
+ SchedulerTask runEntityTaskLater(Entity entity, Runnable runnable, long delayTicks);
+
+ /**
+ * Executes a repeating task specifically linked to an entity.
+ * On Spigot, this defaults to the global thread.
+ *
+ * @param delayTicks Initial delay before the first execution (in ticks).
+ * @param periodTicks Period between each subsequent execution (in ticks).
+ */
+ SchedulerTask runEntityTaskTimer(Entity entity, Runnable runnable, long delayTicks, long periodTicks);
+
+ /**
+ * Executes a task asynchronously immediately, off the main server thread.
+ */
+ SchedulerTask runTaskAsynchronously(Runnable runnable);
+
+ /**
+ * Executes an asynchronous task after a specified delay.
+ *
+ * @param delayTicks Delay before execution, measured in ticks.
+ */
+ SchedulerTask runTaskLaterAsynchronously(Runnable runnable, long delayTicks);
+
+ /**
+ * Executes an asynchronous repeating task.
+ *
+ * @param delayTicks Initial delay before the first execution (in ticks).
+ * @param periodTicks Period between each subsequent execution (in ticks).
+ */
+ SchedulerTask runTaskTimerAsynchronously(Runnable runnable, long delayTicks, long periodTicks);
+}
diff --git a/src/main/java/net/citizensnpcs/api/util/schedulers/SchedulerRunnable.java b/src/main/java/net/citizensnpcs/api/util/schedulers/SchedulerRunnable.java
new file mode 100644
index 00000000..f8648971
--- /dev/null
+++ b/src/main/java/net/citizensnpcs/api/util/schedulers/SchedulerRunnable.java
@@ -0,0 +1,142 @@
+package net.citizensnpcs.api.util.schedulers;
+
+import net.citizensnpcs.api.util.SpigotUtil;
+import net.citizensnpcs.api.util.schedulers.runnables.FoliaSchedulerRunnable;
+import net.citizensnpcs.api.util.schedulers.runnables.SpigotSchedulerRunnable;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+import org.bukkit.plugin.Plugin;
+
+public abstract class SchedulerRunnable implements Runnable {
+ private SchedulerTask task;
+
+ public abstract void run();
+
+ public boolean isCancelled() {
+ checkScheduled();
+ return task.isCancelled();
+ }
+
+ public void cancel() {
+ checkScheduled();
+ task.cancel();
+ }
+
+ public SchedulerTask getTask() {
+ checkScheduled();
+ return task;
+ }
+
+ public int getTaskId() {
+ checkScheduled();
+ return task.getOriginalTask().hashCode();
+ }
+
+ protected SchedulerTask setupTask(SchedulerTask task) {
+ this.task = task;
+ return task;
+ }
+
+ private void checkScheduled() {
+ if (task == null) throw new IllegalStateException("Task not yet scheduled");
+ }
+
+ public SchedulerTask runTask(Plugin plugin) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runTask(plugin) : new SpigotSchedulerRunnableImpl(this).runTask(plugin);
+ }
+
+ public SchedulerTask runTaskLater(Plugin plugin, long delayTicks) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runTaskLater(plugin, delayTicks) : new SpigotSchedulerRunnableImpl(this).runTaskLater(plugin, delayTicks);
+ }
+
+ public SchedulerTask runTaskTimer(Plugin plugin, long delayTicks, long periodTicks) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runTaskTimer(plugin, delayTicks, periodTicks) : new SpigotSchedulerRunnableImpl(this).runTaskTimer(plugin, delayTicks, periodTicks);
+ }
+
+ public SchedulerTask runTaskAsynchronously(Plugin plugin) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runTaskAsynchronously(plugin) : new SpigotSchedulerRunnableImpl(this).runTaskAsynchronously(plugin);
+ }
+
+ public SchedulerTask runTaskLaterAsynchronously(Plugin plugin, long delayTicks) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runTaskLaterAsynchronously(plugin, delayTicks) : new SpigotSchedulerRunnableImpl(this).runTaskLaterAsynchronously(plugin, delayTicks);
+ }
+
+ public SchedulerTask runTaskTimerAsynchronously(Plugin plugin, long delayTicks, long periodTicks) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runTaskTimerAsynchronously(plugin, delayTicks, periodTicks) : new SpigotSchedulerRunnableImpl(this).runTaskTimerAsynchronously(plugin, delayTicks, periodTicks);
+ }
+
+ public SchedulerTask runRegionTask(Plugin plugin, Location location) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runRegionTask(plugin, location) : new SpigotSchedulerRunnableImpl(this).runRegionTask(plugin, location);
+ }
+
+ public SchedulerTask runRegionTask(Plugin plugin, World world, int chunkX, int chunkZ) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runRegionTask(plugin, world, chunkX, chunkZ) : new SpigotSchedulerRunnableImpl(this).runRegionTask(plugin, world, chunkX, chunkZ);
+ }
+
+ public SchedulerTask runRegionTaskLater(Plugin plugin, Location location, long delayTicks) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runRegionTaskLater(plugin, location, delayTicks) : new SpigotSchedulerRunnableImpl(this).runRegionTaskLater(plugin, location, delayTicks);
+ }
+
+ public SchedulerTask runRegionTaskLater(Plugin plugin, World world, int chunkX, int chunkZ, long delayTicks) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runRegionTaskLater(plugin, world, chunkX, chunkZ, delayTicks) : new SpigotSchedulerRunnableImpl(this).runRegionTaskLater(plugin, world, chunkX, chunkZ, delayTicks);
+ }
+
+ public SchedulerTask runRegionTaskTimer(Plugin plugin, Location location, long delayTicks, long periodTicks) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runRegionTaskTimer(plugin, location, delayTicks, periodTicks) : new SpigotSchedulerRunnableImpl(this).runRegionTaskTimer(plugin, location, delayTicks, periodTicks);
+ }
+
+ public SchedulerTask runRegionTaskTimer(Plugin plugin, World world, int chunkX, int chunkZ, long delayTicks, long periodTicks) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runRegionTaskTimer(plugin, world, chunkX, chunkZ, delayTicks, periodTicks) : new SpigotSchedulerRunnableImpl(this).runRegionTaskTimer(plugin, world, chunkX, chunkZ, delayTicks, periodTicks);
+ }
+
+ public SchedulerTask runEntityTask(Plugin plugin, Entity entity, Runnable retired) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runEntityTask(plugin, entity, retired) : new SpigotSchedulerRunnableImpl(this).runEntityTask(plugin, entity, retired);
+ }
+
+ public SchedulerTask runEntityTaskLater(Plugin plugin, Entity entity, Runnable retired, long delayTicks) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runEntityTaskLater(plugin, entity, retired, delayTicks) : new SpigotSchedulerRunnableImpl(this).runEntityTaskLater(plugin, entity, retired, delayTicks);
+ }
+
+ public SchedulerTask runEntityTaskTimer(Plugin plugin, Entity entity, Runnable retired, long delayTicks, long periodTicks) {
+ return SpigotUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runEntityTaskTimer(plugin, entity, retired, delayTicks, periodTicks) : new SpigotSchedulerRunnableImpl(this).runEntityTaskTimer(plugin, entity, retired, delayTicks, periodTicks);
+ }
+
+ private static final class FoliaSchedulerRunnableImpl extends FoliaSchedulerRunnable {
+ private final SchedulerRunnable delegate;
+
+ private FoliaSchedulerRunnableImpl(SchedulerRunnable delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected SchedulerTask setupTask(SchedulerTask task) {
+ delegate.setupTask(task);
+ return task;
+ }
+
+ @Override
+ public void run() {
+ delegate.run();
+ }
+ }
+
+ private static final class SpigotSchedulerRunnableImpl extends SpigotSchedulerRunnable {
+ private final SchedulerRunnable delegate;
+
+ private SpigotSchedulerRunnableImpl(SchedulerRunnable delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ protected SchedulerTask setupTask(SchedulerTask task) {
+ delegate.setupTask(task);
+ return task;
+ }
+
+ @Override
+ public void run() {
+ delegate.run();
+ }
+ }
+}
diff --git a/src/main/java/net/citizensnpcs/api/util/schedulers/SchedulerTask.java b/src/main/java/net/citizensnpcs/api/util/schedulers/SchedulerTask.java
new file mode 100644
index 00000000..a2a7dd17
--- /dev/null
+++ b/src/main/java/net/citizensnpcs/api/util/schedulers/SchedulerTask.java
@@ -0,0 +1,16 @@
+package net.citizensnpcs.api.util.schedulers;
+
+import org.bukkit.plugin.Plugin;
+
+public interface SchedulerTask {
+
+ Plugin getPlugin();
+
+ boolean isCancelled();
+
+ boolean isRepeating();
+
+ void cancel();
+
+ Object getOriginalTask();
+}
\ No newline at end of file
diff --git a/src/main/java/net/citizensnpcs/api/util/schedulers/adapter/FoliaScheduler.java b/src/main/java/net/citizensnpcs/api/util/schedulers/adapter/FoliaScheduler.java
new file mode 100644
index 00000000..44ba8d21
--- /dev/null
+++ b/src/main/java/net/citizensnpcs/api/util/schedulers/adapter/FoliaScheduler.java
@@ -0,0 +1,143 @@
+package net.citizensnpcs.api.util.schedulers.adapter;
+
+import io.papermc.paper.threadedregions.scheduler.AsyncScheduler;
+import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler;
+import io.papermc.paper.threadedregions.scheduler.RegionScheduler;
+import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
+import net.citizensnpcs.api.util.schedulers.SchedulerAdapter;
+import net.citizensnpcs.api.util.schedulers.SchedulerTask;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+import org.bukkit.plugin.Plugin;
+
+import java.util.concurrent.TimeUnit;
+
+public class FoliaScheduler implements SchedulerAdapter {
+
+ private final Plugin plugin;
+ private final GlobalRegionScheduler globalScheduler;
+ private final AsyncScheduler asyncScheduler;
+ private final RegionScheduler regionScheduler;
+
+ public FoliaScheduler(Plugin plugin) {
+ this.plugin = plugin;
+ globalScheduler = Bukkit.getGlobalRegionScheduler();
+ asyncScheduler = Bukkit.getAsyncScheduler();
+ regionScheduler = Bukkit.getRegionScheduler();
+ }
+
+ private long ticksToMillis(long ticks) {
+ return ticks * 50L;
+ }
+
+ private SchedulerTask wrap(ScheduledTask task) {
+ return new FoliaSchedulerTask(task);
+ }
+
+ @Override
+ public SchedulerTask runTask(Runnable runnable) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = globalScheduler.run(plugin, t -> runnable.run());
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runTaskLater(Runnable runnable, long delayTicks) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = globalScheduler.runDelayed(plugin, t -> runnable.run(), Math.max(1, delayTicks));
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runTaskTimer(Runnable runnable, long delayTicks, long periodTicks) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = globalScheduler.runAtFixedRate(plugin, t -> runnable.run(), Math.max(1, delayTicks), Math.max(1, periodTicks));
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runRegionTask(Location location, Runnable runnable) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = regionScheduler.run(plugin, location, t -> runnable.run());
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskLater(Location location, Runnable runnable, long delayTicks) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = regionScheduler.runDelayed(plugin, location, t -> runnable.run(), Math.max(1, delayTicks));
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskTimer(Location location, Runnable runnable, long delayTicks, long periodTicks) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = regionScheduler.runAtFixedRate(plugin, location, t -> runnable.run(), Math.max(1, delayTicks), Math.max(1, periodTicks));
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runRegionTask(World world, int chunkX, int chunkZ, Runnable runnable) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = regionScheduler.run(plugin, world, chunkX, chunkZ, t -> runnable.run());
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskLater(World world, int chunkX, int chunkZ, Runnable runnable, long delayTicks) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = regionScheduler.runDelayed(plugin, world, chunkX, chunkZ, t -> runnable.run(), Math.max(1, delayTicks));
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskTimer(World world, int chunkX, int chunkZ, Runnable runnable, long delayTicks, long periodTicks) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = regionScheduler.runAtFixedRate(plugin, world, chunkX, chunkZ, t -> runnable.run(), Math.max(1, delayTicks), Math.max(1, periodTicks));
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runEntityTask(Entity entity, Runnable runnable) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = entity.getScheduler().run(plugin, t -> runnable.run(), null);
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runEntityTaskLater(Entity entity, Runnable runnable, long delayTicks) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = entity.getScheduler().runDelayed(plugin, t -> runnable.run(), null, Math.max(1, delayTicks));
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runEntityTaskTimer(Entity entity, Runnable runnable, long delayTicks, long periodTicks) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = entity.getScheduler().runAtFixedRate(plugin, t -> runnable.run(), null, Math.max(1, delayTicks), Math.max(1, periodTicks));
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runTaskAsynchronously(Runnable runnable) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = asyncScheduler.runNow(plugin, t -> runnable.run());
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runTaskLaterAsynchronously(Runnable runnable, long delayTicks) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = asyncScheduler.runDelayed(plugin, t -> runnable.run(), ticksToMillis(Math.max(1, delayTicks)), TimeUnit.MILLISECONDS);
+ return wrap(task);
+ }
+
+ @Override
+ public SchedulerTask runTaskTimerAsynchronously(Runnable runnable, long delayTicks, long periodTicks) {
+ if (!plugin.isEnabled()) return null;
+ ScheduledTask task = asyncScheduler.runAtFixedRate(plugin, t -> runnable.run(), ticksToMillis(Math.max(1, delayTicks)), ticksToMillis(Math.max(1, periodTicks)), TimeUnit.MILLISECONDS);
+ return wrap(task);
+ }
+}
diff --git a/src/main/java/net/citizensnpcs/api/util/schedulers/adapter/FoliaSchedulerTask.java b/src/main/java/net/citizensnpcs/api/util/schedulers/adapter/FoliaSchedulerTask.java
new file mode 100644
index 00000000..687cfd3b
--- /dev/null
+++ b/src/main/java/net/citizensnpcs/api/util/schedulers/adapter/FoliaSchedulerTask.java
@@ -0,0 +1,39 @@
+package net.citizensnpcs.api.util.schedulers.adapter;
+
+import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
+import net.citizensnpcs.api.util.schedulers.SchedulerTask;
+import org.bukkit.plugin.Plugin;
+
+public class FoliaSchedulerTask implements SchedulerTask {
+
+ private final ScheduledTask task;
+
+ public FoliaSchedulerTask(ScheduledTask task) {
+ this.task = task;
+ }
+
+ @Override
+ public Plugin getPlugin() {
+ return task.getOwningPlugin();
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return task.isCancelled();
+ }
+
+ @Override
+ public boolean isRepeating() {
+ return task.isRepeatingTask();
+ }
+
+ @Override
+ public void cancel() {
+ task.cancel();
+ }
+
+ @Override
+ public ScheduledTask getOriginalTask() {
+ return task;
+ }
+}
diff --git a/src/main/java/net/citizensnpcs/api/util/schedulers/adapter/SpigotScheduler.java b/src/main/java/net/citizensnpcs/api/util/schedulers/adapter/SpigotScheduler.java
new file mode 100644
index 00000000..7abd9af1
--- /dev/null
+++ b/src/main/java/net/citizensnpcs/api/util/schedulers/adapter/SpigotScheduler.java
@@ -0,0 +1,98 @@
+package net.citizensnpcs.api.util.schedulers.adapter;
+
+import net.citizensnpcs.api.util.schedulers.SchedulerAdapter;
+import net.citizensnpcs.api.util.schedulers.SchedulerTask;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.scheduler.BukkitTask;
+
+public class SpigotScheduler implements SchedulerAdapter {
+
+ private final Plugin plugin;
+
+ public SpigotScheduler(Plugin plugin) {
+ this.plugin = plugin;
+ }
+
+ private SchedulerTask wrap(BukkitTask task) {
+ return new SpigotSchedulerTask(task);
+ }
+
+ @Override
+ public SchedulerTask runTask(Runnable runnable) {
+ return wrap(Bukkit.getScheduler().runTask(plugin, runnable));
+ }
+
+ @Override
+ public SchedulerTask runTaskLater(Runnable runnable, long delayTicks) {
+ return wrap(Bukkit.getScheduler().runTaskLater(plugin, runnable, delayTicks));
+ }
+
+ @Override
+ public SchedulerTask runTaskTimer(Runnable runnable, long delayTicks, long periodTicks) {
+ return wrap(Bukkit.getScheduler().runTaskTimer(plugin, runnable, delayTicks, periodTicks));
+ }
+
+ @Override
+ public SchedulerTask runRegionTask(Location location, Runnable runnable) {
+ return runTask(runnable);
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskLater(Location location, Runnable runnable, long delayTicks) {
+ return runTaskLater(runnable, delayTicks);
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskTimer(Location location, Runnable runnable, long delayTicks, long periodTicks) {
+ return runTaskTimer(runnable, delayTicks, periodTicks);
+ }
+
+ @Override
+ public SchedulerTask runRegionTask(World world, int chunkX, int chunkZ, Runnable runnable) {
+ return runTask(runnable);
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskLater(World world, int chunkX, int chunkZ, Runnable runnable, long delayTicks) {
+ return runTaskLater(runnable, delayTicks);
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskTimer(World world, int chunkX, int chunkZ, Runnable runnable, long delayTicks, long periodTicks) {
+ return runTaskTimer(runnable, delayTicks, periodTicks);
+ }
+
+ @Override
+ public SchedulerTask runEntityTask(Entity entity, Runnable runnable) {
+ return runTask(runnable);
+ }
+
+ @Override
+ public SchedulerTask runEntityTaskLater(Entity entity, Runnable runnable, long delayTicks) {
+ return runTaskLater(runnable, delayTicks);
+ }
+
+ @Override
+ public SchedulerTask runEntityTaskTimer(Entity entity, Runnable runnable, long delayTicks, long periodTicks) {
+ return runTaskTimer(runnable, delayTicks, periodTicks);
+ }
+
+ @Override
+ public SchedulerTask runTaskAsynchronously(Runnable runnable) {
+ return wrap(Bukkit.getScheduler().runTaskAsynchronously(plugin, runnable));
+ }
+
+ @Override
+ public SchedulerTask runTaskLaterAsynchronously(Runnable runnable, long delayTicks) {
+ return wrap(Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, runnable, delayTicks));
+ }
+
+ @Override
+ public SchedulerTask runTaskTimerAsynchronously(Runnable runnable, long delayTicks, long periodTicks) {
+ return wrap(Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, runnable, delayTicks, periodTicks));
+ }
+}
diff --git a/src/main/java/net/citizensnpcs/api/util/schedulers/adapter/SpigotSchedulerTask.java b/src/main/java/net/citizensnpcs/api/util/schedulers/adapter/SpigotSchedulerTask.java
new file mode 100644
index 00000000..d53466a8
--- /dev/null
+++ b/src/main/java/net/citizensnpcs/api/util/schedulers/adapter/SpigotSchedulerTask.java
@@ -0,0 +1,40 @@
+package net.citizensnpcs.api.util.schedulers.adapter;
+
+import net.citizensnpcs.api.util.schedulers.SchedulerTask;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.scheduler.BukkitTask;
+
+public class SpigotSchedulerTask implements SchedulerTask {
+
+ private final BukkitTask task;
+
+ public SpigotSchedulerTask(BukkitTask task) {
+ this.task = task;
+ }
+
+ @Override
+ public Plugin getPlugin() {
+ return task.getOwner();
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return task.isCancelled();
+ }
+
+ @Override
+ public boolean isRepeating() {
+ return false;
+ }
+
+ @Override
+ public void cancel() {
+ task.cancel();
+ }
+
+ @Override
+ public BukkitTask getOriginalTask() {
+ return task;
+ }
+}
+
diff --git a/src/main/java/net/citizensnpcs/api/util/schedulers/runnables/FoliaSchedulerRunnable.java b/src/main/java/net/citizensnpcs/api/util/schedulers/runnables/FoliaSchedulerRunnable.java
new file mode 100644
index 00000000..5e5c56de
--- /dev/null
+++ b/src/main/java/net/citizensnpcs/api/util/schedulers/runnables/FoliaSchedulerRunnable.java
@@ -0,0 +1,91 @@
+package net.citizensnpcs.api.util.schedulers.runnables;
+
+import net.citizensnpcs.api.util.schedulers.SchedulerRunnable;
+import net.citizensnpcs.api.util.schedulers.SchedulerTask;
+import net.citizensnpcs.api.util.schedulers.adapter.FoliaSchedulerTask;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+import org.bukkit.plugin.Plugin;
+
+import java.util.concurrent.TimeUnit;
+
+public abstract class FoliaSchedulerRunnable extends SchedulerRunnable {
+
+ @Override
+ public SchedulerTask runTask(Plugin plugin) {
+ return setupTask(new FoliaSchedulerTask(Bukkit.getGlobalRegionScheduler().run(plugin, t -> run())));
+ }
+
+ @Override
+ public SchedulerTask runTaskLater(Plugin plugin, long delayTicks) {
+ return setupTask(new FoliaSchedulerTask(Bukkit.getGlobalRegionScheduler().runDelayed(plugin, t -> run(), Math.max(1, delayTicks))));
+ }
+
+ @Override
+ public SchedulerTask runTaskTimer(Plugin plugin, long delayTicks, long periodTicks) {
+ return setupTask(new FoliaSchedulerTask(Bukkit.getGlobalRegionScheduler().runAtFixedRate(plugin, t -> run(), Math.max(1, delayTicks), Math.max(1, periodTicks))));
+ }
+
+ @Override
+ public SchedulerTask runTaskAsynchronously(Plugin plugin) {
+ return setupTask(new FoliaSchedulerTask(Bukkit.getAsyncScheduler().runNow(plugin, t -> run())));
+ }
+
+ @Override
+ public SchedulerTask runTaskLaterAsynchronously(Plugin plugin, long delayTicks) {
+ return setupTask(new FoliaSchedulerTask(Bukkit.getAsyncScheduler().runDelayed(plugin, t -> run(), Math.max(1, delayTicks) * 50L, TimeUnit.MILLISECONDS)));
+ }
+
+ @Override
+ public SchedulerTask runTaskTimerAsynchronously(Plugin plugin, long delayTicks, long periodTicks) {
+ return setupTask(new FoliaSchedulerTask(Bukkit.getAsyncScheduler().runAtFixedRate(plugin, t -> run(), Math.max(1, delayTicks) * 50L, Math.max(1, periodTicks) * 50L, TimeUnit.MILLISECONDS)));
+ }
+
+ @Override
+ public SchedulerTask runRegionTask(Plugin plugin, Location location) {
+ return setupTask(new FoliaSchedulerTask(Bukkit.getRegionScheduler().run(plugin, location, t -> run())));
+ }
+
+
+ @Override
+ public SchedulerTask runRegionTask(Plugin plugin, World world, int chunkX, int chunkZ) {
+ return setupTask(new FoliaSchedulerTask(Bukkit.getRegionScheduler().run(plugin, world, chunkX, chunkZ, t -> run())));
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskLater(Plugin plugin, Location location, long delayTicks) {
+ return setupTask(new FoliaSchedulerTask(Bukkit.getRegionScheduler().runDelayed(plugin, location, t -> run(), Math.max(1, delayTicks))));
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskLater(Plugin plugin, World world, int chunkX, int chunkZ, long delayTicks) {
+ return setupTask(new FoliaSchedulerTask(Bukkit.getRegionScheduler().runDelayed(plugin, world, chunkX, chunkZ, t -> run(), Math.max(1, delayTicks))));
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskTimer(Plugin plugin, World world, int chunkX, int chunkZ, long delayTicks, long periodTicks) {
+ return setupTask(new FoliaSchedulerTask(Bukkit.getRegionScheduler().runAtFixedRate(plugin, world, chunkX, chunkZ, t -> run(), Math.max(1, delayTicks), Math.max(1, periodTicks))));
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskTimer(Plugin plugin, Location location, long delayTicks, long periodTicks) {
+ return setupTask(new FoliaSchedulerTask(Bukkit.getRegionScheduler().runAtFixedRate(plugin, location, t -> run(), Math.max(1, delayTicks), Math.max(1, periodTicks))));
+ }
+
+ @Override
+ public SchedulerTask runEntityTask(Plugin plugin, Entity entity, Runnable retired) {
+ return setupTask(new FoliaSchedulerTask(entity.getScheduler().run(plugin, t -> run(), retired)));
+ }
+
+ @Override
+ public SchedulerTask runEntityTaskLater(Plugin plugin, Entity entity, Runnable retired, long delayTicks) {
+ return setupTask(new FoliaSchedulerTask(entity.getScheduler().runDelayed(plugin, t -> run(), retired, Math.max(1, delayTicks))));
+ }
+
+ @Override
+ public SchedulerTask runEntityTaskTimer(Plugin plugin, Entity entity, Runnable retired, long delayTicks, long periodTicks) {
+ return setupTask(new FoliaSchedulerTask(entity.getScheduler().runAtFixedRate(plugin, t -> run(), retired, Math.max(1, delayTicks), Math.max(1, periodTicks))));
+ }
+}
diff --git a/src/main/java/net/citizensnpcs/api/util/schedulers/runnables/SpigotSchedulerRunnable.java b/src/main/java/net/citizensnpcs/api/util/schedulers/runnables/SpigotSchedulerRunnable.java
new file mode 100644
index 00000000..1c4867da
--- /dev/null
+++ b/src/main/java/net/citizensnpcs/api/util/schedulers/runnables/SpigotSchedulerRunnable.java
@@ -0,0 +1,87 @@
+package net.citizensnpcs.api.util.schedulers.runnables;
+
+import net.citizensnpcs.api.util.schedulers.SchedulerRunnable;
+import net.citizensnpcs.api.util.schedulers.SchedulerTask;
+import net.citizensnpcs.api.util.schedulers.adapter.SpigotSchedulerTask;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.entity.Entity;
+import org.bukkit.plugin.Plugin;
+
+public abstract class SpigotSchedulerRunnable extends SchedulerRunnable {
+ @Override
+ public SchedulerTask runTask(Plugin plugin) {
+ return setupTask(new SpigotSchedulerTask(Bukkit.getScheduler().runTask(plugin, this)));
+ }
+
+ @Override
+ public SchedulerTask runTaskLater(Plugin plugin, long delayTicks) {
+ return setupTask(new SpigotSchedulerTask(Bukkit.getScheduler().runTaskLater(plugin, this, delayTicks)));
+ }
+
+ @Override
+ public SchedulerTask runTaskTimer(Plugin plugin, long delayTicks, long periodTicks) {
+ return setupTask(new SpigotSchedulerTask(Bukkit.getScheduler().runTaskTimer(plugin, this, delayTicks, periodTicks)));
+ }
+
+ @Override
+ public SchedulerTask runTaskAsynchronously(Plugin plugin) {
+ return setupTask(new SpigotSchedulerTask(Bukkit.getScheduler().runTaskAsynchronously(plugin, this)));
+ }
+
+ @Override
+ public SchedulerTask runTaskLaterAsynchronously(Plugin plugin, long delayTicks) {
+ return setupTask(new SpigotSchedulerTask(Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, this, delayTicks)));
+ }
+
+ @Override
+ public SchedulerTask runTaskTimerAsynchronously(Plugin plugin, long delayTicks, long periodTicks) {
+ return setupTask(new SpigotSchedulerTask(Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, this, delayTicks, periodTicks)));
+ }
+
+ @Override
+ public SchedulerTask runRegionTask(Plugin plugin, Location location) {
+ return runTask(plugin);
+ }
+
+ @Override
+ public SchedulerTask runRegionTask(Plugin plugin, World world, int chunkX, int chunkZ) {
+ return runTask(plugin);
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskLater(Plugin plugin, Location location, long delayTicks) {
+ return runTaskLater(plugin, delayTicks);
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskTimer(Plugin plugin, Location location, long delayTicks, long periodTicks) {
+ return runTaskTimer(plugin, delayTicks, periodTicks);
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskLater(Plugin plugin, World world, int chunkX, int chunkZ, long delayTicks) {
+ return runTaskLater(plugin, delayTicks);
+ }
+
+ @Override
+ public SchedulerTask runRegionTaskTimer(Plugin plugin, World world, int chunkX, int chunkZ, long delayTicks, long periodTicks) {
+ return runTaskTimer(plugin, delayTicks, periodTicks);
+ }
+
+ @Override
+ public SchedulerTask runEntityTask(Plugin plugin, Entity entity, Runnable retired) {
+ return runTask(plugin);
+ }
+
+ @Override
+ public SchedulerTask runEntityTaskLater(Plugin plugin, Entity entity, Runnable retired, long delayTicks) {
+ return runTaskLater(plugin, delayTicks);
+ }
+
+ @Override
+ public SchedulerTask runEntityTaskTimer(Plugin plugin, Entity entity, Runnable retired, long delayTicks, long periodTicks) {
+ return runTaskTimer(plugin, delayTicks, periodTicks);
+ }
+}