diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 363dfa416..882330e5b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,6 +8,7 @@ xseries = "13.6.0+26.1" lombok = "1.18.44" annotations = "26.1.0" configurate = "4.2.0" +paperlib = "1.0.8" # Test junit = "5.14.3" junit-platform-launcher = "1.14.3" @@ -25,6 +26,7 @@ xseries = { module = "io.github.almighty-satan:XSeries", version.ref = "xseries" lombok = { module = "org.projectlombok:lombok", version.ref = "lombok" } annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" } configurate-yaml = { module = "org.spongepowered:configurate-yaml", version.ref = "configurate" } +paperlib = { module = "io.papermc:paperlib", version.ref = "paperlib" } # Test junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" } junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" } diff --git a/plugin/build.gradle b/plugin/build.gradle index 9f0c752af..033518c10 100644 --- a/plugin/build.gradle +++ b/plugin/build.gradle @@ -10,6 +10,7 @@ repositories { maven { url = "https://oss.sonatype.org/content/repositories/snapshots" } maven { url = "https://repo.codemc.io/repository/maven-public/" } maven { url = "https://repo.extendedclip.com/content/repositories/placeholderapi/" } + maven { url = "https://repo.papermc.io/repository/maven-public/" } } configurations { @@ -32,6 +33,7 @@ dependencies { implementation(libs.item.nbt.api) implementation(libs.xseries) implementation(libs.configurate.yaml) + implementation(libs.paperlib) compileOnly(libs.lombok) annotationProcessor(libs.lombok) @@ -68,6 +70,7 @@ shadowJar { relocate "org.bstats", "eu.decentsoftware.holograms.metrics" relocate "de.tr7zw.changeme.nbtapi", "eu.decentsoftware.holograms.libs.nbtapi" relocate "com.cryptomorin.xseries", "eu.decentsoftware.holograms.libs.xseries" + relocate "io.papermc.lib", "eu.decentsoftware.holograms.libs.paperlib" } publishing { diff --git a/plugin/src/main/java/eu/decentsoftware/holograms/api/DecentHolograms.java b/plugin/src/main/java/eu/decentsoftware/holograms/api/DecentHolograms.java index 60a285f75..74b0db881 100644 --- a/plugin/src/main/java/eu/decentsoftware/holograms/api/DecentHolograms.java +++ b/plugin/src/main/java/eu/decentsoftware/holograms/api/DecentHolograms.java @@ -7,6 +7,9 @@ import eu.decentsoftware.holograms.api.holograms.HologramManager; import eu.decentsoftware.holograms.api.listeners.PlayerListener; import eu.decentsoftware.holograms.api.listeners.WorldListener; +import eu.decentsoftware.holograms.api.utils.scheduler.SchedulerAdapter; +import eu.decentsoftware.holograms.api.utils.scheduler.adapters.BukkitSchedulerAdapter; +import eu.decentsoftware.holograms.api.utils.scheduler.adapters.FoliaSchedulerAdapter; import eu.decentsoftware.holograms.api.utils.BungeeUtils; import eu.decentsoftware.holograms.api.utils.Common; import eu.decentsoftware.holograms.api.utils.Log; @@ -53,6 +56,7 @@ public final class DecentHolograms { private NmsAdapter nmsAdapter; private IntegrationAvailabilityService integrationAvailabilityService; private NmsPacketListenerService nmsPacketListenerService; + private final SchedulerAdapter scheduler; private HologramManager hologramManager; private CommandManager commandManager; private FeatureManager featureManager; @@ -63,6 +67,7 @@ public final class DecentHolograms { DecentHolograms(@NonNull JavaPlugin plugin) { this.plugin = plugin; + this.scheduler = FoliaSchedulerAdapter.isSupported() ? new FoliaSchedulerAdapter(plugin) : new BukkitSchedulerAdapter(plugin); } void enable() { diff --git a/plugin/src/main/java/eu/decentsoftware/holograms/api/actions/ActionType.java b/plugin/src/main/java/eu/decentsoftware/holograms/api/actions/ActionType.java index 35af269c8..ed462127b 100644 --- a/plugin/src/main/java/eu/decentsoftware/holograms/api/actions/ActionType.java +++ b/plugin/src/main/java/eu/decentsoftware/holograms/api/actions/ActionType.java @@ -66,7 +66,7 @@ public boolean execute(Player player, String... args) { Validate.notNull(player); String string = String.join(" ", args); - Bukkit.getScheduler().runTask(DECENT_HOLOGRAMS.getPlugin(), () -> { + DECENT_HOLOGRAMS.getScheduler().executeAtEntity(player, () -> { // player.chat(PAPI.setPlaceholders(player, string.replace("{player}", player.getName()))); }); @@ -80,7 +80,7 @@ public boolean execute(Player player, String... args) { Validate.notNull(player); String string = String.join(" ", args); - Bukkit.getScheduler().runTask(DECENT_HOLOGRAMS.getPlugin(), () -> { + DECENT_HOLOGRAMS.getScheduler().executeAtEntity(player, () -> { // Bukkit.dispatchCommand(Bukkit.getConsoleSender(), PAPI.setPlaceholders(player, string.replace("{player}", player.getName()))); }); @@ -113,7 +113,7 @@ public boolean execute(Player player, String... args) { if (location == null) { return false; } - Bukkit.getScheduler().runTask(DECENT_HOLOGRAMS.getPlugin(), () -> player.teleport(location)); + DECENT_HOLOGRAMS.getScheduler().executeAtEntity(player, () -> io.papermc.lib.PaperLib.teleportAsync(player, location)); return true; } }; diff --git a/plugin/src/main/java/eu/decentsoftware/holograms/api/holograms/Hologram.java b/plugin/src/main/java/eu/decentsoftware/holograms/api/holograms/Hologram.java index d2b181061..7d2b0a32a 100644 --- a/plugin/src/main/java/eu/decentsoftware/holograms/api/holograms/Hologram.java +++ b/plugin/src/main/java/eu/decentsoftware/holograms/api/holograms/Hologram.java @@ -680,7 +680,7 @@ public boolean show(@NonNull Player player, int pageIndex) { } else { // We need to run the task later on older versions as, if we don't, it causes issues with some holograms *randomly* becoming invisible. // I *think* this is from despawning and spawning the entities (with the same ID) in the same tick. - S.sync(() -> showPageTo(player, page, pageIndex), 0L); + S.sync(player, () -> showPageTo(player, page, pageIndex), 0L); } return true; } diff --git a/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/UpdateChecker.java b/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/UpdateChecker.java index 031dde155..78749ae90 100644 --- a/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/UpdateChecker.java +++ b/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/UpdateChecker.java @@ -1,7 +1,7 @@ package eu.decentsoftware.holograms.api.utils; +import eu.decentsoftware.holograms.api.DecentHologramsAPI; import org.apache.commons.lang.Validate; -import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; import java.io.IOException; @@ -23,7 +23,7 @@ public UpdateChecker(JavaPlugin plugin, int resourceId) { } public void getVersion(Consumer consumer) { - Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { + DecentHologramsAPI.get().getScheduler().runAsync(() -> { try (InputStream inputStream = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + resourceId).openStream(); Scanner scanner = new Scanner(inputStream)) { if (scanner.hasNext() && consumer != null) { diff --git a/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/S.java b/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/S.java index 39ad14667..76631a347 100644 --- a/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/S.java +++ b/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/S.java @@ -3,36 +3,29 @@ import eu.decentsoftware.holograms.api.DecentHolograms; import eu.decentsoftware.holograms.api.DecentHologramsAPI; import lombok.experimental.UtilityClass; -import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; import org.bukkit.plugin.IllegalPluginAccessException; import org.bukkit.scheduler.BukkitTask; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; @UtilityClass public class S { private static final DecentHolograms DECENT_HOLOGRAMS = DecentHologramsAPI.get(); - public static void stopTask(int id) { - Bukkit.getScheduler().cancelTask(id); + public static void sync(Entity entity, Runnable runnable, long delay) { + DECENT_HOLOGRAMS.getScheduler().runAtEntityDelayed(entity, runnable, delay); } - public static void sync(Runnable runnable) { - Bukkit.getScheduler().runTask(DECENT_HOLOGRAMS.getPlugin(), runnable); - } - - public static BukkitTask sync(Runnable runnable, long delay) { - return Bukkit.getScheduler().runTaskLater(DECENT_HOLOGRAMS.getPlugin(), runnable, delay); - } - - public static BukkitTask syncTask(Runnable runnable, long interval) { - return Bukkit.getScheduler().runTaskTimer(DECENT_HOLOGRAMS.getPlugin(), runnable, 0, interval); + public static void sync(Entity entity, Runnable runnable) { + DECENT_HOLOGRAMS.getScheduler().executeAtEntity(entity, runnable); } public static void async(Runnable runnable) { try { - Bukkit.getScheduler().runTaskAsynchronously(DECENT_HOLOGRAMS.getPlugin(), runnable); + DECENT_HOLOGRAMS.getScheduler().runAsync(runnable); } catch (IllegalPluginAccessException e) { CompletableFuture.runAsync(runnable); } @@ -40,18 +33,14 @@ public static void async(Runnable runnable) { public static void async(Runnable runnable, long delay) { try { - Bukkit.getScheduler().runTaskLaterAsynchronously(DECENT_HOLOGRAMS.getPlugin(), runnable, delay); + DECENT_HOLOGRAMS.getScheduler().runAsyncDelayed(runnable, delay * 50, TimeUnit.MILLISECONDS); } catch (IllegalPluginAccessException e) { CompletableFuture.runAsync(runnable); } } - public static BukkitTask asyncTask(Runnable runnable, long interval) { - return Bukkit.getScheduler().runTaskTimerAsynchronously(DECENT_HOLOGRAMS.getPlugin(), runnable, 0, interval); - } - public static BukkitTask asyncTask(Runnable runnable, long interval, long delay) { - return Bukkit.getScheduler().runTaskTimerAsynchronously(DECENT_HOLOGRAMS.getPlugin(), runnable, delay, interval); + return DECENT_HOLOGRAMS.getScheduler().runAsyncRate(runnable, 0, interval * 50, TimeUnit.MILLISECONDS); } } diff --git a/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/SchedulerAdapter.java b/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/SchedulerAdapter.java new file mode 100644 index 000000000..a08250245 --- /dev/null +++ b/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/SchedulerAdapter.java @@ -0,0 +1,70 @@ +package eu.decentsoftware.holograms.api.utils.scheduler; + +import org.bukkit.entity.Entity; +import org.bukkit.scheduler.BukkitTask; + +import java.util.concurrent.TimeUnit; + +public interface SchedulerAdapter { + + /** + * Schedules the specified task to be executed asynchronously immediately. + * + * @param runnable The task to execute. + */ + void runAsync(Runnable runnable); + + /** + * Schedules the specified task to be executed asynchronously after the time delay has passed. + * + * @param runnable The task to execute. + * @param delay The time delay to pass before the task should be executed. + * @param unit The time unit for the initial delay and period. + */ + void runAsyncDelayed(Runnable runnable, long delay, TimeUnit unit); + + /** + * 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. + * @param unit The time unit for the initial delay and period. + * @return The BukkitTask that represents the scheduled task. + */ + BukkitTask runAsyncRate(Runnable runnable, long delay, long period, TimeUnit unit); + + /** + * 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); +} diff --git a/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/adapters/BukkitSchedulerAdapter.java b/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/adapters/BukkitSchedulerAdapter.java new file mode 100644 index 000000000..82affaaea --- /dev/null +++ b/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/adapters/BukkitSchedulerAdapter.java @@ -0,0 +1,42 @@ +package eu.decentsoftware.holograms.api.utils.scheduler.adapters; + +import eu.decentsoftware.holograms.api.utils.scheduler.SchedulerAdapter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; + +import java.util.concurrent.TimeUnit; + +@RequiredArgsConstructor +public class BukkitSchedulerAdapter implements SchedulerAdapter { + + private @NonNull Plugin plugin; + + @Override + public void runAsync(Runnable runnable) { + Bukkit.getScheduler().runTaskAsynchronously(plugin, runnable); + } + + @Override + public void runAsyncDelayed(Runnable runnable, long delay, TimeUnit unit) { + Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, runnable, unit.toMillis(delay) / 50); + } + + @Override + public BukkitTask runAsyncRate(Runnable runnable, long delay, long period, TimeUnit unit) { + return Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, runnable, unit.toMillis(delay) / 50, unit.toMillis(period) / 50); + } + + @Override + public void executeAtEntity(Entity entity, Runnable runnable) { + Bukkit.getScheduler().runTask(plugin, runnable); + } + + @Override + public void runAtEntityDelayed(Entity entity, Runnable runnable, long delay) { + Bukkit.getScheduler().runTaskLater(plugin, runnable, delay); + } +} diff --git a/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/adapters/FoliaSchedulerAdapter.java b/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/adapters/FoliaSchedulerAdapter.java new file mode 100644 index 000000000..517200d41 --- /dev/null +++ b/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/scheduler/adapters/FoliaSchedulerAdapter.java @@ -0,0 +1,183 @@ +package eu.decentsoftware.holograms.api.utils.scheduler.adapters; + +import eu.decentsoftware.holograms.api.utils.scheduler.SchedulerAdapter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.val; +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.Nullable; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; + +@SuppressWarnings("JavaLangInvokeHandleSignature") +@RequiredArgsConstructor +public class FoliaSchedulerAdapter implements SchedulerAdapter { + private static final boolean SUPPORTED; + + private static @Nullable MethodHandle ASYNC_SCHEDULER_RUN_NOW; + private static @Nullable MethodHandle ASYNC_SCHEDULER_RUN_DELAYED; + private static @Nullable MethodHandle ASYNC_SCHEDULER_RUN_RATE; + + private static @Nullable MethodHandle ENTITY_SCHEDULER_EXECUTE; + private static @Nullable MethodHandle ENTITY_SCHEDULER_RUN_DELAYED; + + private static @Nullable MethodHandle SCHEDULED_TASK_CANCEL; + + static { + boolean supporting = true; + try { + val lookup = MethodHandles.publicLookup(); + + val scheduledTaskType = Class.forName("io.papermc.paper.threadedregions.scheduler.ScheduledTask"); + SCHEDULED_TASK_CANCEL = lookup.findVirtual(scheduledTaskType, "cancel", MethodType.methodType( + Class.forName("io.papermc.paper.threadedregions.scheduler.ScheduledTask$CancelledState") + )); + + val asyncSchedulerType = Class.forName("io.papermc.paper.threadedregions.scheduler.AsyncScheduler"); + + val getAsyncScheduler = lookup.findVirtual(Server.class, "getAsyncScheduler", MethodType.methodType(asyncSchedulerType)); + val asyncScheduler = getAsyncScheduler.invoke(Bukkit.getServer()); + + ASYNC_SCHEDULER_RUN_NOW = lookup.findVirtual(asyncSchedulerType, "runNow", MethodType.methodType( + scheduledTaskType, Plugin.class, Consumer.class + )).bindTo(asyncScheduler); + ASYNC_SCHEDULER_RUN_DELAYED = lookup.findVirtual(asyncSchedulerType, "runDelayed", MethodType.methodType( + scheduledTaskType, Plugin.class, Consumer.class, long.class, TimeUnit.class + )).bindTo(asyncScheduler); + ASYNC_SCHEDULER_RUN_RATE = lookup.findVirtual(asyncSchedulerType, "runAtFixedRate", MethodType.methodType( + scheduledTaskType, Plugin.class, Consumer.class, long.class, long.class, TimeUnit.class + )).bindTo(asyncScheduler); + + val entitySchedulerType = Class.forName("io.papermc.paper.threadedregions.scheduler.EntityScheduler"); + + val getEntityScheduler = lookup.findVirtual(Entity.class, "getScheduler", MethodType.methodType(entitySchedulerType)); + ENTITY_SCHEDULER_EXECUTE = MethodHandles.filterArguments( + lookup.findVirtual(entitySchedulerType, "execute", MethodType.methodType( + boolean.class, Plugin.class, Runnable.class, Runnable.class, long.class + )), + 0, + getEntityScheduler + ); + ENTITY_SCHEDULER_RUN_DELAYED = MethodHandles.filterArguments( + lookup.findVirtual(entitySchedulerType, "runDelayed", MethodType.methodType( + scheduledTaskType, Plugin.class, Consumer.class, Runnable.class, long.class + )), + 0, + getEntityScheduler + ); + + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + supporting = false; + } catch (Throwable throwable) { + Logger.getLogger(FoliaSchedulerAdapter.class.getName()).log(Level.WARNING, "Error in Folia scheduler adapter initialization", throwable); + } + SUPPORTED = supporting; + } + + private final @NonNull Plugin plugin; + + public static boolean isSupported() { + return SUPPORTED; + } + + @Override + public void runAsync(Runnable runnable) { + try { + final Consumer consumer = task -> runnable.run(); + Objects.requireNonNull(ASYNC_SCHEDULER_RUN_NOW).invoke(plugin, consumer); + } catch (Throwable e) { + plugin.getLogger().log(Level.SEVERE, "Error in task scheduling by the Folia scheduler adapter", e); + } + } + + @Override + public void runAsyncDelayed(Runnable runnable, long delay, TimeUnit unit) { + try { + final Consumer consumer = task -> runnable.run(); + Objects.requireNonNull(ASYNC_SCHEDULER_RUN_DELAYED).invoke(plugin, consumer, delay, unit); + } catch (Throwable e) { + plugin.getLogger().log(Level.SEVERE, "Error in task scheduling by the Folia scheduler adapter", e); + } + } + + @Override + public BukkitTask runAsyncRate(Runnable runnable, long delay, long period, TimeUnit unit) { + try { + final Consumer consumer = task -> runnable.run(); + return new ScheduledTask(Objects.requireNonNull(ASYNC_SCHEDULER_RUN_RATE).invoke(plugin, consumer, delay, period, unit)); + } catch (Throwable e) { + plugin.getLogger().log(Level.SEVERE, "Error in task scheduling by the Folia scheduler adapter", e); + } + + return new ScheduledTask(null); + } + + @Override + public void executeAtEntity(Entity entity, Runnable runnable) { + try { + final Runnable retried = () -> { + }; + Objects.requireNonNull(ENTITY_SCHEDULER_EXECUTE).invoke(entity, plugin, runnable, retried, 0L); + } catch (Throwable e) { + plugin.getLogger().log(Level.SEVERE, "Error in task scheduling by the Folia scheduler adapter", e); + } + } + + @Override + public void runAtEntityDelayed(Entity entity, Runnable runnable, long delay) { + try { + final Consumer consumer = task -> runnable.run(); + final Runnable retried = () -> { + }; + Objects.requireNonNull(ENTITY_SCHEDULER_RUN_DELAYED).invoke(entity, plugin, consumer, retried, delay); + } catch (Throwable e) { + plugin.getLogger().log(Level.SEVERE, "Error in task scheduling by the Folia scheduler adapter", e); + } + } + + @RequiredArgsConstructor + private class ScheduledTask implements BukkitTask { + + private final Object task; + + @Override + public int getTaskId() { + return 0; + } + + @Override + public Plugin getOwner() { + return null; + } + + @Override + public boolean isSync() { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public void cancel() { + try { + Objects.requireNonNull(SCHEDULED_TASK_CANCEL).invoke(task); + } catch (Throwable e) { + plugin.getLogger().log(Level.SEVERE, "Error in task canceling by the Folia scheduler adapter", e); + } + } + } +} diff --git a/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/tick/Ticker.java b/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/tick/Ticker.java index c33c9249a..d3a603ea9 100644 --- a/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/tick/Ticker.java +++ b/plugin/src/main/java/eu/decentsoftware/holograms/api/utils/tick/Ticker.java @@ -30,7 +30,6 @@ public Ticker() { * Stop the ticker and unregister all ticked objects. */ public void destroy() { - S.stopTask(taskId); tickedObjects.clear(); } diff --git a/plugin/src/main/java/eu/decentsoftware/holograms/plugin/commands/HologramSubCommand.java b/plugin/src/main/java/eu/decentsoftware/holograms/plugin/commands/HologramSubCommand.java index b01e469e2..be6c1dd71 100644 --- a/plugin/src/main/java/eu/decentsoftware/holograms/plugin/commands/HologramSubCommand.java +++ b/plugin/src/main/java/eu/decentsoftware/holograms/plugin/commands/HologramSubCommand.java @@ -1096,7 +1096,7 @@ public CommandHandler getCommandHandler() { return (sender, args) -> { Hologram hologram = Validator.getHologram(args[0], Lang.HOLOGRAM_DOES_NOT_EXIST.getValue()); Player player = Validator.getPlayer(sender); - player.teleport(hologram.getLocation()); + io.papermc.lib.PaperLib.teleportAsync(player, hologram.getLocation()); Lang.HOLOGRAM_TELEPORTED.send(sender); return true; diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index 31961f337..33705eaaf 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -1,5 +1,6 @@ main: "eu.decentsoftware.holograms.plugin.DecentHologramsPlugin" api-version: 1.13 +folia-supported: true softdepend: - "PlaceholderAPI" - "HeadDatabase"