diff --git a/CHANGELOG.md b/CHANGELOG.md index eba7cf7..f6115a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 9.3.0 +- Added CraftBukkit compatibility (although I don't unterstand why anyone would prefer CraftBukkit over Spigot) + +## 9.2.0 +- Added support for PlayerVaultsX + ## 9.1.1 - Fixed category files being "too wildcardy" lol diff --git a/pom.xml b/pom.xml index 9e8cc47..d410a71 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ChestSort https://www.chestsort.de Automatically sorts your chests! - 9.1.1 + 9.3.0 jar @@ -51,6 +51,10 @@ org.bstats de.jeff_media.ChestSort + + io.papermc.lib + de.jeff_media.ChestSort.paperlib + @@ -85,6 +89,10 @@ spigot-repo https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + papermc + https://papermc.io/repo/repository/maven-public/ + placeholderapi https://repo.extendedclip.com/content/repositories/placeholderapi/ @@ -179,6 +187,12 @@ RC-15 provided + + io.papermc + paperlib + 1.0.6 + compile + diff --git a/src/main/java/de/jeff_media/ChestSort/ChestSortDebugger.java b/src/main/java/de/jeff_media/ChestSort/ChestSortDebugger.java index 3613a60..4da078f 100644 --- a/src/main/java/de/jeff_media/ChestSort/ChestSortDebugger.java +++ b/src/main/java/de/jeff_media/ChestSort/ChestSortDebugger.java @@ -1,38 +1,40 @@ package de.jeff_media.ChestSort; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; import org.jetbrains.annotations.NotNull; public class ChestSortDebugger implements @NotNull Listener { - - private final ChestSortPlugin plugin; - ChestSortDebugger(ChestSortPlugin plugin) { - plugin.getLogger().warning("======================================="); - plugin.getLogger().warning(" CHESTSORT DEBUG MODE ACTIVATED!"); - plugin.getLogger().warning("Only use this for development purposes!"); - plugin.getLogger().warning("======================================="); - this.plugin=plugin; - } - - @EventHandler - public void onInventoryClickEvent(InventoryClickEvent e) { - // Debug - if(plugin.debug) { - System.out.println(" "); - System.out.println("InventoryClickEvent:"); - System.out.println("- Holder: " + e.getInventory().getHolder()); - if(e.getInventory().getHolder()!=null) { - System.out.println("- Holder class: "+e.getInventory().getHolder().getClass()); + private final ChestSortPlugin plugin; + + ChestSortDebugger(ChestSortPlugin plugin) { + plugin.getLogger().warning("======================================="); + plugin.getLogger().warning(" CHESTSORT DEBUG MODE ACTIVATED!"); + plugin.getLogger().warning("Only use this for development purposes!"); + plugin.getLogger().warning("======================================="); + this.plugin = plugin; + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onInventoryClickEvent(InventoryClickEvent e) { + if(plugin.debug) { + // Debug + plugin.debug(" "); + plugin.debug("InventoryClickEvent:"); + plugin.debug("- Holder: " + e.getInventory().getHolder()); + if (e.getInventory().getHolder() != null) { + plugin.debug("- Holder class: " + e.getInventory().getHolder().getClass()); } - System.out.println("- Slot: "+e.getRawSlot()); - System.out.println("- Left-Click: "+e.isLeftClick()); - System.out.println("- Right-Click: "+e.isRightClick()); - System.out.println("- Shift-Click: "+e.isShiftClick()); - System.out.println(" "); + plugin.debug("- Slot: " + e.getRawSlot()); + plugin.debug("- Left-Click: " + e.isLeftClick()); + plugin.debug("- Right-Click: " + e.isRightClick()); + plugin.debug("- Shift-Click: " + e.isShiftClick()); + plugin.debug(" "); } - } + + } } diff --git a/src/main/java/de/jeff_media/ChestSort/ChestSortListener.java b/src/main/java/de/jeff_media/ChestSort/ChestSortListener.java index 2ddc688..c83c161 100644 --- a/src/main/java/de/jeff_media/ChestSort/ChestSortListener.java +++ b/src/main/java/de/jeff_media/ChestSort/ChestSortListener.java @@ -1,9 +1,6 @@ package de.jeff_media.ChestSort; -import de.jeff_media.ChestSort.hooks.CrateReloadedHook; -import de.jeff_media.ChestSort.hooks.GoldenCratesHook; -import de.jeff_media.ChestSort.hooks.HeadDatabaseHook; -import de.jeff_media.ChestSort.hooks.MinepacksHook; +import de.jeff_media.ChestSort.hooks.*; import de.jeff_media.ChestSort.utils.LlamaUtils; import de.jeff_media.ChestSortAPI.ChestSortEvent; import de.jeff_media.ChestSortAPI.ISortable; @@ -354,36 +351,48 @@ public class ChestSortListener implements Listener { @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onHotkey(InventoryClickEvent event) { + plugin.debug2("Hotkey?"); + if (!(event.getWhoClicked() instanceof Player)) { + plugin.debug2("exit: 0"); return; } + Player p = (Player) event.getWhoClicked(); plugin.registerPlayerIfNeeded(p); if (!plugin.getConfig().getBoolean("allow-sorting-hotkeys")) { + plugin.debug2("exit: 1"); return; } + if (!p.hasPermission("chestsort.use") && !p.hasPermission("chestsort.use.inventory")) { + plugin.debug2("exit: 2"); return; } + //InventoryHolder holder = event.getInventory().getHolder(); if (event.getClickedInventory() == null) { + plugin.debug2("exit: 3"); return; } + boolean isAPICall = isAPICall(event.getClickedInventory()); // Detect generic GUIs if(!isAPICall && (plugin.genericHook.isPluginGUI(event.getInventory()) || plugin.genericHook.isPluginGUI(event.getInventory()))) { + plugin.debug("Aborting hotkey sorting: no API call & generic GUI detected"); return; } + // Possible fix for #57 if (!isAPICall && (event.getClickedInventory().getHolder() != null && event.getClickedInventory().getHolder() == p @@ -392,6 +401,7 @@ public class ChestSortListener implements Listener { return; } + // End Possible fix for #57 InventoryHolder holder = event.getClickedInventory().getHolder(); @@ -405,12 +415,14 @@ public class ChestSortListener implements Listener { return; } + // Prevent player from putting items into GUI inventory if (event.getInventory() == setting.guiInventory) { event.setCancelled(true); return; } + switch (event.getClick()) { case MIDDLE: cause = ChestSortLogger.SortCause.H_MIDDLE; @@ -426,6 +438,7 @@ public class ChestSortListener implements Listener { } break; case DOUBLE_CLICK: + if(event.isShiftClick()) return; cause = ChestSortLogger.SortCause.H_DOUBLE; //if(plugin.getConfig().getBoolean("hotkeys.double-click")) { if (setting.doubleClick) { @@ -461,23 +474,40 @@ public class ChestSortListener implements Listener { return; } - if (isAPICall || belongsToChestLikeBlock(event.getClickedInventory()) || LlamaUtils.belongsToLlama(event.getClickedInventory()) || minepacksHook.isMinepacksBackpack(event.getClickedInventory())) { + + if(plugin.isInHotkeyCooldown(p.getUniqueId())) { + plugin.debug("Skipping: hotkey cooldown"); + return; + } + + + plugin.debug("Hotkey triggered: " + event.getClick().name()); + + if (isAPICall + || belongsToChestLikeBlock(event.getClickedInventory()) + || LlamaUtils.belongsToLlama(event.getClickedInventory()) + || minepacksHook.isMinepacksBackpack(event.getClickedInventory()) + || plugin.playerVaultsHook.isPlayerVault(event.getClickedInventory())) { + if (!p.hasPermission("chestsort.use")) { return; } if (LlamaUtils.belongsToLlama(event.getClickedInventory())) { + plugin.lgr.logSort(p,cause); ChestedHorse llama = (ChestedHorse) event.getInventory().getHolder(); plugin.organizer.sortInventory(event.getClickedInventory(), 2, LlamaUtils.getLlamaChestSize(llama) + 1); plugin.organizer.updateInventoryView(event); return; } + plugin.lgr.logSort(p,cause); plugin.organizer.sortInventory(event.getClickedInventory()); plugin.organizer.updateInventoryView(event); } else if (holder instanceof Player) { + if (!p.hasPermission("chestsort.use.inventory")) { return; } @@ -486,10 +516,12 @@ public class ChestSortListener implements Listener { plugin.lgr.logSort(p,cause); plugin.organizer.sortInventory(p.getInventory(), 0, 8); plugin.organizer.updateInventoryView(event); + } else if (event.getSlotType() == SlotType.CONTAINER) { plugin.lgr.logSort(p,cause); plugin.organizer.sortInventory(p.getInventory(), 9, 35); plugin.organizer.updateInventoryView(event); + } } } diff --git a/src/main/java/de/jeff_media/ChestSort/ChestSortOrganizer.java b/src/main/java/de/jeff_media/ChestSort/ChestSortOrganizer.java index 46ee092..10a291f 100644 --- a/src/main/java/de/jeff_media/ChestSort/ChestSortOrganizer.java +++ b/src/main/java/de/jeff_media/ChestSort/ChestSortOrganizer.java @@ -460,8 +460,10 @@ public class ChestSortOrganizer { // Sort an inventory only between startSlot and endSlot void sortInventory(@NotNull Inventory inv, int startSlot, int endSlot) { if(inv==null) return; + plugin.debug("Attempting to sort an Inventory and calling ChestSortEvent."); Class invClass = inv.getClass(); de.jeff_media.ChestSortAPI.ChestSortEvent chestSortEvent = new de.jeff_media.ChestSortAPI.ChestSortEvent(inv); + try { if (invClass.getMethod("getLocation", null) != null) { // This whole try/catch fixes MethodNotFoundException when using inv.getLocation in Spigot 1.8. @@ -490,6 +492,7 @@ public class ChestSortOrganizer { Bukkit.getPluginManager().callEvent(chestSortEvent); if (chestSortEvent.isCancelled()) { + plugin.debug("ChestSortEvent cancelled, I'll stay in bed."); return; } @@ -597,6 +600,7 @@ public class ChestSortOrganizer { inv.setItem(currentSlot, item); currentSlot++; } + plugin.debug("Sorting successful. I'll go back to bed now."); } public void updateInventoryView(InventoryClickEvent event) { diff --git a/src/main/java/de/jeff_media/ChestSort/ChestSortPlugin.java b/src/main/java/de/jeff_media/ChestSort/ChestSortPlugin.java index a312487..d2441be 100644 --- a/src/main/java/de/jeff_media/ChestSort/ChestSortPlugin.java +++ b/src/main/java/de/jeff_media/ChestSort/ChestSortPlugin.java @@ -41,9 +41,12 @@ import java.util.Iterator; import java.util.Map; import java.util.UUID; +import de.jeff_media.ChestSort.config.Config; import de.jeff_media.ChestSort.hooks.GenericGUIHook; +import de.jeff_media.ChestSort.hooks.PlayerVaultsHook; import de.jeff_media.ChestSort.placeholders.ChestSortPlaceholders; import de.jeff_media.PluginUpdateChecker.PluginUpdateChecker; +import io.papermc.lib.PaperLib; import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -61,6 +64,7 @@ public class ChestSortPlugin extends JavaPlugin implements de.jeff_media.ChestSo ChestSortLogger lgr; Map perPlayerSettings = new HashMap<>(); + HashMap hotkeyCooldown; ChestSortMessages messages; ChestSortOrganizer organizer; PluginUpdateChecker updateChecker; @@ -70,7 +74,7 @@ public class ChestSortPlugin extends JavaPlugin implements de.jeff_media.ChestSo String sortingMethod; ArrayList disabledWorlds; ChestSortAPIHandler api; - final int currentConfigVersion = 44; + final int currentConfigVersion = 45; boolean usingMatchingConfig = true; protected boolean debug = false; boolean verbose = true; @@ -81,6 +85,7 @@ public class ChestSortPlugin extends JavaPlugin implements de.jeff_media.ChestSo public boolean hookMinepacks = false; public GenericGUIHook genericHook; + public PlayerVaultsHook playerVaultsHook; private static long updateCheckInterval = 4*60*60; // in seconds. We check on startup and every 4 hours @@ -133,6 +138,10 @@ public class ChestSortPlugin extends JavaPlugin implements de.jeff_media.ChestSo if(debug) getLogger().warning("[DEBUG] "+t); } + public void debug2(String t) { + if(getConfig().getBoolean(Config.DEBUG2)) getLogger().warning("[DEBUG2] "+t); + } + // Creates the default configuration file // Also checks the config-version of an already existing file. If the existing // config is too @@ -490,12 +499,26 @@ public class ChestSortPlugin extends JavaPlugin implements de.jeff_media.ChestSo messages = new ChestSortMessages(this); organizer = new ChestSortOrganizer(this); settingsGUI = new ChestSortSettingsGUI(this); - updateChecker = new PluginUpdateChecker(this, "https://api.jeff-media.de/chestsort/chestsort-latest-version.txt", "https://chestsort.de", "https://chestsort.de/changelog", "https://chestsort.de/donate"); + try { + if(Class.forName("net.md_5.bungee.api.chat.BaseComponent") != null) { + updateChecker = new PluginUpdateChecker(this, "https://api.jeff-media.de/chestsort/chestsort-latest-version.txt", "https://chestsort.de", "https://chestsort.de/changelog", "https://chestsort.de/donate"); + } else { + getLogger().severe("You are using an unsupported server software! Consider switching to Spigot or Paper!"); + getLogger().severe("The Update Checker will NOT work when using CraftBukkit instead of Spigot/Paper!"); + PaperLib.suggestPaper(this); + } + } catch (ClassNotFoundException e) { + getLogger().severe("You are using an unsupported server software! Consider switching to Spigot or Paper!"); + getLogger().severe("The Update Checker will NOT work when using CraftBukkit instead of Spigot/Paper!"); + PaperLib.suggestPaper(this); + } listener = new ChestSortListener(this); api = new ChestSortAPIHandler(this); + hotkeyCooldown = new HashMap<>(); permissionsHandler = new ChestSortPermissionsHandler(this); updateCheckInterval = (int) (getConfig().getDouble("check-interval")*60*60); sortingMethod = getConfig().getString("sorting-method"); + playerVaultsHook = new PlayerVaultsHook(this); getServer().getPluginManager().registerEvents(listener, this); getServer().getPluginManager().registerEvents(settingsGUI, this); ChestSortChestSortCommand chestsortCommandExecutor = new ChestSortChestSortCommand(this); @@ -536,11 +559,13 @@ public class ChestSortPlugin extends JavaPlugin implements de.jeff_media.ChestSo getLogger().info("Categories: " + getCategoryList()); } - if (getConfig().getString("check-for-updates", "true").equalsIgnoreCase("true")) { - updateChecker.check(updateCheckInterval); - } // When set to on-startup, we check right now (delay 0) - else if (getConfig().getString("check-for-updates", "true").equalsIgnoreCase("on-startup")) { - updateChecker.check(); + if(updateChecker!=null) { + if (getConfig().getString("check-for-updates", "true").equalsIgnoreCase("true")) { + updateChecker.check(updateCheckInterval); + } // When set to on-startup, we check right now (delay 0) + else if (getConfig().getString("check-for-updates", "true").equalsIgnoreCase("on-startup")) { + updateChecker.check(); + } } registerMetrics(); @@ -590,6 +615,17 @@ public class ChestSortPlugin extends JavaPlugin implements de.jeff_media.ChestSo } + public boolean isInHotkeyCooldown(UUID uuid) { + double cooldown = getConfig().getDouble(Config.HOTKEY_COOLDOWN)*1000; + if(cooldown==0) return false; + long lastUsage = hotkeyCooldown.containsKey(uuid) ? hotkeyCooldown.get(uuid) : 0; + long currentTime = System.currentTimeMillis(); + long difference = currentTime-lastUsage; + hotkeyCooldown.put(uuid,currentTime); + debug("Difference: "+difference); + return difference <= cooldown; + } + void registerPlayerIfNeeded(Player p) { // Players are stored by their UUID, so that name changes don't break player's // settings diff --git a/src/main/java/de/jeff_media/ChestSort/config/Config.java b/src/main/java/de/jeff_media/ChestSort/config/Config.java new file mode 100644 index 0000000..85d9d2d --- /dev/null +++ b/src/main/java/de/jeff_media/ChestSort/config/Config.java @@ -0,0 +1,17 @@ +package de.jeff_media.ChestSort.config; + +import de.jeff_media.ChestSort.ChestSortPlugin; + +public class Config { + public static final String HOTKEY_COOLDOWN = "hotkey-cooldown"; + public static final String DEBUG2 = "debug2"; + + private final ChestSortPlugin main; + + public Config(ChestSortPlugin main) { + this.main=main; + + main.getConfig().addDefault(HOTKEY_COOLDOWN,0.0); + main.getConfig().addDefault(DEBUG2,false); + } +} diff --git a/src/main/java/de/jeff_media/ChestSort/hooks/PlayerVaultsHook.java b/src/main/java/de/jeff_media/ChestSort/hooks/PlayerVaultsHook.java new file mode 100644 index 0000000..f8784c9 --- /dev/null +++ b/src/main/java/de/jeff_media/ChestSort/hooks/PlayerVaultsHook.java @@ -0,0 +1,20 @@ +package de.jeff_media.ChestSort.hooks; + +import de.jeff_media.ChestSort.ChestSortPlugin; +import org.bukkit.inventory.Inventory; + +public class PlayerVaultsHook { + + private final ChestSortPlugin main; + + public PlayerVaultsHook(ChestSortPlugin main) { + this.main=main; + } + + public boolean isPlayerVault(Inventory inv) { + if(inv==null) return false; + if(inv.getHolder()==null) return false; + if(!main.getConfig().getBoolean("hook-playervaultsx",true)) return false; + return inv.getHolder().getClass().getName().equals("com.drtshock.playervaults.vaultmanagement.VaultHolder"); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 5ba598e..9947137 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -145,6 +145,10 @@ sorting-hotkeys: # Use shift + right-click on any EMPTY inventory slot as hotkey shift-right-click: true +# Amount in seconds that players have to wait between using hotkeys to prevent them from spamming the +# sorting mechanism +hotkey-cooldown: 0.2 + # Additionally to sorting hotkeys, you can quickly unload your inventory into a chest and vice versa # using left-click or right-click outside of a chest's inventory. # A single click will only affect matching items (items that are already present in the other inventory) @@ -225,6 +229,11 @@ hook-inventorypages: true # backpacks and sort them like a regular chest. hook-minepacks: true +##### PlayerVaultsX ##### +# When PlayerVaultsX is installed, ChestSort can sort your +# player vaults just like regular chests. +hook-playervaultsx: true + ##### CrateReloaded ##### # Prevents the player from using hotkeys on a crate hook-cratereloaded: true @@ -642,10 +651,11 @@ dump: false # Debug mode - you probably do not want this. debug: false +debug2: false # Enable log - you probably do not want this. log: false # Please DO NOT change the following line manually! # It is used by the automatic config updater. -config-version: 44 +config-version: 45 \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 049f191..0c94f64 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,6 +1,6 @@ main: de.jeff_media.ChestSort.ChestSortPlugin name: ChestSort -version: 9.1.1 +version: 9.3.0 api-version: "1.13" description: Allows automatic chest sorting author: mfnalex