diff --git a/build.gradle b/build.gradle index ad278fd..60d78f4 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { allprojects { apply plugin: 'java' - version = '0.3.1' + version = '0.3.2' repositories { diff --git a/platform/common/src/main/java/com/tcoded/folialib/impl/ServerImplementation.java b/platform/common/src/main/java/com/tcoded/folialib/impl/ServerImplementation.java index c0f562a..a1d459a 100644 --- a/platform/common/src/main/java/com/tcoded/folialib/impl/ServerImplementation.java +++ b/platform/common/src/main/java/com/tcoded/folialib/impl/ServerImplementation.java @@ -6,6 +6,7 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -437,6 +438,18 @@ public interface ServerImplementation { */ void cancelAllTasks(); + /** + * Get all tasks owned by this plugin + * @return WrappedTask instances + */ + List getAllTasks(); + + /** + * Get all tasks across the server + * @return WrappedTask instances + */ + List getAllServerTasks(); + /** * Get a player by name (approximately). * When using folia, this can be run sync or async. If this is run async on non-folia platforms, it will block diff --git a/platform/common/src/main/java/com/tcoded/folialib/wrapper/task/WrappedTask.java b/platform/common/src/main/java/com/tcoded/folialib/wrapper/task/WrappedTask.java index a4ca7f3..8958529 100644 --- a/platform/common/src/main/java/com/tcoded/folialib/wrapper/task/WrappedTask.java +++ b/platform/common/src/main/java/com/tcoded/folialib/wrapper/task/WrappedTask.java @@ -11,4 +11,13 @@ public interface WrappedTask { Plugin getOwningPlugin(); + /** + * Whether the task is async or not + *

+ * Async tasks are never run on any world threads, including on Folia + * + * @return true if the task is async + */ + boolean isAsync(); + } diff --git a/platform/folia/src/main/java/com/tcoded/folialib/impl/FoliaImplementation.java b/platform/folia/src/main/java/com/tcoded/folialib/impl/FoliaImplementation.java index 6f8be15..0332682 100644 --- a/platform/folia/src/main/java/com/tcoded/folialib/impl/FoliaImplementation.java +++ b/platform/folia/src/main/java/com/tcoded/folialib/impl/FoliaImplementation.java @@ -9,15 +9,22 @@ import io.papermc.paper.threadedregions.scheduler.AsyncScheduler; import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler; import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import java.util.stream.Collectors; @SuppressWarnings("unused") @@ -363,7 +370,72 @@ public void cancelAllTasks() { this.asyncScheduler.cancelTasks(plugin); } - @Override + @Override + public List getAllTasks() { + try { + // Filter and wrap + return getAllScheduledTasks().stream() + .filter(task -> task.getOwningPlugin().equals(plugin)) + .map(this::wrapTask) + .collect(Collectors.toList()); + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + @Override + public List getAllServerTasks() { + try { + // Filter and wrap + return getAllScheduledTasks().stream() + .map(this::wrapTask) + .collect(Collectors.toList()); + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + @NotNull + private List getAllScheduledTasks() throws NoSuchFieldException, IllegalAccessException { + // Global tasks + Class globalClass = this.globalRegionScheduler.getClass(); + + Field tasksByDeadlineField = globalClass.getDeclaredField("tasksByDeadline"); + boolean wasAccessible = tasksByDeadlineField.isAccessible(); + tasksByDeadlineField.setAccessible(true); + + // noinspection unchecked + Long2ObjectOpenHashMap> globalTasksMap = (Long2ObjectOpenHashMap>) tasksByDeadlineField.get(this.globalRegionScheduler); + tasksByDeadlineField.setAccessible(wasAccessible); + + // Async tasks + Class asyncClass = this.asyncScheduler.getClass(); + + Field asyncTasksField = asyncClass.getDeclaredField("tasks"); + wasAccessible = asyncTasksField.isAccessible(); + asyncTasksField.setAccessible(true); + + Set asyncTasks = (Set) asyncTasksField.get(this.asyncScheduler); + asyncTasksField.setAccessible(wasAccessible); + + // Combine global tasks + List globalTasks = new ArrayList<>(); + for (List list : globalTasksMap.values()) { + globalTasks.addAll(list); + } + + // Combine all tasks + List allTasks = new ArrayList<>(globalTasks.size() + asyncTasks.size()); + allTasks.addAll(globalTasks); + allTasks.addAll(asyncTasks); + return allTasks; + } + + @Override public Player getPlayer(String name) { // This is thread-safe in folia return this.plugin.getServer().getPlayer(name); diff --git a/platform/folia/src/main/java/com/tcoded/folialib/wrapper/task/WrappedFoliaTask.java b/platform/folia/src/main/java/com/tcoded/folialib/wrapper/task/WrappedFoliaTask.java index fede0ad..41e4a25 100644 --- a/platform/folia/src/main/java/com/tcoded/folialib/wrapper/task/WrappedFoliaTask.java +++ b/platform/folia/src/main/java/com/tcoded/folialib/wrapper/task/WrappedFoliaTask.java @@ -6,10 +6,28 @@ public class WrappedFoliaTask implements WrappedTask { + private static final Class ASYNC_TASK_CLASS; + + static { + Class asyncTaskClass = null; + try { + // noinspection unchecked + asyncTaskClass = (Class) Class.forName("io.papermc.paper.threadedregions.scheduler.FoliaAsyncScheduler.AsyncScheduledTask"); + } catch (ClassNotFoundException e) { + // ignore + } + ASYNC_TASK_CLASS = asyncTaskClass; + } + private final ScheduledTask task; + private final boolean async; + public WrappedFoliaTask(ScheduledTask task) { this.task = task; + + if (ASYNC_TASK_CLASS == null) this.async = false; + else this.async = ASYNC_TASK_CLASS.isAssignableFrom(task.getClass()); } @Override @@ -26,4 +44,9 @@ public boolean isCancelled() { public Plugin getOwningPlugin() { return this.task.getOwningPlugin(); } + + @Override + public boolean isAsync() { + return this.async; + } } diff --git a/platform/legacy-spigot/src/main/java/com/tcoded/folialib/impl/LegacySpigotImplementation.java b/platform/legacy-spigot/src/main/java/com/tcoded/folialib/impl/LegacySpigotImplementation.java index 0074e43..913e203 100644 --- a/platform/legacy-spigot/src/main/java/com/tcoded/folialib/impl/LegacySpigotImplementation.java +++ b/platform/legacy-spigot/src/main/java/com/tcoded/folialib/impl/LegacySpigotImplementation.java @@ -15,12 +15,14 @@ import org.bukkit.scheduler.BukkitTask; import org.jetbrains.annotations.NotNull; +import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.stream.Collectors; @SuppressWarnings("unused") public class LegacySpigotImplementation implements ServerImplementation { @@ -297,6 +299,21 @@ public void cancelAllTasks() { this.scheduler.cancelTasks(plugin); } + @Override + public List getAllTasks() { + return this.scheduler.getPendingTasks().stream() + .filter(task -> task.getOwner().equals(plugin)) + .map(this::wrapTask) + .collect(Collectors.toList()); + } + + @Override + public List getAllServerTasks() { + return this.scheduler.getPendingTasks().stream() + .map(this::wrapTask) + .collect(Collectors.toList()); + } + @Override public Player getPlayer(String name) { return this.getPlayerFromMainThread(() -> this.plugin.getServer().getPlayer(name)); diff --git a/platform/legacy-spigot/src/main/java/com/tcoded/folialib/wrapper/task/WrappedLegacyBukkitTask.java b/platform/legacy-spigot/src/main/java/com/tcoded/folialib/wrapper/task/WrappedLegacyBukkitTask.java index 1d44acd..09dfe98 100644 --- a/platform/legacy-spigot/src/main/java/com/tcoded/folialib/wrapper/task/WrappedLegacyBukkitTask.java +++ b/platform/legacy-spigot/src/main/java/com/tcoded/folialib/wrapper/task/WrappedLegacyBukkitTask.java @@ -30,4 +30,10 @@ public boolean isCancelled() { public Plugin getOwningPlugin() { return this.task.getOwner(); } + + @Override + public boolean isAsync() { + return !this.task.isSync(); + } + } diff --git a/platform/spigot/src/main/java/com/tcoded/folialib/impl/SpigotImplementation.java b/platform/spigot/src/main/java/com/tcoded/folialib/impl/SpigotImplementation.java index 351a581..0367931 100644 --- a/platform/spigot/src/main/java/com/tcoded/folialib/impl/SpigotImplementation.java +++ b/platform/spigot/src/main/java/com/tcoded/folialib/impl/SpigotImplementation.java @@ -13,12 +13,14 @@ import org.bukkit.scheduler.BukkitTask; import org.jetbrains.annotations.NotNull; +import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.stream.Collectors; @SuppressWarnings("unused") public class SpigotImplementation implements ServerImplementation { @@ -259,6 +261,21 @@ public void cancelAllTasks() { this.scheduler.cancelTasks(plugin); } + @Override + public List getAllTasks() { + return this.scheduler.getPendingTasks().stream() + .filter(task -> task.getOwner().equals(plugin)) + .map(this::wrapTask) + .collect(Collectors.toList()); + } + + @Override + public List getAllServerTasks() { + return this.scheduler.getPendingTasks().stream() + .map(this::wrapTask) + .collect(Collectors.toList()); + } + @Override public Player getPlayer(String name) { return this.getPlayerFromMainThread(() -> this.plugin.getServer().getPlayer(name)); diff --git a/platform/spigot/src/main/java/com/tcoded/folialib/wrapper/task/WrappedBukkitTask.java b/platform/spigot/src/main/java/com/tcoded/folialib/wrapper/task/WrappedBukkitTask.java index 753e653..2e4f850 100644 --- a/platform/spigot/src/main/java/com/tcoded/folialib/wrapper/task/WrappedBukkitTask.java +++ b/platform/spigot/src/main/java/com/tcoded/folialib/wrapper/task/WrappedBukkitTask.java @@ -25,4 +25,10 @@ public boolean isCancelled() { public Plugin getOwningPlugin() { return this.task.getOwner(); } + + @Override + public boolean isAsync() { + return !this.task.isSync(); + } + }