diff --git a/pom.xml b/pom.xml index 599e1d3..0edafad 100644 --- a/pom.xml +++ b/pom.xml @@ -78,6 +78,12 @@ 1.0.6 compile + + dev.folia + folia-api + 1.21.4-R0.1-SNAPSHOT + provided + org.jetbrains annotations @@ -124,7 +130,7 @@ - ${project.name}-${project.version} + ${project.name}Folia-${project.version} ${project.basedir}/src/main/resources diff --git a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/InventoryRollbackPlus.java b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/InventoryRollbackPlus.java index 7ca7e06..7f22d50 100644 --- a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/InventoryRollbackPlus.java +++ b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/InventoryRollbackPlus.java @@ -2,6 +2,7 @@ import com.nuclyon.technicallycoded.inventoryrollback.UpdateChecker.UpdateResult; import com.nuclyon.technicallycoded.inventoryrollback.commands.Commands; +import com.nuclyon.technicallycoded.inventoryrollback.folia.SchedulerUtils; import com.nuclyon.technicallycoded.inventoryrollback.util.TimeZoneUtil; import com.nuclyon.technicallycoded.inventoryrollback.util.test.SelfTestSerialization; import com.tcoded.lightlibs.bukkitversion.BukkitVersion; @@ -30,6 +31,7 @@ public class InventoryRollbackPlus extends InventoryRollback { private static InventoryRollbackPlus instancePlus; + public static boolean usingFolia = false; private TimeZoneUtil timeZoneUtil = null; @@ -42,6 +44,16 @@ public static InventoryRollbackPlus getInstance() { return instancePlus; } + @Override + public void onLoad() { + try { + Class.forName("io.papermc.paper.threadedregions.scheduler.RegionScheduler"); + usingFolia = true; + } catch (ClassNotFoundException e) { + usingFolia = false; + } + } + @Override public void onEnable() { instancePlus = this; @@ -89,7 +101,7 @@ public void onEnable() { getServer().getPluginManager().registerEvents(new ClickGUI(), this); getServer().getPluginManager().registerEvents(new EventLogs(), this); // Run after all plugin enable - getServer().getScheduler().runTask(this, EventLogs::patchLowestHandlers); + SchedulerUtils.runTask(null, EventLogs::patchLowestHandlers); // PaperLib if (!PaperLib.isPaper()) { @@ -124,7 +136,7 @@ public void onDisable() { HandlerList.unregisterAll(this); // Cancel tasks - this.getServer().getScheduler().cancelTasks(this); + if(!usingFolia) this.getServer().getScheduler().cancelTasks(this); // Clear instance references instancePlus = null; @@ -149,7 +161,7 @@ public boolean isCompatibleCb(String cbVersion) { } public void checkUpdate() { - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), () -> { + SchedulerUtils.runTaskAsynchronously(() -> { InventoryRollbackPlus.getInstance().getConsoleSender().sendMessage(MessageData.getPluginPrefix() + "Checking for updates..."); final UpdateResult result = new UpdateChecker(getInstance(), 85811).getResult(); diff --git a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/Commands.java b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/Commands.java index 47d02d4..d4bc726 100644 --- a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/Commands.java +++ b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/Commands.java @@ -4,8 +4,8 @@ import com.nuclyon.technicallycoded.inventoryrollback.commands.inventoryrollback.*; import me.danjono.inventoryrollback.config.MessageData; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -20,7 +20,7 @@ public class Commands implements CommandExecutor, TabCompleter { private String[] backupOptions = new String[] {"all", "player"}; private String[] importOptions = new String[] {"confirm"}; - private HashMap subCommands = new HashMap<>(); + private ConcurrentHashMap subCommands = new ConcurrentHashMap<>(); public Commands(InventoryRollbackPlus mainIn) { this.main = mainIn; diff --git a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/inventoryrollback/ImportSubCmd.java b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/inventoryrollback/ImportSubCmd.java index af926d9..aac06d8 100644 --- a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/inventoryrollback/ImportSubCmd.java +++ b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/inventoryrollback/ImportSubCmd.java @@ -2,6 +2,7 @@ import com.nuclyon.technicallycoded.inventoryrollback.InventoryRollbackPlus; import com.nuclyon.technicallycoded.inventoryrollback.commands.IRPCommand; +import com.nuclyon.technicallycoded.inventoryrollback.folia.SchedulerUtils; import com.nuclyon.technicallycoded.inventoryrollback.util.LegacyBackupConversionUtil; import me.danjono.inventoryrollback.config.MessageData; import org.bukkit.Bukkit; @@ -32,7 +33,7 @@ public void onCommand(CommandSender sender, Command cmd, String label, String[] suggestConfirm.set(true); // Reset suggestion availability after 10 seconds - this.main.getServer().getScheduler().runTaskLaterAsynchronously(this.main, () -> { + SchedulerUtils.runTaskLaterAsynchronously(null, () -> { suggestConfirm.set(false); }, 10 * 20); @@ -40,7 +41,7 @@ public void onCommand(CommandSender sender, Command cmd, String label, String[] } // Execute import - Bukkit.getScheduler().runTaskAsynchronously(main, LegacyBackupConversionUtil::convertOldBackupData); + SchedulerUtils.runTaskAsynchronously(LegacyBackupConversionUtil::convertOldBackupData); // Reset suggestion to not visible suggestConfirm.set(false); diff --git a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/inventoryrollback/RestoreSubCmd.java b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/inventoryrollback/RestoreSubCmd.java index d5e7e23..cde277d 100644 --- a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/inventoryrollback/RestoreSubCmd.java +++ b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/inventoryrollback/RestoreSubCmd.java @@ -2,6 +2,7 @@ import com.nuclyon.technicallycoded.inventoryrollback.InventoryRollbackPlus; import com.nuclyon.technicallycoded.inventoryrollback.commands.IRPCommand; +import com.nuclyon.technicallycoded.inventoryrollback.folia.SchedulerUtils; import me.danjono.inventoryrollback.InventoryRollback; import me.danjono.inventoryrollback.config.ConfigData; import me.danjono.inventoryrollback.config.MessageData; @@ -90,14 +91,14 @@ private void openMainMenu(Player staff) { MainMenu menu = new MainMenu(staff, 1); staff.openInventory(menu.getInventory()); - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), menu::getMainMenu); + SchedulerUtils.runTaskAsynchronously(menu::getMainMenu); } private void openPlayerMenu(Player staff, OfflinePlayer offlinePlayer) { PlayerMenu menu = new PlayerMenu(staff, offlinePlayer); staff.openInventory(menu.getInventory()); - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), menu::getPlayerMenu); + SchedulerUtils.runTaskAsynchronously(menu::getPlayerMenu); } } diff --git a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/inventoryrollback/VersionSubCmd.java b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/inventoryrollback/VersionSubCmd.java index 084394b..8d7c324 100644 --- a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/inventoryrollback/VersionSubCmd.java +++ b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/commands/inventoryrollback/VersionSubCmd.java @@ -23,7 +23,7 @@ public void onCommand(CommandSender sender, Command cmd, String label, String[] .append(ChatColor.WHITE) .append("Plugin:").append("\n") .append(ChatColor.GRAY) - .append(" Running InventoryRollbackPlus"); + .append(" Running InventoryRollbackPlusFolia"); // Can see version? if (hasVersionPerm) strb.append(" v").append(InventoryRollback.getPluginVersion()); strb.append("\n"); @@ -37,10 +37,10 @@ public void onCommand(CommandSender sender, Command cmd, String label, String[] .append("Authors:").append("\n") .append(ChatColor.GRAY) .append(" - Maintained/updated by: TechnicallyCoded").append("\n") - .append(" - Original author: danjono").append("\n") + .append(" - Original author: danjono").append("\n").append(" - Folia support: yLeoft") .append("\n") .append(ChatColor.WHITE).append("Update link:").append("\n") - .append(ChatColor.BLUE).append(ChatColor.ITALIC).append(" https://www.spigotmc.org/resources/inventoryrollback-plus.85811/"); + .append(ChatColor.BLUE).append(ChatColor.ITALIC).append(" https://github.com/yL3oft/Inventory-Rollback-Plus-Folia/releases"); // Send diff --git a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/folia/FoliaRunnable.java b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/folia/FoliaRunnable.java new file mode 100644 index 0000000..b467021 --- /dev/null +++ b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/folia/FoliaRunnable.java @@ -0,0 +1,22 @@ +package com.nuclyon.technicallycoded.inventoryrollback.folia; + +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import org.bukkit.scheduler.BukkitRunnable; + +public class FoliaRunnable extends BukkitRunnable { + + private ScheduledTask foliaTask; + + @Override + public synchronized void cancel() throws IllegalStateException { + if(foliaTask != null) foliaTask.cancel(); + } + + @Override + public void run() { + } + + public void setScheduledTask(ScheduledTask task) { + this.foliaTask = task; + } +} diff --git a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/folia/SchedulerUtils.java b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/folia/SchedulerUtils.java new file mode 100644 index 0000000..2eac2ff --- /dev/null +++ b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/folia/SchedulerUtils.java @@ -0,0 +1,264 @@ +package com.nuclyon.technicallycoded.inventoryrollback.folia; + +import com.nuclyon.technicallycoded.inventoryrollback.InventoryRollbackPlus; +import io.papermc.paper.threadedregions.scheduler.AsyncScheduler; +import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler; +import io.papermc.paper.threadedregions.scheduler.RegionScheduler; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Method; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import static com.nuclyon.technicallycoded.inventoryrollback.InventoryRollbackPlus.usingFolia; + +/** + * Utility class for scheduling tasks in a Paper/Folia server. + */ +public abstract class SchedulerUtils { + + /** + * Schedules a task to run later on the main server thread. + * @param loc The location where the task should run, or null for the main thread. + * @param task The task to run. + * @param delay The delay in ticks before the task runs. + */ + public static void runTaskLater(@Nullable Location loc, @NotNull Runnable task, long delay) { + JavaPlugin plugin = InventoryRollbackPlus.getInstance(); + if (usingFolia) { + try { + if (loc != null) { + Method getRegionScheduler = plugin.getServer().getClass().getMethod("getRegionScheduler"); + RegionScheduler regionScheduler = (RegionScheduler) getRegionScheduler.invoke(plugin.getServer()); + regionScheduler.runDelayed( + plugin, + loc, + (ScheduledTask scheduledTask) -> task.run(), + delay + ); + } else { + Method getGlobalScheduler = plugin.getServer().getClass().getMethod("getGlobalRegionScheduler"); + GlobalRegionScheduler globalScheduler = (GlobalRegionScheduler) getGlobalScheduler.invoke(plugin.getServer()); + globalScheduler.runDelayed( + plugin, + (ScheduledTask scheduledTask) -> task.run(), + delay + ); + } + return; + } catch (Exception e) { + e.printStackTrace(); + } + } + plugin.getServer().getScheduler().runTaskLater(plugin, task, delay); + } + + /** + * Schedules a task to run later asynchronously on the main server thread. + * @param loc The location where the task should run, or null for the main thread. + * @param task The task to run. + * @param delay The delay in ticks before the task runs. + */ + public static void runTaskLaterAsynchronously(@Nullable Location loc, @NotNull Runnable task, long delay) { + JavaPlugin plugin = InventoryRollbackPlus.getInstance(); + if (usingFolia) { + try { + // Use the async scheduler not a region/global tick thread! + // the async scheduler is time based so convert ticks to milliseconds. + Method getAsyncScheduler = plugin.getServer().getClass().getMethod("getAsyncScheduler"); + AsyncScheduler asyncScheduler = (AsyncScheduler) getAsyncScheduler.invoke(plugin.getServer()); + if (delay <= 0) { + asyncScheduler.runNow(plugin, (ScheduledTask scheduledTask) -> task.run()); + } else { + asyncScheduler.runDelayed( + plugin, + (ScheduledTask scheduledTask) -> task.run(), + delay * 50L, + TimeUnit.MILLISECONDS + ); + } + return; + } catch (Exception e) { + e.printStackTrace(); + } + } + plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, task, delay); + } + + /** + * Schedules a task to run repeatedly on the main server thread. + * @param loc The location where the task should run, or null for the main thread. + * @param runnable The BukkitRunnable to run. + * @param delay The delay in ticks before the task runs. + * @param period The period in ticks between subsequent runs of the task. + */ + public static void runTaskTimer(@Nullable Location loc, @NotNull FoliaRunnable runnable, long delay, long period) { + JavaPlugin plugin = InventoryRollbackPlus.getInstance(); + if (usingFolia) { + try { + ScheduledTask task; + if (loc != null) { + Method getRegionScheduler = plugin.getServer().getClass().getMethod("getRegionScheduler"); + RegionScheduler regionScheduler = (RegionScheduler) getRegionScheduler.invoke(plugin.getServer()); + task = regionScheduler.runAtFixedRate( + plugin, + loc, + (ScheduledTask t) -> runnable.run(), + delay, + period + ); + } else { + Method getGlobalScheduler = plugin.getServer().getClass().getMethod("getGlobalRegionScheduler"); + GlobalRegionScheduler globalScheduler = (GlobalRegionScheduler) getGlobalScheduler.invoke(plugin.getServer()); + task = globalScheduler.runAtFixedRate( + plugin, + (ScheduledTask t) -> runnable.run(), + delay, + period + ); + } + runnable.setScheduledTask(task); + } catch (Exception e) { + e.printStackTrace(); + } + return; + } + runnable.runTaskTimer(plugin, delay, period); + } + + /** + * Schedules a task to run repeatedly on the main server thread asynchronously. + * @param runnable The FoliaRunnable to run. + * @param delay The delay in ticks before the task runs. + * @param period The period in ticks between subsequent runs of the task. + */ + public static void runTaskTimerAsynchronously(@NotNull FoliaRunnable runnable, long delay, long period) { + JavaPlugin plugin = InventoryRollbackPlus.getInstance(); + if (usingFolia) { + try { + // True asyncronous repeating task via the async scheduler (time based) + // requires strictly positive initial delay/period, so convert ticks to ms and clamp to a minimum of one tick + Method getAsyncScheduler = plugin.getServer().getClass().getMethod("getAsyncScheduler"); + AsyncScheduler asyncScheduler = (AsyncScheduler) getAsyncScheduler.invoke(plugin.getServer()); + long initialDelayMs = Math.max(1L, delay) * 50L; + long periodMs = Math.max(1L, period) * 50L; + ScheduledTask task = asyncScheduler.runAtFixedRate( + plugin, + (ScheduledTask t) -> runnable.run(), + initialDelayMs, + periodMs, + TimeUnit.MILLISECONDS + ); + runnable.setScheduledTask(task); + } catch (Exception e) { + e.printStackTrace(); + } + return; + } + runnable.runTaskTimerAsynchronously(plugin, delay, period); + } + + /** + * Runs a task asynchronously on the main server thread. + * @param task The task to run. + */ + public static void runTaskAsynchronously(@NotNull Runnable task) { + JavaPlugin plugin = InventoryRollbackPlus.getInstance(); + if (usingFolia) { + try { + // Must use the async scheduler NOT the global region scheduler + // running here lets callers safely block (eg CompletableFuture.get()) without + // freezing the global region. Using the global scheduler caused a self deadlock + // whenever a task here blocked on a future completed by another such "async" task + Method getAsyncScheduler = plugin.getServer().getClass().getMethod("getAsyncScheduler"); + AsyncScheduler asyncScheduler = (AsyncScheduler) getAsyncScheduler.invoke(plugin.getServer()); + asyncScheduler.runNow(plugin, (ScheduledTask t) -> task.run()); + return; + } catch (Exception e) { + e.printStackTrace(); + } + } + plugin.getServer().getScheduler().runTaskAsynchronously(plugin, task); + } + + /** + * Runs a task on the main server thread at a specific location or globally. + * @param loc The location where the task should run, or null for the main thread. + * @param task The task to run. + */ + public static void runTask(@Nullable Location loc, @NotNull Runnable task) { + JavaPlugin plugin = InventoryRollbackPlus.getInstance(); + if (usingFolia) { + try { + if (loc != null) { + Method getRegionScheduler = plugin.getServer().getClass().getMethod("getRegionScheduler"); + RegionScheduler regionScheduler = (RegionScheduler) getRegionScheduler.invoke(plugin.getServer()); + regionScheduler.execute(plugin, loc, task); + } else { + Method getGlobalScheduler = plugin.getServer().getClass().getMethod("getGlobalRegionScheduler"); + GlobalRegionScheduler globalScheduler = (GlobalRegionScheduler) getGlobalScheduler.invoke(plugin.getServer()); + globalScheduler.execute(plugin, task); + } + return; + } catch (Exception e) { + e.printStackTrace(); + } + } + plugin.getServer().getScheduler().runTask(plugin, task); + } + + public static CompletableFuture callSyncMethod(@Nullable Location loc, @NotNull Callable task) { + JavaPlugin plugin = InventoryRollbackPlus.getInstance(); + if (usingFolia) { + CompletableFuture future = new CompletableFuture<>(); + try { + if (loc != null) { + Method getRegionScheduler = plugin.getServer().getClass().getMethod("getRegionScheduler"); + RegionScheduler regionScheduler = (RegionScheduler) getRegionScheduler.invoke(plugin.getServer()); + regionScheduler.execute(plugin, loc, () -> { + try { + future.complete(task.call()); + } catch (Exception e) { + future.completeExceptionally(e); + } + }); + } else { + Method getGlobalScheduler = plugin.getServer().getClass().getMethod("getGlobalRegionScheduler"); + GlobalRegionScheduler globalScheduler = (GlobalRegionScheduler) getGlobalScheduler.invoke(plugin.getServer()); + globalScheduler.execute(plugin, () -> { + try { + future.complete(task.call()); + } catch (Exception e) { + future.completeExceptionally(e); + } + }); + } + } catch (Exception e) { + future.completeExceptionally(e); + } + return future; + } + CompletableFuture cf = new CompletableFuture<>(); + try { + Future bukkitFuture = plugin.getServer().getScheduler().callSyncMethod(plugin, task); + plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> { + try { + T result = bukkitFuture.get(); + cf.complete(result); + } catch (Throwable ex) { + cf.completeExceptionally(ex); + } + }); + } catch (Throwable e) { + cf.completeExceptionally(e); + } + return cf; + } +} diff --git a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/util/TimeZoneUtil.java b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/util/TimeZoneUtil.java index b4fc373..3be2104 100644 --- a/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/util/TimeZoneUtil.java +++ b/src/main/java/com/nuclyon/technicallycoded/inventoryrollback/util/TimeZoneUtil.java @@ -4,7 +4,7 @@ import java.time.Instant; import java.util.Date; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; public class TimeZoneUtil { @@ -125,8 +125,8 @@ public String getTimeZoneFullName(String shortCode) { // INIT DATA public void loadDefaultData() { - HashMap offsetsMapBuilder = new HashMap<>(); - HashMap shortCodeNamesBuilder = new HashMap<>(); + ConcurrentHashMap offsetsMapBuilder = new ConcurrentHashMap<>(); + ConcurrentHashMap shortCodeNamesBuilder = new ConcurrentHashMap<>(); offsetsMapBuilder.put("ACDT", "GMT+10:30"); offsetsMapBuilder.put("ACST", "GMT+09:30"); diff --git a/src/main/java/me/danjono/inventoryrollback/InventoryRollback.java b/src/main/java/me/danjono/inventoryrollback/InventoryRollback.java index cb8b56a..ea75bd9 100644 --- a/src/main/java/me/danjono/inventoryrollback/InventoryRollback.java +++ b/src/main/java/me/danjono/inventoryrollback/InventoryRollback.java @@ -1,6 +1,7 @@ package me.danjono.inventoryrollback; import com.nuclyon.technicallycoded.inventoryrollback.InventoryRollbackPlus; +import com.nuclyon.technicallycoded.inventoryrollback.folia.SchedulerUtils; import me.danjono.inventoryrollback.UpdateChecker.UpdateResult; import me.danjono.inventoryrollback.commands.Commands; import me.danjono.inventoryrollback.config.ConfigData; @@ -168,7 +169,7 @@ public void bStats() { } public void checkUpdate() { - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), () -> { + SchedulerUtils.runTaskAsynchronously(() -> { logger.log(Level.INFO, MessageData.getPluginPrefix() + "Checking for updates..."); final UpdateResult result = new UpdateChecker(getInstance(), 85811).getResult(); diff --git a/src/main/java/me/danjono/inventoryrollback/commands/Commands.java b/src/main/java/me/danjono/inventoryrollback/commands/Commands.java index 591ce15..e6368cb 100644 --- a/src/main/java/me/danjono/inventoryrollback/commands/Commands.java +++ b/src/main/java/me/danjono/inventoryrollback/commands/Commands.java @@ -1,5 +1,6 @@ package me.danjono.inventoryrollback.commands; +import com.nuclyon.technicallycoded.inventoryrollback.folia.SchedulerUtils; import me.danjono.inventoryrollback.InventoryRollback; import me.danjono.inventoryrollback.config.ConfigData; import me.danjono.inventoryrollback.config.MessageData; @@ -114,14 +115,14 @@ private void openMainMenu(Player staff) { MainMenu menu = new MainMenu(staff, 1); staff.openInventory(menu.getInventory()); - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), menu::getMainMenu); + SchedulerUtils.runTaskAsynchronously(menu::getMainMenu); } private void openPlayerMenu(Player staff, OfflinePlayer offlinePlayer) { PlayerMenu menu = new PlayerMenu(staff, offlinePlayer); staff.openInventory(menu.getInventory()); - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), menu::getPlayerMenu); + SchedulerUtils.runTaskAsynchronously(menu::getPlayerMenu); } private void forceBackupCommand(CommandSender sender, String[] args) { @@ -220,13 +221,13 @@ private void versionCommand(CommandSender sender) { private void convertMySQL(CommandSender sender) { if (sender instanceof ConsoleCommandSender && sender.isOp()) { - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), MySQL::convertYAMLToMySQL); + SchedulerUtils.runTaskAsynchronously(MySQL::convertYAMLToMySQL); } } private void convertYAML(CommandSender sender) { if (sender instanceof ConsoleCommandSender && sender.isOp()) { - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), YAML::convertOldBackupData); + SchedulerUtils.runTaskAsynchronously(YAML::convertOldBackupData); } } diff --git a/src/main/java/me/danjono/inventoryrollback/data/PlayerData.java b/src/main/java/me/danjono/inventoryrollback/data/PlayerData.java index dd717b7..54bcb6c 100644 --- a/src/main/java/me/danjono/inventoryrollback/data/PlayerData.java +++ b/src/main/java/me/danjono/inventoryrollback/data/PlayerData.java @@ -9,6 +9,8 @@ import java.util.concurrent.CompletableFuture; import com.nuclyon.technicallycoded.inventoryrollback.InventoryRollbackPlus; +import com.nuclyon.technicallycoded.inventoryrollback.folia.FoliaRunnable; +import com.nuclyon.technicallycoded.inventoryrollback.folia.SchedulerUtils; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.inventory.ItemStack; @@ -16,7 +18,6 @@ import me.danjono.inventoryrollback.InventoryRollback; import me.danjono.inventoryrollback.config.ConfigData; import me.danjono.inventoryrollback.config.ConfigData.SaveType; -import org.bukkit.scheduler.BukkitRunnable; public class PlayerData { @@ -134,7 +135,7 @@ public CompletableFuture purgeExcessSaves(boolean shouldSaveAsync) { }; InventoryRollbackPlus instance = InventoryRollbackPlus.getInstance(); - if (saveAsync) instance.getServer().getScheduler().runTaskAsynchronously(instance, purgeTask); + if (saveAsync) SchedulerUtils.runTaskAsynchronously(purgeTask); else purgeTask.run(); return future; @@ -263,7 +264,7 @@ public void getRollbackMenuData() { public CompletableFuture getAllBackupData() { CompletableFuture future = new CompletableFuture<>(); if (ConfigData.getSaveType() == SaveType.MYSQL) { - new BukkitRunnable() { + SchedulerUtils.runTaskAsynchronously(new FoliaRunnable() { @Override public void run() { try { @@ -273,7 +274,7 @@ public void run() { } future.complete(null); } - }.runTaskAsynchronously(InventoryRollbackPlus.getInstance()); + }); } return future; } @@ -437,7 +438,7 @@ public void saveData(boolean shouldSaveAsync) { } }; - if (saveAsync) Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(),saveDataTask); + if (saveAsync) SchedulerUtils.runTaskAsynchronously(saveDataTask); else saveDataTask.run(); } diff --git a/src/main/java/me/danjono/inventoryrollback/gui/menu/EnderChestBackupMenu.java b/src/main/java/me/danjono/inventoryrollback/gui/menu/EnderChestBackupMenu.java index 235123c..2c05f28 100644 --- a/src/main/java/me/danjono/inventoryrollback/gui/menu/EnderChestBackupMenu.java +++ b/src/main/java/me/danjono/inventoryrollback/gui/menu/EnderChestBackupMenu.java @@ -1,6 +1,8 @@ package me.danjono.inventoryrollback.gui.menu; import com.nuclyon.technicallycoded.inventoryrollback.InventoryRollbackPlus; +import com.nuclyon.technicallycoded.inventoryrollback.folia.FoliaRunnable; +import com.nuclyon.technicallycoded.inventoryrollback.folia.SchedulerUtils; import com.tcoded.lightlibs.bukkitversion.MCVersion; import me.danjono.inventoryrollback.config.ConfigData; import me.danjono.inventoryrollback.config.MessageData; @@ -12,7 +14,6 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; -import org.bukkit.scheduler.BukkitRunnable; import java.util.ArrayList; import java.util.List; @@ -90,7 +91,7 @@ public void showEnderChestItems() { try { // Add items, 5 per tick - new BukkitRunnable() { + SchedulerUtils.runTaskTimer(null, new FoliaRunnable() { int invPosition = 0; int itemPos = (pageNumber - 1) * 27; @@ -112,7 +113,7 @@ public void run() { itemPos++; } } - }.runTaskTimer(InventoryRollbackPlus.getInstance(), 0, 1); + }, 1, 1); } catch (NullPointerException e) { staff.sendMessage(MessageData.getPluginPrefix() + MessageData.getErrorInventory()); return; diff --git a/src/main/java/me/danjono/inventoryrollback/gui/menu/MainInventoryBackupMenu.java b/src/main/java/me/danjono/inventoryrollback/gui/menu/MainInventoryBackupMenu.java index 7c52678..9b1a6e0 100644 --- a/src/main/java/me/danjono/inventoryrollback/gui/menu/MainInventoryBackupMenu.java +++ b/src/main/java/me/danjono/inventoryrollback/gui/menu/MainInventoryBackupMenu.java @@ -1,6 +1,8 @@ package me.danjono.inventoryrollback.gui.menu; import com.nuclyon.technicallycoded.inventoryrollback.InventoryRollbackPlus; +import com.nuclyon.technicallycoded.inventoryrollback.folia.FoliaRunnable; +import com.nuclyon.technicallycoded.inventoryrollback.folia.SchedulerUtils; import com.tcoded.lightlibs.bukkitversion.MCVersion; import me.danjono.inventoryrollback.config.ConfigData; import me.danjono.inventoryrollback.config.MessageData; @@ -12,11 +14,9 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; -import org.bukkit.scheduler.BukkitRunnable; import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicInteger; public class MainInventoryBackupMenu { @@ -105,12 +105,12 @@ public void showBackupItems() { assert !Bukkit.isPrimaryThread(); int item = 0; - int position = 0; + AtomicInteger position = new AtomicInteger(); //If the backup file is invalid it will return null, we want to catch it here try { // Add items, 5 per tick - new BukkitRunnable() { + SchedulerUtils.runTaskTimer(null, new FoliaRunnable() { boolean processedHotbar; int menuPos = 27; @@ -143,7 +143,7 @@ public void run() { backupPos++; } } - }.runTaskTimer(main, 0, 1); + }, 1, 1); } catch (Exception ex) { ex.printStackTrace(); staff.sendMessage(MessageData.getPluginPrefix() + MessageData.getErrorInventory()); @@ -151,45 +151,37 @@ public void run() { } item = 36; - position = 44; + position.set(44); //Add armor if (armor != null && armor.length > 0) { - try { - for (int i = 0; i < armor.length; i++) { - // Place item safely - final int finalPos = position; - final int finalItem = i; - Future placeItemFuture = main.getServer().getScheduler().callSyncMethod(main, - () -> { - inventory.setItem(finalPos, armor[finalItem]); - return null; - }); - placeItemFuture.get(); - position--; - } - } catch (ExecutionException | InterruptedException ex) { - ex.printStackTrace(); - } + for (int i = 0; i < armor.length; i++) { + // Place item safely + final int finalPos = position.getAndDecrement(); + final int finalItem = i; + SchedulerUtils.callSyncMethod(null, () -> { + inventory.setItem(finalPos, armor[finalItem]); + return null; + }).whenComplete((res, ex) -> { + if (ex != null) ex.printStackTrace(); + }); + } } else { - try { - for (; item < mainInvLen; item++) { - if (mainInventory[item] != null) { - // Place item safely - final int finalPos = position; - final int finalItem = item; - Future placeItemFuture = main.getServer().getScheduler().callSyncMethod(main, - () -> { - inventory.setItem(finalPos, mainInventory[finalItem]); - return null; - }); - placeItemFuture.get(); - position--; - } - } - } catch (ExecutionException | InterruptedException ex) { - ex.printStackTrace(); - } + for (; item < mainInvLen; item++) { + if (mainInventory[item] != null) { + // Place item safely + final int finalPos = position.getAndDecrement(); + final int finalItem = item; + SchedulerUtils.callSyncMethod(null, () -> { + inventory.setItem(finalPos, mainInventory[finalItem]); + return null; + }).whenComplete((res, ex) -> { + if (ex != null) ex.printStackTrace(); + }); + } else { + position.getAndDecrement(); + } + } } } diff --git a/src/main/java/me/danjono/inventoryrollback/inventory/SaveInventory.java b/src/main/java/me/danjono/inventoryrollback/inventory/SaveInventory.java index 4da41e2..136bdbf 100644 --- a/src/main/java/me/danjono/inventoryrollback/inventory/SaveInventory.java +++ b/src/main/java/me/danjono/inventoryrollback/inventory/SaveInventory.java @@ -1,6 +1,7 @@ package me.danjono.inventoryrollback.inventory; import com.nuclyon.technicallycoded.inventoryrollback.InventoryRollbackPlus; +import com.nuclyon.technicallycoded.inventoryrollback.folia.SchedulerUtils; import com.nuclyon.technicallycoded.inventoryrollback.util.UserLogRateLimiter; import com.nuclyon.technicallycoded.inventoryrollback.util.serialization.ItemStackSerialization; import com.tcoded.lightlibs.bukkitversion.BukkitVersion; @@ -17,13 +18,13 @@ import org.jetbrains.annotations.Nullable; import java.util.Arrays; -import java.util.HashMap; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; public class SaveInventory { - private static final HashMap rateLimiters = new HashMap<>(); + private static final ConcurrentHashMap rateLimiters = new ConcurrentHashMap<>(); private final InventoryRollbackPlus main; @@ -98,7 +99,7 @@ public void save(PlayerDataSnapshot snapshot, boolean async) { purgeTask.thenRun(() -> data.saveData(saveAsync)); }; - if (saveAsync) main.getServer().getScheduler().runTaskAsynchronously(main, saveTask); + if (saveAsync) SchedulerUtils.runTaskAsynchronously(saveTask); else saveTask.run(); } diff --git a/src/main/java/me/danjono/inventoryrollback/listeners/ClickGUI.java b/src/main/java/me/danjono/inventoryrollback/listeners/ClickGUI.java index 4fc9b52..404ef1c 100644 --- a/src/main/java/me/danjono/inventoryrollback/listeners/ClickGUI.java +++ b/src/main/java/me/danjono/inventoryrollback/listeners/ClickGUI.java @@ -2,10 +2,11 @@ import com.nuclyon.technicallycoded.inventoryrollback.InventoryRollbackPlus; import com.nuclyon.technicallycoded.inventoryrollback.customdata.CustomDataItemEditor; +import com.nuclyon.technicallycoded.inventoryrollback.folia.FoliaRunnable; +import com.nuclyon.technicallycoded.inventoryrollback.folia.SchedulerUtils; import com.tcoded.lightlibs.bukkitversion.BukkitVersion; import com.tcoded.lightlibs.bukkitversion.MCVersion; import io.papermc.lib.PaperLib; -import me.danjono.inventoryrollback.InventoryRollback; import me.danjono.inventoryrollback.config.ConfigData; import me.danjono.inventoryrollback.config.MessageData; import me.danjono.inventoryrollback.config.SoundData; @@ -22,18 +23,15 @@ import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryDragEvent; -import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BlockStateMeta; import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.scheduler.BukkitRunnable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; public class ClickGUI implements Listener { @@ -141,7 +139,7 @@ private void mainMenu(InventoryClickEvent e, Player staff, ItemStack icon) { MainMenu menu = new MainMenu(staff, page); staff.openInventory(menu.getInventory()); - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), menu::getMainMenu); + SchedulerUtils.runTaskAsynchronously(menu::getMainMenu); } //Clicked a player head else { @@ -149,7 +147,7 @@ private void mainMenu(InventoryClickEvent e, Player staff, ItemStack icon) { PlayerMenu menu = new PlayerMenu(staff, offlinePlayer); staff.openInventory(menu.getInventory()); - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), menu::getPlayerMenu); + SchedulerUtils.runTaskAsynchronously(menu::getPlayerMenu); } } else { if (e.getRawSlot() >= e.getInventory().getSize() && !e.isShiftClick()) { @@ -175,13 +173,13 @@ private void playerMenu(InventoryClickEvent e, Player staff, ItemStack icon) { MainMenu menu = new MainMenu(staff, 1); staff.openInventory(menu.getInventory()); - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), menu::getMainMenu); + SchedulerUtils.runTaskAsynchronously(menu::getMainMenu); } else { LogType logType = LogType.valueOf(nbt.getString("logType")); RollbackListMenu menu = new RollbackListMenu(staff, offlinePlayer, logType, 1); staff.openInventory(menu.getInventory()); - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), menu::showBackups); + SchedulerUtils.runTaskAsynchronously(menu::showBackups); } } else { @@ -207,7 +205,7 @@ private void rollbackMenu(InventoryClickEvent e, Player staff, ItemStack icon) { String location = nbt.getString("location"); // Run all data retrieval operations async to avoid tick lag - new BukkitRunnable() { + SchedulerUtils.runTaskAsynchronously(new FoliaRunnable() { @Override public void run() { // Init from MySQL or, if YAML, init & load config file @@ -225,20 +223,21 @@ public void run() { // Create inventory MainInventoryBackupMenu menu = new MainInventoryBackupMenu(staff, data, location); - // Display inventory to player - Future inventoryViewFuture = - main.getServer().getScheduler().callSyncMethod(main, - () -> staff.openInventory(menu.getInventory())); - //If the backup file is invalid it will return null, we want to catch it here - try { - inventoryViewFuture.get(); - // Start placing items in the inventory async + SchedulerUtils.callSyncMethod( + e.getWhoClicked().getLocation(), + () -> staff.openInventory(menu.getInventory()) + ).whenComplete((view, ex) -> { + if (ex != null) { + ex.printStackTrace(); + return; + } + if (view == null) { + return; + } menu.showBackupItems(); - } catch (NullPointerException | ExecutionException | InterruptedException ex) { - ex.printStackTrace(); - } + }); } - }.runTaskAsynchronously(main); + }); } //Player has selected a page icon @@ -251,13 +250,13 @@ else if (icon.getType().equals(Buttons.getPageSelectorIcon())) { PlayerMenu menu = new PlayerMenu(staff, player); staff.openInventory(menu.getInventory()); - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), menu::getPlayerMenu); + SchedulerUtils.runTaskAsynchronously(menu::getPlayerMenu); } else { LogType logType = LogType.valueOf(nbt.getString("logType")); RollbackListMenu menu = new RollbackListMenu(staff, player, logType, page); staff.openInventory(menu.getInventory()); - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), menu::showBackups); + SchedulerUtils.runTaskAsynchronously(menu::showBackups); } } } else { @@ -285,7 +284,7 @@ private void mainBackupMenu(InventoryClickEvent e, Player staff, ItemStack icon) RollbackListMenu menu = new RollbackListMenu(staff, offlinePlayer, logType, 1); staff.openInventory(menu.getInventory()); - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), menu::showBackups); + SchedulerUtils.runTaskAsynchronously(menu::showBackups); } //Click on page selector button to go back to rollback menu @@ -296,7 +295,7 @@ else if (e.getRawSlot() == MainInventoryBackupMenu.GIVE_SHULKERS_BUTTON_SLOT) { return; } - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), () -> { + SchedulerUtils.runTaskAsynchronously(() -> { // Unsupported on older versions if (main.getVersion().lessThan(MCVersion.v1_11.toBukkitVersion())) { return; @@ -367,7 +366,7 @@ else if (e.getRawSlot() == MainInventoryBackupMenu.GIVE_SHULKERS_BUTTON_SLOT) { } } - Bukkit.getScheduler().runTask(main, t -> { + SchedulerUtils.runTask(staff.getLocation(), () -> { staff.getInventory().addItem(firstShulker, secondShulker); staff.closeInventory(); }); @@ -385,7 +384,7 @@ else if (icon.getType().equals(Buttons.getRestoreAllInventoryIcon())) { if (offlinePlayer.isOnline()) { Player player = (Player) offlinePlayer; - new BukkitRunnable() { + SchedulerUtils.runTaskAsynchronously(new FoliaRunnable() { @Override public void run() { // Init from MySQL or, if YAML, init & load config file @@ -404,35 +403,36 @@ public void run() { ItemStack[] armour = data.getArmour(); // Place inventory items sync (compressed code) - Future futureSetInv = main.getServer().getScheduler().callSyncMethod(main, - () -> { player.getInventory().setContents(inventory); return null; }); - try { futureSetInv.get(); } - catch (ExecutionException | InterruptedException ex) { ex.printStackTrace(); } - - // If 1.8, place armor contents separately - if (main.getVersion().lessOrEqThan(BukkitVersion.v1_8_R3)) { - // Place items sync (compressed code) - Future futureSetArmor = main.getServer().getScheduler().callSyncMethod(main, - () -> { player.getInventory().setArmorContents(armour); return null; }); - try { futureSetArmor.get(); } - catch (ExecutionException | InterruptedException ex) { ex.printStackTrace(); } - } - - // Play sound effect is enabled - if (SoundData.isInventoryRestoreEnabled()) { - // Play sound sync (compressed code) - Future futurePlaySound = main.getServer().getScheduler().callSyncMethod(main, - () -> { player.playSound(player.getLocation(), SoundData.getInventoryRestored(), 1, 1); return null; }); - try { futurePlaySound.get(); } - catch (ExecutionException | InterruptedException ex) { ex.printStackTrace(); } - } - - // Send player & staff feedback - player.sendMessage(MessageData.getPluginPrefix() + MessageData.getMainInventoryRestoredPlayer(staff.getName())); - if (!staff.getUniqueId().equals(player.getUniqueId())) - staff.sendMessage(MessageData.getPluginPrefix() + MessageData.getMainInventoryRestored(offlinePlayer.getName())); + SchedulerUtils.callSyncMethod(e.getWhoClicked().getLocation(), () -> { + player.getInventory().setContents(inventory); + return null; + }).whenComplete((res, ex) -> { + if (ex != null) ex.printStackTrace(); + else { + if (main.getVersion().lessOrEqThan(BukkitVersion.v1_8_R3)) { + SchedulerUtils.callSyncMethod(e.getWhoClicked().getLocation(), () -> { + player.getInventory().setArmorContents(armour); + return null; + }).whenComplete((r, e) -> { + if (e != null) e.printStackTrace(); + }); + } + if (SoundData.isInventoryRestoreEnabled()) { + SchedulerUtils.callSyncMethod(e.getWhoClicked().getLocation(), () -> { + player.playSound(player.getLocation(), SoundData.getInventoryRestored(), 1, 1); + return null; + }).whenComplete((r, e) -> { + if (e != null) e.printStackTrace(); + }); + } + + player.sendMessage(MessageData.getPluginPrefix() + MessageData.getMainInventoryRestoredPlayer(staff.getName())); + if (!staff.getUniqueId().equals(player.getUniqueId())) + staff.sendMessage(MessageData.getPluginPrefix() + MessageData.getMainInventoryRestored(offlinePlayer.getName())); + } + }); } - }.runTaskAsynchronously(main); + }); } else { staff.sendMessage(MessageData.getPluginPrefix() + MessageData.getMainInventoryNotOnline(offlinePlayer.getName())); @@ -463,7 +463,7 @@ else if (icon.getType().equals(Buttons.getTeleportLocationIcon())) { .add(0.5, 0.5, 0.5); // Teleport player on a slight delay to block the teleport icon glitching out into the player inventory - Bukkit.getScheduler().runTaskLater(InventoryRollback.getInstance(), () -> { + SchedulerUtils.runTaskLater(e.getWhoClicked().getLocation(), () -> { e.getWhoClicked().closeInventory(); PaperLib.teleportAsync(staff,loc).thenAccept((result) -> { if (SoundData.isTeleportEnabled()) @@ -477,7 +477,7 @@ else if (icon.getType().equals(Buttons.getTeleportLocationIcon())) { // Clicked icon to restore backup players ender chest else if (icon.getType().equals(Buttons.getEnderChestIcon())) { - new BukkitRunnable() { + SchedulerUtils.runTaskAsynchronously(new FoliaRunnable() { @Override public void run() { // Init from MySQL or, if YAML, init & load config file @@ -496,21 +496,17 @@ public void run() { EnderChestBackupMenu menu = new EnderChestBackupMenu(staff, data, 1); // Open inventory sync (compressed code) - Future futureOpenInv = main.getServer().getScheduler().callSyncMethod(main, - () -> { - staff.openInventory(menu.getInventory()); - return null; - }); - try { - futureOpenInv.get(); - } catch (ExecutionException | InterruptedException ex) { - ex.printStackTrace(); - } - - // Place items async - menu.showEnderChestItems(); + SchedulerUtils.callSyncMethod(e.getWhoClicked().getLocation(), () -> { + staff.openInventory(menu.getInventory()); + return null; + }).whenComplete((res, ex) -> { + if (ex != null) ex.printStackTrace(); + else { + menu.showEnderChestItems(); + } + }); } - }.runTaskAsynchronously(this.main); + }); } // Clicked icon to restore backup players health @@ -638,7 +634,7 @@ private void enderChestBackupMenu(InventoryClickEvent e, Player staff, ItemStack if (page == 0) { // Run all data retrieval operations async to avoid tick lag - new BukkitRunnable() { + SchedulerUtils.runTaskAsynchronously(new FoliaRunnable() { @Override public void run() { // Init from MySQL or, if YAML, init & load config file @@ -660,22 +656,24 @@ public void run() { MainInventoryBackupMenu menu = new MainInventoryBackupMenu(staff, data, location); // Display inventory to player - Future inventoryViewFuture = main.getServer().getScheduler().callSyncMethod(main, - () -> staff.openInventory(menu.getInventory())); - //If the backup file is invalid it will return null, we want to catch it here - try { - inventoryViewFuture.get(); - // Start placing items in the inventory async - menu.showBackupItems(); - } catch (NullPointerException | ExecutionException | InterruptedException ex) { - ex.printStackTrace(); - } + SchedulerUtils.callSyncMethod(e.getWhoClicked().getLocation(), () -> staff.openInventory(menu.getInventory())) + .whenComplete((view, ex) -> { + if (ex != null) { + ex.printStackTrace(); + return; + } + if (view == null) { + // backup inválido, tratar aqui se quiser + return; + } + menu.showBackupItems(); + }); } - }.runTaskAsynchronously(main); + }); } else { - new BukkitRunnable() { + SchedulerUtils.runTaskAsynchronously(new FoliaRunnable() { @Override public void run() { // Init from MySQL or, if YAML, init & load config file @@ -694,21 +692,17 @@ public void run() { EnderChestBackupMenu menu = new EnderChestBackupMenu(staff, data, page); // Open inventory sync (compressed code) - Future futureOpenInv = main.getServer().getScheduler().callSyncMethod(main, - () -> { - staff.openInventory(menu.getInventory()); - return null; - }); - try { - futureOpenInv.get(); - } catch (ExecutionException | InterruptedException ex) { - ex.printStackTrace(); - } - - // Place items async - menu.showEnderChestItems(); + SchedulerUtils.callSyncMethod(e.getWhoClicked().getLocation(), () -> { + staff.openInventory(menu.getInventory()); + return null; + }).whenComplete((res, ex) -> { + if (ex != null) ex.printStackTrace(); + else { + menu.showEnderChestItems(); + } + }); } - }.runTaskAsynchronously(this.main); + }); } } @@ -720,7 +714,7 @@ else if (e.getRawSlot() == EnderChestBackupMenu.GIVE_SHULKERS_BUTTON_SLOT) { return; } - Bukkit.getScheduler().runTaskAsynchronously(InventoryRollback.getInstance(), () -> { + SchedulerUtils.runTaskAsynchronously(() -> { // Unsupported on older versions if (main.getVersion().lessThan(MCVersion.v1_11.toBukkitVersion())) { return; @@ -776,7 +770,7 @@ else if (e.getRawSlot() == EnderChestBackupMenu.GIVE_SHULKERS_BUTTON_SLOT) { } }; - Bukkit.getScheduler().runTask(main, t -> { + SchedulerUtils.runTask(null, () -> { staff.getInventory().addItem(shulkers.toArray(new ItemStack[0])); staff.closeInventory(); }); @@ -795,7 +789,7 @@ else if (icon.getType().equals(Buttons.getRestoreAllInventoryIcon())) { Player player = (Player) offlinePlayer; // Run all data retrieval operations async to avoid tick lag - new BukkitRunnable() { + SchedulerUtils.runTaskAsynchronously(new FoliaRunnable() { @Override public void run() { // Init from MySQL or, if YAML, init & load config file @@ -811,22 +805,14 @@ public void run() { } // Display inventory to player - Future inventoryReplaceFuture = main.getServer().getScheduler().callSyncMethod(main, - () -> { - ItemStack[] enderChest = data.getEnderChest(); - if (enderChest == null) enderChest = new ItemStack[0]; - player.getEnderChest().setContents(enderChest); - return null; - }); - - //If the backup file is invalid it will return null, we want to catch it here - try { - inventoryReplaceFuture.get(); - } catch (NullPointerException | ExecutionException | InterruptedException ex) { - ex.printStackTrace(); - } + SchedulerUtils.callSyncMethod(e.getWhoClicked().getLocation(), () -> { + player.getEnderChest().setContents(data.getEnderChest() != null ? data.getEnderChest() : new ItemStack[0]); + return null; + }).whenComplete((res, ex) -> { + if (ex != null) ex.printStackTrace(); + }); } - }.runTaskAsynchronously(main); + }); if (SoundData.isInventoryRestoreEnabled()) player.playSound(player.getLocation(), SoundData.getInventoryRestored(), 1, 1); diff --git a/src/main/java/me/danjono/inventoryrollback/listeners/EventLogs.java b/src/main/java/me/danjono/inventoryrollback/listeners/EventLogs.java index 170ddc2..d6cfab7 100644 --- a/src/main/java/me/danjono/inventoryrollback/listeners/EventLogs.java +++ b/src/main/java/me/danjono/inventoryrollback/listeners/EventLogs.java @@ -1,6 +1,7 @@ package me.danjono.inventoryrollback.listeners; import com.nuclyon.technicallycoded.inventoryrollback.InventoryRollbackPlus; +import com.nuclyon.technicallycoded.inventoryrollback.folia.SchedulerUtils; import com.tcoded.lightlibs.bukkitversion.BukkitVersion; import me.danjono.inventoryrollback.config.ConfigData; import me.danjono.inventoryrollback.data.LogType; @@ -93,7 +94,7 @@ private void playerQuit(PlayerQuitEvent e) { // Run the cleanup 1 tick later in case the rate limiter should need to provide debug data. // If the cleanup would run and the event is being spammed, this cleanup would delete the rate limiter's data // before it has a chance to act. - main.getServer().getScheduler().runTaskLater(main, () -> { + SchedulerUtils.runTaskLater(e.getPlayer().getLocation(), () -> { // Double check that the player is offline if (main.getServer().getPlayer(uuid) != null) return; // Cleanup the player's data diff --git a/src/main/java/me/danjono/inventoryrollback/reflections/LegacyNBTWrapper.java b/src/main/java/me/danjono/inventoryrollback/reflections/LegacyNBTWrapper.java index 78cb39a..85e22e4 100644 --- a/src/main/java/me/danjono/inventoryrollback/reflections/LegacyNBTWrapper.java +++ b/src/main/java/me/danjono/inventoryrollback/reflections/LegacyNBTWrapper.java @@ -9,7 +9,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; public class LegacyNBTWrapper implements CustomDataItemEditor { @@ -17,8 +17,8 @@ public class LegacyNBTWrapper implements CustomDataItemEditor { private ItemStack item; private final NMSHandler nmsHandler; - private static HashMap, String> getTagElementMethodNames; - private static HashMap, String> setTagElementMethodNames; + private static ConcurrentHashMap, String> getTagElementMethodNames; + private static ConcurrentHashMap, String> setTagElementMethodNames; // 1.8 - 1.20.4 private static String getTagMethodName; @@ -104,8 +104,8 @@ else if (nmsVersion.greaterOrEqThan(BukkitVersion.v1_18_R2)) { } private void resolveNbtTagCompoundReflectionNames(BukkitVersion nmsVersion) { - getTagElementMethodNames = new HashMap<>(); - setTagElementMethodNames = new HashMap<>(); + getTagElementMethodNames = new ConcurrentHashMap<>(); + setTagElementMethodNames = new ConcurrentHashMap<>(); if (nmsVersion.greaterOrEqThan(BukkitVersion.v1_18_R1)) { getTagElementMethodNames.put(Integer.class, "h"); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 9d56c25..04041fe 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -3,8 +3,9 @@ main: ${project.groupId}.InventoryRollbackPlus version: ${project.version} description: ${project.description} author: ${project.organization.name} -authors: [${project.organization.name}, danjono] +authors: [${project.organization.name}, danjono, yLeoft] api-version: 1.13 +folia-supported: true loadbefore: - DeluxeCombat