From 935511e5545c80ebf3f2945b78c62c766998af1d Mon Sep 17 00:00:00 2001 From: 1robie Date: Sun, 1 Feb 2026 15:17:15 +0100 Subject: [PATCH] fix: click limiter + parsing --- .../java/fr/maxlego08/menu/api/Inventory.java | 2 ++ .../menu/api/engine/BaseInventory.java | 13 ++++--- .../menu/common/utils/yaml/YamlParser.java | 35 ++++++++++++++++--- .../PacketEventClickLimiterListener.java | 29 ++++++++++----- .../maxlego08/menu/hooks/ComponentMeta.java | 8 ++--- .../java/fr/maxlego08/menu/ZInventory.java | 10 ++++++ .../maxlego08/menu/inventory/VInventory.java | 11 ++++++ .../inventories/InventoryDefault.java | 1 + .../menu/loader/InventoryLoader.java | 6 +++- src/main/resources/config.yml | 2 +- 10 files changed, 94 insertions(+), 23 deletions(-) diff --git a/API/src/main/java/fr/maxlego08/menu/api/Inventory.java b/API/src/main/java/fr/maxlego08/menu/api/Inventory.java index 83230aa5..3b058327 100644 --- a/API/src/main/java/fr/maxlego08/menu/api/Inventory.java +++ b/API/src/main/java/fr/maxlego08/menu/api/Inventory.java @@ -229,4 +229,6 @@ public interface Inventory { List getCloseActions(); ClearInvType getClearInvType(); + + boolean isClickLimiterEnabled(); } \ No newline at end of file diff --git a/API/src/main/java/fr/maxlego08/menu/api/engine/BaseInventory.java b/API/src/main/java/fr/maxlego08/menu/api/engine/BaseInventory.java index 57890039..fc7982ff 100644 --- a/API/src/main/java/fr/maxlego08/menu/api/engine/BaseInventory.java +++ b/API/src/main/java/fr/maxlego08/menu/api/engine/BaseInventory.java @@ -29,19 +29,19 @@ public interface BaseInventory extends InventoryHolder { @Contract("_, null -> null") @Nullable - ItemButton addItem(int slot,@Nullable ItemStack itemStack); + ItemButton addItem(int slot, @Nullable ItemStack itemStack); @Contract("_, null,_ -> null") @Nullable - ItemButton addItem(int slot,@Nullable ItemStack itemStack, boolean enableAntiDupe); + ItemButton addItem(int slot, @Nullable ItemStack itemStack, boolean enableAntiDupe); @Contract("_, _, null -> null") @Nullable - ItemButton addItem(boolean inPlayerInventory, int slot,@Nullable ItemStack itemStack); + ItemButton addItem(boolean inPlayerInventory, int slot, @Nullable ItemStack itemStack); @Contract("_, _, null, _ -> null") @Nullable - ItemButton addItem(boolean inPlayerInventory, int slot,@Nullable ItemStack itemStack, boolean enableAntiDupe); + ItemButton addItem(boolean inPlayerInventory, int slot, @Nullable ItemStack itemStack, boolean enableAntiDupe); @Contract(pure = true) @Nullable @@ -97,4 +97,9 @@ public interface BaseInventory extends InventoryHolder { void setClearInvType(ClearInvType clearInvType); ClearInvType getClearInvType(); + + void setClickLimiterEnabled(boolean enabled); + + boolean isClickLimiterEnabled(); + } diff --git a/Common/src/main/java/fr/maxlego08/menu/common/utils/yaml/YamlParser.java b/Common/src/main/java/fr/maxlego08/menu/common/utils/yaml/YamlParser.java index e4ccbbfb..ba1a3485 100644 --- a/Common/src/main/java/fr/maxlego08/menu/common/utils/yaml/YamlParser.java +++ b/Common/src/main/java/fr/maxlego08/menu/common/utils/yaml/YamlParser.java @@ -105,10 +105,15 @@ private static void parseMap(@NotNull Map map, @NotNull Map nestedMap -> { Map parsedMap = new HashMap<>(); - //noinspection unchecked - parseMap((Map) nestedMap, parsedMap, placeholders); + Map convertedMap = convertMapKeys(nestedMap); + parseMap(convertedMap, parsedMap, placeholders); destination.put(key, parsedMap); } + case Integer i -> destination.put(key, i); + case Double d -> destination.put(key, d); + case Float f -> destination.put(key, f); + case Long l -> destination.put(key, l); + case Boolean b -> destination.put(key, b); case null, default -> destination.put(key, value); } } @@ -126,10 +131,15 @@ private static List parseList(@NotNull List list, @NotNull Map processStringValue(string, listPlaceholders, placeholders, result::add); case Map map -> { Map parsedMap = new HashMap<>(); - //noinspection unchecked - parseMap((Map) map, parsedMap, placeholders); + Map convertedMap = convertMapKeys(map); + parseMap(convertedMap, parsedMap, placeholders); result.add(parsedMap); } + case Integer i -> result.add(i); + case Double d -> result.add(d); + case Float f -> result.add(f); + case Long l -> result.add(l); + case Boolean b -> result.add(b); case null, default -> result.add(item); } } @@ -232,4 +242,21 @@ private static String findListPlaceholderInString(@NotNull String string, @NotNu } return null; } + + /** + * Converts a Map with potentially non-String keys to a Map with String keys. + * This is necessary because YAML can have integer keys or other types. + * + * @param map the map to convert + * @return a new map with String keys + */ + @NotNull + private static Map convertMapKeys(@NotNull Map map) { + Map result = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey() != null ? entry.getKey().toString() : "null"; + result.put(key, entry.getValue()); + } + return result; + } } \ No newline at end of file diff --git a/Hooks/PacketEvents/src/main/java/fr/maxlego08/menu/hooks/packetevents/listener/PacketEventClickLimiterListener.java b/Hooks/PacketEvents/src/main/java/fr/maxlego08/menu/hooks/packetevents/listener/PacketEventClickLimiterListener.java index 00579968..5381e30f 100644 --- a/Hooks/PacketEvents/src/main/java/fr/maxlego08/menu/hooks/packetevents/listener/PacketEventClickLimiterListener.java +++ b/Hooks/PacketEvents/src/main/java/fr/maxlego08/menu/hooks/packetevents/listener/PacketEventClickLimiterListener.java @@ -5,7 +5,10 @@ import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon; import fr.maxlego08.menu.api.configuration.Configuration; +import fr.maxlego08.menu.api.engine.BaseInventory; +import fr.maxlego08.menu.api.utils.CompatibilityUtil; import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; import java.util.HashMap; import java.util.Map; @@ -16,19 +19,27 @@ public class PacketEventClickLimiterListener implements PacketListener { @Override public void onPacketReceive(PacketReceiveEvent event) { + if (!Configuration.enablePacketEventClickLimiter) return; PacketTypeCommon packetType = event.getPacketType(); - if (packetType == PacketType.Play.Client.CLICK_WINDOW){ + if (packetType == PacketType.Play.Client.CLICK_WINDOW) { Player player = event.getPlayer(); - UUID playerUniqueId = player.getUniqueId(); + Inventory topInventory = CompatibilityUtil.getTopInventory(player); + try { + if (topInventory != null && topInventory.getHolder() instanceof BaseInventory baseInventory && baseInventory.isClickLimiterEnabled()) { + UUID playerUniqueId = player.getUniqueId(); - long currentTime = System.currentTimeMillis(); - Long lastClickTime = this.lastClickTimes.get(playerUniqueId); - if (lastClickTime != null && (currentTime - lastClickTime) < Configuration.packetEventClickLimiterMilliseconds){ - event.setCancelled(true); - return; + long currentTime = System.currentTimeMillis(); + Long lastClickTime = this.lastClickTimes.get(playerUniqueId); + if (lastClickTime != null && (currentTime - lastClickTime) < Configuration.packetEventClickLimiterMilliseconds) { + event.setCancelled(true); + return; + } + this.lastClickTimes.put(playerUniqueId, currentTime); + } + } catch (Exception ignored) { } - this.lastClickTimes.put(playerUniqueId, currentTime); - } if (packetType == PacketType.Play.Client.CLOSE_WINDOW) { + + } else if (packetType == PacketType.Play.Client.CLOSE_WINDOW) { Player player = event.getPlayer(); UUID playerUniqueId = player.getUniqueId(); this.lastClickTimes.remove(playerUniqueId); diff --git a/Hooks/Paper/src/main/java/fr/maxlego08/menu/hooks/ComponentMeta.java b/Hooks/Paper/src/main/java/fr/maxlego08/menu/hooks/ComponentMeta.java index 9e3417dc..ea5ed05b 100644 --- a/Hooks/Paper/src/main/java/fr/maxlego08/menu/hooks/ComponentMeta.java +++ b/Hooks/Paper/src/main/java/fr/maxlego08/menu/hooks/ComponentMeta.java @@ -176,10 +176,10 @@ public void updateLore(@NonNull ItemMeta itemMeta, @NonNull List lore, @ private Inventory createInventoryInternal(String inventoryName, InventoryHolder inventoryHolder, Object inventoryTypeOrSize) { Component component = this.cache.get(inventoryName, () -> this.MINI_MESSAGE.deserialize(colorMiniMessage(inventoryName))); try { - if (inventoryTypeOrSize instanceof Integer) { - return (Inventory) inventoryMethod.invoke(null, inventoryHolder, inventoryTypeOrSize, component); - } else if (inventoryTypeOrSize instanceof InventoryType) { - return (Inventory) inventoryTypeMethod.invoke(null, inventoryHolder, inventoryTypeOrSize, component); + if (inventoryTypeOrSize instanceof Integer integer) { + return (Inventory) inventoryMethod.invoke(null, inventoryHolder, integer, component); + } else if (inventoryTypeOrSize instanceof InventoryType inventoryType) { + return (Inventory) inventoryTypeMethod.invoke(null, inventoryHolder, inventoryType, component); } } catch (IllegalAccessException | InvocationTargetException exception) { exception.printStackTrace(); diff --git a/src/main/java/fr/maxlego08/menu/ZInventory.java b/src/main/java/fr/maxlego08/menu/ZInventory.java index b0fe9793..62ca5449 100644 --- a/src/main/java/fr/maxlego08/menu/ZInventory.java +++ b/src/main/java/fr/maxlego08/menu/ZInventory.java @@ -43,6 +43,7 @@ public class ZInventory extends ZUtils implements Inventory { private boolean clearInventory; private ClearInvType clearInvType = ClearInvType.DEFAULT; private boolean ItemPickupDisabled; + private boolean isClickLimiterEnabled = true; private Requirement openRequirement; private OpenWithItem openWithItem; private InventoryType type = InventoryType.CHEST; @@ -416,6 +417,15 @@ public ClearInvType getClearInvType() { return this.clearInvType; } + @Override + public boolean isClickLimiterEnabled() { + return this.isClickLimiterEnabled; + } + + public void setClickLimiterEnabled(boolean enabled) { + this.isClickLimiterEnabled = enabled; + } + public void setClearInvType(ClearInvType clearInvType) { this.clearInvType = clearInvType; } diff --git a/src/main/java/fr/maxlego08/menu/inventory/VInventory.java b/src/main/java/fr/maxlego08/menu/inventory/VInventory.java index c7e2427b..ab7bd19d 100644 --- a/src/main/java/fr/maxlego08/menu/inventory/VInventory.java +++ b/src/main/java/fr/maxlego08/menu/inventory/VInventory.java @@ -38,6 +38,7 @@ public abstract class VInventory extends ZUtils implements Cloneable, BaseInvent protected String guiName; protected boolean disableClick = true; protected boolean disablePlayerInventoryClick = true; + protected boolean isClickLimiterEnabled = true; private TitleAnimation titleAnimation; private PlayerTitleAnimation playerTitleAnimation; private ClearInvType clearInvType = ClearInvType.DEFAULT; @@ -288,6 +289,16 @@ public ClearInvType getClearInvType() { return this.clearInvType; } + @Override + public void setClickLimiterEnabled(boolean enabled) { + this.isClickLimiterEnabled = enabled; + } + + @Override + public boolean isClickLimiterEnabled() { + return this.isClickLimiterEnabled; + } + public void onInventoryClick(InventoryClickEvent event, ZMenuPlugin plugin, Player player) { } diff --git a/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java b/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java index c958dcec..7369404e 100644 --- a/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java +++ b/src/main/java/fr/maxlego08/menu/inventory/inventories/InventoryDefault.java @@ -51,6 +51,7 @@ public InventoryResult openInventory(ZMenuPlugin main, Player player, int page, } super.setClearInvType(this.inventory.getClearInvType()); + super.setClickLimiterEnabled(this.inventory.isClickLimiterEnabled()); this.oldInventories = extractOldInventories(args); diff --git a/src/main/java/fr/maxlego08/menu/loader/InventoryLoader.java b/src/main/java/fr/maxlego08/menu/loader/InventoryLoader.java index 0b404e4a..7d99a92f 100644 --- a/src/main/java/fr/maxlego08/menu/loader/InventoryLoader.java +++ b/src/main/java/fr/maxlego08/menu/loader/InventoryLoader.java @@ -33,7 +33,10 @@ import java.io.File; import java.lang.reflect.Constructor; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; public class InventoryLoader extends ZUtils implements Loader { @@ -137,6 +140,7 @@ public Inventory load(@NonNull YamlConfiguration configuration, @NonNull String Logger.info("Clear inventory type " + clearInvTypeStr + " is not valid in " + file.getAbsolutePath(), Logger.LogType.ERROR); } } + inventory.setClickLimiterEnabled(configuration.getBoolean(path + "click-limiter-enabled", true)); inventory.setFile(file); this.loadFillItem(configuration, inventory, menuItemStackLoader, file); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ec8ed1fd..a4f63c4c 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -154,7 +154,7 @@ enable-cooldown-click: true # Cooldown duration in milliseconds between clicks. cooldown-click-milliseconds: 100 -# Enable packet event click limiter. Limits all click packets received from players (not just inside zMenu inventories). +# Enable packet event click limiter. Limits all click packets received from players (only for zMenu inventories). # Prevents players from sending excessive click packets. Helps reduce packet spam and potential exploits. (Requires PacketEvent plugin) enable-packet-event-click-limiter: false