diff --git a/.github/workflows/pr-checker.yml b/.github/workflows/pr-checker.yml index 9e6d1f36eb..40414debc2 100644 --- a/.github/workflows/pr-checker.yml +++ b/.github/workflows/pr-checker.yml @@ -43,7 +43,7 @@ jobs: echo "SHORT_COMMIT_HASH=$SHORT_COMMIT_HASH" >> "$GITHUB_ENV" echo "SHORT_COMMIT_HASH=$SHORT_COMMIT_HASH" >> "$GITHUB_OUTPUT" echo "JAR_VERSION=$JAR_VERSION" >> "$GITHUB_ENV" - sed -i "s/DEV-SNAPSHOT<\/version>/$JAR_VERSION<\/version>/g" pom.xml + sed -i "0,/[^<]*<\/version>/{s/[^<]*<\/version>/$JAR_VERSION<\/version>/}" pom.xml - name: Build with Maven run: mvn package -Dversioning.disable --errors diff --git a/pom.xml b/pom.xml index 120c53f6c6..549ac70a3c 100644 --- a/pom.xml +++ b/pom.xml @@ -109,6 +109,10 @@ codemc-repo https://repo.codemc.io/repository/maven-public/ + + tcoded-releases + https://repo.tcoded.com/releases + @@ -175,6 +179,10 @@ net.guizhanss.guizhanlib io.github.thebusybiscuit.slimefun4.libraries.guizhanlib + + com.tcoded.folialib + io.github.thebusybiscuit.slimefun4.libraries.folialib + @@ -421,6 +429,12 @@ 42.7.7 compile + + com.tcoded + FoliaLib + 0.5.1 + compile + diff --git a/src/main/java/city/norain/slimefun4/EnvironmentChecker.java b/src/main/java/city/norain/slimefun4/EnvironmentChecker.java index 07ce7af3d0..5d0996ae3c 100644 --- a/src/main/java/city/norain/slimefun4/EnvironmentChecker.java +++ b/src/main/java/city/norain/slimefun4/EnvironmentChecker.java @@ -74,9 +74,8 @@ static boolean checkHybridServer() { } static void scheduleSlimeGlueCheck(@Nonnull Slimefun sf) { - Bukkit.getScheduler() - .runTaskLater( - sf, + sf.getPlatformScheduler() + .runLater( () -> { if (Bukkit.getPluginManager().getPlugin("SlimeGlue") == null) { sf.getLogger().log(Level.WARNING, "检测到没有安装 SlimeGlue (粘液胶), 你将缺失对一些插件的额外保护检查!"); diff --git a/src/main/java/city/norain/slimefun4/dough/TaskNode.java b/src/main/java/city/norain/slimefun4/dough/TaskNode.java new file mode 100644 index 0000000000..3650c04faa --- /dev/null +++ b/src/main/java/city/norain/slimefun4/dough/TaskNode.java @@ -0,0 +1,64 @@ +package city.norain.slimefun4.dough; + +import java.util.function.IntConsumer; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.Validate; +import org.bukkit.Location; +import org.bukkit.entity.Entity; + +class TaskNode { + + private final IntConsumer runnable; + + @Getter + private final boolean asynchronous; + + @Getter + private int delay = 0; + + @Nullable @Getter + @Setter + private Location location; + + @Nullable @Getter + @Setter + private Entity entity; + + private TaskNode nextNode; + + protected TaskNode(@Nonnull IntConsumer consumer, boolean async) { + this.runnable = consumer; + this.asynchronous = async; + } + + protected TaskNode(@Nonnull IntConsumer consumer, int delay, boolean async) { + this.runnable = consumer; + this.delay = delay; + this.asynchronous = async; + } + + protected boolean hasNextNode() { + return nextNode != null; + } + + public @Nullable TaskNode getNextNode() { + return nextNode; + } + + public void setNextNode(@Nullable TaskNode node) { + this.nextNode = node; + } + + public void execute(int index) { + runnable.accept(index); + } + + public void setDelay(int delay) { + Validate.isTrue(delay >= 0, "The delay cannot be negative."); + + this.delay = delay; + } +} diff --git a/src/main/java/city/norain/slimefun4/dough/TaskQueue.java b/src/main/java/city/norain/slimefun4/dough/TaskQueue.java new file mode 100644 index 0000000000..ef5408eef3 --- /dev/null +++ b/src/main/java/city/norain/slimefun4/dough/TaskQueue.java @@ -0,0 +1,595 @@ +package city.norain.slimefun4.dough; + +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import java.util.function.IntConsumer; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; + +/** + * This class provides useful utilities to schedule Tasks (sync and async). + * Tasks are added into a Queue and then run sequentially via {@link TaskQueue#execute(Plugin)} + * You can provide a delay between the individual tasks via the ticks argument in + * {@link TaskQueue#thenRun(int, Runnable)} + * If you need to access the index of your current task (whether it is the first, last or xth task) you can use + * the methods with {@link IntConsumer} as an argument, otherwise just use the ones with {@link Runnable} + * + * @author TheBusyBiscuit + * + */ +public class TaskQueue { + + private TaskNode head; + + /** + * Use this method to execute the final Task Queue. + * You should add the tasks before-hand. + * An {@link IllegalStateException} will be thrown if the queue is empty. + * + * @param plugin + * The plugin that is performing this execution + */ + public void execute(@Nonnull Plugin plugin) { + if (head == null) { + throw new IllegalStateException("Cannot execute TaskQueue, no head was found"); + } + + run(plugin, head, 0); + } + + private void run(@Nonnull Plugin plugin, @Nullable TaskNode node, int index) { + if (node == null) { + return; + } + + Runnable runnable = () -> { + node.execute(index); + run(plugin, node.getNextNode(), index + 1); + }; + + scheduleTask(node, runnable); + } + + private void scheduleTask(@Nonnull TaskNode node, @Nonnull Runnable runnable) { + if (node.getLocation() != null) { + scheduleAtLocation(node, runnable); + } else if (node.getEntity() != null) { + scheduleAtEntity(node, runnable); + } else { + scheduleGeneral(node, runnable); + } + } + + private void scheduleAtLocation(@Nonnull TaskNode node, @Nonnull Runnable runnable) { + if (node.getDelay() > 0) { + Slimefun.getPlatformScheduler() + .runAtLocationLater(node.getLocation(), (task) -> runnable.run(), node.getDelay()); + } else { + Slimefun.getPlatformScheduler().runAtLocation(node.getLocation(), (task) -> runnable.run()); + } + } + + private void scheduleAtEntity(@Nonnull TaskNode node, @Nonnull Runnable runnable) { + if (node.getDelay() > 0) { + Slimefun.getPlatformScheduler() + .runAtEntityLater(node.getEntity(), (task) -> runnable.run(), node.getDelay()); + } else { + Slimefun.getPlatformScheduler().runAtEntity(node.getEntity(), (task) -> runnable.run()); + } + } + + private void scheduleGeneral(@Nonnull TaskNode node, @Nonnull Runnable runnable) { + if (node.isAsynchronous()) { + if (node.getDelay() > 0) { + Slimefun.getPlatformScheduler().runLater(runnable, node.getDelay()); + } else { + Slimefun.getPlatformScheduler().runNextTick((task) -> runnable.run()); + } + } else { + if (node.getDelay() > 0) { + Slimefun.getPlatformScheduler().runLaterAsync(runnable, node.getDelay()); + } else { + Slimefun.getPlatformScheduler().runAsync((task) -> runnable.run()); + } + } + } + + /** + * This method will schedule the given Task with no delay and synchronously. + * Use the {@link Integer} parameter in your {@link IntConsumer} to determine the task's index. + * + * @param consumer + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRun(@Nonnull IntConsumer consumer, @Nullable Location l, @Nullable Entity entity) { + TaskNode node = new TaskNode(consumer, false); + if (l != null) { + node.setLocation(l); + } else if (entity != null) { + node.setEntity(entity); + } + + return append(node); + } + + /** + * This method will schedule the given Task with no delay and synchronously. + * + * @param runnable + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRun(@Nonnull Runnable runnable) { + return thenRun(i -> runnable.run(), null, null); + } + + public @Nonnull TaskQueue thenRun(@Nonnull Runnable runnable, Location l) { + return thenRun(i -> runnable.run(), l, null); + } + + public @Nonnull TaskQueue thenRun(@Nonnull Runnable runnable, Entity entity) { + return thenRun(i -> runnable.run(), null, entity); + } + + /** + * This method will schedule the given Task with no delay and asynchronously. + * Use the {@link Integer} parameter in your {@link IntConsumer} to determine the task's index. + * + * @param consumer + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRunAsynchronously(@Nonnull IntConsumer consumer) { + return append(new TaskNode(consumer, true)); + } + + /** + * This method will schedule the given Task with no delay and asynchronously. + * + * @param runnable + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRunAsynchronously(@Nonnull Runnable runnable) { + return thenRunAsynchronously(i -> runnable.run()); + } + + /** + * This method will schedule the given Task with the given delay and synchronously. + * Use the {@link Integer} parameter in your {@link IntConsumer} to determine the task's index. + * + * @param ticks + * The time to wait before running this task after the previous one. + * @param consumer + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRun(int ticks, @Nonnull IntConsumer consumer) { + return thenRun(ticks, consumer, null, null); + } + + public @Nonnull TaskQueue thenRun( + int ticks, @Nonnull IntConsumer consumer, @Nullable Location l, @Nullable Entity entity) { + if (ticks < 1) { + throw new IllegalArgumentException("thenAfter() must be given a time that is greater than zero!"); + } + + TaskNode node = new TaskNode(consumer, ticks, false); + + if (l != null) { + node.setLocation(l); + } else if (entity != null) { + node.setEntity(entity); + } + + return append(node); + } + + /** + * This method will schedule the given Task with the given delay and synchronously. + * + * @param ticks + * The time to wait before running this task after the previous one. + * @param runnable + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRun(int ticks, @Nonnull Runnable runnable) { + return thenRun(ticks, i -> runnable.run()); + } + + public @Nonnull TaskQueue thenRun(int ticks, @Nonnull Runnable runnable, @Nullable Location l) { + return thenRun(ticks, i -> runnable.run(), l, null); + } + + public @Nonnull TaskQueue thenRun(int ticks, @Nonnull Runnable runnable, @Nullable Entity entity) { + return thenRun(ticks, i -> runnable.run(), null, entity); + } + + /** + * This method will schedule the given Task with the given delay and asynchronously. + * Use the {@link Integer} parameter in your {@link IntConsumer} to determine the task's index. + * + * @param ticks + * The time to wait before running this task after the previous one. + * @param consumer + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRunAsynchronously(int ticks, @Nonnull IntConsumer consumer) { + if (ticks < 1) { + throw new IllegalArgumentException("thenAfter() must be given a time that is greater than zero!"); + } + + return append(new TaskNode(consumer, ticks, true)); + } + + /** + * This method will schedule the given Task with the given delay and synchronously. + * + * @param ticks + * The time to wait before running this task after the previous one. + * @param runnable + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRunAsynchronously(int ticks, @Nonnull Runnable runnable) { + return thenRunAsynchronously(ticks, i -> runnable.run()); + } + + /** + * This method will schedule the given Task with no delay and synchronously. + * The task will be repeated for the given amount of iterations. + * Use the {@link Integer} parameter in your {@link IntConsumer} to determine the task's index. + * + * @param iterations + * The amount of times to repeat this task + * @param consumer + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRepeat(int iterations, @Nonnull IntConsumer consumer) { + for (int i = 0; i < iterations; i++) { + append(new TaskNode(consumer, false)); + } + + return this; + } + + /** + * This method will schedule the given Task with no delay and synchronously. + * The task will be repeated for the given amount of iterations. + * + * @param iterations + * The amount of times to repeat this task + * @param runnable + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRepeat(int iterations, @Nonnull Runnable runnable) { + return thenRepeat(iterations, i -> runnable.run()); + } + + /** + * This method will schedule the given Task with no delay and asynchronously. + * The task will be repeated for the given amount of iterations. + * Use the {@link Integer} parameter in your {@link IntConsumer} to determine the task's index. + * + * @param iterations + * The amount of times to repeat this task + * @param consumer + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRepeatAsynchronously(int iterations, @Nonnull IntConsumer consumer) { + for (int i = 0; i < iterations; i++) { + append(new TaskNode(consumer, true)); + } + + return this; + } + + /** + * This method will schedule the given Task with no delay and asynchronously. + * The task will be repeated for the given amount of iterations. + * + * @param iterations + * The amount of times to repeat this task + * @param runnable + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRepeatAsynchronously(int iterations, @Nonnull Runnable runnable) { + return thenRepeatAsynchronously(iterations, i -> runnable.run()); + } + + /** + * This method will schedule the given Task with the given delay and synchronously. + * The task will be repeated for the given amount of iterations. + * Use the {@link Integer} parameter in your {@link IntConsumer} to determine the task's index. + * + * @param ticks + * The delay between executions (including the start delay) + * @param iterations + * The amount of times to repeat this task + * @param consumer + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRepeatEvery(int ticks, int iterations, @Nonnull IntConsumer consumer) { + return thenRepeatEvery(ticks, iterations, consumer, null, null); + } + + public @Nonnull TaskQueue thenRepeatEvery( + int ticks, int iterations, @Nonnull IntConsumer consumer, @Nullable Location l, @Nullable Entity entity) { + if (ticks < 1) { + throw new IllegalArgumentException("thenRepeatEvery() must be given a time that is greater than zero!"); + } + + for (int i = 0; i < iterations; i++) { + TaskNode node = new TaskNode(consumer, ticks, false); + if (l != null) { + node.setLocation(l); + } else if (entity != null) { + node.setEntity(entity); + } + append(node); + } + + return this; + } + + /** + * This method will schedule the given Task with the given delay and synchronously. + * The task will be repeated for the given amount of iterations. + * + * @param ticks + * The delay between executions (including the start delay) + * @param iterations + * The amount of times to repeat this task + * @param runnable + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRepeatEvery(int ticks, int iterations, @Nonnull Runnable runnable) { + return thenRepeatEvery(ticks, iterations, i -> runnable.run()); + } + + public @Nonnull TaskQueue thenRepeatEvery( + int ticks, int iterations, @Nonnull Runnable runnable, @Nullable Location l) { + return thenRepeatEvery(ticks, iterations, i -> runnable.run(), l, null); + } + + public @Nonnull TaskQueue thenRepeatEvery( + int ticks, int iterations, @Nonnull Runnable runnable, @Nullable Entity entity) { + return thenRepeatEvery(ticks, iterations, i -> runnable.run(), null, entity); + } + + /** + * This method will schedule the given Task with the given delay and asynchronously. + * The task will be repeated for the given amount of iterations. + * Use the {@link Integer} parameter in your {@link IntConsumer} to determine the task's index. + * + * @param ticks + * The delay between executions (including the start delay) + * @param iterations + * The amount of times to repeat this task + * @param consumer + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRepeatEveryAsynchronously(int ticks, int iterations, @Nonnull IntConsumer consumer) { + return thenRepeatEveryAsynchronously(ticks, iterations, consumer, null, null); + } + + public @Nonnull TaskQueue thenRepeatEveryAsynchronously( + int ticks, int iterations, @Nonnull IntConsumer consumer, @Nullable Location l, @Nullable Entity entity) { + if (ticks < 1) { + throw new IllegalArgumentException( + "thenRepeatEveryAsynchronously() must be given a time that is greater than zero!"); + } + + for (int i = 0; i < iterations; i++) { + TaskNode node = new TaskNode(consumer, ticks, true); + if (l != null) { + node.setLocation(l); + } else if (entity != null) { + node.setEntity(entity); + } + append(node); + } + + return this; + } + + /** + * This method will schedule the given Task with the given delay and asynchronously. + * The task will be repeated for the given amount of iterations. + * + * @param ticks + * The delay between executions (including the start delay) + * @param iterations + * The amount of times to repeat this task + * @param runnable + * The callback to run + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenRepeatEveryAsynchronously(int ticks, int iterations, @Nonnull Runnable runnable) { + return thenRepeatEveryAsynchronously(ticks, iterations, i -> runnable.run()); + } + + /** + * This method will make the task run the given callback until eternity. + * The task will be run with no delay and synchronously. + * Do not add other tasks after calling this method. + * + * @param consumer + * The callback to run + */ + public void thenLoop(@Nonnull IntConsumer consumer) { + TaskNode node = new TaskNode(consumer, false); + node.setNextNode(node); + append(node); + } + + /** + * This method will make the task run the given callback until eternity. + * The task will be run with no delay and synchronously. + * Do not add other tasks after calling this method. + * + * @param runnable + * The callback to run + */ + public void thenLoop(@Nonnull Runnable runnable) { + thenLoop(i -> runnable.run()); + } + + /** + * This method will make the task run the given callback until eternity. + * The task will be run with no delay and asynchronously. + * Do not add other tasks after calling this method. + * + * @param consumer + * The callback to run + */ + public void thenLoopAsynchronously(@Nonnull IntConsumer consumer) { + TaskNode node = new TaskNode(consumer, true); + node.setNextNode(node); + append(node); + } + + /** + * This method will make the task run the given callback until eternity. + * The task will be run with no delay and asynchronously. + * Do not add other tasks after calling this method. + * + * @param runnable + * The callback to run + */ + public void thenLoopAsynchronously(@Nonnull Runnable runnable) { + thenLoopAsynchronously(i -> runnable.run()); + } + + /** + * This method will make the task run the given callback until eternity. + * The task will be run with the given delay and synchronously. + * Do not add other tasks after calling this method. + * + * @param ticks + * The delay between executions (including the start delay) + * @param consumer + * The callback to run + */ + public void thenLoopEvery(int ticks, @Nonnull IntConsumer consumer) { + if (ticks < 1) { + throw new IllegalArgumentException("thenLoopEvery() must be given a time that is greater than zero!"); + } + + TaskNode node = new TaskNode(consumer, ticks, false); + node.setNextNode(node); + append(node); + } + + /** + * This method will make the task run the given callback until eternity. + * The task will be run with the given delay and synchronously. + * Do not add other tasks after calling this method. + * + * @param ticks + * The delay between executions (including the start delay) + * @param runnable + * The callback to run + */ + public void thenLoopEvery(int ticks, @Nonnull Runnable runnable) { + thenLoopEvery(ticks, i -> runnable.run()); + } + + /** + * This method will make the task run the given callback until eternity. + * The task will be run with the given delay and asynchronously. + * Do not add other tasks after calling this method. + * + * @param ticks + * The delay between executions (including the start delay) + * @param consumer + * The callback to run + */ + public void thenLoopEveryAsynchronously(int ticks, @Nonnull IntConsumer consumer) { + if (ticks < 1) { + throw new IllegalArgumentException( + "thenLoopEveryAsynchronously() must be given a time that is greater than zero!"); + } + + TaskNode node = new TaskNode(consumer, ticks, true); + node.setNextNode(node); + append(node); + } + + /** + * This method will make the task run the given callback until eternity. + * The task will be run with the given delay and asynchronously. + * Do not add other tasks after calling this method. + * + * @param ticks + * The delay between executions (including the start delay) + * @param runnable + * The callback to run + */ + public void thenLoopEveryAsynchronously(int ticks, @Nonnull Runnable runnable) { + thenLoopEveryAsynchronously(ticks, i -> runnable.run()); + } + + /** + * This method will make the Queue just do nothing for the given amount of ticks. + * You should not really be using this method but it exists. + * + * @param ticks + * The amount of ticks to wait for + * + * @return The current instance of {@link TaskQueue} + */ + public @Nonnull TaskQueue thenWait(int ticks) { + TaskNode node = new TaskNode(i -> {}, false); + node.setDelay(ticks); + return append(node); + } + + private @Nonnull TaskQueue append(@Nonnull TaskNode node) { + if (head == null) { + head = node; + } else { + TaskNode current = head; + + while (current.hasNextNode()) { + if (current == current.getNextNode()) { + throw new IllegalAccessError("You cannot append to a TaskQueue that contains a loop"); + } + + current = current.getNextNode(); + } + + current.setNextNode(node); + } + + return this; + } +} diff --git a/src/main/java/city/norain/slimefun4/utils/InventoryUtil.java b/src/main/java/city/norain/slimefun4/utils/InventoryUtil.java index 04f9836967..acc82d5f72 100644 --- a/src/main/java/city/norain/slimefun4/utils/InventoryUtil.java +++ b/src/main/java/city/norain/slimefun4/utils/InventoryUtil.java @@ -32,7 +32,7 @@ public void closeInventory(Inventory inventory) { return; } - if (Bukkit.isPrimaryThread()) { + if (!Slimefun.isFolia() && Bukkit.isPrimaryThread()) { new LinkedList<>(inventory.getViewers()).forEach(HumanEntity::closeInventory); } else { Slimefun.runSync(() -> new LinkedList<>(inventory.getViewers()).forEach(HumanEntity::closeInventory)); @@ -42,7 +42,7 @@ public void closeInventory(Inventory inventory) { public void closeInventory(Inventory inventory, Runnable callback) { closeInventory(inventory); - if (Bukkit.isPrimaryThread()) { + if (!Slimefun.isFolia() && Bukkit.isPrimaryThread()) { callback.run(); } else { Slimefun.runSync(callback); diff --git a/src/main/java/city/norain/slimefun4/utils/SlimefunPoolExecutor.java b/src/main/java/city/norain/slimefun4/utils/SlimefunPoolExecutor.java index 5ccfdf797f..448500920b 100644 --- a/src/main/java/city/norain/slimefun4/utils/SlimefunPoolExecutor.java +++ b/src/main/java/city/norain/slimefun4/utils/SlimefunPoolExecutor.java @@ -5,6 +5,7 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.FutureTask; +import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -27,9 +28,29 @@ public SlimefunPoolExecutor( @Nonnull TimeUnit unit, @Nonnull BlockingQueue workQueue, @Nonnull ThreadFactory threadFactory) { + this( + name, + corePoolSize, + maximumPoolSize, + keepAliveTime, + unit, + workQueue, + threadFactory, + new SlimefunRejectedExecutionHandler()); + } + + public SlimefunPoolExecutor( + String name, + int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + @Nonnull TimeUnit unit, + @Nonnull BlockingQueue workQueue, + @Nonnull ThreadFactory threadFactory, + @Nonnull RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); - setRejectedExecutionHandler(new SlimefunRejectedExecutionHandler()); + setRejectedExecutionHandler(handler); this.name = name; diff --git a/src/main/java/city/norain/slimefun4/utils/TaskUtil.java b/src/main/java/city/norain/slimefun4/utils/TaskUtil.java index b9785508a7..21d6f3a8dc 100644 --- a/src/main/java/city/norain/slimefun4/utils/TaskUtil.java +++ b/src/main/java/city/norain/slimefun4/utils/TaskUtil.java @@ -2,12 +2,17 @@ import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import lombok.SneakyThrows; import lombok.experimental.UtilityClass; import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; @UtilityClass public class TaskUtil { @@ -21,14 +26,68 @@ public void runSyncMethod(Runnable runnable) { } @SneakyThrows - public T runSyncMethod(Callable callable) { + public T runSyncMethod(@Nonnull Callable callable) { + if (Slimefun.isFolia()) { + throw new IllegalArgumentException("Location must be provided when executing sync task on Folia!"); + } + + return runSyncMethod(callable, null, null); + } + + @SneakyThrows + public T runSyncMethod(@Nonnull Callable callable, @Nonnull Location l) { + return runSyncMethod(callable, l, null); + } + + @SneakyThrows + public T runSyncMethod(@Nonnull Callable callable, @Nonnull Entity entity) { + if (Slimefun.isFolia()) { + throw new IllegalArgumentException("Entity must be provided when executing sync task on Folia!"); + } + + return runSyncMethod(callable, null, entity); + } + + @SneakyThrows + public T runSyncMethod(@Nonnull Callable callable, @Nullable Location l, @Nullable Entity entity) { if (Bukkit.isPrimaryThread()) { return callable.call(); } else { try { - return Bukkit.getScheduler() - .callSyncMethod(Slimefun.instance(), callable) - .get(1, TimeUnit.SECONDS); + if (Slimefun.isFolia()) { + final CompletableFuture result = new CompletableFuture<>(); + + System.out.println("Sync task created, hashcode = " + result.hashCode()); + + if (l != null) { + Slimefun.getPlatformScheduler().runAtLocation(l, task -> { + try { + result.complete(callable.call()); + } catch (Exception e) { + result.completeExceptionally(e); + } + }); + } else { + if (entity != null) { + Slimefun.getPlatformScheduler().runAtEntity(entity, task -> { + try { + result.complete(callable.call()); + } catch (Exception e) { + result.completeExceptionally(e); + } + }); + } else { + throw new IllegalArgumentException( + "Location or entity must be provided when executing sync task on Folia!"); + } + } + + return result.get(2, TimeUnit.SECONDS); + } else { + return Bukkit.getScheduler() + .callSyncMethod(Slimefun.instance(), callable) + .get(1, TimeUnit.SECONDS); + } } catch (TimeoutException e) { Slimefun.logger().log(Level.WARNING, "Timeout when executing sync method", e); return null; diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/BlockDataController.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/BlockDataController.java index 707b960563..22d213482d 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/BlockDataController.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/controller/BlockDataController.java @@ -4,6 +4,7 @@ import city.norain.slimefun4.api.menu.UniversalMenuPreset; import city.norain.slimefun4.utils.InventoryUtil; import city.norain.slimefun4.utils.StringUtil; +import com.tcoded.folialib.wrapper.task.WrappedTask; import com.xzavier0722.mc.plugin.slimefun4.storage.adapter.IDataSourceAdapter; import com.xzavier0722.mc.plugin.slimefun4.storage.callback.IAsyncReadCallback; import com.xzavier0722.mc.plugin.slimefun4.storage.common.DataScope; @@ -44,8 +45,6 @@ import org.bukkit.Location; import org.bukkit.World; import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; -import org.bukkit.scheduler.BukkitTask; /** * 方块数据控制器 @@ -87,7 +86,7 @@ public class BlockDataController extends ADataController { private boolean enableDelayedSaving = false; private int delayedSecond = 0; - private BukkitTask looperTask; + private WrappedTask looperTask; /** * 区块数据加载模式 * {@link ChunkDataLoadMode} @@ -129,16 +128,15 @@ private void initLoadData() { case LOAD_ON_STARTUP -> loadLoadedWorlds(); } - Bukkit.getScheduler().runTaskLater(Slimefun.instance(), this::loadUniversalRecord, 1); + Slimefun.getPlatformScheduler().runLater(this::loadUniversalRecord, 1); } /** * 加载所有服务器已加载的世界中的数据 */ private void loadLoadedWorlds() { - Bukkit.getScheduler() - .runTaskLater( - Slimefun.instance(), + Slimefun.getPlatformScheduler() + .runLater( () -> { for (var world : Bukkit.getWorlds()) { loadWorld(world); @@ -151,9 +149,8 @@ private void loadLoadedWorlds() { * 加载所有服务器已加载的世界区块中的数据 */ private void loadLoadedChunks() { - Bukkit.getScheduler() - .runTaskLater( - Slimefun.instance(), + Slimefun.getPlatformScheduler() + .runLater( () -> { for (var world : Bukkit.getWorlds()) { for (var chunk : world.getLoadedChunks()) { @@ -171,16 +168,15 @@ private void loadLoadedChunks() { * @param delayedSecond 首次执行延时 * @param forceSavePeriod 强制保存周期 */ - public void initDelayedSaving(Plugin p, int delayedSecond, int forceSavePeriod) { + public void initDelayedSaving(Slimefun p, int delayedSecond, int forceSavePeriod) { checkDestroy(); if (delayedSecond < 1 || forceSavePeriod < 1) { throw new IllegalArgumentException("save period second must be greater than 0!"); } enableDelayedSaving = true; this.delayedSecond = delayedSecond; - looperTask = Bukkit.getScheduler() - .runTaskTimerAsynchronously( - p, + looperTask = Slimefun.getPlatformScheduler() + .runTimerAsync( new DelayedSavingLooperTask( forceSavePeriod, () -> new HashMap<>(delayedWriteTasks), delayedWriteTasks::remove), 20, @@ -1572,7 +1568,8 @@ private void migrateUniversalData( .updateUniversalDataUUID(l.getBlock(), String.valueOf(universalData.getUUID())); } }, - 10L); + 10L, + l); kvData.forEach(recordSet -> universalData.setData( recordSet.get(FieldKey.DATA_KEY), DataUtils.blockDataDebase64(recordSet.get(FieldKey.DATA_VALUE)))); diff --git a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/util/StorageCacheUtils.java b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/util/StorageCacheUtils.java index 1f65b402ec..6cd7b75190 100644 --- a/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/util/StorageCacheUtils.java +++ b/src/main/java/com/xzavier0722/mc/plugin/slimefun4/storage/util/StorageCacheUtils.java @@ -42,9 +42,11 @@ public static boolean hasBlock(Location l) { @ParametersAreNonnullByDefault public static boolean hasUniversalBlock(Location l) { - var uniDataByNBT = TaskUtil.runSyncMethod(() -> Slimefun.getBlockDataService() - .getUniversalDataUUID(l.getBlock()) - .isPresent()); + var uniDataByNBT = TaskUtil.runSyncMethod( + () -> Slimefun.getBlockDataService() + .getUniversalDataUUID(l.getBlock()) + .isPresent(), + l); if (uniDataByNBT) { return true; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/GPSNetwork.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/GPSNetwork.java index 4871d4b31d..1d55d2a353 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/GPSNetwork.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/GPSNetwork.java @@ -25,6 +25,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; import javax.annotation.Nonnull; import javax.annotation.ParametersAreNonnullByDefault; import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu; @@ -91,7 +92,7 @@ public GPSNetwork(@Nonnull Slimefun plugin) { * Whether that {@link GPSTransmitter} is online */ public void updateTransmitter(@Nonnull Location l, @Nonnull UUID uuid, boolean online) { - Set set = transmitters.computeIfAbsent(uuid, id -> new HashSet<>()); + Set set = transmitters.computeIfAbsent(uuid, id -> new CopyOnWriteArraySet<>()); if (online) { set.add(l); @@ -404,33 +405,36 @@ public void addWaypoint(@Nonnull Player p, @Nonnull String name, @Nonnull Locati return; } - Slimefun.runSync(() -> { - WaypointCreateEvent event = new WaypointCreateEvent(p, name, l); - Bukkit.getPluginManager().callEvent(event); - - if (!event.isCancelled()) { - String id = ChatColor.stripColor(ChatColors.color(event.getName())) - .toUpperCase(Locale.ROOT) - .replace(' ', '_'); - - for (Waypoint wp : profile.getWaypoints()) { - if (wp.getId().equals(id)) { - Slimefun.getLocalization() - .sendMessage( - p, - "gps.waypoint.duplicate", - true, - msg -> msg.replace("%waypoint%", event.getName())); - return; + Slimefun.runSync( + () -> { + WaypointCreateEvent event = new WaypointCreateEvent(p, name, l); + Bukkit.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + String id = ChatColor.stripColor(ChatColors.color(event.getName())) + .toUpperCase(Locale.ROOT) + .replace(' ', '_'); + + for (Waypoint wp : profile.getWaypoints()) { + if (wp.getId().equals(id)) { + Slimefun.getLocalization() + .sendMessage( + p, + "gps.waypoint.duplicate", + true, + msg -> msg.replace("%waypoint%", event.getName())); + return; + } + } + + profile.addWaypoint( + new Waypoint(p.getUniqueId(), id, event.getLocation(), event.getName())); + + SoundEffect.GPS_NETWORK_ADD_WAYPOINT.playFor(p); + Slimefun.getLocalization().sendMessage(p, "gps.waypoint.added", true); } - } - - profile.addWaypoint(new Waypoint(p.getUniqueId(), id, event.getLocation(), event.getName())); - - SoundEffect.GPS_NETWORK_ADD_WAYPOINT.playFor(p); - Slimefun.getLocalization().sendMessage(p, "gps.waypoint.added", true); - } - }); + }, + l); }); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/TeleportationManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/TeleportationManager.java index b738dcb1c4..b347a054d6 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/TeleportationManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/gps/TeleportationManager.java @@ -145,7 +145,7 @@ public void openTeleporterGUI(Player p, UUID ownerUUID, Block b, int complexity) getTeleportationPageHandler(pr, ownerUUID, b, complexity, -1), getTeleportationPageHandler(pr, ownerUUID, b, complexity, 1)); - Slimefun.runSync(() -> menu.open(p)); + Slimefun.runSync(() -> menu.open(p), p); }); } } @@ -280,27 +280,29 @@ private void onTeleport(Player p, Location destination, boolean success, boolean * This needs to run on the main Thread so we force it, as * the async teleportation might happen on a separate Thread. */ - Slimefun.runSync(() -> { - if (success) { - // Apply Resistance Effect, if enabled - if (resistance) { - p.addPotionEffect(new PotionEffect(VersionedPotionEffectType.RESISTANCE, 600, 20)); - Slimefun.getLocalization().sendMessage(p, "machines.TELEPORTER.invulnerability"); - } - - // Spawn some particles for aesthetic reasons. - Location loc = new Location( - destination.getWorld(), destination.getX(), destination.getY() + 1, destination.getZ()); - destination.getWorld().spawnParticle(Particle.PORTAL, loc, 200, 0.2F, 0.8F, 0.2F); - SoundEffect.TELEPORT_SOUND.playFor(p); - teleporterUsers.remove(p.getUniqueId()); - } else { - /* - * Make sure the Player is removed from the actively teleporting - * users and notified about the failed teleportation - */ - cancel(p.getUniqueId(), p); - } - }); + Slimefun.runSync( + () -> { + if (success) { + // Apply Resistance Effect, if enabled + if (resistance) { + p.addPotionEffect(new PotionEffect(VersionedPotionEffectType.RESISTANCE, 600, 20)); + Slimefun.getLocalization().sendMessage(p, "machines.TELEPORTER.invulnerability"); + } + + // Spawn some particles for aesthetic reasons. + Location loc = new Location( + destination.getWorld(), destination.getX(), destination.getY() + 1, destination.getZ()); + destination.getWorld().spawnParticle(Particle.PORTAL, loc, 200, 0.2F, 0.8F, 0.2F); + SoundEffect.TELEPORT_SOUND.playFor(p); + teleporterUsers.remove(p.getUniqueId()); + } else { + /* + * Make sure the Player is removed from the actively teleporting + * users and notified about the failed teleportation + */ + cancel(p.getUniqueId(), p); + } + }, + p); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java b/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java index a237574544..493fae55a8 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/api/network/Network.java @@ -6,10 +6,10 @@ import io.github.thebusybiscuit.slimefun4.core.networks.NetworkManager; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.listeners.NetworkListener; -import java.util.ArrayDeque; -import java.util.HashSet; import java.util.Queue; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.commons.lang.Validate; @@ -38,11 +38,11 @@ public abstract class Network { */ protected Location regulator; - private final Queue nodeQueue = new ArrayDeque<>(); - protected final Set connectedLocations = new HashSet<>(); - protected final Set regulatorNodes = new HashSet<>(); - protected final Set connectorNodes = new HashSet<>(); - protected final Set terminusNodes = new HashSet<>(); + private final Queue nodeQueue = new ConcurrentLinkedQueue<>(); + protected final Set connectedLocations = ConcurrentHashMap.newKeySet(); + protected final Set regulatorNodes = ConcurrentHashMap.newKeySet(); + protected final Set connectorNodes = ConcurrentHashMap.newKeySet(); + protected final Set terminusNodes = ConcurrentHashMap.newKeySet(); /** * This constructs a new {@link Network} at the given {@link Location}. @@ -227,7 +227,7 @@ private void discoverNeighbors(@Nonnull Location l) { */ public void display() { if (manager.isVisualizerEnabled()) { - Slimefun.runSync(new NetworkVisualizer(this, Color.BLUE)); + Slimefun.runSync(new NetworkVisualizer(this, Color.BLUE), this.regulator); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java index 1aac0d5d02..e513af37ae 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/SlimefunRegistry.java @@ -251,6 +251,7 @@ public Set getGlobalItemHandlers(@Nonnull Class new HashSet<>()); } + @Deprecated @Nonnull public Map getChunks() { return chunks; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/HologramOwner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/HologramOwner.java index 3b7ecaeae3..59280224cb 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/HologramOwner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/attributes/HologramOwner.java @@ -37,7 +37,7 @@ default void updateHologram(@Nonnull Block b, @Nonnull String text) { } default void updateHologram(@Nonnull Block b, @Nonnull String text, Supplier abort) { - if (Bukkit.isPrimaryThread()) { + if (!Slimefun.isFolia() && Bukkit.isPrimaryThread()) { if (abort.get()) { return; } @@ -45,12 +45,14 @@ default void updateHologram(@Nonnull Block b, @Nonnull String text, Supplier { - if (abort.get()) { - return; - } - updateHologram(b, text); - }); + Slimefun.runSync( + () -> { + if (abort.get()) { + return; + } + updateHologram(b, text); + }, + b.getLocation()); } /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/MigrateCommand.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/MigrateCommand.java index e32b0b521a..f6c0e96e6d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/MigrateCommand.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/commands/subcommands/MigrateCommand.java @@ -8,7 +8,6 @@ import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import java.util.logging.Level; import javax.annotation.Nonnull; -import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; @@ -29,7 +28,7 @@ public void onExecute(@Nonnull CommandSender sender, @Nonnull String[] args) { if (args.length > 1 && args[1].equalsIgnoreCase("confirm")) { Slimefun.getLocalization().sendMessage(sender, "commands.migrate.started", true); - Bukkit.getScheduler().runTaskAsynchronously(Slimefun.instance(), () -> { + Slimefun.getPlatformScheduler().runAsync((task) -> { try { var status = PlayerProfileMigrator.getInstance().migrateData(); sendMigrateStatus("玩家数据", sender, status); @@ -39,7 +38,7 @@ public void onExecute(@Nonnull CommandSender sender, @Nonnull String[] args) { } }); - Bukkit.getScheduler().runTaskAsynchronously(Slimefun.instance(), () -> { + Slimefun.getPlatformScheduler().runAsync((task) -> { try { var status = BlockStorageMigrator.getInstance().migrateData(); sendMigrateStatus("方块数据", sender, status); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/config/SlimefunConfigManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/config/SlimefunConfigManager.java index 03df32597c..9f380bee3a 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/config/SlimefunConfigManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/config/SlimefunConfigManager.java @@ -13,6 +13,7 @@ import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import lombok.Getter; +import lombok.Setter; import org.apache.commons.lang.Validate; import org.bukkit.NamespacedKey; import org.bukkit.Server; @@ -71,6 +72,18 @@ public class SlimefunConfigManager { @Getter private boolean bypassItemLengthCheck; + @Getter + @Setter + private int asyncTickerInitSize; + + @Getter + @Setter + private int asyncTickerMaxSize; + + @Getter + @Setter + private int asyncTickerQueueSize; + public SlimefunConfigManager(@Nonnull Slimefun plugin) { Validate.notNull(plugin, "The Plugin instance cannot be null"); @@ -104,6 +117,8 @@ public boolean load() { public boolean load(boolean reload) { boolean isSuccessful = true; + var serverHalfProcs = Math.max(1, Runtime.getRuntime().availableProcessors() / 2); + try { pluginConfig.reload(); itemsConfig.reload(); @@ -128,16 +143,45 @@ public boolean load(boolean reload) { pluginConfig.setDefaultValue("researches.auto-convert", false); researchAutoConvert = pluginConfig.getBoolean("researches.auto-convert"); + + pluginConfig.setDefaultValue("URID.custom-async-ticker.init-size", serverHalfProcs); + pluginConfig.setDefaultValue( + "URID.custom-async-ticker.max-size", Runtime.getRuntime().availableProcessors()); + pluginConfig.setDefaultValue("URID.custom-async-ticker.queue-size", 1024); + + asyncTickerInitSize = pluginConfig.getInt("URID.custom-async-ticker.init-size"); + asyncTickerMaxSize = pluginConfig.getInt("URID.custom-async-ticker.max-size"); + asyncTickerQueueSize = pluginConfig.getInt("URID.custom-async-ticker.queue-size"); } catch (Exception x) { plugin.getLogger() .log( Level.SEVERE, x, - () -> "An Exception was caught while (re)loading the config files for Slimefun v" + () -> "An Exception was caught while loading the config files for Slimefun v" + plugin.getDescription().getVersion()); isSuccessful = false; } + if (asyncTickerInitSize < 0) { + asyncTickerInitSize = serverHalfProcs; + Slimefun.logger().log(Level.WARNING, "当前设置的 Ticker 线程池初始大小异常,已自动修改为默认值"); + } + + if (asyncTickerMaxSize < 0) { + asyncTickerMaxSize = Runtime.getRuntime().availableProcessors(); + Slimefun.logger().log(Level.WARNING, "当前设置的 Ticker 线程池最大大小异常,已自动修改为默认值"); + } + + if (asyncTickerInitSize > asyncTickerMaxSize) { + asyncTickerInitSize = serverHalfProcs; + Slimefun.logger().log(Level.WARNING, "当前设置的 Ticker 线程池初始大小过大,已自动修改为默认值"); + } + + if (asyncTickerQueueSize < 0) { + asyncTickerInitSize = 1024; + Slimefun.logger().log(Level.WARNING, "当前设置的 Ticker 线程池任务队列大小异常,已自动修改为默认值"); + } + if (!reload) { return true; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java index 0c7363a966..b3696281d3 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/cargo/CargoNet.java @@ -9,12 +9,13 @@ import io.github.thebusybiscuit.slimefun4.core.attributes.HologramOwner; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; import java.util.logging.Level; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -42,10 +43,10 @@ public class CargoNet extends AbstractItemNetwork implements HologramOwner { private static final int RANGE = 5; - private final Set inputNodes = new HashSet<>(); - private final Set outputNodes = new HashSet<>(); + private final Set inputNodes = new CopyOnWriteArraySet<>(); + private final Set outputNodes = new CopyOnWriteArraySet<>(); - protected final Map roundRobin = new HashMap<>(); + protected final Map roundRobin = new ConcurrentHashMap<>(); private int tickDelayThreshold = 0; public static @Nullable CargoNet getNetworkFromLocation(@Nonnull Location l) { @@ -151,20 +152,22 @@ public void tick(@Nonnull Block b, SlimefunBlockData blockData) { display(); } - Slimefun.runSync(() -> { - if (blockData.isPendingRemove()) { - return; - } - var event = new CargoTickEvent(inputs, outputs); - Bukkit.getPluginManager().callEvent(event); - event.getHologramMsg().ifPresent(msg -> updateHologram(b, msg)); - if (event.isCancelled()) { - return; - } - - Slimefun.getProfiler().scheduleEntries(inputs.size() + 1); - new CargoNetworkTask(this, inputs, outputs).run(); - }); + Slimefun.runSync( + () -> { + if (blockData.isPendingRemove()) { + return; + } + var event = new CargoTickEvent(inputs, outputs); + Bukkit.getPluginManager().callEvent(event); + event.getHologramMsg().ifPresent(msg -> updateHologram(b, msg)); + if (event.isCancelled()) { + return; + } + + Slimefun.getProfiler().scheduleEntries(inputs.size() + 1); + new CargoNetworkTask(this, inputs, outputs).run(); + }, + b.getLocation()); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java index 3c48fa401e..02e16ef6cb 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/networks/energy/EnergyNet.java @@ -13,11 +13,11 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; import io.github.thebusybiscuit.slimefun4.utils.NumberUtils; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.function.LongConsumer; import javax.annotation.Nonnull; @@ -43,9 +43,9 @@ public class EnergyNet extends Network implements HologramOwner { private static final int RANGE = 6; - private final Map generators = new HashMap<>(); - private final Map capacitors = new HashMap<>(); - private final Map consumers = new HashMap<>(); + private final Map generators = new ConcurrentHashMap<>(); + private final Map capacitors = new ConcurrentHashMap<>(); + private final Map consumers = new ConcurrentHashMap<>(); protected EnergyNet(@Nonnull Location l) { super(Slimefun.getNetworkManager(), l); @@ -303,10 +303,12 @@ private long tickAllGenerators(@Nonnull LongConsumer timings) { explodedBlocks.add(loc); Slimefun.getDatabaseManager().getBlockDataController().removeBlock(loc); - Slimefun.runSync(() -> { - loc.getBlock().setType(Material.LAVA); - loc.getWorld().createExplosion(loc, 0F, false); - }); + Slimefun.runSync( + () -> { + loc.getBlock().setType(Material.LAVA); + loc.getWorld().createExplosion(loc, 0F, false); + }, + loc); } else { supply = NumberUtils.flowSafeAddition(supply, energy); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java index 560c6d5510..4ce3e93e2e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/AutoSavingService.java @@ -30,11 +30,9 @@ public class AutoSavingService { public void start(@Nonnull Slimefun plugin, int interval) { this.interval = interval; - plugin.getServer().getScheduler().runTaskTimer(plugin, this::saveAllPlayers, 2000L, interval * 60L * 20L); - plugin.getServer() - .getScheduler() - .runTaskTimerAsynchronously( - plugin, + plugin.getPlatformScheduler().runTimer(this::saveAllPlayers, 2000L, interval * 60L * 20L); + plugin.getPlatformScheduler() + .runTimerAsync( () -> { Slimefun.getDatabaseManager() .getBlockDataController() diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/ThreadService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/ThreadService.java index c99557bef2..61f8e34c87 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/ThreadService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/ThreadService.java @@ -17,12 +17,8 @@ public final class ThreadService { public ThreadService(JavaPlugin plugin) { this.group = new ThreadGroup(plugin.getName()); - this.cachedPool = Executors.newCachedThreadPool(new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - return new Thread(group, r, plugin.getName() + " - ThreadService"); - } - }); + this.cachedPool = + Executors.newCachedThreadPool(r -> new Thread(group, r, plugin.getName() + " - ThreadService")); this.scheduledPool = Executors.newScheduledThreadPool(1, new ThreadFactory() { @Override diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java index 5ba15770e4..db431d1593 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubService.java @@ -69,7 +69,7 @@ public void start(@Nonnull Slimefun plugin) { long period = TimeUnit.HOURS.toMillis(1); GitHubTask task = new GitHubTask(this); - plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, task, 30 * 20L, period); + Slimefun.getPlatformScheduler().runTimerAsync(task, 30 * 20L, period); } /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java index c16d34b24a..e3dc009673 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/core/services/github/GitHubTask.java @@ -79,7 +79,7 @@ private void grabTextures() { && Slimefun.instance() != null && Slimefun.instance().isEnabled()) { // Slow down API requests and wait a minute after more than x requests were made - Bukkit.getScheduler().runTaskLaterAsynchronously(Slimefun.instance(), this::grabTextures, 2 * 60 * 20L); + Slimefun.getPlatformScheduler().runLaterAsync(this::grabTextures, 2 * 60 * 20L); } for (GitHubConnector connector : gitHubService.getConnectors()) { @@ -119,6 +119,7 @@ private int requestTexture(@Nonnull Contributor contributor, @Nonnull Map c if (Bukkit.isPrimaryThread()) { runnable.run(); } else { - Slimefun.runSync(runnable); + Slimefun.runSync(runnable, loc); } } @@ -279,25 +282,35 @@ private void updateHologram(@Nonnull Location loc, @Nonnull Consumer c * @return Whether the {@link Hologram} could be removed, false if the {@link Hologram} does not * exist or was already removed */ + @SneakyThrows public boolean removeHologram(@Nonnull Location loc) { Validate.notNull(loc, "Location cannot be null"); - if (Bukkit.isPrimaryThread()) { - try { - Hologram hologram = getHologram(loc, false); + var removeHologramTask = new Callable() { + @Override + public Boolean call() { + try { + Hologram hologram = getHologram(loc, false); - if (hologram != null) { - cache.remove(new BlockPosition(loc)); - hologram.remove(); - return true; - } else { + if (hologram != null) { + cache.remove(new BlockPosition(loc)); + hologram.remove(); + return true; + } else { + return false; + } + } catch (Exception | LinkageError x) { + Slimefun.logger().log(Level.SEVERE, "Hologram located at {0}", new BlockPosition(loc)); + Slimefun.logger().log(Level.SEVERE, "Something went wrong while trying to remove this hologram", x); return false; } - } catch (Exception | LinkageError x) { - Slimefun.logger().log(Level.SEVERE, "Hologram located at {0}", new BlockPosition(loc)); - Slimefun.logger().log(Level.SEVERE, "Something went wrong while trying to remove this hologram", x); - return false; } + }; + + if (Bukkit.isPrimaryThread()) { + return removeHologramTask.call(); + } else if (Slimefun.isFolia()) { + return TaskUtil.runSyncMethod(removeHologramTask, loc); } else { throw new UnsupportedOperationException("You cannot remove a hologram asynchronously."); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java index 07d5677583..b6ee7ebb27 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/Slimefun.java @@ -2,6 +2,10 @@ import city.norain.slimefun4.SlimefunExtended; import city.norain.slimefun4.timings.SQLProfiler; +import com.tcoded.folialib.FoliaLib; +import com.tcoded.folialib.enums.EntityTaskResult; +import com.tcoded.folialib.impl.PlatformScheduler; +import com.tcoded.folialib.wrapper.task.WrappedTask; import com.xzavier0722.mc.plugin.slimefun4.chat.PlayerChatCatcher; import com.xzavier0722.mc.plugin.slimefun4.storage.migrator.BlockStorageMigrator; import com.xzavier0722.mc.plugin.slimefun4.storage.migrator.PlayerProfileMigrator; @@ -116,6 +120,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -126,9 +131,11 @@ import net.guizhanss.slimefun4.updater.AutoUpdateTask; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.Server; import org.bukkit.World; import org.bukkit.command.Command; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.bukkit.inventory.Recipe; @@ -169,6 +176,11 @@ public final class Slimefun extends JavaPlugin implements SlimefunAddon, ICompat */ private boolean isNewlyInstalled = false; + /** + * Folia support library + */ + private final FoliaLib foliaLib = new FoliaLib(this); + // Various things we need private final SlimefunConfigManager cfgManager = new SlimefunConfigManager(this); private final SlimefunDatabaseManager databaseManager = new SlimefunDatabaseManager(this); @@ -327,7 +339,7 @@ private void onPluginStart() { Slimefun.logger().warning("请在服务器加载完成后, 使用 /sf migrate confirm 进行迁移!"); Slimefun.logger().warning("如果不迁移, 你将会丢失先前版本的数据!!!"); Slimefun.logger().warning("\n"); - Slimefun.logger().warning("需要使用 MySQL 数据库的用户, 请关服后修改两个配置文件"); + Slimefun.logger().warning("需要使用 MySQL 数据库的用户, 请关服后修改以下配置文件再进行迁移"); Slimefun.logger().warning("block-storage.yml 和 profile-storage.yml"); Slimefun.logger().warning("\n"); Slimefun.logger().warning("===================================================="); @@ -406,7 +418,7 @@ private void onPluginStart() { + ")"); } }), - 0); + 1); // Setting up our commands try { @@ -430,7 +442,7 @@ private void onPluginStart() { // Starting our tasks autoSavingService.start(this, config.getInt("options.auto-save-delay-in-minutes")); hologramsService.start(); - ticker.start(this); + ticker.start(); logger.log(Level.INFO, "正在加载第三方插件支持..."); integrations.start(); @@ -439,7 +451,7 @@ private void onPluginStart() { if (cfgManager.isAutoUpdate()) { // 汉化版自动更新 - Bukkit.getScheduler().scheduleSyncDelayedTask(this, new AutoUpdateTask(this, getFile())); + getPlatformScheduler().runNextTick((task) -> new AutoUpdateTask(this, getFile()).run()); } // Hooray! @@ -476,11 +488,10 @@ public void onDisable() { getSQLProfiler().shutdown(); // Cancel all tasks from this plugin immediately - Bukkit.getScheduler().cancelTasks(this); + foliaLib.getScheduler().cancelAllTasks(); // Finishes all started movements/removals of block data - ticker.setPaused(true); - ticker.halt(); + ticker.shutdown(); /**try { * ticker.halt(); * ticker.run(); @@ -1130,7 +1141,7 @@ public static boolean isNewlyInstalled() { * * @return The resulting {@link BukkitTask} or null if Slimefun was disabled */ - public static @Nullable BukkitTask runSync(@Nonnull Runnable runnable, long delay) { + public static @Nullable WrappedTask runSync(@Nonnull Runnable runnable, long delay) { Validate.notNull(runnable, "Cannot run null"); Validate.isTrue(delay >= 0, "The delay cannot be negative"); @@ -1144,7 +1155,7 @@ public static boolean isNewlyInstalled() { return null; } - return instance.getServer().getScheduler().runTaskLater(instance, runnable, delay); + return getPlatformScheduler().runLater(runnable, Math.max(1, delay)); } /** @@ -1159,7 +1170,33 @@ public static boolean isNewlyInstalled() { * * @return The resulting {@link BukkitTask} or null if Slimefun was disabled */ - public static @Nullable BukkitTask runSync(@Nonnull Runnable runnable) { + public static @Nullable CompletableFuture runSync(@Nonnull Runnable runnable) { + Validate.notNull(runnable, "Cannot run null"); + + // Run the task instantly within a Unit Test + if (getMinecraftVersion() == MinecraftVersion.UNIT_TEST) { + runnable.run(); + return null; + } + + if (instance == null || !instance.isEnabled()) { + return null; + } + + return instance.getPlatformScheduler().runNextTick((task) -> runnable.run()); + } + + /** + * This method schedules a synchronous task for Slimefun. + * For Slimefun only, not for addons. + * + * This method should only be invoked by Slimefun itself. + * Addons must schedule their own tasks using their own {@link Plugin} instance. + * + * @param runnable The {@link Runnable} to run + * @return The resulting {@link BukkitTask} or null if Slimefun was disabled + */ + public static @Nullable CompletableFuture runSync(@Nonnull Runnable runnable, Entity entity) { Validate.notNull(runnable, "Cannot run null"); // Run the task instantly within a Unit Test @@ -1172,7 +1209,49 @@ public static boolean isNewlyInstalled() { return null; } - return instance.getServer().getScheduler().runTask(instance, runnable); + return instance.getPlatformScheduler().runAtEntity(entity, (task) -> runnable.run()); + } + + /** + * This method schedules a synchronous task for Slimefun. + * For Slimefun only, not for addons. + * + * This method should only be invoked by Slimefun itself. + * Addons must schedule their own tasks using their own {@link Plugin} instance. + * + * @param runnable The {@link Runnable} to run + * @return The resulting {@link BukkitTask} or null if Slimefun was disabled + */ + public static @Nullable CompletableFuture runSync(@Nonnull Runnable runnable, Location l) { + Validate.notNull(runnable, "Cannot run null"); + + // Run the task instantly within a Unit Test + if (getMinecraftVersion() == MinecraftVersion.UNIT_TEST) { + runnable.run(); + return null; + } + + if (instance == null || !instance.isEnabled()) { + return null; + } + + return instance.getPlatformScheduler().runAtLocation(l, (task) -> runnable.run()); + } + + public static @Nullable CompletableFuture runSync(@Nonnull Runnable runnable, long delay, Location l) { + Validate.notNull(runnable, "Cannot run null"); + + // Run the task instantly within a Unit Test + if (getMinecraftVersion() == MinecraftVersion.UNIT_TEST) { + runnable.run(); + return null; + } + + if (instance == null || !instance.isEnabled()) { + return null; + } + + return getPlatformScheduler().runAtLocationLater(l, (task) -> runnable.run(), delay); } @Nonnull @@ -1180,6 +1259,14 @@ public File getFile() { return super.getFile(); } + public static boolean isFolia() { + return instance.foliaLib.isFolia(); + } + + public static PlatformScheduler getPlatformScheduler() { + return instance.foliaLib.getScheduler(); + } + public static @Nonnull PlayerChatCatcher getChatCatcher() { validateInstance(); return instance.chatCatcher; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java index 0b75a4ed89..f83aea98ea 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/androids/ProgrammableAndroid.java @@ -263,7 +263,7 @@ public AndroidFuelSource getFuelSource() { public void preRegister() { super.preRegister(); - addItemHandler(new BlockTicker(true) { + addItemHandler(new BlockTicker() { @Override public void tick(Block b, SlimefunItem item, SlimefunUniversalData data) { @@ -276,6 +276,11 @@ public void tick(Block b, SlimefunItem item, SlimefunUniversalData data) { public boolean isSynchronized() { return true; } + + @Override + public boolean useUniversalData() { + return true; + } }); } @@ -986,7 +991,7 @@ public void addItems(Block b, ItemStack... items) { Validate.notNull(b, "The Block cannot be null."); Optional uuid = - TaskUtil.runSyncMethod(() -> Slimefun.getBlockDataService().getUniversalDataUUID(b)); + TaskUtil.runSyncMethod(() -> Slimefun.getBlockDataService().getUniversalDataUUID(b), b.getLocation()); if (uuid.isEmpty()) { throw new IllegalStateException("Android missing uuid"); @@ -1042,14 +1047,16 @@ protected void move(Block from, BlockFace face, Block to) { Slimefun.getDatabaseManager().getBlockDataController().move(uniData, to.getLocation()); - Slimefun.runSync(() -> { - PlayerSkin skin = PlayerSkin.fromBase64(texture); - Material type = to.getType(); - // Ensure that this Block is still a Player Head - if (type == Material.PLAYER_HEAD || type == Material.PLAYER_WALL_HEAD) { - PlayerHead.setSkin(to, skin, true); - } - }); + Slimefun.runSync( + () -> { + PlayerSkin skin = PlayerSkin.fromBase64(texture); + Material type = to.getType(); + // Ensure that this Block is still a Player Head + if (type == Material.PLAYER_HEAD || type == Material.PLAYER_WALL_HEAD) { + PlayerHead.setSkin(to, skin, true); + } + }, + to.getLocation()); from.setType(Material.AIR); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/AbstractAutoCrafter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/AbstractAutoCrafter.java index abcf514f10..98ab2ed7f8 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/AbstractAutoCrafter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/autocrafters/AbstractAutoCrafter.java @@ -34,6 +34,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import java.util.logging.Level; import javax.annotation.Nonnull; @@ -111,7 +112,7 @@ protected AbstractAutoCrafter( recipeStorageKey = new NamespacedKey(Slimefun.instance(), "recipe_key"); recipeEnabledKey = new NamespacedKey(Slimefun.instance(), "recipe_enabled"); - recipeCache = new HashMap<>(); + recipeCache = new ConcurrentHashMap<>(); addItemHandler(new BlockTicker() { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Composter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Composter.java index 60c9c193ba..99ffedfe96 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Composter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Composter.java @@ -1,7 +1,7 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.blocks; +import city.norain.slimefun4.dough.TaskQueue; import io.github.bakedlibs.dough.protection.Interaction; -import io.github.bakedlibs.dough.scheduling.TaskQueue; import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; @@ -87,15 +87,23 @@ public BlockUseHandler getItemHandler() { if (output != null) { TaskQueue tasks = new TaskQueue(); - tasks.thenRepeatEvery(30, 10, () -> { - Material material = input.getType().isBlock() ? input.getType() : Material.HAY_BLOCK; - b.getWorld().playEffect(b.getLocation(), Effect.STEP_SOUND, material); - }); - - tasks.thenRun(20, () -> { - SoundEffect.COMPOSTER_COMPOST_SOUND.playFor(p); - pushItem(b, output.clone()); - }); + tasks.thenRepeatEvery( + 30, + 10, + () -> { + Material material = + input.getType().isBlock() ? input.getType() : Material.HAY_BLOCK; + b.getWorld().playEffect(b.getLocation(), Effect.STEP_SOUND, material); + }, + b.getLocation()); + + tasks.thenRun( + 20, + () -> { + SoundEffect.COMPOSTER_COMPOST_SOUND.playFor(p); + pushItem(b, output.clone()); + }, + b.getLocation()); tasks.execute(Slimefun.instance()); } else { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Crucible.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Crucible.java index b921eefac4..470801750c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Crucible.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/Crucible.java @@ -191,8 +191,12 @@ private void addLiquidLevel(@Nonnull Block block, boolean water) { } if (level == 0) { - Slimefun.runSync(() -> runPostTask( - block, water ? SoundEffect.CRUCIBLE_ADD_WATER_SOUND : SoundEffect.CRUCIBLE_ADD_LAVA_SOUND, 1)); + Slimefun.runSync( + () -> runPostTask( + block, + water ? SoundEffect.CRUCIBLE_ADD_WATER_SOUND : SoundEffect.CRUCIBLE_ADD_LAVA_SOUND, + 1), + block.getLocation()); } else { int finalLevel = 7 - level; Slimefun.runSync( diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/HologramProjector.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/HologramProjector.java index b875bd0187..c570ff6b74 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/HologramProjector.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/blocks/HologramProjector.java @@ -36,10 +36,8 @@ * @author TheBusyBiscuit * @author Kry-Vosa * @author SoSeDiK - * * @see HologramOwner * @see HologramsService - * */ public class HologramProjector extends SlimefunItem implements HologramOwner { @@ -125,10 +123,12 @@ private void openEditor(@Nonnull Player p, @Nonnull Block projector) { return; } - ArmorStand hologram = getArmorStand(projector, true); - hologram.setCustomName(ChatColors.color(message)); - StorageCacheUtils.setData(projector.getLocation(), "text", hologram.getCustomName()); - openEditor(pl, projector); + Slimefun.getPlatformScheduler().runAtLocation(projector.getLocation(), (task) -> { + ArmorStand hologram = getArmorStand(projector, true); + hologram.setCustomName(ChatColors.color(message)); + StorageCacheUtils.setData(projector.getLocation(), "text", hologram.getCustomName()); + openEditor(pl, projector); + }); }); return false; @@ -152,7 +152,7 @@ private void openEditor(@Nonnull Player p, @Nonnull Block projector) { ArmorStand hologram = getArmorStand(projector, true); Location l = new Location( projector.getWorld(), projector.getX() + 0.5, projector.getY() + offset, projector.getZ() + 0.5); - hologram.teleport(l); + Slimefun.getPlatformScheduler().teleportAsync(hologram, l); blockData.setData(OFFSET_PARAMETER, String.valueOf(offset)); openEditor(pl, projector); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoManager.java index a53f3d3689..85883f8f34 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/CargoManager.java @@ -2,7 +2,6 @@ import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunBlockData; import com.xzavier0722.mc.plugin.slimefun4.storage.util.StorageCacheUtils; -import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent; import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; @@ -57,24 +56,20 @@ public boolean isSynchronized() { return false; } }, - new BlockUseHandler() { + (BlockUseHandler) e -> { + Optional block = e.getClickedBlock(); - @Override - public void onRightClick(PlayerRightClickEvent e) { - Optional block = e.getClickedBlock(); - - if (block.isPresent()) { - Player p = e.getPlayer(); - Block b = block.get(); + if (block.isPresent()) { + Player p = e.getPlayer(); + Block b = block.get(); - var blockData = StorageCacheUtils.getBlock(b.getLocation()); - if (blockData.getData("visualizer") == null) { - blockData.setData("visualizer", "disabled"); - p.sendMessage(ChatColor.translateAlternateColorCodes('&', "&c货运网络可视化: " + "&4\u2718")); - } else { - blockData.removeData("visualizer"); - p.sendMessage(ChatColor.translateAlternateColorCodes('&', "&c货运网络可视化: " + "&2\u2714")); - } + var blockData = StorageCacheUtils.getBlock(b.getLocation()); + if (blockData.getData("visualizer") == null) { + blockData.setData("visualizer", "disabled"); + p.sendMessage(ChatColor.translateAlternateColorCodes('&', "&c货运网络可视化: " + "&4\u2718")); + } else { + blockData.removeData("visualizer"); + p.sendMessage(ChatColor.translateAlternateColorCodes('&', "&c货运网络可视化: " + "&2\u2714")); } } }); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/TrashCan.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/TrashCan.java index 8a6348acc3..2ae7f0379f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/TrashCan.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/cargo/TrashCan.java @@ -60,8 +60,14 @@ public void preRegister() { public void tick(Block b, SlimefunItem item, SlimefunBlockData data) { BlockMenu menu = data.getBlockMenu(); - for (int slot : getInputSlots()) { - menu.replaceExistingItem(slot, null); + try { + menu.lock(); + + for (int slot : getInputSlots()) { + menu.replaceExistingItem(slot, null); + } + } finally { + menu.unlock(); } } @@ -69,6 +75,11 @@ public void tick(Block b, SlimefunItem item, SlimefunBlockData data) { public boolean isSynchronized() { return false; } + + @Override + public boolean isConcurrent() { + return true; + } }); } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AbstractEntityAssembler.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AbstractEntityAssembler.java index f5fbfbb590..c12b6af42c 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AbstractEntityAssembler.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/machines/entities/AbstractEntityAssembler.java @@ -215,17 +215,19 @@ public void tick(Block b, SlimefunItem sf, SlimefunBlockData data) { removeCharge(b.getLocation(), getEnergyConsumption()); double offset = Double.parseDouble(data.getData(KEY_OFFSET)); - Slimefun.runSync(() -> { - Location loc = - new Location(b.getWorld(), b.getX() + 0.5D, b.getY() + offset, b.getZ() + 0.5D); - spawnEntity(loc); - - b.getWorld() - .playEffect( - b.getLocation(), - Effect.STEP_SOUND, - getHead().getType()); - }); + Slimefun.runSync( + () -> { + Location loc = new Location( + b.getWorld(), b.getX() + 0.5D, b.getY() + offset, b.getZ() + 0.5D); + spawnEntity(loc); + + b.getWorld() + .playEffect( + b.getLocation(), + Effect.STEP_SOUND, + getHead().getType()); + }, + b.getLocation()); } } } @@ -239,6 +241,11 @@ public void uniqueTick() { public boolean isSynchronized() { return false; } + + @Override + public boolean isConcurrent() { + return true; + } }; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/reactors/NetherStarReactor.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/reactors/NetherStarReactor.java index ded6c39f47..b8d0205b81 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/reactors/NetherStarReactor.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/reactors/NetherStarReactor.java @@ -40,14 +40,16 @@ protected void registerDefaultFuelTypes() { @Override public void extraTick(@Nonnull Location l) { - Slimefun.runSync(() -> { - for (Entity entity : - l.getWorld().getNearbyEntities(l, 5, 5, 5, n -> n instanceof LivingEntity && n.isValid())) { - if (entity instanceof LivingEntity livingEntity) { - livingEntity.addPotionEffect(new PotionEffect(PotionEffectType.WITHER, 60, 1)); - } - } - }); + Slimefun.runSync( + () -> { + for (Entity entity : + l.getWorld().getNearbyEntities(l, 5, 5, 5, n -> n instanceof LivingEntity && n.isValid())) { + if (entity instanceof LivingEntity livingEntity) { + livingEntity.addPotionEffect(new PotionEffect(PotionEffectType.WITHER, 60, 1)); + } + } + }, + l); } @Override diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/reactors/Reactor.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/reactors/Reactor.java index 4c979d3775..e91e46b2cf 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/reactors/Reactor.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/electric/reactors/Reactor.java @@ -397,13 +397,15 @@ public boolean willExplode(Location l, SlimefunBlockData data) { boolean explosion = explosionsQueue.contains(l); if (explosion) { - Slimefun.runSync(() -> { - ReactorExplodeEvent event = new ReactorExplodeEvent(l, Reactor.this); - Bukkit.getPluginManager().callEvent(event); + Slimefun.runSync( + () -> { + ReactorExplodeEvent event = new ReactorExplodeEvent(l, Reactor.this); + Bukkit.getPluginManager().callEvent(event); - data.getBlockMenu().close(); - removeHologram(l.getBlock()); - }); + data.getBlockMenu().close(); + removeHologram(l.getBlock()); + }, + l); explosionsQueue.remove(l); processor.endOperation(l); @@ -413,18 +415,20 @@ public boolean willExplode(Location l, SlimefunBlockData data) { } private void checkForWaterBlocks(Location l) { - Slimefun.runSync(() -> { - /* - * We will pick a surrounding block at random and see if this is water. - * If it isn't, then we will make it explode. - */ - int index = ThreadLocalRandom.current().nextInt(WATER_BLOCKS.length); - BlockFace randomNeighbour = WATER_BLOCKS[index]; - - if (l.getBlock().getRelative(randomNeighbour).getType() != Material.WATER) { - explosionsQueue.add(l); - } - }); + Slimefun.runSync( + () -> { + /* + * We will pick a surrounding block at random and see if this is water. + * If it isn't, then we will make it explode. + */ + int index = ThreadLocalRandom.current().nextInt(WATER_BLOCKS.length); + BlockFace randomNeighbour = WATER_BLOCKS[index]; + + if (l.getBlock().getRelative(randomNeighbour).getType() != Material.WATER) { + explosionsQueue.add(l); + } + }, + l); } private void createByproduct( diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/elevator/ElevatorPlate.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/elevator/ElevatorPlate.java index 903f0a0eb5..fc0d62c10d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/elevator/ElevatorPlate.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/elevator/ElevatorPlate.java @@ -236,30 +236,32 @@ private void openFloorSelector(Block b, List floors, Player p, in @ParametersAreNonnullByDefault private void teleport(Player player, ElevatorFloor floor) { - Slimefun.runSync(() -> { - users.add(player.getUniqueId()); - - float yaw = player.getEyeLocation().getYaw() + 180; - - if (yaw > 180) { - yaw = -180 + (yaw - 180); - } - - Location loc = floor.getLocation(); - Location destination = new Location( - player.getWorld(), - loc.getX() + 0.5, - loc.getY() + 0.4, - loc.getZ() + 0.5, - yaw, - player.getEyeLocation().getPitch()); - - PaperLib.teleportAsync(player, destination).thenAccept(teleported -> { - if (teleported.booleanValue()) { - player.sendTitle(ChatColor.WHITE + ChatColors.color(floor.getName()), null, 20, 60, 20); - } - }); - }); + Slimefun.runSync( + () -> { + users.add(player.getUniqueId()); + + float yaw = player.getEyeLocation().getYaw() + 180; + + if (yaw > 180) { + yaw = -180 + (yaw - 180); + } + + Location loc = floor.getLocation(); + Location destination = new Location( + player.getWorld(), + loc.getX() + 0.5, + loc.getY() + 0.4, + loc.getZ() + 0.5, + yaw, + player.getEyeLocation().getPitch()); + + PaperLib.teleportAsync(player, destination).thenAccept(teleported -> { + if (teleported.booleanValue()) { + player.sendTitle(ChatColor.WHITE + ChatColors.color(floor.getName()), null, 20, 60, 20); + } + }); + }, + player); } @ParametersAreNonnullByDefault diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java index 8dfec8a569..88a874031f 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/geo/GEOMiner.java @@ -303,6 +303,11 @@ public void tick(Block b, SlimefunItem sf, SlimefunBlockData data) { public boolean isSynchronized() { return false; } + + @Override + public boolean isConcurrent() { + return true; + } }); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSTransmitter.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSTransmitter.java index f0cb60ae89..8407f784fe 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSTransmitter.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/gps/GPSTransmitter.java @@ -95,6 +95,11 @@ public void tick(Block b, SlimefunItem item, SlimefunBlockData data) { public boolean isSynchronized() { return false; } + + @Override + public boolean isConcurrent() { + return true; + } }; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/InfusedHopper.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/InfusedHopper.java index 704b2eb4b1..27035452b4 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/InfusedHopper.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/InfusedHopper.java @@ -81,7 +81,7 @@ public void tick(Block b, SlimefunItem sfItem, SlimefunBlockData data) { // Check for any nearby Items that can be picked up for (Entity item : b.getWorld().getNearbyEntities(l, range, range, range, n -> isValidItem(l, n))) { item.setVelocity(new Vector(0, 0.1, 0)); - item.teleport(l); + Slimefun.getPlatformScheduler().teleportAsync(item, l); playSound = true; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/TelepositionScroll.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/TelepositionScroll.java index 8dd1844172..6af509b097 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/TelepositionScroll.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/magical/TelepositionScroll.java @@ -7,6 +7,7 @@ import io.github.thebusybiscuit.slimefun4.api.items.settings.IntRangeSetting; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.items.SimpleSlimefunItem; import javax.annotation.ParametersAreNonnullByDefault; import org.bukkit.Location; @@ -49,13 +50,16 @@ public ItemUseHandler getItemHandler() { yaw = yaw - 360F; } - n.teleport(new Location( - n.getWorld(), - n.getLocation().getX(), - n.getLocation().getY(), - n.getLocation().getZ(), - yaw, - n.getLocation().getPitch())); + Slimefun.getPlatformScheduler() + .teleportAsync( + n, + new Location( + n.getWorld(), + n.getLocation().getX(), + n.getLocation().getY(), + n.getLocation().getZ(), + yaw, + n.getLocation().getPitch())); } } }; diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/AutomatedPanningMachine.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/AutomatedPanningMachine.java index 7b5e9a1c96..acbc0c4d20 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/AutomatedPanningMachine.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/AutomatedPanningMachine.java @@ -1,7 +1,7 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks; +import city.norain.slimefun4.dough.TaskQueue; import io.github.bakedlibs.dough.items.ItemUtils; -import io.github.bakedlibs.dough.scheduling.TaskQueue; import io.github.thebusybiscuit.slimefun4.api.events.MultiBlockCraftEvent; import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; @@ -101,23 +101,30 @@ public void onInteract(Player p, Block b) { TaskQueue queue = new TaskQueue(); - queue.thenRepeatEvery(20, 5, () -> b.getWorld() - .playEffect(b.getRelative(BlockFace.DOWN).getLocation(), Effect.STEP_SOUND, material)); - queue.thenRun(20, () -> { - if (finalOutput.getType() != Material.AIR) { - Optional outputChest = OutputChest.findOutputChestFor(b.getRelative(BlockFace.DOWN), output); - - if (outputChest.isPresent()) { - outputChest.get().addItem(finalOutput.clone()); - } else { - b.getWorld().dropItemNaturally(b.getLocation(), finalOutput.clone()); - } - - SoundEffect.AUTOMATED_PANNING_MACHINE_SUCCESS_SOUND.playAt(b); - } else { - SoundEffect.AUTOMATED_PANNING_MACHINE_FAIL_SOUND.playAt(b); - } - }); + queue.thenRepeatEvery( + 20, + 5, + () -> b.getWorld().playEffect(b.getRelative(BlockFace.DOWN).getLocation(), Effect.STEP_SOUND, material), + b.getLocation()); + queue.thenRun( + 20, + () -> { + if (finalOutput.getType() != Material.AIR) { + Optional outputChest = + OutputChest.findOutputChestFor(b.getRelative(BlockFace.DOWN), output); + + if (outputChest.isPresent()) { + outputChest.get().addItem(finalOutput.clone()); + } else { + b.getWorld().dropItemNaturally(b.getLocation(), finalOutput.clone()); + } + + SoundEffect.AUTOMATED_PANNING_MACHINE_SUCCESS_SOUND.playAt(b); + } else { + SoundEffect.AUTOMATED_PANNING_MACHINE_FAIL_SOUND.playAt(b); + } + }, + b.getLocation()); queue.execute(Slimefun.instance()); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/miner/MiningTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/miner/MiningTask.java index dd9387dcc2..2514e57c11 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/miner/MiningTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/multiblocks/miner/MiningTask.java @@ -1,10 +1,10 @@ package io.github.thebusybiscuit.slimefun4.implementation.items.multiblocks.miner; +import city.norain.slimefun4.dough.TaskQueue; import io.github.bakedlibs.dough.blocks.BlockPosition; import io.github.bakedlibs.dough.inventory.InvUtils; import io.github.bakedlibs.dough.items.ItemUtils; import io.github.bakedlibs.dough.protection.Interaction; -import io.github.bakedlibs.dough.scheduling.TaskQueue; import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.utils.compatibility.VersionedParticle; @@ -122,46 +122,48 @@ private void warmUp() { */ TaskQueue queue = new TaskQueue(); - queue.thenRun(4, () -> setPistonState(pistons[0], true)); - queue.thenRun(10, () -> setPistonState(pistons[0], false)); + queue.thenRun(4, () -> setPistonState(pistons[0], true), pistons[0].getLocation()); + queue.thenRun(10, () -> setPistonState(pistons[0], false), pistons[0].getLocation()); - queue.thenRun(8, () -> setPistonState(pistons[1], true)); - queue.thenRun(10, () -> setPistonState(pistons[1], false)); + queue.thenRun(8, () -> setPistonState(pistons[1], true), pistons[1].getLocation()); + queue.thenRun(10, () -> setPistonState(pistons[1], false), pistons[1].getLocation()); /* * Fixes #3336 * Trigger each piston once, so that the structure is validated. * Then consume fuel. */ - queue.thenRun(() -> { - consumeFuel(); + queue.thenRun( + () -> { + consumeFuel(); - if (fuelLevel <= 0) { - // This Miner has not got enough fuel to run. - stop(MinerStoppingReason.NO_FUEL); - return; - } - }); + if (fuelLevel <= 0) { + // This Miner has not got enough fuel to run. + stop(MinerStoppingReason.NO_FUEL); + return; + } + }, + chest.getLocation()); - queue.thenRun(6, () -> setPistonState(pistons[0], true)); - queue.thenRun(9, () -> setPistonState(pistons[0], false)); + queue.thenRun(6, () -> setPistonState(pistons[0], true), pistons[0].getLocation()); + queue.thenRun(9, () -> setPistonState(pistons[0], false), pistons[0].getLocation()); - queue.thenRun(4, () -> setPistonState(pistons[1], true)); - queue.thenRun(7, () -> setPistonState(pistons[1], false)); + queue.thenRun(4, () -> setPistonState(pistons[1], true), pistons[1].getLocation()); + queue.thenRun(7, () -> setPistonState(pistons[1], false), pistons[1].getLocation()); - queue.thenRun(3, () -> setPistonState(pistons[0], true)); - queue.thenRun(5, () -> setPistonState(pistons[0], false)); + queue.thenRun(3, () -> setPistonState(pistons[0], true), pistons[0].getLocation()); + queue.thenRun(5, () -> setPistonState(pistons[0], false), pistons[0].getLocation()); - queue.thenRun(2, () -> setPistonState(pistons[1], true)); - queue.thenRun(4, () -> setPistonState(pistons[1], false)); + queue.thenRun(2, () -> setPistonState(pistons[1], true), pistons[1].getLocation()); + queue.thenRun(4, () -> setPistonState(pistons[1], false), pistons[1].getLocation()); - queue.thenRun(1, () -> setPistonState(pistons[0], true)); - queue.thenRun(3, () -> setPistonState(pistons[0], false)); + queue.thenRun(1, () -> setPistonState(pistons[0], true), pistons[0].getLocation()); + queue.thenRun(3, () -> setPistonState(pistons[0], false), pistons[0].getLocation()); - queue.thenRun(1, () -> setPistonState(pistons[1], true)); - queue.thenRun(2, () -> setPistonState(pistons[1], false)); + queue.thenRun(1, () -> setPistonState(pistons[1], true), pistons[1].getLocation()); + queue.thenRun(2, () -> setPistonState(pistons[1], false), pistons[1].getLocation()); - queue.thenRun(1, this); + queue.thenRun(1, this, chest.getLocation()); queue.execute(Slimefun.instance()); } @@ -174,54 +176,56 @@ public void run() { TaskQueue queue = new TaskQueue(); - queue.thenRun(1, () -> setPistonState(pistons[0], true)); - queue.thenRun(3, () -> setPistonState(pistons[0], false)); - - queue.thenRun(1, () -> setPistonState(pistons[1], true)); - queue.thenRun(3, () -> setPistonState(pistons[1], false)); - - queue.thenRun(() -> { - try { - Block furnace = chest.getRelative(BlockFace.DOWN); - furnace.getWorld().playEffect(furnace.getLocation(), Effect.STEP_SOUND, Material.STONE); - - World world = start.getWorld(); - for (int y = height; y > world.getMinHeight(); y--) { - Block b = world.getBlockAt(x, y, z); - - if (!Slimefun.getProtectionManager() - .hasPermission(Bukkit.getOfflinePlayer(owner), b, Interaction.BREAK_BLOCK)) { - stop(MinerStoppingReason.NO_PERMISSION); - return; - } - - if (miner.canMine(b) && push(miner.getOutcome(b.getType()))) { - // Not changed since this is supposed to be a natural sound. - furnace.getWorld().playEffect(furnace.getLocation(), Effect.STEP_SOUND, b.getType()); - - SoundEffect.MINING_TASK_SOUND.playAt(furnace); - - b.setType(Material.AIR); - fuelLevel--; - ores++; - - // Repeat the same column when we hit an ore. - Slimefun.runSync(this, 4); - return; + queue.thenRun(1, () -> setPistonState(pistons[0], true), pistons[0].getLocation()); + queue.thenRun(3, () -> setPistonState(pistons[0], false), pistons[0].getLocation()); + + queue.thenRun(1, () -> setPistonState(pistons[1], true), pistons[1].getLocation()); + queue.thenRun(3, () -> setPistonState(pistons[1], false), pistons[1].getLocation()); + + queue.thenRun( + () -> { + try { + Block furnace = chest.getRelative(BlockFace.DOWN); + furnace.getWorld().playEffect(furnace.getLocation(), Effect.STEP_SOUND, Material.STONE); + + World world = start.getWorld(); + for (int y = height; y > world.getMinHeight(); y--) { + Block b = world.getBlockAt(x, y, z); + + if (!Slimefun.getProtectionManager() + .hasPermission(Bukkit.getOfflinePlayer(owner), b, Interaction.BREAK_BLOCK)) { + stop(MinerStoppingReason.NO_PERMISSION); + return; + } + + if (miner.canMine(b) && push(miner.getOutcome(b.getType()))) { + // Not changed since this is supposed to be a natural sound. + furnace.getWorld().playEffect(furnace.getLocation(), Effect.STEP_SOUND, b.getType()); + + SoundEffect.MINING_TASK_SOUND.playAt(furnace); + + b.setType(Material.AIR); + fuelLevel--; + ores++; + + // Repeat the same column when we hit an ore. + Slimefun.runSync(this, 4); + return; + } + } + + nextColumn(); + } catch (Exception e) { + Slimefun.logger() + .log( + Level.SEVERE, + e, + () -> "An Error occurred while running an Industrial Miner at " + + new BlockPosition(chest)); + stop(); } - } - - nextColumn(); - } catch (Exception e) { - Slimefun.logger() - .log( - Level.SEVERE, - e, - () -> "An Error occurred while running an Industrial Miner at " - + new BlockPosition(chest)); - stop(); - } - }); + }, + chest.getLocation()); queue.execute(Slimefun.instance()); } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfTheSeeker.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfTheSeeker.java index f0fe461e77..c5898beb06 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfTheSeeker.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/items/tools/PickaxeOfTheSeeker.java @@ -69,7 +69,7 @@ public PickaxeOfTheSeeker(ItemGroup itemGroup, SlimefunItemStack item, RecipeTyp p.getLocation().getZ(), yaw, pitch); - p.teleport(loc); + Slimefun.getPlatformScheduler().teleportAsync(p, loc); } damageItem(p, e.getItem()); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ElytraImpactListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ElytraImpactListener.java index d96acef45f..850fe99d62 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ElytraImpactListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/ElytraImpactListener.java @@ -45,7 +45,7 @@ public void onGlideToggle(EntityToggleGlideEvent event) { gliding.add(uuid); } // We tick 1 tick later because the player is being toggled of at the same tick as it takes damage. - Slimefun.instance().getServer().getScheduler().runTaskLater(Slimefun.instance(), gliding::clear, 1); + Slimefun.getPlatformScheduler().runLater(gliding::clear, 1); } @EventHandler diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/GrapplingHookListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/GrapplingHookListener.java index 85c44357b3..4d302669c7 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/GrapplingHookListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/GrapplingHookListener.java @@ -3,11 +3,11 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.items.tools.GrapplingHook; -import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; @@ -44,7 +44,7 @@ public class GrapplingHookListener implements Listener { private GrapplingHook grapplingHook; - private final Map activeHooks = new HashMap<>(); + private final Map activeHooks = new ConcurrentHashMap<>(); private final Set invulnerability = new HashSet<>(); public void register(@Nonnull Slimefun plugin, @Nonnull GrapplingHook grapplingHook) { diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SoulboundListener.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SoulboundListener.java index c51fa8cf95..8b6c696e8e 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SoulboundListener.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/listeners/SoulboundListener.java @@ -1,5 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.listeners; +import com.tcoded.folialib.wrapper.task.WrappedTask; import io.github.thebusybiscuit.slimefun4.core.attributes.Soulbound; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; @@ -10,6 +11,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.inventory.ItemStack; @@ -25,6 +27,7 @@ public class SoulboundListener implements Listener { private final Map> soulbound = new HashMap<>(); + private final Map returnTasks = new HashMap<>(); public SoulboundListener(@Nonnull Slimefun plugin) { plugin.getServer().getPluginManager().registerEvents(this, plugin); @@ -63,6 +66,39 @@ public void onRespawn(PlayerRespawnEvent e) { returnSoulboundItems(e.getPlayer()); } + // Folia implementation. since folia not support for PlayerRespawnEvent for now + // May need remove in future + @EventHandler + public void onRespawn(EntityDeathEvent event) { + if (!Slimefun.isFolia()) { + return; + } + + if (event.getEntity() instanceof Player p) { + if (returnTasks.containsKey(p.getUniqueId())) { + return; + } + + WrappedTask task = Slimefun.getPlatformScheduler() + .runAtEntityLater( + p, + () -> { + if (p.getHealth() > 0) { + returnSoulboundItems(p); + + WrappedTask returnTask = returnTasks.remove(p.getUniqueId()); + + if (returnTask != null) { + returnTask.cancel(); + } + } + }, + 10L); + + returnTasks.put(event.getEntity().getUniqueId(), task); + } + } + private void returnSoulboundItems(@Nonnull Player p) { Map items = soulbound.remove(p.getUniqueId()); diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/ArmorTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/ArmorTask.java index 6972158f16..c6a7321362 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/ArmorTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/ArmorTask.java @@ -109,16 +109,19 @@ private void handleSlimefunArmor(Player p, ItemStack[] armor, HashedArmorpiece[] } if (item != null && armorpiece.getItem().isPresent()) { - Slimefun.runSync(() -> { - SlimefunArmorPiece slimefunArmor = armorpiece.getItem().get(); - - if (slimefunArmor.canUse(p, true)) { - for (PotionEffect effect : slimefunArmor.getPotionEffects()) { - p.removePotionEffect(effect.getType()); - p.addPotionEffect(effect); - } - } - }); + Slimefun.runSync( + () -> { + SlimefunArmorPiece slimefunArmor = + armorpiece.getItem().get(); + + if (slimefunArmor.canUse(p, true)) { + for (PotionEffect effect : slimefunArmor.getPotionEffects()) { + p.removePotionEffect(effect.getType()); + p.addPotionEffect(effect); + } + } + }, + p); } } } @@ -172,14 +175,16 @@ private boolean checkAndApplyRadiation(@Nonnull Player p, @Nullable ItemStack it // If the item is enabled in the world, then make radioactivity do its job Slimefun.getLocalization().sendMessage(p, "messages.radiation"); - Slimefun.runSync(() -> { - p.addPotionEffects(radiationEffects); + Slimefun.runSync( + () -> { + p.addPotionEffects(radiationEffects); - // if radioactive fire is enabled, set them on fire - if (radioactiveFire) { - p.setFireTicks(400); - } - }); + // if radioactive fire is enabled, set them on fire + if (radioactiveFire) { + p.setFireTicks(400); + } + }, + p); return true; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/AsyncRecipeChoiceTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/AsyncRecipeChoiceTask.java index 02dbcac6fc..90045cc32b 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/AsyncRecipeChoiceTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/AsyncRecipeChoiceTask.java @@ -1,5 +1,6 @@ package io.github.thebusybiscuit.slimefun4.implementation.tasks; +import com.tcoded.folialib.wrapper.task.WrappedTask; import io.github.bakedlibs.dough.collections.LoopIterator; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.guide.SurvivalSlimefunGuide; @@ -9,7 +10,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.annotation.Nonnull; import org.apache.commons.lang.Validate; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Tag; import org.bukkit.inventory.Inventory; @@ -35,7 +35,7 @@ public class AsyncRecipeChoiceTask implements Runnable { private final ReadWriteLock lock = new ReentrantReadWriteLock(); private Inventory inventory; - private int id; + private WrappedTask task; /** * This will start this task for the given {@link Inventory}. @@ -47,9 +47,7 @@ public void start(@Nonnull Inventory inv) { Validate.notNull(inv, "Inventory must not be null"); inventory = inv; - id = Bukkit.getScheduler() - .runTaskTimerAsynchronously(Slimefun.instance(), this, 0, UPDATE_INTERVAL) - .getTaskId(); + task = Slimefun.getPlatformScheduler().runTimerAsync(this, 0, UPDATE_INTERVAL); } public void add(int slot, @Nonnull MaterialChoice choice) { @@ -109,7 +107,7 @@ public void clear() { public void run() { // Terminate the task when noone is viewing the Inventory if (inventory.getViewers().isEmpty()) { - Bukkit.getScheduler().cancelTask(id); + Slimefun.getPlatformScheduler().cancelTask(task); return; } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java index c64a3402fa..247c8afbd2 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/TickerTask.java @@ -1,5 +1,7 @@ package io.github.thebusybiscuit.slimefun4.implementation.tasks; +import city.norain.slimefun4.utils.SlimefunPoolExecutor; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.xzavier0722.mc.plugin.slimefun4.storage.controller.ASlimefunDataContainer; import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunBlockData; import com.xzavier0722.mc.plugin.slimefun4.storage.controller.SlimefunUniversalData; @@ -17,6 +19,10 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.stream.Collectors; import javax.annotation.Nonnull; @@ -27,7 +33,6 @@ import org.apache.commons.lang.Validate; import org.bukkit.Chunk; import org.bukkit.Location; -import org.bukkit.scheduler.BukkitScheduler; /** * The {@link TickerTask} is responsible for ticking every {@link BlockTicker}, @@ -52,24 +57,71 @@ public class TickerTask implements Runnable { */ private final Map bugs = new ConcurrentHashMap<>(); + private final ThreadFactory tickerThreadFactory = new ThreadFactoryBuilder() + .setNameFormat("SF-Ticker-%d") + .setDaemon(true) + .setUncaughtExceptionHandler( + (t, e) -> Slimefun.logger().log(Level.SEVERE, e, () -> "tick 时发生异常 (@" + t.getName() + ")")) + .build(); + + /** + * 负责并发运行部分可异步的 Tick 任务的 {@link ExecutorService} 实例. + */ + private ExecutorService asyncTickerService; + + private ExecutorService fallbackTickerService; + private int tickRate; - private boolean halted = false; - private boolean running = false; + /** + * 该标记代表 TickerTask 已被终止. + */ + private volatile boolean halted = false; + + /** + * 该标记代表 TickerTask 正在运行. + */ + private volatile boolean running = false; + + /** + * 该标记代表 TickerTask 暂时被暂停. + */ @Setter private volatile boolean paused = false; /** * This method starts the {@link TickerTask} on an asynchronous schedule. - * - * @param plugin - * The instance of our {@link Slimefun} */ - public void start(@Nonnull Slimefun plugin) { + public void start() { this.tickRate = Slimefun.getCfg().getInt("URID.custom-ticker-delay"); - BukkitScheduler scheduler = plugin.getServer().getScheduler(); - scheduler.runTaskTimerAsynchronously(plugin, this, 100L, tickRate); + var initSize = Slimefun.getConfigManager().getAsyncTickerInitSize(); + var maxSize = Slimefun.getConfigManager().getAsyncTickerMaxSize(); + var poolSize = Slimefun.getConfigManager().getAsyncTickerQueueSize(); + + this.asyncTickerService = new SlimefunPoolExecutor( + "Slimefun-Ticker-Pool", + initSize - 1, + maxSize - 1, + 1, + TimeUnit.MINUTES, + new LinkedBlockingQueue<>(poolSize), + tickerThreadFactory, + (r, e) -> { + // 任务队列已满,使用备用的单线程池执行该任务 + fallbackTickerService.submit(r); + }); + + this.fallbackTickerService = new SlimefunPoolExecutor( + "Slimefun-Ticker-Fallback-Service", + 1, + 1, + 0, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), + tickerThreadFactory); + + Slimefun.getPlatformScheduler().runTimerAsync(this, 100L, tickRate); } /** @@ -99,9 +151,7 @@ public void run() { if (!halted) { Set>> loc; - synchronized (tickingLocations) { - loc = new HashSet<>(tickingLocations.entrySet()); - } + loc = new HashSet<>(tickingLocations.entrySet()); for (Map.Entry> entry : loc) { tickChunk(entry.getKey(), tickers, new HashSet<>(entry.getValue())); @@ -113,7 +163,6 @@ public void run() { ticker.startNewTick(); } - reset(); Slimefun.getProfiler().stop(); } catch (Exception | LinkageError x) { Slimefun.logger() @@ -122,6 +171,7 @@ public void run() { x, () -> "An Exception was caught while ticking the Block Tickers Task for Slimefun v" + Slimefun.getVersion()); + } finally { reset(); } } @@ -205,16 +255,36 @@ private void tickUniversalLocation(UUID uuid, Location l, @Nonnull Set { - if (data.isPendingRemove()) { - return; - } - tickBlock(l, item, data, System.nanoTime()); - }); + Slimefun.runSync( + () -> { + if (data.isPendingRemove()) { + return; + } + tickBlock(l, item, data, System.nanoTime()); + }, + l); } else { long timestamp = Slimefun.getProfiler().newEntry(); item.getBlockTicker().update(); - tickBlock(l, item, data, timestamp); + + Runnable func = () -> { + try { + if (Slimefun.isFolia()) { + Slimefun.getPlatformScheduler() + .runAtLocation(l, task -> tickBlock(l, item, data, timestamp)); + } else { + tickBlock(l, item, data, timestamp); + } + } catch (Exception x) { + reportErrors(l, item, x); + } + }; + + if (item.getBlockTicker().isConcurrent()) { + asyncTickerService.execute(func); + } else { + fallbackTickerService.execute(func); + } } tickers.add(item.getBlockTicker()); @@ -227,7 +297,7 @@ private void tickUniversalLocation(UUID uuid, Location l, @Nonnull Set loc.removeIf(tk -> uuid.equals(tk.getUuid()))); } } + + public void shutdown() { + setPaused(true); + halt(); + + try { + asyncTickerService.shutdown(); + if (!asyncTickerService.awaitTermination(10, TimeUnit.SECONDS)) { + asyncTickerService.shutdownNow(); + } + } catch (InterruptedException e) { + asyncTickerService.shutdownNow(); + } finally { + asyncTickerService = null; + } + + try { + fallbackTickerService.shutdown(); + if (!fallbackTickerService.awaitTermination(10, TimeUnit.SECONDS)) { + fallbackTickerService.shutdownNow(); + } + } catch (InterruptedException e) { + fallbackTickerService.shutdownNow(); + } finally { + fallbackTickerService = null; + } + } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/AbstractArmorTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/AbstractArmorTask.java index 6748cf20b0..9dd313fc4d 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/AbstractArmorTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/AbstractArmorTask.java @@ -54,7 +54,7 @@ public final void schedule(@Nonnull Slimefun plugin, long tickInterval) { new Object[] {getClass().getSimpleName(), tickInterval}); } - plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, this, 0L, tickInterval); + plugin.getPlatformScheduler().runTimerAsync(this, 0L, tickInterval); } /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/RadiationTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/RadiationTask.java index 2975ee7d21..e587b748ca 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/RadiationTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/RadiationTask.java @@ -76,13 +76,15 @@ protected void onPlayerTick(Player p, PlayerProfile profile) { int exposureLevelAfter = RadiationUtils.getExposure(p); - Slimefun.runSync(() -> { - for (RadiationSymptom symptom : symptoms) { - if (symptom.shouldApply(exposureLevelAfter)) { - symptom.apply(p); - } - } - }); + Slimefun.runSync( + () -> { + for (RadiationSymptom symptom : symptoms) { + if (symptom.shouldApply(exposureLevelAfter)) { + symptom.apply(p); + } + } + }, + p); if (exposureLevelAfter > 0 || exposureLevelBefore > 0) { String msg = Slimefun.getLocalization() diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/SlimefunArmorTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/SlimefunArmorTask.java index 187cd8248e..37df058b78 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/SlimefunArmorTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/armor/SlimefunArmorTask.java @@ -44,13 +44,16 @@ private void updateAndHandleArmor(Player p, ItemStack[] armor, HashedArmorpiece[ } if (item != null && armorPiece.getItem().isPresent()) { - Slimefun.runSync(() -> { - SlimefunArmorPiece sfArmorPiece = armorPiece.getItem().get(); + Slimefun.runSync( + () -> { + SlimefunArmorPiece sfArmorPiece = + armorPiece.getItem().get(); - if (sfArmorPiece.canUse(p, true)) { - onArmorPieceTick(p, sfArmorPiece, item); - } - }); + if (sfArmorPiece.canUse(p, true)) { + onArmorPieceTick(p, sfArmorPiece, item); + } + }, + p); } } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/player/AbstractPlayerTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/player/AbstractPlayerTask.java index 4419c2830d..95ca512db8 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/player/AbstractPlayerTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/player/AbstractPlayerTask.java @@ -1,29 +1,29 @@ package io.github.thebusybiscuit.slimefun4.implementation.tasks.player; +import com.tcoded.folialib.wrapper.task.WrappedTask; import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import javax.annotation.Nonnull; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; abstract class AbstractPlayerTask implements Runnable { protected final Player p; - private int id; + private WrappedTask task; AbstractPlayerTask(@Nonnull Player p) { this.p = p; } - private void setID(int id) { - this.id = id; + private void setTask(@Nonnull WrappedTask task) { + this.task = task; } public void schedule(long delay) { - setID(Bukkit.getScheduler().scheduleSyncDelayedTask(Slimefun.instance(), this, delay)); + setTask(Slimefun.getPlatformScheduler().runLaterAsync(this, delay)); } public void scheduleRepeating(long delay, long interval) { - setID(Bukkit.getScheduler().scheduleSyncRepeatingTask(Slimefun.instance(), this, delay, interval)); + setTask(Slimefun.getPlatformScheduler().runTimer(this, delay, interval)); } @Override @@ -37,7 +37,7 @@ public final void run() { * This method cancels this {@link AbstractPlayerTask}. */ public final void cancel() { - Bukkit.getScheduler().cancelTask(id); + Slimefun.getPlatformScheduler().cancelTask(task); } /** diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/player/InfusedMagnetTask.java b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/player/InfusedMagnetTask.java index a312f0bf1d..71f7e5c7ff 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/player/InfusedMagnetTask.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/implementation/tasks/player/InfusedMagnetTask.java @@ -1,6 +1,7 @@ package io.github.thebusybiscuit.slimefun4.implementation.tasks.player; import io.github.thebusybiscuit.slimefun4.core.services.sounds.SoundEffect; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; import io.github.thebusybiscuit.slimefun4.implementation.items.magical.InfusedMagnet; import io.github.thebusybiscuit.slimefun4.utils.SlimefunUtils; import javax.annotation.Nonnull; @@ -49,7 +50,7 @@ protected void executeTask() { && !SlimefunUtils.hasNoPickupFlag(item) && item.getPickupDelay() <= 0 && p.getLocation().distanceSquared(item.getLocation()) > 0.3) { - item.teleport(p.getLocation()); + Slimefun.getPlatformScheduler().teleportAsync(item, p.getLocation()); playSound = true; } } diff --git a/src/main/java/io/github/thebusybiscuit/slimefun4/integrations/IntegrationsManager.java b/src/main/java/io/github/thebusybiscuit/slimefun4/integrations/IntegrationsManager.java index 0b4360aa03..44fd60ae60 100644 --- a/src/main/java/io/github/thebusybiscuit/slimefun4/integrations/IntegrationsManager.java +++ b/src/main/java/io/github/thebusybiscuit/slimefun4/integrations/IntegrationsManager.java @@ -89,8 +89,7 @@ public final void start() { // Load any soft dependencies onServerLoad(); - // Load any integrations which aren't dependencies (loadBefore) - plugin.getServer().getScheduler().runTask(plugin, this::onServerStart); + Slimefun.getPlatformScheduler().runNextTick((task) -> onServerStart()); } /** diff --git a/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/ChestMenu.java b/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/ChestMenu.java index 871de1a997..223d39b95e 100644 --- a/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/ChestMenu.java +++ b/src/main/java/me/mrCookieSlime/CSCoreLibPlugin/general/Inventory/ChestMenu.java @@ -10,7 +10,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; import javax.annotation.Nonnull; import lombok.Getter; import org.bukkit.Bukkit; @@ -50,7 +50,7 @@ public class ChestMenu extends SlimefunInventoryHolder { // 何意味 private final Set viewers = new CopyOnWriteArraySet<>(); - private final AtomicBoolean lock = new AtomicBoolean(false); + private final ReentrantLock lock = new ReentrantLock(); /** * Creates a new ChestMenu with the specified @@ -396,16 +396,16 @@ public boolean isSizeAutomaticallyInferred() { } public boolean locked() { - return lock.get(); + return lock.isLocked(); } public void lock() { - lock.getAndSet(true); + lock.lock(); InventoryUtil.closeInventory(this.inventory); } public void unlock() { - lock.getAndSet(false); + lock.unlock(); } @FunctionalInterface diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AContainer.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AContainer.java index 60cb68b073..dfce454006 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AContainer.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/SlimefunItem/abstractItems/AContainer.java @@ -349,6 +349,11 @@ public void tick(Block b, SlimefunItem sf, SlimefunBlockData data) { public boolean isSynchronized() { return false; } + + @Override + public boolean isConcurrent() { + return true; + } }); } diff --git a/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/BlockTicker.java b/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/BlockTicker.java index 83d0140bf8..e7617ee923 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/BlockTicker.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/Objects/handlers/BlockTicker.java @@ -8,24 +8,13 @@ import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.core.attributes.NotPlaceable; import java.util.Optional; -import lombok.Getter; import me.mrCookieSlime.CSCoreLibPlugin.Configuration.Config; import org.bukkit.block.Block; public abstract class BlockTicker implements ItemHandler { - - @Getter - private final boolean universal; - protected boolean unique = true; - public BlockTicker() { - this.universal = false; - } - - public BlockTicker(boolean universal) { - this.universal = universal; - } + public BlockTicker() {} /** * 刷新当前 ticker 执行状态 @@ -60,6 +49,24 @@ public Optional validate(SlimefunItem item) { */ public abstract boolean isSynchronized(); + /** + * 声明当前 {@link BlockTicker} 是否使用了通用数据 + */ + public boolean useUniversalData() { + return false; + } + + /** + * 声明当前 {@link BlockTicker} 是否线程安全 + *
+ * 默认不启用,此时会将这些机器放置到单线程调度器 (旧方法) 上运行 + * + * @return 是否并发安全 + */ + public boolean isConcurrent() { + return false; + } + /** * This method is called every tick for every block * diff --git a/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java b/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java index 829d66755c..d680c93094 100644 --- a/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java +++ b/src/main/java/me/mrCookieSlime/Slimefun/api/inventory/BlockMenuPreset.java @@ -220,15 +220,17 @@ protected void clone(@Nonnull DirtyChestMenu menu) { public void newInstance(@Nonnull BlockMenu menu, @Nonnull Location l) { Validate.notNull(l, "Cannot create a new BlockMenu without a Location"); - Slimefun.runSync(() -> { - locked = true; - - try { - newInstance(menu, l.getBlock()); - } catch (Exception | LinkageError x) { - getSlimefunItem().error("An Error occurred while trying to create a BlockMenu", x); - } - }); + Slimefun.runSync( + () -> { + locked = true; + + try { + newInstance(menu, l.getBlock()); + } catch (Exception | LinkageError x) { + getSlimefunItem().error("An Error occurred while trying to create a BlockMenu", x); + } + }, + l); } /** diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index e98a78c741..165d8a962a 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -64,6 +64,14 @@ URID: info-delay: 3000 custom-ticker-delay: 10 enable-tickers: true + # 自定义 Ticker 的异步线程池配置 + custom-async-ticker: + # 线程池初始线程数, 推荐数为主机的 CPU 核心数 + init-size: 2 + # 线程数最大线程数,推荐数为主机的 CPU 核心数 * 2 + max-size: 8 + # 每个线程池的任务队列大小,数值越大每个线程能处理的任务越多,但会增加总体延迟 + queue-size: 1024 networks: max-size: 200 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 5827ce8c36..8596341d74 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -11,6 +11,8 @@ website: https://github.com/TheBusyBiscuit/Slimefun4 main: io.github.thebusybiscuit.slimefun4.implementation.Slimefun api-version: '1.16' +folia-supported: true + # (Soft) dependencies of Slimefun, we hook into these plugins. softdepend: - PlaceholderAPI