diff --git a/src/main/java/top/vulpine/simpleLobby/SimpleLobby.java b/src/main/java/top/vulpine/simpleLobby/SimpleLobby.java index 87e6389..9a03076 100644 --- a/src/main/java/top/vulpine/simpleLobby/SimpleLobby.java +++ b/src/main/java/top/vulpine/simpleLobby/SimpleLobby.java @@ -6,6 +6,9 @@ import top.vulpine.simpleLobby.command.SpawnCommand; import top.vulpine.simpleLobby.listener.PlayerListener; import top.vulpine.simpleLobby.listener.WorldListener; +import top.vulpine.simpleLobby.scheduler.BukkitSchedulerAdapter; +import top.vulpine.simpleLobby.scheduler.FoliaScheduler; +import top.vulpine.simpleLobby.scheduler.SchedulerAdapter; import top.vulpine.simpleLobby.utils.ActionParser; import top.vulpine.simpleLobby.utils.logger.LogLevel; import top.vulpine.simpleLobby.utils.logger.Logger; @@ -19,6 +22,7 @@ public final class SimpleLobby extends JavaPlugin { private ActionParser actionParser; + private SchedulerAdapter scheduler; private static final int PLUGIN_ID = 28227; @@ -36,6 +40,17 @@ public void onEnable() { } Logger.init(logLevel); + boolean folia; + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + folia = true; + } catch (ClassNotFoundException e) { + folia = false; + } + this.scheduler = folia ? new FoliaScheduler(this) : new BukkitSchedulerAdapter(this); + Logger.debug("Detected " + (folia ? "Folia" : "Bukkit/Spigot") + " server, using " + + scheduler.getClass().getSimpleName()); + String[] message = { "", " _____ __", diff --git a/src/main/java/top/vulpine/simpleLobby/UpdateNotifier.java b/src/main/java/top/vulpine/simpleLobby/UpdateNotifier.java index 79c1c4d..2ba4bcb 100644 --- a/src/main/java/top/vulpine/simpleLobby/UpdateNotifier.java +++ b/src/main/java/top/vulpine/simpleLobby/UpdateNotifier.java @@ -8,30 +8,30 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.plugin.java.JavaPlugin; import top.vulpine.simpleLobby.utils.Colorize; import top.vulpine.simpleLobby.utils.PermissionChecker; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; +import java.util.concurrent.TimeUnit; public class UpdateNotifier implements Listener { - private final JavaPlugin plugin; + private final SimpleLobby plugin; private final String projectSlug; private final String message; private volatile String cachedLatestVersion; - public UpdateNotifier(JavaPlugin plugin, String projectSlug, String message) { + public UpdateNotifier(SimpleLobby plugin, String projectSlug, String message) { this.plugin = plugin; this.projectSlug = projectSlug; this.message = message; Bukkit.getPluginManager().registerEvents(this, plugin); - Bukkit.getScheduler().runTaskAsynchronously(plugin, this::updateCache); - Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, this::updateCache, 20L * 60L * 30L, 20L * 60L * 30L); + plugin.getScheduler().runAsync(this::updateCache); + plugin.getScheduler().runAsyncRepeating(this::updateCache, 30, 30, TimeUnit.MINUTES); } private void updateCache() { diff --git a/src/main/java/top/vulpine/simpleLobby/command/SpawnCommand.java b/src/main/java/top/vulpine/simpleLobby/command/SpawnCommand.java index 6430bab..7a4f3df 100644 --- a/src/main/java/top/vulpine/simpleLobby/command/SpawnCommand.java +++ b/src/main/java/top/vulpine/simpleLobby/command/SpawnCommand.java @@ -11,9 +11,8 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerMoveEvent; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.scheduler.BukkitTask; import top.vulpine.simpleLobby.SimpleLobby; +import top.vulpine.simpleLobby.scheduler.Cancellable; import top.vulpine.simpleLobby.utils.ActionParser; import top.vulpine.simpleLobby.utils.Colorize; import top.vulpine.simpleLobby.utils.PermissionChecker; @@ -31,7 +30,7 @@ public class SpawnCommand implements CommandExecutor, TabCompleter, Listener { private final SimpleLobby plugin; private final ActionParser actionParser; - private final Map tasks = new ConcurrentHashMap<>(); + private final Map tasks = new ConcurrentHashMap<>(); private final Map locations = new ConcurrentHashMap<>(); public SpawnCommand(SimpleLobby plugin) { @@ -78,30 +77,22 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String UUID uuid = player.getUniqueId(); locations.put(uuid, player.getLocation().clone()); - BukkitTask task = new BukkitRunnable() { + Cancellable task = plugin.getScheduler().runEntityLater(player, () -> { + PlayerUtils.teleportPlayer(plugin, player); + tasks.remove(uuid); + locations.remove(uuid); - @Override - public void run() { - PlayerUtils.teleportPlayer(plugin, player); - tasks.remove(uuid); - locations.remove(uuid); - - List teleportActions = plugin.getConfig().getStringList("spawn.actions.teleported"); - actionParser.executeActions(teleportActions, player, 0, new HashMap<>()); - } - - }.runTaskLater(plugin, seconds * 20L); + List teleportActions = plugin.getConfig().getStringList("spawn.actions.teleported"); + actionParser.executeActions(teleportActions, player, 0, new HashMap<>()); + }, seconds * 20L); tasks.put(uuid, task); } else { - new BukkitRunnable() { - @Override - public void run() { - PlayerUtils.teleportPlayer(plugin, player); - } - }.runTaskLater(plugin, seconds * 20L); + plugin.getScheduler().runEntityLater(player, + () -> PlayerUtils.teleportPlayer(plugin, player), + seconds * 20L); } @@ -136,7 +127,7 @@ public void onPlayerMove(PlayerMoveEvent event) { if (from.getBlockX() != to.getBlockX() || from.getBlockY() != to.getBlockY() || from.getBlockZ() != to.getBlockZ()) { - BukkitTask task = tasks.remove(uuid); + Cancellable task = tasks.remove(uuid); if (task != null) task.cancel(); locations.remove(uuid); diff --git a/src/main/java/top/vulpine/simpleLobby/scheduler/BukkitSchedulerAdapter.java b/src/main/java/top/vulpine/simpleLobby/scheduler/BukkitSchedulerAdapter.java new file mode 100644 index 0000000..ab033e8 --- /dev/null +++ b/src/main/java/top/vulpine/simpleLobby/scheduler/BukkitSchedulerAdapter.java @@ -0,0 +1,79 @@ +package top.vulpine.simpleLobby.scheduler; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; + +import java.util.concurrent.TimeUnit; + +public class BukkitSchedulerAdapter implements SchedulerAdapter { + + private final Plugin plugin; + + public BukkitSchedulerAdapter(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public void runGlobal(Runnable task) { + if (Bukkit.isPrimaryThread()) { + task.run(); + } else { + Bukkit.getScheduler().runTask(plugin, task); + } + } + + @Override + public void runEntity(Entity entity, Runnable task) { + if (Bukkit.isPrimaryThread()) { + task.run(); + } else { + Bukkit.getScheduler().runTask(plugin, task); + } + } + + @Override + public Cancellable runEntityLater(Entity entity, Runnable task, long ticks) { + BukkitTask bt = Bukkit.getScheduler().runTaskLater(plugin, task, Math.max(1L, ticks)); + return bt::cancel; + } + + @Override + public Cancellable runGlobalLater(Runnable task, long ticks) { + BukkitTask bt = Bukkit.getScheduler().runTaskLater(plugin, task, Math.max(1L, ticks)); + return bt::cancel; + } + + @Override + public Cancellable runAsync(Runnable task) { + BukkitTask bt = Bukkit.getScheduler().runTaskAsynchronously(plugin, task); + return bt::cancel; + } + + @Override + public Cancellable runAsyncLater(Runnable task, long delay, TimeUnit unit) { + long ticks = Math.max(1L, unit.toMillis(delay) / 50L); + BukkitTask bt = Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, task, ticks); + return bt::cancel; + } + + @Override + public Cancellable runAsyncRepeating(Runnable task, long initialDelay, long period, TimeUnit unit) { + long delayTicks = Math.max(1L, unit.toMillis(initialDelay) / 50L); + long periodTicks = Math.max(1L, unit.toMillis(period) / 50L); + BukkitTask bt = Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, task, delayTicks, periodTicks); + return bt::cancel; + } + + @Override + public void teleport(Player player, Location location) { + if (Bukkit.isPrimaryThread()) { + player.teleport(location); + } else { + Bukkit.getScheduler().runTask(plugin, () -> player.teleport(location)); + } + } +} diff --git a/src/main/java/top/vulpine/simpleLobby/scheduler/Cancellable.java b/src/main/java/top/vulpine/simpleLobby/scheduler/Cancellable.java new file mode 100644 index 0000000..4e46d3a --- /dev/null +++ b/src/main/java/top/vulpine/simpleLobby/scheduler/Cancellable.java @@ -0,0 +1,13 @@ +package top.vulpine.simpleLobby.scheduler; + +/** + * Handle to a scheduled task that can be cancelled. + * Returned by {@link SchedulerAdapter} delayed-task methods. + */ +@FunctionalInterface +public interface Cancellable { + + Cancellable NOOP = () -> {}; + + void cancel(); +} diff --git a/src/main/java/top/vulpine/simpleLobby/scheduler/FoliaScheduler.java b/src/main/java/top/vulpine/simpleLobby/scheduler/FoliaScheduler.java new file mode 100644 index 0000000..56163be --- /dev/null +++ b/src/main/java/top/vulpine/simpleLobby/scheduler/FoliaScheduler.java @@ -0,0 +1,172 @@ +package top.vulpine.simpleLobby.scheduler; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import java.lang.reflect.Method; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * Folia implementation of {@link SchedulerAdapter} using reflection. + */ +public class FoliaScheduler implements SchedulerAdapter { + + private final Plugin plugin; + + private final Method entityGetScheduler; + private final Method entitySchedulerRun; + private final Method entitySchedulerRunDelayed; + private final Method bukkitGetGlobalScheduler; + private final Method globalSchedulerExecute; + private final Method globalSchedulerRunDelayed; + private final Method asyncSchedulerRunNow; + private final Method asyncSchedulerRunDelayed; + private final Method asyncSchedulerRunAtFixedRate; + private final Method scheduledTaskCancel; + private final Method playerTeleportAsync; + + public FoliaScheduler(Plugin plugin) { + this.plugin = plugin; + try { + this.entityGetScheduler = Entity.class.getMethod("getScheduler"); + + Class entitySchedulerCls = Class.forName("io.papermc.paper.threadedregions.scheduler.EntityScheduler"); + this.entitySchedulerRun = entitySchedulerCls.getMethod("run", Plugin.class, Consumer.class, Runnable.class); + this.entitySchedulerRunDelayed = entitySchedulerCls.getMethod("runDelayed", Plugin.class, Consumer.class, Runnable.class, long.class); + + this.bukkitGetGlobalScheduler = Bukkit.class.getMethod("getGlobalRegionScheduler"); + Class globalSchedulerCls = Class.forName("io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler"); + this.globalSchedulerExecute = globalSchedulerCls.getMethod("execute", Plugin.class, Runnable.class); + this.globalSchedulerRunDelayed = globalSchedulerCls.getMethod("runDelayed", Plugin.class, Consumer.class, long.class); + + Method getAsyncScheduler = Bukkit.class.getMethod("getServer"); + Object server = getAsyncScheduler.invoke(null); + Method serverAsync = server.getClass().getMethod("getAsyncScheduler"); + Class asyncSchedulerCls = Class.forName("io.papermc.paper.threadedregions.scheduler.AsyncScheduler"); + this.asyncSchedulerRunNow = asyncSchedulerCls.getMethod("runNow", Plugin.class, Consumer.class); + this.asyncSchedulerRunDelayed = asyncSchedulerCls.getMethod("runDelayed", Plugin.class, Consumer.class, long.class, TimeUnit.class); + this.asyncSchedulerRunAtFixedRate = asyncSchedulerCls.getMethod("runAtFixedRate", Plugin.class, Consumer.class, long.class, long.class, TimeUnit.class); + + Class scheduledTaskCls = Class.forName("io.papermc.paper.threadedregions.scheduler.ScheduledTask"); + this.scheduledTaskCancel = scheduledTaskCls.getMethod("cancel"); + + this.playerTeleportAsync = Player.class.getMethod("teleportAsync", Location.class); + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("Failed to bind Folia scheduler reflection", e); + } + } + + @Override + public void runGlobal(Runnable task) { + try { + Object globalSched = bukkitGetGlobalScheduler.invoke(null); + globalSchedulerExecute.invoke(globalSched, plugin, task); + } catch (ReflectiveOperationException e) { + plugin.getLogger().warning("FoliaScheduler#runGlobal failed: " + e.getMessage()); + } + } + + @Override + public void runEntity(Entity entity, Runnable task) { + try { + Object entSched = entityGetScheduler.invoke(entity); + Consumer consumer = ignored -> task.run(); + entitySchedulerRun.invoke(entSched, plugin, consumer, null); + } catch (ReflectiveOperationException e) { + plugin.getLogger().warning("FoliaScheduler#runEntity failed: " + e.getMessage()); + } + } + + @Override + public Cancellable runEntityLater(Entity entity, Runnable task, long ticks) { + try { + Object entSched = entityGetScheduler.invoke(entity); + Consumer consumer = ignored -> task.run(); + Object scheduledTask = entitySchedulerRunDelayed.invoke( + entSched, plugin, consumer, null, Math.max(1L, ticks)); + return cancellableOf(scheduledTask); + } catch (ReflectiveOperationException e) { + plugin.getLogger().warning("FoliaScheduler#runEntityLater failed: " + e.getMessage()); + return Cancellable.NOOP; + } + } + + @Override + public Cancellable runGlobalLater(Runnable task, long ticks) { + try { + Object globalSched = bukkitGetGlobalScheduler.invoke(null); + Consumer consumer = ignored -> task.run(); + Object scheduledTask = globalSchedulerRunDelayed.invoke( + globalSched, plugin, consumer, Math.max(1L, ticks)); + return cancellableOf(scheduledTask); + } catch (ReflectiveOperationException e) { + plugin.getLogger().warning("FoliaScheduler#runGlobalLater failed: " + e.getMessage()); + return Cancellable.NOOP; + } + } + + @Override + public Cancellable runAsync(Runnable task) { + try { + Object server = Bukkit.class.getMethod("getServer").invoke(null); + Object asyncSched = server.getClass().getMethod("getAsyncScheduler").invoke(server); + Consumer consumer = ignored -> task.run(); + Object scheduledTask = asyncSchedulerRunNow.invoke(asyncSched, plugin, consumer); + return cancellableOf(scheduledTask); + } catch (ReflectiveOperationException e) { + plugin.getLogger().warning("FoliaScheduler#runAsync failed: " + e.getMessage()); + return Cancellable.NOOP; + } + } + + @Override + public Cancellable runAsyncLater(Runnable task, long delay, TimeUnit unit) { + try { + Object server = Bukkit.class.getMethod("getServer").invoke(null); + Object asyncSched = server.getClass().getMethod("getAsyncScheduler").invoke(server); + Consumer consumer = ignored -> task.run(); + Object scheduledTask = asyncSchedulerRunDelayed.invoke(asyncSched, plugin, consumer, Math.max(1L, delay), unit); + return cancellableOf(scheduledTask); + } catch (ReflectiveOperationException e) { + plugin.getLogger().warning("FoliaScheduler#runAsyncLater failed: " + e.getMessage()); + return Cancellable.NOOP; + } + } + + @Override + public Cancellable runAsyncRepeating(Runnable task, long initialDelay, long period, TimeUnit unit) { + try { + Object server = Bukkit.class.getMethod("getServer").invoke(null); + Object asyncSched = server.getClass().getMethod("getAsyncScheduler").invoke(server); + Consumer consumer = ignored -> task.run(); + Object scheduledTask = asyncSchedulerRunAtFixedRate.invoke(asyncSched, plugin, consumer, Math.max(1L, initialDelay), Math.max(1L, period), unit); + return cancellableOf(scheduledTask); + } catch (ReflectiveOperationException e) { + plugin.getLogger().warning("FoliaScheduler#runAsyncRepeating failed: " + e.getMessage()); + return Cancellable.NOOP; + } + } + + @Override + public void teleport(Player player, Location location) { + try { + playerTeleportAsync.invoke(player, location); + } catch (ReflectiveOperationException e) { + plugin.getLogger().warning("FoliaScheduler#teleport failed: " + e.getMessage()); + } + } + + private Cancellable cancellableOf(Object scheduledTask) { + if (scheduledTask == null) return Cancellable.NOOP; + return () -> { + try { + scheduledTaskCancel.invoke(scheduledTask); + } catch (ReflectiveOperationException ignored) { + } + }; + } +} diff --git a/src/main/java/top/vulpine/simpleLobby/scheduler/SchedulerAdapter.java b/src/main/java/top/vulpine/simpleLobby/scheduler/SchedulerAdapter.java new file mode 100644 index 0000000..ea8f21d --- /dev/null +++ b/src/main/java/top/vulpine/simpleLobby/scheduler/SchedulerAdapter.java @@ -0,0 +1,38 @@ +package top.vulpine.simpleLobby.scheduler; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +import java.util.concurrent.TimeUnit; + +/** + * Abstraction over the Bukkit and Folia schedulers. + * Implementations route work to the correct thread for the running server flavor. + */ +public interface SchedulerAdapter { + + /** Run on the global region (Folia) or the main thread (Bukkit). */ + void runGlobal(Runnable task); + + /** Run on the entity's region (Folia) or the main thread (Bukkit). */ + void runEntity(Entity entity, Runnable task); + + /** Schedule a task on the entity's region after the given tick delay. */ + Cancellable runEntityLater(Entity entity, Runnable task, long ticks); + + /** Schedule a task on the global region after the given tick delay. */ + Cancellable runGlobalLater(Runnable task, long ticks); + + /** Runs a task asynchronously. */ + Cancellable runAsync(Runnable task); + + /** Runs a delayed asynchronous task. */ + Cancellable runAsyncLater(Runnable task, long delay, TimeUnit unit); + + /** Runs a repeating asynchronous task. */ + Cancellable runAsyncRepeating(Runnable task, long initialDelay, long period, TimeUnit unit); + + /** Teleport a player in a way that is safe on both Bukkit and Folia. */ + void teleport(Player player, Location location); +} diff --git a/src/main/java/top/vulpine/simpleLobby/utils/ActionParser.java b/src/main/java/top/vulpine/simpleLobby/utils/ActionParser.java index 22febfb..47919a9 100644 --- a/src/main/java/top/vulpine/simpleLobby/utils/ActionParser.java +++ b/src/main/java/top/vulpine/simpleLobby/utils/ActionParser.java @@ -123,9 +123,9 @@ private void executeCommand(String params, Player player) { String command = parts[1].trim().replace("%player%", player.getName()); if (target.equalsIgnoreCase("console")) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command); + plugin.getScheduler().runGlobal(() -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command)); } else if (target.equalsIgnoreCase("player")) { - player.performCommand(command); + plugin.getScheduler().runEntity(player, () -> player.performCommand(command)); } } @@ -153,10 +153,10 @@ private void executeGamemode(String params, Player player) { if (target.equalsIgnoreCase("global")) { for (Player p : Bukkit.getOnlinePlayers()) { - p.setGameMode(gamemode); + plugin.getScheduler().runEntity(p, () -> p.setGameMode(gamemode)); } } else if (target.equalsIgnoreCase("player")) { - player.setGameMode(gamemode); + plugin.getScheduler().runEntity(player, () -> player.setGameMode(gamemode)); } } @@ -180,17 +180,16 @@ private void executeTitle(String params, Player player, Map plac int stay = parts.length > 4 ? Integer.parseInt(parts[4].trim()) : 40; int fadeOut = parts.length > 5 ? Integer.parseInt(parts[5].trim()) : 10; - title = replacePlaceholders(player, title, placeholders); - subtitle = replacePlaceholders(player, subtitle, placeholders); + String finalTitle = replacePlaceholders(player, title, placeholders); + String finalSubtitle = replacePlaceholders(player, subtitle, placeholders); if (target.equalsIgnoreCase("global")) { for (Player p : Bukkit.getOnlinePlayers()) { - p.sendTitle(title, subtitle, fadeIn, stay, fadeOut); + plugin.getScheduler().runEntity(p, () -> p.sendTitle(finalTitle, finalSubtitle, fadeIn, stay, fadeOut)); } } else if (target.equalsIgnoreCase("player")) { - player.sendTitle(title, subtitle, fadeIn, stay, fadeOut); + plugin.getScheduler().runEntity(player, () -> player.sendTitle(finalTitle, finalSubtitle, fadeIn, stay, fadeOut)); } - } /** @@ -209,10 +208,10 @@ private void executeActionBar(String params, Player player, Map if (target.equalsIgnoreCase("global")) { for (Player p : Bukkit.getOnlinePlayers()) { - p.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message)); + plugin.getScheduler().runEntity(p, () -> p.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message))); } } else if (target.equalsIgnoreCase("player")) { - player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message)); + plugin.getScheduler().runEntity(player, () -> player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(message))); } } @@ -231,14 +230,14 @@ private void executeMessage(String params, Player player, Map pl String target = parts[0].trim(); String message = Colorize.color(parts[1].trim()); - message = replacePlaceholders(player, message, placeholders); + String finalMessage = replacePlaceholders(player, message, placeholders); if (target.equalsIgnoreCase("global")) { for (Player p : Bukkit.getOnlinePlayers()) { - p.sendMessage(message); + plugin.getScheduler().runEntity(p, () -> p.sendMessage(finalMessage)); } } else if (target.equalsIgnoreCase("player")) { - player.sendMessage(message); + plugin.getScheduler().runEntity(player, () -> player.sendMessage(finalMessage)); } } @@ -262,10 +261,10 @@ private void executeSound(String params, Player player) { if (target.equalsIgnoreCase("global")) { for (Player p : Bukkit.getOnlinePlayers()) { - p.playSound(p.getLocation(), sound, volume, pitch); + plugin.getScheduler().runEntity(p, () -> p.playSound(p.getLocation(), sound, volume, pitch)); } } else if (target.equalsIgnoreCase("player")) { - player.playSound(player.getLocation(), sound, volume, pitch); + plugin.getScheduler().runEntity(player, () -> player.playSound(player.getLocation(), sound, volume, pitch)); } } @@ -282,8 +281,11 @@ private void executeSound(String params, Player player) { private void executeDelay(String params, List actions, Player player, int nextIndex, Map placeholders) { int delay = Integer.parseInt(params.trim()); + long ticks = delay / 50L; - plugin.getServer().getScheduler().runTaskLater(plugin, () -> executeActions(actions, player, nextIndex, placeholders), delay / 50L); + plugin.getScheduler().runEntityLater(player, + () -> executeActions(actions, player, nextIndex, placeholders), + ticks); } diff --git a/src/main/java/top/vulpine/simpleLobby/utils/PlayerUtils.java b/src/main/java/top/vulpine/simpleLobby/utils/PlayerUtils.java index 4e79372..df76bd1 100644 --- a/src/main/java/top/vulpine/simpleLobby/utils/PlayerUtils.java +++ b/src/main/java/top/vulpine/simpleLobby/utils/PlayerUtils.java @@ -35,7 +35,7 @@ public static void teleportPlayer(SimpleLobby plugin, Player player) { float yaw = (float) config.getDouble("spawn.location.yaw"); float pitch = (float) config.getDouble("spawn.location.pitch"); - player.teleport(new Location(world, x, y, z, yaw, pitch)); + plugin.getScheduler().teleport(player, new Location(world, x, y, z, yaw, pitch)); Logger.debug("Teleported player " + player.getName() + " to spawn at " + world + " (" + x + ", " + y + ", " + z + ")"); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 2cb9f51..8487053 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,6 +2,7 @@ name: SimpleLobby version: '1.2.3' main: top.vulpine.simpleLobby.SimpleLobby api-version: '1.13' +folia-supported: true authors: [ VulpineFriend87 ] website: https://vulpine.top softdepend: