diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitRegionContainer.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitRegionContainer.java index 79d7b3446..42ef9ea73 100644 --- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitRegionContainer.java +++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitRegionContainer.java @@ -94,7 +94,7 @@ public void onChunkUnload(ChunkUnloadEvent event) { } }, plugin); - Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, cache::invalidateAll, CACHE_INVALIDATION_INTERVAL, CACHE_INVALIDATION_INTERVAL); + plugin.getScheduler().runAsyncRate(cache::invalidateAll, CACHE_INVALIDATION_INTERVAL, CACHE_INVALIDATION_INTERVAL); } public void shutdown() { diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitWorldGuardPlatform.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitWorldGuardPlatform.java index 37e7f07ac..71456f012 100644 --- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitWorldGuardPlatform.java +++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/BukkitWorldGuardPlatform.java @@ -29,6 +29,7 @@ import com.sk89q.worldedit.world.gamemode.GameModes; import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.bukkit.scheduler.FoliaSchedulerAdapter; import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.util.profile.resolver.PaperPlayerService; @@ -235,7 +236,7 @@ public void stackPlayerInventory(LocalPlayer localPlayer) { public void addPlatformReports(ReportList report) { report.add(new ServerReport()); report.add(new PluginReport()); - report.add(new SchedulerReport()); + if (!FoliaSchedulerAdapter.isSupported()) report.add(new SchedulerReport()); report.add(new ServicesReport()); report.add(new WorldReport()); report.add(new PerformanceReport()); diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java index 3ba997921..e2bb96a03 100644 --- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java +++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/WorldGuardPlugin.java @@ -58,6 +58,9 @@ import com.sk89q.worldguard.bukkit.listener.WorldGuardWeatherListener; import com.sk89q.worldguard.bukkit.listener.WorldGuardWorldListener; import com.sk89q.worldguard.bukkit.listener.WorldRulesListener; +import com.sk89q.worldguard.bukkit.scheduler.BukkitSchedulerAdapter; +import com.sk89q.worldguard.bukkit.scheduler.FoliaSchedulerAdapter; +import com.sk89q.worldguard.bukkit.scheduler.SchedulerAdapter; import com.sk89q.worldguard.bukkit.session.BukkitSessionManager; import com.sk89q.worldguard.bukkit.util.ClassSourceValidator; import com.sk89q.worldguard.bukkit.util.Entities; @@ -107,6 +110,8 @@ public class WorldGuardPlugin extends JavaPlugin { private static WorldGuardPlugin inst; private static BukkitWorldGuardPlatform platform; private final CommandsManager commands; + private final SchedulerAdapter scheduler = FoliaSchedulerAdapter.isSupported() + ? new FoliaSchedulerAdapter(this) : new BukkitSchedulerAdapter(this); private PlayerMoveListener playerMoveListener; private static final int BSTATS_PLUGIN_ID = 3283; @@ -164,7 +169,7 @@ public void onEnable() { reg.register(GeneralCommands.class); } - getServer().getScheduler().scheduleSyncRepeatingTask(this, sessionManager, BukkitSessionManager.RUN_DELAY, BukkitSessionManager.RUN_DELAY); + getScheduler().runAsyncRate(sessionManager, BukkitSessionManager.RUN_DELAY, BukkitSessionManager.RUN_DELAY); // Register events getServer().getPluginManager().registerEvents(sessionManager, this); @@ -205,12 +210,12 @@ public void onEnable() { } worldListener.registerEvents(); - Bukkit.getScheduler().runTask(this, () -> { - for (Player player : Bukkit.getServer().getOnlinePlayers()) { + for (Player player : Bukkit.getServer().getOnlinePlayers()) { + getScheduler().executeAtEntity(player, () -> { ProcessPlayerEvent event = new ProcessPlayerEvent(player); Events.fire(event); - } - }); + }); + } ((SimpleFlagRegistry) WorldGuard.getInstance().getFlagRegistry()).setInitialized(true); ((SimpleDomainRegistry) WorldGuard.getInstance().getDomainRegistry()).setInitialized(true); @@ -266,7 +271,7 @@ private void setupCustomCharts(Metrics metrics) { @Override public void onDisable() { WorldGuard.getInstance().disable(); - this.getServer().getScheduler().cancelTasks(this); + getScheduler().cancelTasks(); } @Override @@ -526,4 +531,7 @@ public PlayerMoveListener getPlayerMoveListener() { return playerMoveListener; } + public SchedulerAdapter getScheduler() { + return scheduler; + } } diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java index e39a4c3cf..7bea6a62a 100644 --- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java +++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/EventAbstractionListener.java @@ -990,8 +990,7 @@ public void onInventoryMoveItem(InventoryMoveItemEvent event) { handleInventoryHolderUse(event, cause, targetHolder); if (event.isCancelled() && causeHolder instanceof Hopper && wcfg.breakDeniedHoppers) { - Bukkit.getScheduler().scheduleSyncDelayedTask(getPlugin(), - () -> ((Hopper) causeHolder).getBlock().breakNaturally()); + getPlugin().getScheduler().executeAtRegion(((Hopper) causeHolder).getLocation(), () -> ((Hopper) causeHolder).getBlock().breakNaturally()); } else { entry.setCancelled(event.isCancelled()); } diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/PlayerMoveListener.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/PlayerMoveListener.java index 7b95a5743..b1be8431f 100644 --- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/PlayerMoveListener.java +++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/PlayerMoveListener.java @@ -25,7 +25,7 @@ import com.sk89q.worldguard.bukkit.WorldGuardPlugin; import com.sk89q.worldguard.session.MoveType; import com.sk89q.worldguard.session.Session; -import org.bukkit.Bukkit; +import io.papermc.lib.PaperLib; import org.bukkit.Location; import org.bukkit.entity.AbstractHorse; import org.bukkit.entity.Entity; @@ -118,16 +118,16 @@ public void onPlayerMove(PlayerMoveEvent event) { current.eject(); vehicle.setVelocity(new Vector()); if (vehicle instanceof LivingEntity) { - vehicle.teleport(override.clone()); + PaperLib.teleportAsync(vehicle, override.clone()); } else { - vehicle.teleport(override.clone().add(0, 1, 0)); + PaperLib.teleportAsync(vehicle, override.clone().add(0, 1, 0)); } current = current.getVehicle(); } - player.teleport(override.clone().add(0, 1, 0)); + PaperLib.teleportAsync(player, override.clone().add(0, 1, 0)); - Bukkit.getScheduler().runTaskLater(getPlugin(), () -> player.teleport(override.clone().add(0, 1, 0)), 1); + getPlugin().getScheduler().runAtEntityDelayed(player, () -> PaperLib.teleportAsync(player, override.clone().add(0, 1, 0)), 1); } } } @@ -141,7 +141,7 @@ public void onPlayerQuit(PlayerQuitEvent event) { com.sk89q.worldedit.util.Location loc = session.testMoveTo(localPlayer, BukkitAdapter.adapt(event.getPlayer().getLocation()), MoveType.OTHER_CANCELLABLE); // white lie if (loc != null) { - player.teleport(BukkitAdapter.adapt(loc)); + PaperLib.teleportAsync(player, BukkitAdapter.adapt(loc)); } session.uninitialize(localPlayer); diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardVehicleListener.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardVehicleListener.java index fe604dad3..00007c4cc 100644 --- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardVehicleListener.java +++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/listener/WorldGuardVehicleListener.java @@ -28,6 +28,7 @@ import com.sk89q.worldguard.config.WorldConfiguration; import com.sk89q.worldguard.session.MoveType; import com.sk89q.worldguard.util.Locations; +import io.papermc.lib.PaperLib; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.entity.Vehicle; @@ -66,10 +67,10 @@ public void onVehicleMove(VehicleMoveEvent event) { if ((lastValid = WorldGuard.getInstance().getPlatform().getSessionManager().get(localPlayer) .testMoveTo(localPlayer, BukkitAdapter.adapt(event.getTo()), MoveType.RIDE)) != null) { vehicle.setVelocity(new Vector(0, 0, 0)); - vehicle.teleport(event.getFrom()); + PaperLib.teleportAsync(vehicle, event.getFrom()); if (Locations.isDifferentBlock(lastValid, BukkitAdapter.adapt(event.getFrom()))) { Vector dir = player.getLocation().getDirection(); - player.teleport(BukkitAdapter.adapt(lastValid).setDirection(dir)); + PaperLib.teleportAsync(player, BukkitAdapter.adapt(lastValid).setDirection(dir)); } return; } diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/scheduler/BukkitSchedulerAdapter.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/scheduler/BukkitSchedulerAdapter.java new file mode 100644 index 000000000..4d80c64a2 --- /dev/null +++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/scheduler/BukkitSchedulerAdapter.java @@ -0,0 +1,62 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.scheduler; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitScheduler; + +public class BukkitSchedulerAdapter implements SchedulerAdapter { + + private final Plugin plugin; + @SuppressWarnings("deprecation") + private final BukkitScheduler scheduler; + + public BukkitSchedulerAdapter(final Plugin plugin) { + this.plugin = plugin; + this.scheduler = plugin.getServer().getScheduler(); + } + + @Override + public void runAsyncRate(Runnable runnable, long delay, long period) { + scheduler.runTaskTimerAsynchronously(plugin, runnable, delay, period); + } + + @Override + public void executeAtEntity(Entity entity, Runnable runnable) { + scheduler.runTask(plugin, runnable); + } + + @Override + public void runAtEntityDelayed(final Entity entity, final Runnable runnable, final long delay) { + scheduler.runTaskLater(plugin, runnable, delay); + } + + @Override + public void executeAtRegion(Location location, Runnable runnable) { + scheduler.runTask(plugin, runnable); + } + + @Override + public void cancelTasks() { + scheduler.cancelTasks(plugin); + } +} diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/scheduler/FoliaSchedulerAdapter.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/scheduler/FoliaSchedulerAdapter.java new file mode 100644 index 000000000..2d63ce3a5 --- /dev/null +++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/scheduler/FoliaSchedulerAdapter.java @@ -0,0 +1,81 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.scheduler; + +import io.papermc.paper.threadedregions.scheduler.AsyncScheduler; +import io.papermc.paper.threadedregions.scheduler.RegionScheduler; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; + +import java.util.concurrent.TimeUnit; + +public class FoliaSchedulerAdapter implements SchedulerAdapter { + private static final boolean SUPPORTED = checkSupport(); + + private final Plugin plugin; + private final AsyncScheduler asyncScheduler; + private final RegionScheduler regionScheduler; + + public FoliaSchedulerAdapter(final Plugin plugin) { + this.plugin = plugin; + this.asyncScheduler = plugin.getServer().getAsyncScheduler(); + this.regionScheduler = plugin.getServer().getRegionScheduler(); + } + + public static boolean isSupported() { + return SUPPORTED; + } + + private static boolean checkSupport() { + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + + @Override + public void runAsyncRate(final Runnable runnable, final long delay, final long period) { + asyncScheduler.runAtFixedRate(plugin, task -> runnable.run(), delay * 50, period * 50, TimeUnit.MILLISECONDS); + } + + @Override + public void executeAtEntity(final Entity entity, final Runnable runnable) { + entity.getScheduler().run(plugin, task -> runnable.run(), null); + } + + @Override + public void runAtEntityDelayed(final Entity entity, final Runnable runnable, final long delay) { + entity.getScheduler().execute(plugin, runnable, null, delay); + } + + @Override + public void executeAtRegion(final Location location, final Runnable runnable) { + regionScheduler.execute(plugin, location, runnable); + } + + @Override + public void cancelTasks() { + asyncScheduler.cancelTasks(plugin); + } + +} diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/scheduler/SchedulerAdapter.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/scheduler/SchedulerAdapter.java new file mode 100644 index 000000000..f7db0e468 --- /dev/null +++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/scheduler/SchedulerAdapter.java @@ -0,0 +1,82 @@ +/* + * WorldGuard, a suite of tools for Minecraft + * Copyright (C) sk89q + * Copyright (C) WorldGuard team and contributors + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldguard.bukkit.scheduler; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; + +public interface SchedulerAdapter { + + /** + * Schedules the specified task to be executed asynchronously after the delay has passed, + * and then periodically executed with the specified period. + * + * @param runnable The task to execute. + * @param delay The time delay to pass before the task should be executed. + * @param period The time between task executions after the first execution of the task. + */ + void runAsyncRate(Runnable runnable, long delay, long period); + + /** + * Schedules a task. If the task failed to schedule because the scheduler is retired (entity removed), + * then returns {@code false}. Otherwise, either the run callback will be invoked after the specified delay, + * or the retired callback will be invoked if the scheduler is retired. + * Note that the retired callback is invoked in critical code, so it should not attempt to remove the entity, + * remove other entities, load chunks, load worlds, modify ticket levels, etc. + * + *

+ * It is guaranteed that the task and retired callback are invoked on the region which owns the entity. + *

+ * + * @param entity The entity relative to which the scheduler is obtained. + * @param runnable The task to execute. + */ + void executeAtEntity(Entity entity, Runnable runnable); + + /** + * Schedules a task with the given delay. If the task failed to schedule because the scheduler is retired (entity removed), + * then returns {@code false}. Otherwise, either the run callback will be invoked after the specified delay, + * or the retired callback will be invoked if the scheduler is retired. + * Note that the retired callback is invoked in critical code, so it should not attempt to remove the entity, + * remove other entities, load chunks, load worlds, modify ticket levels, etc. + * + *

+ * It is guaranteed that the task and retired callback are invoked on the region which owns the entity. + *

+ * + * @param entity The entity relative to which the scheduler is obtained. + * @param runnable The task to execute. + * @param delay The time delay to pass before the task should be executed, in ticks. + */ + void runAtEntityDelayed(Entity entity, Runnable runnable, long delay); + + /** + * Schedules a task to be executed on the region which owns the location. + * + * @param location The location at which the region executing should own. + * @param runnable The task to execute. + */ + void executeAtRegion(Location location, Runnable runnable); + + /** + * Attempts to cancel all tasks scheduled by the plugin. + */ + void cancelTasks(); +} diff --git a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/session/BukkitSessionManager.java b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/session/BukkitSessionManager.java index df589cbc0..c731ec4b6 100644 --- a/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/session/BukkitSessionManager.java +++ b/worldguard-bukkit/src/main/java/com/sk89q/worldguard/bukkit/session/BukkitSessionManager.java @@ -60,8 +60,7 @@ public void resetAllStates() { @EventHandler public void onPlayerProcess(ProcessPlayerEvent event) { // Pre-load a session - LocalPlayer player = WorldGuardPlugin.inst().wrapPlayer(event.getPlayer()); - get(player).initialize(player); + get(WorldGuardPlugin.inst().wrapPlayer(event.getPlayer())); } @Override diff --git a/worldguard-bukkit/src/main/resources/plugin.yml b/worldguard-bukkit/src/main/resources/plugin.yml index 223be72f7..cdee59d2b 100644 --- a/worldguard-bukkit/src/main/resources/plugin.yml +++ b/worldguard-bukkit/src/main/resources/plugin.yml @@ -4,3 +4,4 @@ version: "${internalVersion}" depend: [WorldEdit] softdepend: [CommandBook] api-version: "1.20" +folia-supported: true diff --git a/worldguard-core/src/main/java/com/sk89q/worldguard/session/Session.java b/worldguard-core/src/main/java/com/sk89q/worldguard/session/Session.java index cdc8401a3..8473e104a 100644 --- a/worldguard-core/src/main/java/com/sk89q/worldguard/session/Session.java +++ b/worldguard-core/src/main/java/com/sk89q/worldguard/session/Session.java @@ -130,8 +130,11 @@ public void uninitialize(LocalPlayer player) { Location location = player.getLocation(); ApplicableRegionSet set = query.getApplicableRegions(location); - for (Handler handler : handlers.values()) { - handler.uninitialize(player, location, set); + synchronized (this) { + for (Handler handler : handlers.values()) { + handler.uninitialize(player, location, set); + } + initialized = false; } }