From ff1c5738f74925af9ebb0d2740a668d51cf6d3c7 Mon Sep 17 00:00:00 2001 From: Bierque Euphyllia Date: Tue, 24 Mar 2026 12:27:20 +0100 Subject: [PATCH 1/7] Implement Folia and Bukkit Scheduler --- .../com/earth2me/essentials/Essentials.java | 19 +- .../com/earth2me/essentials/IEssentials.java | 3 + .../essentials/utils/VersionUtil.java | 10 + .../utils/schedulers/SchedulerAdapter.java | 192 ++++++++++++++++++ .../utils/schedulers/SchedulerRunnable.java | 175 ++++++++++++++++ .../utils/schedulers/SchedulerTask.java | 15 ++ .../schedulers/adapter/FoliaScheduler.java | 189 +++++++++++++++++ .../adapter/FoliaSchedulerTask.java | 39 ++++ .../schedulers/adapter/SpigotScheduler.java | 119 +++++++++++ .../adapter/SpigotSchedulerTask.java | 38 ++++ .../runnables/FoliaSchedulerRunnable.java | 103 ++++++++++ .../runnables/SpigotSchedulerRunnable.java | 92 +++++++++ 12 files changed, 992 insertions(+), 2 deletions(-) create mode 100644 Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/SchedulerAdapter.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/SchedulerRunnable.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/SchedulerTask.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/FoliaScheduler.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/FoliaSchedulerTask.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/SpigotScheduler.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/SpigotSchedulerTask.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/runnables/FoliaSchedulerRunnable.java create mode 100644 Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/runnables/SpigotSchedulerRunnable.java diff --git a/Essentials/src/main/java/com/earth2me/essentials/Essentials.java b/Essentials/src/main/java/com/earth2me/essentials/Essentials.java index 2241b53d5d9..0583e619298 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Essentials.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Essentials.java @@ -47,6 +47,8 @@ import com.earth2me.essentials.userstorage.ModernUserMap; import com.earth2me.essentials.utils.FormatUtil; import com.earth2me.essentials.utils.VersionUtil; +import com.earth2me.essentials.utils.schedulers.SchedulerAdapter; +import com.earth2me.essentials.utils.schedulers.adapter.FoliaScheduler; import io.papermc.lib.PaperLib; import net.ess3.api.Economy; import com.earth2me.essentials.config.EssentialsConfiguration; @@ -154,6 +156,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials { private static final Logger BUKKIT_LOGGER = Logger.getLogger("Essentials"); private static Logger LOGGER = null; public static boolean TESTING = false; + private static SchedulerAdapter schedulerAdapter; private final transient TNTExplodeListener tntListener = new TNTExplodeListener(); private final transient Set vanishedPlayers = new LinkedHashSet<>(); private final transient Map commandMap = new HashMap<>(); @@ -209,6 +212,13 @@ public void onEnable() { if (BUKKIT_LOGGER != super.getLogger()) { BUKKIT_LOGGER.setParent(super.getLogger()); } + if (schedulerAdapter == null) { + if (VersionUtil.isFoliaServer()) { + schedulerAdapter = new FoliaScheduler(this); + } else { + schedulerAdapter = new FoliaScheduler(this); + } + } LOGGER = EssentialsLogger.getLoggerProvider(this); EssentialsLogger.updatePluginLogger(this); @@ -428,7 +438,7 @@ public void onEnable() { alternativeCommandsHandler = new AlternativeCommandsHandler(this); timer = new EssentialsTimer(this); - scheduleSyncRepeatingTask(timer, 1000, 50); + getSchedulerAdapter().runTaskTimer(timer, 1000, 50); Economy.setEss(this); execTimer.mark("RegHandler"); @@ -439,7 +449,7 @@ public void onEnable() { if (!TESTING) { updateChecker = new UpdateChecker(this); - runTaskAsynchronously(() -> { + getSchedulerAdapter().runTaskAsynchronously(() -> { getLogger().log(Level.INFO, getAdventureFacet().miniToLegacy(tlLiteral("versionFetching"))); for (final ComponentHolder component : updateChecker.getVersionMessages(false, true, new CommandSource(this, Bukkit.getConsoleSender()))) { getLogger().log(getSettings().isUpdateCheckEnabled() ? Level.WARNING : Level.INFO, getAdventureFacet().adventureToLegacy(component)); @@ -1255,6 +1265,11 @@ public int scheduleSyncRepeatingTask(final Runnable run, final long delay, final return this.getScheduler().scheduleSyncRepeatingTask(this, run, delay, period); } + @Override + public SchedulerAdapter getSchedulerAdapter() { + return schedulerAdapter; + } + @Override public PermissionsHandler getPermissionsHandler() { return permissionsHandler; diff --git a/Essentials/src/main/java/com/earth2me/essentials/IEssentials.java b/Essentials/src/main/java/com/earth2me/essentials/IEssentials.java index 6e69b938bdb..5fac125d225 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/IEssentials.java +++ b/Essentials/src/main/java/com/earth2me/essentials/IEssentials.java @@ -9,6 +9,7 @@ import com.earth2me.essentials.perm.PermissionsHandler; import com.earth2me.essentials.updatecheck.UpdateChecker; import com.earth2me.essentials.userstorage.IUserMap; +import com.earth2me.essentials.utils.schedulers.SchedulerAdapter; import net.ess3.provider.Provider; import net.essentialsx.api.v2.services.BalanceTop; import net.essentialsx.api.v2.services.mail.MailService; @@ -108,6 +109,8 @@ public interface IEssentials extends Plugin { int scheduleSyncRepeatingTask(Runnable run, long delay, long period); + SchedulerAdapter getSchedulerAdapter(); + PermissionsHandler getPermissionsHandler(); AlternativeCommandsHandler getAlternativeCommandsHandler(); diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java index 47be345e824..99f46347d6b 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/VersionUtil.java @@ -74,6 +74,7 @@ public final class VersionUtil { private static final Map unsupportedServerClasses; private static final String PFX = make("8(;4>`"); + private static boolean FOLIA_SERVER = false; static { final ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); @@ -110,6 +111,11 @@ public final class VersionUtil { } unsupportedServerClasses = builder.build(); + + try { + FOLIA_SERVER = Class.forName("io.papermc.paper.threadedregions.RegionizedServer") != null; + } catch (ClassNotFoundException ignored) { + } } private static BukkitVersion serverVersion = null; @@ -124,6 +130,10 @@ public static boolean isPaper() { return PaperLib.isPaper(); } + public static boolean isFoliaServer() { + return FOLIA_SERVER; + } + public static BukkitVersion getServerBukkitVersion() { if (serverVersion == null) { serverVersion = BukkitVersion.fromString(Bukkit.getServer().getBukkitVersion()); diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/SchedulerAdapter.java b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/SchedulerAdapter.java new file mode 100644 index 00000000000..6b43b39b7d8 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/SchedulerAdapter.java @@ -0,0 +1,192 @@ +package com.earth2me.essentials.utils.schedulers; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; + +/** + * An abstraction layer for scheduling tasks across different Minecraft server implementations, such as Folia and + * Spigot. + */ +public interface SchedulerAdapter { + default void checkedRunEntityTask(Entity entity, Runnable runnable) { + if (isOnOwnerThread(entity)) { + runnable.run(); + } else { + runEntityTask(entity, runnable); + } + } + + default void checkedRunRegionTask(Location location, Runnable runnable) { + if (isOnOwnerThread(location)) { + runnable.run(); + } else { + runRegionTask(location, runnable); + } + } + + /** + * Returns true if the current thread is the correct owner thread for safely accessing the target. + *

+ * Spigot: true if running on the main server thread. + *

+ *

+ * Folia: true if the current thread owns the target's region (isOwnedByCurrentRegion). + *

+ * + * @param block the target block + * @return {@code true} if the current thread is the owner thread for the block; {@code false} otherwise + */ + boolean isOnOwnerThread(Block block); + + /** + * Returns true if the current thread is the correct owner thread for safely accessing the target. + *

+ * Spigot: true if running on the main server thread. + *

+ *

+ * Folia: true if the current thread owns the target's region (isOwnedByCurrentRegion). + *

+ * + * @param entity the target entity + * @return {@code true} if the current thread is the owner thread for the entity; {@code false} otherwise + */ + boolean isOnOwnerThread(Entity entity); + + /** + * Returns true if the current thread is the correct owner thread for safely accessing the target. + *

+ * Spigot: true if running on the main server thread. + *

+ *

+ * Folia: true if the current thread owns the target's region (isOwnedByCurrentRegion). + *

+ * + * @param location the target location + * @return {@code true} if the current thread is the owner thread for the location; {@code false} otherwise + */ + boolean isOnOwnerThread(Location location); + + /** + * Returns true if the current thread is the correct owner thread for safely accessing the target. + *

+ * Spigot: true if running on the main server thread. + *

+ *

+ * Folia: true if the current thread owns the target's region (isOwnedByCurrentRegion). + *

+ * + * @param world the target world + * @param chunkX the target chunk X coordinate + * @param chunkZ the target chunk Z coordinate + * @return {@code true} if the current thread is the owner thread for the chunk; {@code false} otherwise + */ + boolean isOnOwnerThread(World world, int chunkX, int chunkZ); + + /** + * 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 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. 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(Location location, Runnable runnable, long delayTicks); + + /** + * 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(Location location, Runnable runnable, long delayTicks, long periodTicks); + + /** + * 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 on the global server thread. + */ + SchedulerTask runTask(Runnable runnable); + + /** + * Executes a task asynchronously immediately, off the main server thread. + */ + SchedulerTask runTaskAsynchronously(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 an asynchronous task after a specified delay. + * + * @param delayTicks Delay before execution, measured in ticks. + */ + SchedulerTask runTaskLaterAsynchronously(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 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); + +} \ No newline at end of file diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/SchedulerRunnable.java b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/SchedulerRunnable.java new file mode 100644 index 00000000000..b9fb798f2fc --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/SchedulerRunnable.java @@ -0,0 +1,175 @@ +package com.earth2me.essentials.utils.schedulers; + +import com.earth2me.essentials.utils.VersionUtil; +import com.earth2me.essentials.utils.schedulers.runnables.FoliaSchedulerRunnable; +import com.earth2me.essentials.utils.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 void cancel() { + checkScheduled(); + task.cancel(); + } + + private void checkScheduled() { + if (task == null) + throw new IllegalStateException("Task not yet scheduled"); + } + + public SchedulerTask getTask() { + checkScheduled(); + return task; + } + + public int getTaskId() { + checkScheduled(); + return task.getOriginalTask().hashCode(); + } + + public boolean isCancelled() { + checkScheduled(); + return task.isCancelled(); + } + + @Override + public abstract void run(); + + public SchedulerTask runEntityTask(Plugin plugin, Entity entity, Runnable retired) { + return VersionUtil.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 VersionUtil.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 VersionUtil.isFoliaServer() + ? new FoliaSchedulerRunnableImpl(this).runEntityTaskTimer(plugin, entity, retired, delayTicks, + periodTicks) + : new SpigotSchedulerRunnableImpl(this).runEntityTaskTimer(plugin, entity, retired, delayTicks, + periodTicks); + } + + public SchedulerTask runRegionTask(Plugin plugin, Location location) { + return VersionUtil.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 VersionUtil.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 VersionUtil.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 VersionUtil.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 VersionUtil.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 VersionUtil.isFoliaServer() + ? new FoliaSchedulerRunnableImpl(this).runRegionTaskTimer(plugin, world, chunkX, chunkZ, delayTicks, + periodTicks) + : new SpigotSchedulerRunnableImpl(this).runRegionTaskTimer(plugin, world, chunkX, chunkZ, delayTicks, + periodTicks); + } + + public SchedulerTask runTask(Plugin plugin) { + return VersionUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runTask(plugin) + : new SpigotSchedulerRunnableImpl(this).runTask(plugin); + } + + public SchedulerTask runTaskAsynchronously(Plugin plugin) { + return VersionUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runTaskAsynchronously(plugin) + : new SpigotSchedulerRunnableImpl(this).runTaskAsynchronously(plugin); + } + + public SchedulerTask runTaskLater(Plugin plugin, long delayTicks) { + return VersionUtil.isFoliaServer() ? new FoliaSchedulerRunnableImpl(this).runTaskLater(plugin, delayTicks) + : new SpigotSchedulerRunnableImpl(this).runTaskLater(plugin, delayTicks); + } + + public SchedulerTask runTaskLaterAsynchronously(Plugin plugin, long delayTicks) { + return VersionUtil.isFoliaServer() + ? new FoliaSchedulerRunnableImpl(this).runTaskLaterAsynchronously(plugin, delayTicks) + : new SpigotSchedulerRunnableImpl(this).runTaskLaterAsynchronously(plugin, delayTicks); + } + + public SchedulerTask runTaskTimer(Plugin plugin, long delayTicks, long periodTicks) { + return VersionUtil.isFoliaServer() + ? new FoliaSchedulerRunnableImpl(this).runTaskTimer(plugin, delayTicks, periodTicks) + : new SpigotSchedulerRunnableImpl(this).runTaskTimer(plugin, delayTicks, periodTicks); + } + + public SchedulerTask runTaskTimerAsynchronously(Plugin plugin, long delayTicks, long periodTicks) { + return VersionUtil.isFoliaServer() + ? new FoliaSchedulerRunnableImpl(this).runTaskTimerAsynchronously(plugin, delayTicks, periodTicks) + : new SpigotSchedulerRunnableImpl(this).runTaskTimerAsynchronously(plugin, delayTicks, periodTicks); + } + + protected SchedulerTask setupTask(SchedulerTask task) { + this.task = task; + return task; + } + + private static final class FoliaSchedulerRunnableImpl extends FoliaSchedulerRunnable { + private final SchedulerRunnable delegate; + + private FoliaSchedulerRunnableImpl(SchedulerRunnable delegate) { + this.delegate = delegate; + } + + @Override + public void run() { + delegate.run(); + } + + @Override + protected SchedulerTask setupTask(SchedulerTask task) { + delegate.setupTask(task); + return task; + } + } + + private static final class SpigotSchedulerRunnableImpl extends SpigotSchedulerRunnable { + private final SchedulerRunnable delegate; + + private SpigotSchedulerRunnableImpl(SchedulerRunnable delegate) { + this.delegate = delegate; + } + + @Override + public void run() { + delegate.run(); + } + + @Override + protected SchedulerTask setupTask(SchedulerTask task) { + delegate.setupTask(task); + return task; + } + } +} \ No newline at end of file diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/SchedulerTask.java b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/SchedulerTask.java new file mode 100644 index 00000000000..71b6f04855a --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/SchedulerTask.java @@ -0,0 +1,15 @@ +package com.earth2me.essentials.utils.schedulers; + +import org.bukkit.plugin.Plugin; + +public interface SchedulerTask { + void cancel(); + + Object getOriginalTask(); + + Plugin getPlugin(); + + boolean isCancelled(); + + boolean isRepeating(); +} \ No newline at end of file diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/FoliaScheduler.java b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/FoliaScheduler.java new file mode 100644 index 00000000000..4e855e197f9 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/FoliaScheduler.java @@ -0,0 +1,189 @@ +package com.earth2me.essentials.utils.schedulers.adapter; + +import com.earth2me.essentials.utils.schedulers.SchedulerAdapter; +import com.earth2me.essentials.utils.schedulers.SchedulerTask; +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 org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; + +import java.util.concurrent.TimeUnit; + + +public class FoliaScheduler implements SchedulerAdapter { + + private final AsyncScheduler asyncScheduler; + private final GlobalRegionScheduler globalScheduler; + private final Plugin plugin; + private final RegionScheduler regionScheduler; + + public FoliaScheduler(Plugin plugin) { + this.plugin = plugin; + globalScheduler = Bukkit.getGlobalRegionScheduler(); + asyncScheduler = Bukkit.getAsyncScheduler(); + regionScheduler = Bukkit.getRegionScheduler(); + } + + @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 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 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(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 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(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 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 runTask(Runnable runnable) { + if (!plugin.isEnabled()) + return null; + ScheduledTask task = globalScheduler.run(plugin, t -> runnable.run()); + 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 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 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 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 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); + } + + @Override + public boolean isOnOwnerThread(Entity entity) { + return Bukkit.isOwnedByCurrentRegion(entity); + } + + @Override + public boolean isOnOwnerThread(Location location) { + return Bukkit.isOwnedByCurrentRegion(location); + } + + @Override + public boolean isOnOwnerThread(World world, int chunkX, int chunkZ) { + return Bukkit.isOwnedByCurrentRegion(world, chunkX, chunkZ); + } + + @Override + public boolean isOnOwnerThread(Block block) { + return Bukkit.isOwnedByCurrentRegion(block); + } + + private long ticksToMillis(long ticks) { + return ticks * 50L; + } + + private SchedulerTask wrap(ScheduledTask task) { + return new FoliaSchedulerTask(task); + } +} \ No newline at end of file diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/FoliaSchedulerTask.java b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/FoliaSchedulerTask.java new file mode 100644 index 00000000000..daf6ad6e048 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/FoliaSchedulerTask.java @@ -0,0 +1,39 @@ +package com.earth2me.essentials.utils.schedulers.adapter; + +import com.earth2me.essentials.utils.schedulers.SchedulerTask; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import org.bukkit.plugin.Plugin; + + +public class FoliaSchedulerTask implements SchedulerTask { + private final ScheduledTask task; + + public FoliaSchedulerTask(ScheduledTask task) { + this.task = task; + } + + @Override + public void cancel() { + task.cancel(); + } + + @Override + public ScheduledTask getOriginalTask() { + return task; + } + + @Override + public Plugin getPlugin() { + return task.getOwningPlugin(); + } + + @Override + public boolean isCancelled() { + return task.isCancelled(); + } + + @Override + public boolean isRepeating() { + return task.isRepeatingTask(); + } +} \ No newline at end of file diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/SpigotScheduler.java b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/SpigotScheduler.java new file mode 100644 index 00000000000..f22185f33a9 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/SpigotScheduler.java @@ -0,0 +1,119 @@ +package com.earth2me.essentials.utils.schedulers.adapter; + +import com.earth2me.essentials.utils.schedulers.SchedulerAdapter; +import com.earth2me.essentials.utils.schedulers.SchedulerTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +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; + } + + @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 runRegionTask(Location location, Runnable runnable) { + return runTask(runnable); + } + + @Override + public SchedulerTask runRegionTask(World world, int chunkX, int chunkZ, Runnable runnable) { + return runTask(runnable); + } + + @Override + public SchedulerTask runRegionTaskLater(Location location, Runnable runnable, long delayTicks) { + return runTaskLater(runnable, delayTicks); + } + + @Override + public SchedulerTask runRegionTaskLater(World world, int chunkX, int chunkZ, 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 runRegionTaskTimer(World world, int chunkX, int chunkZ, Runnable runnable, long delayTicks, + long periodTicks) { + return runTaskTimer(runnable, delayTicks, periodTicks); + } + + @Override + public SchedulerTask runTask(Runnable runnable) { + return wrap(Bukkit.getScheduler().runTask(plugin, runnable)); + } + + @Override + public SchedulerTask runTaskAsynchronously(Runnable runnable) { + return wrap(Bukkit.getScheduler().runTaskAsynchronously(plugin, runnable)); + } + + @Override + public SchedulerTask runTaskLater(Runnable runnable, long delayTicks) { + return wrap(Bukkit.getScheduler().runTaskLater(plugin, runnable, delayTicks)); + } + + @Override + public SchedulerTask runTaskLaterAsynchronously(Runnable runnable, long delayTicks) { + return wrap(Bukkit.getScheduler().runTaskLaterAsynchronously(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 runTaskTimerAsynchronously(Runnable runnable, long delayTicks, long periodTicks) { + return wrap(Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, runnable, delayTicks, periodTicks)); + } + + @Override + public boolean isOnOwnerThread(Entity entity) { + return Bukkit.isPrimaryThread(); + } + + @Override + public boolean isOnOwnerThread(Location location) { + return Bukkit.isPrimaryThread(); + } + + @Override + public boolean isOnOwnerThread(World world, int chunkX, int chunkZ) { + return Bukkit.isPrimaryThread(); + } + + @Override + public boolean isOnOwnerThread(Block block) { + return Bukkit.isPrimaryThread(); + } + + private SchedulerTask wrap(BukkitTask task) { + return new SpigotSchedulerTask(task); + } +} \ No newline at end of file diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/SpigotSchedulerTask.java b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/SpigotSchedulerTask.java new file mode 100644 index 00000000000..e3d21c0ca64 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/adapter/SpigotSchedulerTask.java @@ -0,0 +1,38 @@ +package com.earth2me.essentials.utils.schedulers.adapter; + +import com.earth2me.essentials.utils.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 void cancel() { + task.cancel(); + } + + @Override + public BukkitTask getOriginalTask() { + return task; + } + + @Override + public Plugin getPlugin() { + return task.getOwner(); + } + + @Override + public boolean isCancelled() { + return task.isCancelled(); + } + + @Override + public boolean isRepeating() { + return false; + } +} \ No newline at end of file diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/runnables/FoliaSchedulerRunnable.java b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/runnables/FoliaSchedulerRunnable.java new file mode 100644 index 00000000000..98c3358d344 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/runnables/FoliaSchedulerRunnable.java @@ -0,0 +1,103 @@ +package com.earth2me.essentials.utils.schedulers.runnables; + +import com.earth2me.essentials.utils.schedulers.SchedulerRunnable; +import com.earth2me.essentials.utils.schedulers.SchedulerTask; +import com.earth2me.essentials.utils.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 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)))); + } + + @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, 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 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 runTask(Plugin plugin) { + return setupTask(new FoliaSchedulerTask(Bukkit.getGlobalRegionScheduler().run(plugin, t -> run()))); + } + + @Override + public SchedulerTask runTaskAsynchronously(Plugin plugin) { + return setupTask(new FoliaSchedulerTask(Bukkit.getAsyncScheduler().runNow(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 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 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 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))); + } +} \ No newline at end of file diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/runnables/SpigotSchedulerRunnable.java b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/runnables/SpigotSchedulerRunnable.java new file mode 100644 index 00000000000..5349131ba30 --- /dev/null +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/schedulers/runnables/SpigotSchedulerRunnable.java @@ -0,0 +1,92 @@ +package com.earth2me.essentials.utils.schedulers.runnables; + +import com.earth2me.essentials.utils.schedulers.SchedulerRunnable; +import com.earth2me.essentials.utils.schedulers.SchedulerTask; +import com.earth2me.essentials.utils.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 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); + } + + @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 runRegionTaskLater(Plugin plugin, World world, int chunkX, int chunkZ, 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 runRegionTaskTimer(Plugin plugin, World world, int chunkX, int chunkZ, long delayTicks, + long periodTicks) { + return runTaskTimer(plugin, delayTicks, periodTicks); + } + + @Override + public SchedulerTask runTask(Plugin plugin) { + return setupTask(new SpigotSchedulerTask(Bukkit.getScheduler().runTask(plugin, this))); + } + + @Override + public SchedulerTask runTaskAsynchronously(Plugin plugin) { + return setupTask(new SpigotSchedulerTask(Bukkit.getScheduler().runTaskAsynchronously(plugin, this))); + } + + @Override + public SchedulerTask runTaskLater(Plugin plugin, long delayTicks) { + return setupTask(new SpigotSchedulerTask(Bukkit.getScheduler().runTaskLater(plugin, this, delayTicks))); + } + + @Override + public SchedulerTask runTaskLaterAsynchronously(Plugin plugin, long delayTicks) { + return setupTask( + new SpigotSchedulerTask(Bukkit.getScheduler().runTaskLaterAsynchronously(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 runTaskTimerAsynchronously(Plugin plugin, long delayTicks, long periodTicks) { + return setupTask(new SpigotSchedulerTask( + Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, this, delayTicks, periodTicks))); + } +} \ No newline at end of file From 98d706ef448e5ec2c4b43ba39c379f431bbc0571 Mon Sep 17 00:00:00 2001 From: Bierque Euphyllia Date: Tue, 24 Mar 2026 13:03:19 +0100 Subject: [PATCH 2/7] Replace scheduler Essentials Module --- .../earth2me/essentials/AsyncTeleport.java | 4 +-- .../essentials/AsyncTimedTeleport.java | 13 +++++---- .../java/com/earth2me/essentials/Backup.java | 19 ++++++------ .../earth2me/essentials/BalanceTopImpl.java | 2 +- .../essentials/EssentialsBlockListener.java | 2 +- .../essentials/EssentialsEntityListener.java | 2 +- .../essentials/EssentialsPlayerListener.java | 29 ++++++++++--------- .../java/com/earth2me/essentials/Kit.java | 8 +++-- .../earth2me/essentials/RandomTeleport.java | 2 +- .../com/earth2me/essentials/Settings.java | 6 ++-- .../java/com/earth2me/essentials/User.java | 4 +-- .../commands/Commandbalancetop.java | 4 +-- .../essentials/commands/Commandbeezooka.java | 2 +- .../essentials/commands/Commandcreatekit.java | 2 +- .../commands/Commandkittycannon.java | 2 +- .../essentials/commands/Commandmail.java | 12 ++++---- .../essentials/commands/Commandseen.java | 4 +-- .../essentials/commands/Commandskull.java | 4 +-- .../essentials/commands/Commandsudo.java | 2 +- .../commands/essentials/CleanupCommand.java | 2 +- .../commands/essentials/DumpCommand.java | 2 +- .../commands/essentials/HomesCommand.java | 4 +-- .../commands/essentials/UsermapCommand.java | 2 +- .../commands/essentials/VersionCommand.java | 2 +- .../essentials/economy/EconomyLayers.java | 2 +- .../essentials/updatecheck/UpdateChecker.java | 4 +-- 26 files changed, 73 insertions(+), 68 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/AsyncTeleport.java b/Essentials/src/main/java/com/earth2me/essentials/AsyncTeleport.java index b5e687f2557..58f18eac07d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/AsyncTeleport.java +++ b/Essentials/src/main/java/com/earth2me/essentials/AsyncTeleport.java @@ -169,8 +169,8 @@ protected void nowAsync(final IUser teleportee, final ITarget target, final Tele } try { - runOnMain(() -> teleportee.getBase().eject()); //EntityDismountEvent requires a sync context. - } catch (final ExecutionException | InterruptedException e) { + ess.getSchedulerAdapter().runEntityTask(teleportee.getBase(), () -> teleportee.getBase().eject()); //EntityDismountEvent requires a sync context. + } catch (final Exception e) { future.completeExceptionally(e); return; } diff --git a/Essentials/src/main/java/com/earth2me/essentials/AsyncTimedTeleport.java b/Essentials/src/main/java/com/earth2me/essentials/AsyncTimedTeleport.java index d1c37423c06..b7a6f925cac 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/AsyncTimedTeleport.java +++ b/Essentials/src/main/java/com/earth2me/essentials/AsyncTimedTeleport.java @@ -3,6 +3,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; +import com.earth2me.essentials.utils.schedulers.SchedulerTask; import org.bukkit.Location; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; @@ -31,7 +32,7 @@ public class AsyncTimedTeleport implements Runnable { private final boolean timer_canMove; private final Trade timer_chargeFor; private final TeleportCause timer_cause; - private int timer_task; + private SchedulerTask timer_task; private double timer_health; AsyncTimedTeleport(final IUser user, final IEssentials ess, final AsyncTeleport teleport, final long delay, final IUser teleportUser, final ITarget target, final Trade chargeFor, final TeleportCause cause, final boolean respawn) { @@ -55,7 +56,7 @@ public class AsyncTimedTeleport implements Runnable { this.timer_respawn = respawn; this.timer_canMove = user.isAuthorized("essentials.teleport.timer.move"); - timer_task = ess.runTaskTimerAsynchronously(this, 20, 20).getTaskId(); + timer_task = ess.getSchedulerAdapter().runTaskTimerAsynchronously(this, 20*50, 20*50); if (future != null) { this.parentFuture = future; @@ -142,16 +143,16 @@ public void run() { } } - ess.scheduleSyncDelayedTask(new DelayedTeleportTask()); + ess.getSchedulerAdapter().runTask(new DelayedTeleportTask()); } //If we need to cancelTimer a pending teleportPlayer call this method void cancelTimer(final boolean notifyUser) { - if (timer_task == -1) { + if (timer_task == null) { return; } try { - ess.getServer().getScheduler().cancelTask(timer_task); + timer_task.cancel(); final IUser teleportUser = ess.getUser(this.timer_teleportee); if (teleportUser != null && teleportUser.getBase() != null) { @@ -167,7 +168,7 @@ void cancelTimer(final boolean notifyUser) { } } } finally { - timer_task = -1; + timer_task = null; } } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/Backup.java b/Essentials/src/main/java/com/earth2me/essentials/Backup.java index a6831ab3bb1..fe5e06a6eef 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Backup.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Backup.java @@ -1,5 +1,6 @@ package com.earth2me.essentials; +import com.earth2me.essentials.utils.schedulers.SchedulerTask; import net.ess3.api.IEssentials; import org.bukkit.Server; import org.bukkit.command.CommandSender; @@ -18,7 +19,7 @@ public class Backup implements Runnable { private transient final IEssentials ess; private final AtomicBoolean pendingShutdown = new AtomicBoolean(false); private transient boolean running = false; - private transient int taskId = -1; + private transient SchedulerTask taskId = null; private transient boolean active = false; private transient CompletableFuture taskLock = null; @@ -26,7 +27,7 @@ public Backup(final IEssentials ess) { this.ess = ess; server = ess.getServer(); if (!ess.getOnlinePlayers().isEmpty() || ess.getSettings().isAlwaysRunBackup()) { - ess.runTaskAsynchronously(this::startTask); + ess.getSchedulerAdapter().runTaskAsynchronously(this::startTask); } } @@ -36,10 +37,10 @@ public void onPlayerJoin() { public synchronized void stopTask() { running = false; - if (taskId != -1) { - server.getScheduler().cancelTask(taskId); + if (taskId != null) { + taskId.cancel(); } - taskId = -1; + taskId = null; } private synchronized void startTask() { @@ -48,7 +49,7 @@ private synchronized void startTask() { if (interval < 1200) { return; } - taskId = ess.scheduleSyncRepeatingTask(this, interval, interval); + taskId = ess.getSchedulerAdapter().runTaskTimer(this, interval, interval); running = true; } } @@ -84,13 +85,13 @@ public void run() { server.dispatchCommand(cs, "save-all"); server.dispatchCommand(cs, "save-off"); - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { try { final ProcessBuilder childBuilder = new ProcessBuilder(command.split(" ")); childBuilder.redirectErrorStream(true); childBuilder.directory(ess.getDataFolder().getParentFile().getParentFile()); final Process child = childBuilder.start(); - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { try { try (final BufferedReader reader = new BufferedReader(new InputStreamReader(child.getInputStream()))) { String line; @@ -123,7 +124,7 @@ public void run() { } if (!pendingShutdown.get()) { - ess.scheduleSyncDelayedTask(new BackupEnableSaveTask()); + ess.getSchedulerAdapter().runTask(new BackupEnableSaveTask()); } } }); diff --git a/Essentials/src/main/java/com/earth2me/essentials/BalanceTopImpl.java b/Essentials/src/main/java/com/earth2me/essentials/BalanceTopImpl.java index 2dddd712603..03fd36629d3 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/BalanceTopImpl.java +++ b/Essentials/src/main/java/com/earth2me/essentials/BalanceTopImpl.java @@ -72,7 +72,7 @@ public CompletableFuture calculateBalanceTopMapAsync() { return cacheLock; } cacheLock = new CompletableFuture<>(); - ess.runTaskAsynchronously(this::calculateBalanceTopMap); + ess.getSchedulerAdapter().runTaskAsynchronously(this::calculateBalanceTopMap); return cacheLock; } diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsBlockListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsBlockListener.java index 2890bc2a79e..d61381841a7 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsBlockListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsBlockListener.java @@ -44,7 +44,7 @@ public void onBlockPlace(final BlockPlaceEvent event) { final User user = ess.getUser(event.getPlayer()); if (user.hasUnlimited(is) && user.getBase().getGameMode() == GameMode.SURVIVAL) { - ess.scheduleSyncDelayedTask(() -> { + ess.getSchedulerAdapter().runEntityTask(user.getBase(), () -> { if (is != null && is.getType() != null && !MaterialUtil.isAir(is.getType())) { final ItemStack cloneIs = is.clone(); cloneIs.setAmount(1); diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsEntityListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsEntityListener.java index 1d1fbff1eec..5e0767d0ac2 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsEntityListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsEntityListener.java @@ -110,7 +110,7 @@ public void run() { } } - ess.scheduleSyncDelayedTask(new PowerToolInteractTask()); + ess.getSchedulerAdapter().runEntityTask(attacker.getBase(), new PowerToolInteractTask()); event.setCancelled(true); return; diff --git a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java index c1fb775d7b4..6ad122332ea 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java +++ b/Essentials/src/main/java/com/earth2me/essentials/EssentialsPlayerListener.java @@ -15,6 +15,7 @@ import com.earth2me.essentials.utils.LocationUtil; import com.earth2me.essentials.utils.MaterialUtil; import com.earth2me.essentials.utils.VersionUtil; +import com.earth2me.essentials.utils.schedulers.SchedulerTask; import io.papermc.lib.PaperLib; import io.papermc.paper.ban.BanListType; import io.papermc.paper.event.connection.configuration.AsyncPlayerConnectionConfigureEvent; @@ -94,7 +95,7 @@ public class EssentialsPlayerListener implements Listener { private final transient IEssentials ess; - private final ConcurrentHashMap pendingMotdTasks = new ConcurrentHashMap<>(); + private final ConcurrentHashMap pendingMotdTasks = new ConcurrentHashMap<>(); public EssentialsPlayerListener(final IEssentials parent) { this.ess = parent; @@ -289,9 +290,9 @@ public void onPlayerMove(final PlayerMoveEvent event) { public void onPlayerQuit(final PlayerQuitEvent event) { final User user = ess.getUser(event.getPlayer()); - final Integer pendingId = pendingMotdTasks.remove(user.getUUID()); + final SchedulerTask pendingId = pendingMotdTasks.remove(user.getUUID()); if (pendingId != null) { - ess.getScheduler().cancelTask(pendingId); + pendingId.cancel(); } if (hideJoinQuitMessages() || ess.getSettings().allowSilentJoinQuit() && user.isAuthorized("essentials.silentquit")) { @@ -408,7 +409,7 @@ private boolean hideJoinQuitMessages() { private void legacyJoinFlow(final PlayerJoinEvent event) { final String joinMessage = event.getJoinMessage(); - ess.runTaskAsynchronously(() -> delayedJoin(event.getPlayer(), joinMessage)); + ess.getSchedulerAdapter().runTaskAsynchronously(() -> delayedJoin(event.getPlayer(), joinMessage)); if (hideJoinQuitMessages() || ess.getSettings().allowSilentJoinQuit() || ess.getSettings().isCustomJoinMessage()) { event.setJoinMessage(null); @@ -473,13 +474,13 @@ private void joinFlow(final User user, final long currentTime, final String mess joinMessageConsumer.accept(effectiveMessage); } - ess.runTaskAsynchronously(() -> ess.getServer().getPluginManager().callEvent(new AsyncUserDataLoadEvent(user, effectiveMessage))); + ess.getSchedulerAdapter().runTaskAsynchronously(() -> ess.getServer().getPluginManager().callEvent(new AsyncUserDataLoadEvent(user, effectiveMessage))); if (ess.getSettings().getMotdDelay() >= 0) { final int motdDelay = ess.getSettings().getMotdDelay() / 50; final Runnable motdTask = () -> motdFlow(user); if (motdDelay > 0) { - pendingMotdTasks.put(user.getUUID(), ess.scheduleSyncDelayedTask(motdTask, motdDelay)); + pendingMotdTasks.put(user.getUUID(), ess.getSchedulerAdapter().runTaskLater(motdTask, motdDelay)); } else { motdTask.run(); } @@ -496,7 +497,7 @@ private void joinFlow(final User user, final long currentTime, final String mess } if (user.isAuthorized("essentials.updatecheck")) { - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { for (final ComponentHolder component : ess.getUpdateChecker().getVersionMessages(false, false, user.getSource())) { user.sendComponent(component); } @@ -581,7 +582,7 @@ private void delayedJoin(final Player player, final String message) { dUser.updateActivity(false, AfkStatusChangeEvent.Cause.JOIN); dUser.stopTransaction(); - ess.scheduleSyncDelayedTask(() -> { + ess.getSchedulerAdapter().runEntityTask(player, () -> { final User user = ess.getUser(player); if (!user.getBase().isOnline()) { @@ -751,7 +752,7 @@ public void onPlayerBucketEmpty(final PlayerBucketEmptyEvent event) { final User user = ess.getUser(event.getPlayer()); if (user.hasUnlimited(new ItemStack(event.getBucket()))) { event.getItemStack().setType(event.getBucket()); - ess.scheduleSyncDelayedTask(user.getBase()::updateInventory); + ess.getSchedulerAdapter().runEntityTask(user.getBase(), user.getBase()::updateInventory); } } @@ -1012,7 +1013,7 @@ public void run() { } } - ess.scheduleSyncDelayedTask(new DelayedClickJumpTask()); + ess.getSchedulerAdapter().runEntityTask(user.getBase(), new DelayedClickJumpTask()); } catch (final Exception ex) { if (ess.getSettings().isDebug()) { ess.getLogger().log(Level.WARNING, ex.getMessage(), ex); @@ -1043,7 +1044,7 @@ public void run() { } } - ess.scheduleSyncDelayedTask(new PowerToolUseTask()); + ess.getSchedulerAdapter().runEntityTask(user.getBase(), new PowerToolUseTask()); } } @@ -1105,7 +1106,7 @@ public void onInventoryClickEvent(final InventoryClickEvent event) { } if (refreshPlayer != null) { - ess.scheduleSyncDelayedTask(refreshPlayer::updateInventory, 1); + ess.getSchedulerAdapter().runEntityTaskLater(refreshPlayer, refreshPlayer::updateInventory, 1); } } @@ -1168,7 +1169,7 @@ public void onInventoryCloseEvent(final InventoryCloseEvent event) { } if (refreshPlayer != null) { - ess.scheduleSyncDelayedTask(refreshPlayer::updateInventory, 1); + ess.getSchedulerAdapter().runEntityTaskLater(refreshPlayer, refreshPlayer::updateInventory, 1); } } @@ -1192,7 +1193,7 @@ public void onPlayerGameModeChange(final PlayerGameModeChangeEvent event) { final Player player = event.getPlayer(); if (player.isFlying() && player.getAllowFlight() && user.isAuthorized("essentials.fly")) { // The gamemode change happens after the event, so we need to delay the flight enable - ess.scheduleSyncDelayedTask(() -> { + ess.getSchedulerAdapter().runEntityTaskLater(player, () -> { player.setAllowFlight(true); player.setFlying(true); }, 1); diff --git a/Essentials/src/main/java/com/earth2me/essentials/Kit.java b/Essentials/src/main/java/com/earth2me/essentials/Kit.java index 895b13d8220..da2c739e49b 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Kit.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Kit.java @@ -286,9 +286,11 @@ public boolean expandItems(final User user, final List items) throws Exc t.pay(user, OverflowType.DROP); } - for (final String cmd : commandQueue) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd); - } + ess.getSchedulerAdapter().runTask(() -> { + for (final String cmd : commandQueue) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd); + } + }); if (spew) { user.sendTl("kitInvFull"); diff --git a/Essentials/src/main/java/com/earth2me/essentials/RandomTeleport.java b/Essentials/src/main/java/com/earth2me/essentials/RandomTeleport.java index 86513571c41..485b8b338b7 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/RandomTeleport.java +++ b/Essentials/src/main/java/com/earth2me/essentials/RandomTeleport.java @@ -156,7 +156,7 @@ public CompletableFuture getRandomLocation(final Location center, fina // Prompts caching random valid locations, up to a maximum number of attempts. public void cacheRandomLocations(final String name) { - ess.getServer().getScheduler().scheduleSyncDelayedTask(ess, () -> { + ess.getSchedulerAdapter().runTask(() -> { for (int i = 0; i < this.getFindAttempts(); ++i) { calculateRandomLocation(getCenter(name), getMinRange(name), getMaxRange(name)).thenAccept(location -> { if (isValidRandomLocation(location)) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/Settings.java b/Essentials/src/main/java/com/earth2me/essentials/Settings.java index 8d41da405e7..05b0bc550f1 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Settings.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Settings.java @@ -837,7 +837,7 @@ public void reloadConfig() { if (reloadCount.get() < 2) { // on startup: add plugins again in case they registered commands with the new API // we need to schedule this task before any of the below tasks using _addAlternativeCommand. - ess.scheduleSyncDelayedTask(() -> { + ess.getSchedulerAdapter().runTask(() -> { for (final Plugin plugin : ess.getServer().getPluginManager().getPlugins()) { if (plugin.isEnabled()) { ess.getAlternativeCommandsHandler().addPlugin(plugin); @@ -863,7 +863,7 @@ public void reloadConfig() { // This is 2 because Settings are reloaded twice in the startup lifecycle if (reloadCount.get() < 2) { - ess.scheduleSyncDelayedTask(() -> _addAlternativeCommand(effectiveAlias, toDisable)); + ess.getSchedulerAdapter().runTask(() -> _addAlternativeCommand(effectiveAlias, toDisable)); } else { _addAlternativeCommand(effectiveAlias, toDisable); } @@ -878,7 +878,7 @@ public void reloadConfig() { ess.getLogger().log(Level.INFO, "Syncing commands"); } if (reloadCount.get() < 2) { - ess.scheduleSyncDelayedTask(syncCommandsProvider::syncCommands); + ess.getSchedulerAdapter().runTask(syncCommandsProvider::syncCommands); } else { syncCommandsProvider.syncCommands(); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/User.java b/Essentials/src/main/java/com/earth2me/essentials/User.java index a261880720b..66f5c8db790 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/User.java +++ b/Essentials/src/main/java/com/earth2me/essentials/User.java @@ -322,7 +322,7 @@ public boolean canAfford(final BigDecimal cost, final boolean permcheck) { } public void dispose() { - ess.runTaskAsynchronously(this::_dispose); + ess.getSchedulerAdapter().runTaskAsynchronously(this::_dispose); } private void _dispose() { @@ -821,7 +821,7 @@ public void updateActivityOnInteract(final boolean broadcast) { public void updateActivityOnChat(final boolean broadcast) { if (ess.getSettings().cancelAfkOnChat()) { //Chat happens async, make sure we have a sync context - ess.scheduleSyncDelayedTask(() -> updateActivity(broadcast, AfkStatusChangeEvent.Cause.CHAT)); + ess.getSchedulerAdapter().runEntityTask(base, () -> updateActivity(broadcast, AfkStatusChangeEvent.Cause.CHAT)); } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalancetop.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalancetop.java index e11f8a17abf..3d421232f7d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalancetop.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbalancetop.java @@ -44,7 +44,7 @@ private void outputCache(final CommandSource sender, final int page) { new TextPager(cache).showPage(Integer.toString(page), null, "balancetop", sender); }; if (sender.getSender() instanceof BlockCommandSender) { - ess.scheduleSyncDelayedTask(runnable); + ess.getSchedulerAdapter().runTask(runnable); } else { runnable.run(); } @@ -74,7 +74,7 @@ protected void run(final Server server, final CommandSource sender, final String sender.sendTl("orderBalances", ess.getUsers().getUserCount()); } - ess.runTaskAsynchronously(new Viewer(sender, page, force)); + ess.getSchedulerAdapter().runTaskAsynchronously(new Viewer(sender, page, force)); } @Override diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbeezooka.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbeezooka.java index 19e156f6643..bf71a206c92 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbeezooka.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandbeezooka.java @@ -23,7 +23,7 @@ protected void run(final Server server, final User user, final String commandLab final Entity bee = Mob.BEE.spawn(user.getWorld(), server, user.getBase().getEyeLocation()); bee.setVelocity(user.getBase().getEyeLocation().getDirection().multiply(2)); - ess.scheduleSyncDelayedTask(() -> { + ess.getSchedulerAdapter().runEntityTaskLater(bee, () -> { final Location loc = bee.getLocation(); bee.remove(); loc.getWorld().createExplosion(loc, 0F); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandcreatekit.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandcreatekit.java index 0c5189f82ba..2295d85d58b 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandcreatekit.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandcreatekit.java @@ -73,7 +73,7 @@ public void run(final Server server, final User user, final String commandLabel, } private void uploadPaste(final CommandSource sender, final String kitName, final long delay, final List list) { - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { try { final StringWriter sw = new StringWriter(); final YamlConfigurationLoader loader = YamlConfigurationLoader.builder().sink(() -> new BufferedWriter(sw)).indent(2).nodeStyle(NodeStyle.BLOCK).build(); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkittycannon.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkittycannon.java index 18397f82f37..725e8496576 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkittycannon.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandkittycannon.java @@ -49,7 +49,7 @@ private static Entity spawnCat(final Server server, final User user) throws Mob. @Override protected void run(final Server server, final User user, final String commandLabel, final String[] args) throws Exception { final Entity ocelot = Mob.CAT.getType() == null ? spawnOcelot(server, user) : spawnCat(server, user); - ess.scheduleSyncDelayedTask(() -> { + ess.getSchedulerAdapter().runEntityTaskLater(ocelot, () -> { final Location loc = ocelot.getLocation(); ocelot.remove(); loc.getWorld().createExplosion(loc, 0F); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmail.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmail.java index 683fce75332..b165d255a90 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmail.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandmail.java @@ -151,7 +151,7 @@ public void run(final Server server, final User user, final String commandLabel, if (!user.isAuthorized("essentials.mail.sendall")) { throw new TranslatableException("noPerm", "essentials.mail.sendall"); } - ess.runTaskAsynchronously(new SendAll(user, + ess.getSchedulerAdapter().runTaskAsynchronously(new SendAll(user, FormatUtil.formatMessage(user, "essentials.mail", StringUtil.sanitizeString(FormatUtil.stripFormat(getFinalArg(args, 1)))), 0)); user.sendTl("mailSent"); @@ -161,7 +161,7 @@ public void run(final Server server, final User user, final String commandLabel, if (!user.isAuthorized("essentials.mail.sendtempall")) { throw new TranslatableException("noPerm", "essentials.mail.sendtempall"); } - ess.runTaskAsynchronously(new SendAll(user, + ess.getSchedulerAdapter().runTaskAsynchronously(new SendAll(user, FormatUtil.formatMessage(user, "essentials.mail", StringUtil.sanitizeString(FormatUtil.stripFormat(getFinalArg(args, 2)))), DateUtil.parseDateDiff(args[1], true))); user.sendTl("mailSent"); @@ -207,7 +207,7 @@ public void run(final Server server, final User user, final String commandLabel, throw new TranslatableException("noPerm", "essentials.mail.clearall"); } - ess.runTaskAsynchronously(new ClearAll()); + ess.getSchedulerAdapter().runTaskAsynchronously(new ClearAll()); user.sendTl("mailClearedAll"); return; @@ -242,7 +242,7 @@ protected void run(final Server server, final CommandSource sender, final String sender.sendTl("mailCleared"); return; } else if (args.length >= 1 && "clearall".equalsIgnoreCase(args[0])){ - ess.runTaskAsynchronously(new ClearAll()); + ess.getSchedulerAdapter().runTaskAsynchronously(new ClearAll()); sender.sendTl("mailClearedAll"); return; } else if (args.length >= 3 && "send".equalsIgnoreCase(args[0])) { @@ -267,12 +267,12 @@ protected void run(final Server server, final CommandSource sender, final String sender.sendTl("mailSent"); return; } else if (args.length >= 2 && "sendall".equalsIgnoreCase(args[0])) { - ess.runTaskAsynchronously(new SendAll(Console.getInstance(), FormatUtil.replaceFormat(getFinalArg(args, 1)), 0)); + ess.getSchedulerAdapter().runTaskAsynchronously(new SendAll(Console.getInstance(), FormatUtil.replaceFormat(getFinalArg(args, 1)), 0)); sender.sendTl("mailSent"); return; } else if (args.length >= 3 && "sendtempall".equalsIgnoreCase(args[0])) { final long dateDiff = DateUtil.parseDateDiff(args[1], true); - ess.runTaskAsynchronously(new SendAll(Console.getInstance(), FormatUtil.replaceFormat(getFinalArg(args, 2)), dateDiff)); + ess.getSchedulerAdapter().runTaskAsynchronously(new SendAll(Console.getInstance(), FormatUtil.replaceFormat(getFinalArg(args, 2)), dateDiff)); sender.sendTl("mailSent"); return; } else if (args.length >= 2) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandseen.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandseen.java index a9a851ad6a3..5b8e6041aa0 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandseen.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandseen.java @@ -61,7 +61,7 @@ protected void run(final Server server, final CommandSource sender, final String return; } } - ess.getScheduler().runTaskAsynchronously(ess, new Runnable() { + ess.getSchedulerAdapter().runTaskAsynchronously(new Runnable() { @Override public void run() { final User userFromBukkit = ess.getUsers().getUser(args[0]); @@ -203,7 +203,7 @@ private void seenOffline(final CommandSource sender, final User user, final bool private void seenIP(final CommandSource sender, final String ipAddress, final String display) { sender.sendTl("runningPlayerMatch", AdventureUtil.parsed(ess.getAdventureFacet().legacyToMini(display))); - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { final List matches = new ArrayList<>(); for (final UUID u : ess.getUsers().getAllUserUUIDs()) { final User user = ess.getUsers().loadUncachedUser(u); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandskull.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandskull.java index b279b477c5c..8ae87c81a29 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandskull.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandskull.java @@ -106,7 +106,7 @@ protected void run(final Server server, final User user, final String commandLab } private void editSkull(final User user, final User receive, final ItemStack stack, final SkullMeta skullMeta, final String owner, final boolean spawn) { - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { // Run this stuff async because it causes an HTTP request String shortOwnerName; @@ -159,7 +159,7 @@ private void editSkull(final User user, final User receive, final ItemStack stac final String shortNameFinal = shortOwnerName; - ess.scheduleSyncDelayedTask(() -> { + ess.getSchedulerAdapter().runEntityTask(user.getBase(), () -> { stack.setItemMeta(skullMeta); if (spawn) { Inventories.addItem(receive.getBase(), stack); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsudo.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsudo.java index 861cf6b302f..59b3881361e 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsudo.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandsudo.java @@ -53,7 +53,7 @@ public void run() { } } - ess.scheduleSyncDelayedTask(new SudoCommandTask()); + ess.getSchedulerAdapter().runEntityTask(user.getBase(), new SudoCommandTask()); } } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/CleanupCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/CleanupCommand.java index b7eba8ff058..1b3187f4f66 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/CleanupCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/CleanupCommand.java @@ -34,7 +34,7 @@ protected void run(final CommandSource sender, final String commandLabel, final final double moneyArg = args.length >= 2 ? FloatUtil.parseDouble(args[1].replaceAll("[^0-9.]", "")) : 0; final int homesArg = args.length >= 3 && NumberUtil.isInt(args[2]) ? Integer.parseInt(args[2]) : 0; - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { final long currTime = System.currentTimeMillis(); for (final UUID u : ess.getUsers().getAllUserUUIDs()) { final User user = ess.getUsers().loadUncachedUser(u); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DumpCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DumpCommand.java index ba5f078e3ed..49784997934 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DumpCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/DumpCommand.java @@ -133,7 +133,7 @@ protected void run(CommandSource sender, String commandLabel, String[] args) thr final Map disabledCommandsCopy = new HashMap<>(ess.getAlternativeCommandsHandler().disabledCommands()); // Further operations will be heavy IO - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { boolean config = false; boolean discord = false; boolean kits = false; diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/HomesCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/HomesCommand.java index 5a84c401c3f..ff01fdb6060 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/HomesCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/HomesCommand.java @@ -34,7 +34,7 @@ protected void run(final CommandSource sender, final String commandLabel, final switch (args[0]) { case "fix": sender.sendTl("fixingHomes"); - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { for (final UUID u : ess.getUsers().getAllUserUUIDs()) { final User user = ess.getUsers().loadUncachedUser(u); if (user == null) { @@ -63,7 +63,7 @@ protected void run(final CommandSource sender, final String commandLabel, final } else { sender.sendTl("deletingHomes"); } - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { for (final UUID u : ess.getUsers().getAllUserUUIDs()) { final User user = ess.getUsers().loadUncachedUser(u); if (user == null) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/UsermapCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/UsermapCommand.java index a7f80ec8c1c..eb0ffdbcd79 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/UsermapCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/UsermapCommand.java @@ -41,7 +41,7 @@ protected void run(final CommandSource sender, final String commandLabel, final sender.sendTl("usermapPurge", String.valueOf(seppuku)); final Set uuids = new HashSet<>(ess.getUsers().getAllUserUUIDs()); - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { final File userdataFolder = new File(ess.getDataFolder(), "userdata"); final File backupFolder = new File(ess.getDataFolder(), "userdata-npc-backup-boogaloo-" + System.currentTimeMillis()); diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java index 82796f9802f..f23a0d84735 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/VersionCommand.java @@ -145,7 +145,7 @@ protected void run(CommandSource sender, String commandLabel, String[] args) thr } sender.sendTl("versionFetching"); - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { for (final ComponentHolder component : ess.getUpdateChecker().getVersionMessages(true, true, sender)) { sender.sendComponent(component); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/economy/EconomyLayers.java b/Essentials/src/main/java/com/earth2me/essentials/economy/EconomyLayers.java index a4184e5588a..6f7ab9fda42 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/economy/EconomyLayers.java +++ b/Essentials/src/main/java/com/earth2me/essentials/economy/EconomyLayers.java @@ -32,7 +32,7 @@ public static void init() { } public static void onEnable(final Essentials ess) { - ess.scheduleSyncDelayedTask(() -> { + ess.getSchedulerAdapter().runTask(() -> { serverStarted = true; for (final Plugin plugin : Bukkit.getPluginManager().getPlugins()) { if (!plugin.isEnabled()) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/updatecheck/UpdateChecker.java b/Essentials/src/main/java/com/earth2me/essentials/updatecheck/UpdateChecker.java index 0f596a1e3f6..99d9b5c01b8 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/updatecheck/UpdateChecker.java +++ b/Essentials/src/main/java/com/earth2me/essentials/updatecheck/UpdateChecker.java @@ -76,7 +76,7 @@ public CompletableFuture fetchLatestDev() { return pendingDevFuture; } pendingDevFuture = new CompletableFuture<>(); - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { pendingDevFuture.complete(cachedDev = fetchDistance(BRANCH, getVersionIdentifier())); pendingDevFuture = null; lastFetchTime = System.currentTimeMillis(); @@ -92,7 +92,7 @@ public CompletableFuture fetchLatestRelease() { return pendingReleaseFuture; } pendingReleaseFuture = new CompletableFuture<>(); - ess.runTaskAsynchronously(() -> { + ess.getSchedulerAdapter().runTaskAsynchronously(() -> { catchBlock: try { final HttpURLConnection connection = tryRequestWithFallback(LATEST_RELEASE_URL, LATEST_RELEASE_PROXY_URL); From 519eee7168ce58aec2faa8dc82ab635a9d95cccb Mon Sep 17 00:00:00 2001 From: Bierque Euphyllia Date: Tue, 24 Mar 2026 13:06:34 +0100 Subject: [PATCH 3/7] Replace BukkitRunnable --- .../earth2me/essentials/commands/essentials/NyanCommand.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/NyanCommand.java b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/NyanCommand.java index 9e42fc503e9..c8e85fcf00d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/NyanCommand.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/essentials/NyanCommand.java @@ -3,10 +3,10 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.commands.EssentialsTreeNode; import com.earth2me.essentials.utils.RegistryUtil; +import com.earth2me.essentials.utils.schedulers.SchedulerRunnable; import com.google.common.collect.ImmutableMap; import org.bukkit.Sound; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitRunnable; import java.util.Collection; import java.util.Map; @@ -32,7 +32,7 @@ protected void run(final CommandSource sender, final String commandLabel, final currentTune.runTaskTimer(ess, 20, 2); } - private static class TuneRunnable extends BukkitRunnable { + private static class TuneRunnable extends SchedulerRunnable { private static final Map noteMap = ImmutableMap.builder() .put("1F#", 0.5f) .put("1G", 0.53f) From ddccae068d589cdbf6b7cd7db5542ffb8a118c9e Mon Sep 17 00:00:00 2001 From: Bierque Euphyllia Date: Tue, 24 Mar 2026 13:28:10 +0100 Subject: [PATCH 4/7] Fix multiple commands --- .../essentials/commands/Commandgc.java | 105 +++++- .../essentials/commands/Commandlightning.java | 21 +- .../essentials/commands/Commandremove.java | 298 ++++++++++-------- .../essentials/commands/Commandtime.java | 3 +- .../essentials/commands/Commandweather.java | 16 +- 5 files changed, 292 insertions(+), 151 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgc.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgc.java index b35449507e7..f42159e2b57 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgc.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandgc.java @@ -3,6 +3,7 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.utils.DateUtil; import com.earth2me.essentials.utils.NumberUtil; +import com.earth2me.essentials.utils.VersionUtil; import net.ess3.provider.TileEntityProvider; import org.bukkit.ChatColor; import org.bukkit.Chunk; @@ -11,6 +12,8 @@ import java.lang.management.ManagementFactory; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import java.util.logging.Level; public class Commandgc extends EssentialsCommand { @@ -31,7 +34,7 @@ protected void run(final Server server, final CommandSource sender, final String } sender.sendTl("uptime", DateUtil.formatDateDiff(ManagementFactory.getRuntimeMXBean().getStartTime())); - sender.sendTl("tps", "" + color + NumberUtil.formatDouble(tps)); + if (!VersionUtil.isFoliaServer()) sender.sendTl("tps", "" + color + NumberUtil.formatDouble(tps)); sender.sendTl("gcmax", Runtime.getRuntime().maxMemory() / 1024 / 1024); sender.sendTl("gctotal", Runtime.getRuntime().totalMemory() / 1024 / 1024); sender.sendTl("gcfree", Runtime.getRuntime().freeMemory() / 1024 / 1024); @@ -39,27 +42,105 @@ protected void run(final Server server, final CommandSource sender, final String final List worlds = server.getWorlds(); final TileEntityProvider tileEntityProvider = ess.provider(TileEntityProvider.class); for (final World w : worlds) { - String worldType = "World"; - switch (w.getEnvironment()) { - case NETHER: - worldType = "Nether"; - break; - case THE_END: - worldType = "The End"; - break; - } + getChunkStatsAsync(tileEntityProvider, w, result -> { + sender.sendTl("gcWorld", + getWorldType(w), + w.getName(), + w.getLoadedChunks().length, + result.getEntities(), + result.getTileEntities() + ); + }); + } + } + + private String getWorldType(World world) { + switch (world.getEnvironment()) { + case NETHER: + return "Nether"; + case THE_END: + return "The End"; + default: + return "World"; + } + } + private void getChunkStatsAsync(TileEntityProvider tileEntityProvider, World w, Consumer callback) { + + Chunk[] chunks = w.getLoadedChunks(); + + if (chunks.length == 0) { + callback.accept(new Result(0, 0)); + return; + } + + if (!VersionUtil.isFoliaServer()) { int tileEntities = 0; try { - for (final Chunk chunk : w.getLoadedChunks()) { + for (final Chunk chunk : chunks) { tileEntities += tileEntityProvider.getTileEntities(chunk).length; } } catch (final java.lang.ClassCastException ex) { ess.getLogger().log(Level.SEVERE, "Corrupted chunk data on world " + w, ex); } - sender.sendTl("gcWorld", worldType, w.getName(), w.getLoadedChunks().length, w.getEntities().size(), tileEntities); + callback.accept(new Result( + tileEntities, + w.getEntities().size() + )); + return; + } + + AtomicInteger remaining = new AtomicInteger(chunks.length); + AtomicInteger totalTileEntities = new AtomicInteger(0); + AtomicInteger totalEntities = new AtomicInteger(0); + AtomicInteger errors = new AtomicInteger(0); + + for (Chunk chunk : chunks) { + int chunkX = chunk.getX(); + int chunkZ = chunk.getZ(); + + ess.getSchedulerAdapter().runRegionTask(w, chunkX, chunkZ, () -> { + try { + totalTileEntities.addAndGet( + tileEntityProvider.getTileEntities(chunk).length + ); + totalEntities.addAndGet( + chunk.getEntities().length + ); + } catch (Exception ex) { + errors.incrementAndGet(); + ess.getLogger().log(Level.SEVERE, "Corrupted chunk data", ex); + } finally { + if (remaining.decrementAndGet() == 0) { + ess.getSchedulerAdapter().runTask(() -> + callback.accept(new Result( + totalTileEntities.get(), + totalEntities.get() + )) + ); + } + } + }); + } + } + + private static class Result { + private final int tileEntities; + private final int entities; + + public Result(int tileEntities, int entities) { + this.tileEntities = tileEntities; + this.entities = entities; + } + + public int getTileEntities() { + return tileEntities; + } + + public int getEntities() { + return entities; } } } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandlightning.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandlightning.java index 046923c684a..38ae5202f23 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandlightning.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandlightning.java @@ -5,6 +5,7 @@ import com.google.common.collect.Lists; import org.bukkit.Server; import org.bukkit.entity.LightningStrike; +import org.bukkit.entity.Player; import java.util.Collections; import java.util.List; @@ -33,15 +34,19 @@ public void run(final Server server, final CommandSource sender, final String co } final int finalPower = power; loopOnlinePlayersConsumer(server, sender, false, true, args[0], player -> { - sender.sendTl("lightningUse", player.getDisplayName()); - final LightningStrike strike = player.getBase().getWorld().strikeLightningEffect(player.getBase().getLocation()); + Player bukkitPlayer = player.getBase(); - if (!player.isGodModeEnabled()) { - player.getBase().damage(finalPower, strike); - } - if (ess.getSettings().warnOnSmite()) { - player.sendTl("lightningSmited"); - } + ess.getSchedulerAdapter().runEntityTask(bukkitPlayer, () -> { + sender.sendTl("lightningUse", player.getDisplayName()); + final LightningStrike strike = player.getBase().getWorld().strikeLightningEffect(player.getBase().getLocation()); + + if (!player.isGodModeEnabled()) { + player.getBase().damage(finalPower, strike); + } + if (ess.getSettings().warnOnSmite()) { + player.sendTl("lightningSmited"); + } + }); }); loopOnlinePlayers(server, sender, true, true, args[0], null); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandremove.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandremove.java index 65e7ad9a693..81038c0332f 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandremove.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandremove.java @@ -3,12 +3,10 @@ import com.earth2me.essentials.CommandSource; import com.earth2me.essentials.Mob; import com.earth2me.essentials.User; +import com.earth2me.essentials.utils.VersionUtil; import com.google.common.collect.Lists; import net.ess3.api.TranslatableException; -import org.bukkit.Chunk; -import org.bukkit.OfflinePlayer; -import org.bukkit.Server; -import org.bukkit.World; +import org.bukkit.*; import org.bukkit.entity.Ambient; import org.bukkit.entity.Animals; import org.bukkit.entity.Boat; @@ -36,6 +34,8 @@ import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; // This could be rewritten in a simpler form if we made a mapping of all Entity names to their types (which would also provide possible mod support) @@ -106,10 +106,11 @@ private void parseCommand(final Server server, final CommandSource sender, final } private void removeHandler(final CommandSource sender, final List types, final List customTypes, final World world, int radius) { - int removed = 0; + AtomicInteger removed = new AtomicInteger(0); if (radius > 0) { radius *= radius; } + final int radiusFinal = radius; final ArrayList removeTypes = new ArrayList<>(); final ArrayList customRemoveTypes = new ArrayList<>(); @@ -133,134 +134,185 @@ private void removeHandler(final CommandSource sender, final List types, sender.sendTl("invalidMob"); } - for (final Chunk chunk : world.getLoadedChunks()) { - for (final Entity e : chunk.getEntities()) { - if (radius > 0) { - if (sender.getPlayer().getLocation().distanceSquared(e.getLocation()) > radius) { - continue; + Location playerLocation; + if (radiusFinal > 0 && sender.getPlayer() != null) { + playerLocation = sender.getPlayer().getLocation().clone(); + } else { + playerLocation = null; + } + + Chunk[] chunks = world.getLoadedChunks(); + + if (chunks.length == 0) { + sender.sendTl("removed", removed.get()); + return; + } + + if (VersionUtil.isFoliaServer()) { + CompletableFuture future = new CompletableFuture<>(); + AtomicInteger remaining = new AtomicInteger(chunks.length); + + for (final Chunk chunk : chunks) { + ess.getSchedulerAdapter().runRegionTask(world, chunk.getX(), chunk.getZ(), () -> { + try { + for (final Entity e : chunk.getEntities()) { + if (shouldBeRemovable(e, playerLocation, radiusFinal, customRemoveTypes, removeTypes)) { + removed.getAndIncrement(); + } + } + } finally { + if (remaining.decrementAndGet() == 0) { + future.complete(removed.get()); + } + } + }); + } + future.thenAccept(totalRemoved -> { + ess.getSchedulerAdapter().runTask(() -> + sender.sendTl("removed", totalRemoved) + ); + }); + } else { + for (final Chunk chunk : chunks) { + for (final Entity e : chunk.getEntities()) { + if (shouldBeRemovable(e, playerLocation, radiusFinal, customRemoveTypes, removeTypes)) { + removed.getAndIncrement(); } } - if (e instanceof HumanEntity) { - continue; - } + } + sender.sendTl("removed", removed.get()); + } + } - for (final ToRemove toRemove : removeTypes) { - // We should skip any animals tamed by players unless we are specifially targetting them. - if (e instanceof Tameable && ((Tameable) e).isTamed() && (((Tameable) e).getOwner() instanceof Player || ((Tameable) e).getOwner() instanceof OfflinePlayer) && !removeTypes.contains(ToRemove.TAMED)) { - continue; - } + private boolean shouldBeRemovable(final Entity e, Location playerLocation, int radius, ArrayList customRemoveTypes, ArrayList removeTypes) { + if (radius > 0 && playerLocation != null) { + double dx = playerLocation.getX() - e.getX(); + double dy = playerLocation.getY() - e.getY(); + double dz = playerLocation.getZ() - e.getZ(); - // We should skip any NAMED animals unless we are specifially targetting them. - if (e instanceof LivingEntity && e.getCustomName() != null && !removeTypes.contains(ToRemove.NAMED)) { - continue; - } + if ((dx * dx + dy * dy + dz * dz) > radius) { + return false; + } + } + if (e instanceof HumanEntity) { + return false; + } - switch (toRemove) { - case TAMED: - if (e instanceof Tameable && ((Tameable) e).isTamed()) { - e.remove(); - removed++; - } - break; - case NAMED: - if (e instanceof LivingEntity && e.getCustomName() != null) { - e.remove(); - removed++; - } - break; - case DROPS: - if (e instanceof Item) { - e.remove(); - removed++; - } - break; - case ARROWS: - if (e instanceof Projectile) { - e.remove(); - removed++; - } - break; - case BOATS: - if (e instanceof Boat) { - e.remove(); - removed++; - } - break; - case MINECARTS: - if (e instanceof Minecart) { - e.remove(); - removed++; - } - break; - case XP: - if (e instanceof ExperienceOrb) { - e.remove(); - removed++; - } - break; - case PAINTINGS: - if (e instanceof Painting) { - e.remove(); - removed++; - } - break; - case ITEMFRAMES: - if (e instanceof ItemFrame) { - e.remove(); - removed++; - } - break; - case ENDERCRYSTALS: - if (e instanceof EnderCrystal) { - e.remove(); - removed++; - } - break; - case AMBIENT: - if (e instanceof Flying) { - e.remove(); - removed++; - } - break; - case HOSTILE: - case MONSTERS: - if (e instanceof Monster || e instanceof ComplexLivingEntity || e instanceof Flying || e instanceof Slime) { - e.remove(); - removed++; - } - break; - case PASSIVE: - case ANIMALS: - if (e instanceof Animals || e instanceof NPC || e instanceof Snowman || e instanceof WaterMob || e instanceof Ambient) { - e.remove(); - removed++; - } - break; - case MOBS: - if (e instanceof Animals || e instanceof NPC || e instanceof Snowman || e instanceof WaterMob || e instanceof Monster || e instanceof ComplexLivingEntity || e instanceof Flying || e instanceof Slime || e instanceof Ambient) { - e.remove(); - removed++; - } - break; - case ENTITIES: - case ALL: + for (final ToRemove toRemove : removeTypes) { + + // We should skip any animals tamed by players unless we are specifially targetting them. + if (e instanceof Tameable && ((Tameable) e).isTamed() && (((Tameable) e).getOwner() instanceof Player || ((Tameable) e).getOwner() instanceof OfflinePlayer) && !removeTypes.contains(ToRemove.TAMED)) { + return false; + } + + // We should skip any NAMED animals unless we are specifially targetting them. + if (e instanceof LivingEntity && e.getCustomName() != null && !removeTypes.contains(ToRemove.NAMED)) { + return false; + } + + switch (toRemove) { + case TAMED: + if (e instanceof Tameable && ((Tameable) e).isTamed()) { + e.remove(); + return true; + } + break; + case NAMED: + if (e instanceof LivingEntity && e.getCustomName() != null) { + e.remove(); + return true; + } + break; + case DROPS: + if (e instanceof Item) { + e.remove(); + return true; + } + break; + case ARROWS: + if (e instanceof Projectile) { + e.remove(); + return true; + } + break; + case BOATS: + if (e instanceof Boat) { + e.remove(); + return true; + } + break; + case MINECARTS: + if (e instanceof Minecart) { + e.remove(); + return true; + } + break; + case XP: + if (e instanceof ExperienceOrb) { + e.remove(); + return true; + } + break; + case PAINTINGS: + if (e instanceof Painting) { + e.remove(); + return true; + } + break; + case ITEMFRAMES: + if (e instanceof ItemFrame) { + e.remove(); + return true; + } + break; + case ENDERCRYSTALS: + if (e instanceof EnderCrystal) { + e.remove(); + return true; + } + break; + case AMBIENT: + if (e instanceof Flying) { + e.remove(); + return true; + } + break; + case HOSTILE: + case MONSTERS: + if (e instanceof Monster || e instanceof ComplexLivingEntity || e instanceof Flying || e instanceof Slime) { + e.remove(); + return true; + } + break; + case PASSIVE: + case ANIMALS: + if (e instanceof Animals || e instanceof NPC || e instanceof Snowman || e instanceof WaterMob || e instanceof Ambient) { + e.remove(); + return true; + } + break; + case MOBS: + if (e instanceof Animals || e instanceof NPC || e instanceof Snowman || e instanceof WaterMob || e instanceof Monster || e instanceof ComplexLivingEntity || e instanceof Flying || e instanceof Slime || e instanceof Ambient) { + e.remove(); + return true; + } + break; + case ENTITIES: + case ALL: + e.remove(); + return true; + case CUSTOM: + for (final Mob type : customRemoveTypes) { + if (e.getType() == type.getType()) { e.remove(); - removed++; - break; - case CUSTOM: - for (final Mob type : customRemoveTypes) { - if (e.getType() == type.getType()) { - e.remove(); - removed++; - } - } - break; + return true; + } } - } + break; } } - sender.sendTl("removed", removed); + return false; } @Override diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtime.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtime.java index f376bdb8719..8a78270a59d 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtime.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandtime.java @@ -86,7 +86,8 @@ public void run(final Server server, final CommandSource sender, final String co if (!add) { time -= time % 24000; } - world.setTime(time + (add ? 0 : 24000) + timeTick); + final long timeFinal = time + (add ? 0 : 24000) + timeTick; + ess.getSchedulerAdapter().runTask(() -> world.setTime(timeFinal)); joiner.add(world.getName()); } diff --git a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandweather.java b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandweather.java index 242f0305277..1b07f6e379b 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/commands/Commandweather.java +++ b/Essentials/src/main/java/com/earth2me/essentials/commands/Commandweather.java @@ -32,14 +32,16 @@ public void run(final Server server, final User user, final String commandLabel, final World world = user.getWorld(); - if (args.length > 1) { + ess.getSchedulerAdapter().runTask(() -> { + if (args.length > 1) { + world.setStorm(isStorm); + world.setWeatherDuration(Integer.parseInt(args[1]) * 20); + user.sendTl(isStorm ? "weatherStormFor" : "weatherSunFor", world.getName(), args[1]); + return; + } world.setStorm(isStorm); - world.setWeatherDuration(Integer.parseInt(args[1]) * 20); - user.sendTl(isStorm ? "weatherStormFor" : "weatherSunFor", world.getName(), args[1]); - return; - } - world.setStorm(isStorm); - user.sendTl(isStorm ? "weatherStorm" : "weatherSun", world.getName()); + user.sendTl(isStorm ? "weatherStorm" : "weatherSun", world.getName()); + }); } @Override From f6083c0e329a10769fa4ca78cf4a406c1a065e9d Mon Sep 17 00:00:00 2001 From: Bierque Euphyllia Date: Tue, 24 Mar 2026 13:29:39 +0100 Subject: [PATCH 5/7] Fix execute command on Folia --- .../main/java/com/earth2me/essentials/User.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/User.java b/Essentials/src/main/java/com/earth2me/essentials/User.java index 66f5c8db790..d8a2e55ef59 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/User.java +++ b/Essentials/src/main/java/com/earth2me/essentials/User.java @@ -852,14 +852,16 @@ public void checkActivity() { } } else { // If `afk-timeout-commands` in config.yml is populated, execute the command(s) instead of kicking the player. - for (final String command : ess.getSettings().getAfkTimeoutCommands()) { - if (command == null || command.isEmpty()){ - continue; + ess.getSchedulerAdapter().runTask(() -> { + for (final String command : ess.getSettings().getAfkTimeoutCommands()) { + if (command == null || command.isEmpty()){ + continue; + } + // Replace placeholders in the command with actual values. + final String cmd = command.replace("{USERNAME}", getName()).replace("{KICKTIME}", String.valueOf(kickTime)); + ess.getServer().dispatchCommand(ess.getServer().getConsoleSender(), cmd); } - // Replace placeholders in the command with actual values. - final String cmd = command.replace("{USERNAME}", getName()).replace("{KICKTIME}", String.valueOf(kickTime)); - ess.getServer().dispatchCommand(ess.getServer().getConsoleSender(), cmd); - } + }); } } final long autoafk = ess.getSettings().getAutoAfk(); From 1857f0359ac55ce8cec79676489ef32504aa618b Mon Sep 17 00:00:00 2001 From: Bierque Euphyllia Date: Tue, 24 Mar 2026 13:41:41 +0100 Subject: [PATCH 6/7] Add folia-supported in plugin.yml --- Essentials/src/main/resources/plugin.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Essentials/src/main/resources/plugin.yml b/Essentials/src/main/resources/plugin.yml index 844f9231926..6081ddfc6cf 100644 --- a/Essentials/src/main/resources/plugin.yml +++ b/Essentials/src/main/resources/plugin.yml @@ -7,6 +7,7 @@ description: Provides an essential, core set of commands for Bukkit. softdepend: [Vault, LuckPerms] authors: [Zenexer, ementalo, Aelux, Brettflan, KimKandor, snowleo, ceulemans, Xeology, KHobbits, md_5, Iaccidentally, drtshock, vemacs, SupaHam, mdcfe, JRoy, pop4959] api-version: "1.13" +folia-supported: true commands: afk: description: Marks you as away-from-keyboard. From 5eb771eb24639e48147c393e3633d765e8b88827 Mon Sep 17 00:00:00 2001 From: Bierque Euphyllia Date: Tue, 24 Mar 2026 15:03:30 +0100 Subject: [PATCH 7/7] Mistake load scheduler --- .../src/main/java/com/earth2me/essentials/Essentials.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Essentials/src/main/java/com/earth2me/essentials/Essentials.java b/Essentials/src/main/java/com/earth2me/essentials/Essentials.java index 0583e619298..e3f4e492cae 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/Essentials.java +++ b/Essentials/src/main/java/com/earth2me/essentials/Essentials.java @@ -49,6 +49,7 @@ import com.earth2me.essentials.utils.VersionUtil; import com.earth2me.essentials.utils.schedulers.SchedulerAdapter; import com.earth2me.essentials.utils.schedulers.adapter.FoliaScheduler; +import com.earth2me.essentials.utils.schedulers.adapter.SpigotScheduler; import io.papermc.lib.PaperLib; import net.ess3.api.Economy; import com.earth2me.essentials.config.EssentialsConfiguration; @@ -216,7 +217,7 @@ public void onEnable() { if (VersionUtil.isFoliaServer()) { schedulerAdapter = new FoliaScheduler(this); } else { - schedulerAdapter = new FoliaScheduler(this); + schedulerAdapter = new SpigotScheduler(this); } } LOGGER = EssentialsLogger.getLoggerProvider(this);