From 43cde63f95dbd6162ebb70cb6b48caeafc79c3dc Mon Sep 17 00:00:00 2001 From: Cory Batchelor Date: Sat, 20 Sep 2025 13:14:24 +0100 Subject: [PATCH 1/3] Add permission based chest placement limits - Added wildchests.limit.. permission system - Added default-chest-limits config section (0=unlimited, 1+=limited, -1=disabled) - Added enable-chest-limits config option (default: false) - Added limit enforcement in BlockListener with lang messages - Added ChestLimitUtils for permission and config limit checking - Added getChestCount() method to ChestsManager API - Fixed v1_21_5 NMS compatibility issues (might of been an issue in my project only) --- .../api/handlers/ChestsManager.java | 11 +++- NMS/v1_21_5/build.gradle | 2 +- .../nms/v1_21_5/NMSAdapterImpl.java | 17 +++--- .../inventory/WildChestBlockEntity.java | 8 +-- settings.gradle | 2 +- .../com/bgsoftware/wildchests/Locale.java | 1 + .../wildchests/handlers/ChestsHandler.java | 8 +++ .../wildchests/handlers/SettingsHandler.java | 14 ++++- .../wildchests/listeners/BlockListener.java | 19 ++++++- .../wildchests/utils/ChestLimitUtils.java | 56 +++++++++++++++++++ src/main/resources/config.yml | 18 ++++++ src/main/resources/lang.yml | 5 +- 12 files changed, 143 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/bgsoftware/wildchests/utils/ChestLimitUtils.java diff --git a/API/src/main/java/com/bgsoftware/wildchests/api/handlers/ChestsManager.java b/API/src/main/java/com/bgsoftware/wildchests/api/handlers/ChestsManager.java index 4a358e23..144ccbff 100644 --- a/API/src/main/java/com/bgsoftware/wildchests/api/handlers/ChestsManager.java +++ b/API/src/main/java/com/bgsoftware/wildchests/api/handlers/ChestsManager.java @@ -113,4 +113,13 @@ public interface ChestsManager { */ List getAllChestData(); -} + /** + * Get the count of chests placed by a specific player of a specific type. + * + * @param placer The UUID of the player who placed the chests + * @param chestType The name of the chest type to count + * @return The number of chests of the specified type placed by the player + */ + int getChestCount(UUID placer, String chestType); + +} \ No newline at end of file diff --git a/NMS/v1_21_5/build.gradle b/NMS/v1_21_5/build.gradle index 2be80123..c2885e02 100644 --- a/NMS/v1_21_5/build.gradle +++ b/NMS/v1_21_5/build.gradle @@ -11,7 +11,7 @@ java { group 'NMS:v1_21_5' dependencies { - paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT") + paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT") compileOnly project(":API") compileOnly rootProject } diff --git a/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/NMSAdapterImpl.java b/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/NMSAdapterImpl.java index 0443b291..8b61d866 100644 --- a/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/NMSAdapterImpl.java +++ b/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/NMSAdapterImpl.java @@ -94,12 +94,12 @@ public InventoryHolder[] deserialze(String serialized) { try { CompoundTag compoundTag = NbtUtils.read(new DataInputStream(inputStream)); - int length = compoundTag.getIntOr("Length", 0); + int length = compoundTag.getInt("Length"); inventories = new InventoryHolder[length]; for (int i = 0; i < length; i++) { if (compoundTag.contains(i + "")) { - CompoundTag itemCompound = compoundTag.getCompoundOrEmpty(i + ""); + CompoundTag itemCompound = compoundTag.getCompound(i + ""); inventories[i] = deserializeInventory(itemCompound); } } @@ -174,7 +174,8 @@ public String getChestName(org.bukkit.inventory.ItemStack bukkitItem) { CustomData customData = itemStack.get(DataComponents.CUSTOM_DATA); if (customData != null) { CompoundTag compoundTag = customData.getUnsafe(); - return compoundTag.getString("chest-name").orElse(null); + if (compoundTag.contains("chest-name", 8)) + return compoundTag.getString("chest-name"); } return null; @@ -229,7 +230,7 @@ private static org.bukkit.inventory.ItemStack deserializeItemFromBytes(byte[] da throw new RuntimeException(ex); } - int itemVersion = compoundTag.getIntOr("DataVersion", 0); + int itemVersion = compoundTag.getInt("DataVersion"); if (itemVersion != DATA_VERSION) { compoundTag = (CompoundTag) DataFixers.getDataFixer().update(References.ITEM_STACK, new Dynamic<>(NbtOps.INSTANCE, compoundTag), itemVersion, DATA_VERSION).getValue(); @@ -266,12 +267,12 @@ private static CompoundTag serializeItemAsCompoundTag(org.bukkit.inventory.ItemS } private static InventoryHolder deserializeInventory(CompoundTag compoundTag) { - InventoryHolder inventory = new InventoryHolder(compoundTag.getIntOr("Size", 0), "Chest"); - ListTag itemsList = compoundTag.getListOrEmpty("Items"); + InventoryHolder inventory = new InventoryHolder(compoundTag.getInt("Size"), "Chest"); + ListTag itemsList = compoundTag.getList("Items", 10); for (int i = 0; i < itemsList.size(); i++) { - CompoundTag itemTag = itemsList.getCompoundOrEmpty(i); - inventory.setItem(itemTag.getIntOr("Slot", 0), deserializeItemFromCompoundTag(itemTag)); + CompoundTag itemTag = itemsList.getCompound(i); + inventory.setItem(itemTag.getByte("Slot"), deserializeItemFromCompoundTag(itemTag)); } return inventory; diff --git a/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/inventory/WildChestBlockEntity.java b/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/inventory/WildChestBlockEntity.java index d2352776..e6ab2193 100644 --- a/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/inventory/WildChestBlockEntity.java +++ b/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/inventory/WildChestBlockEntity.java @@ -188,9 +188,9 @@ public void tick(Level level, BlockPos blockPos, BlockState blockState, WildChes double z = blockPos.getZ() + level.getRandom().nextFloat(); for (String particle : chestData.getChestParticles()) { try { - this.serverLevel.sendParticlesSource(null, + this.serverLevel.sendParticles(null, CraftParticle.createParticleParam(Particle.valueOf(particle), null), - false, false, x, y, z, 0, 0.0, 0.0, 0.0, 1.0); + false, x, y, z, 0, 0.0, 0.0, 0.0, 1.0); } catch (Exception ignored) { } } @@ -335,9 +335,9 @@ private void handleSuctionItems(ChestData chestData) { } private void handleItemSuctionRemoval(ItemEntity itemEntity) { - this.serverLevel.sendParticlesSource(null, + this.serverLevel.sendParticles(null, CraftParticle.createParticleParam(Particle.CLOUD, null), - false, false, itemEntity.getX(), itemEntity.getY(), itemEntity.getZ(), + false, itemEntity.getX(), itemEntity.getY(), itemEntity.getZ(), 0, 0.0, 0.0, 0.0, 1.0); itemEntity.discard(); } diff --git a/settings.gradle b/settings.gradle index 01d2b62f..e9e0ae99 100644 --- a/settings.gradle +++ b/settings.gradle @@ -29,4 +29,4 @@ include 'NMS:v1_20_4' include 'NMS:v1_21' include 'NMS:v1_21_3' include 'NMS:v1_21_4' -include 'NMS:v1_21_5' \ No newline at end of file +// include 'NMS:v1_21_5' // Temporarily disabled due to API incompatibilities \ No newline at end of file diff --git a/src/main/java/com/bgsoftware/wildchests/Locale.java b/src/main/java/com/bgsoftware/wildchests/Locale.java index a8178b81..2e98cb39 100644 --- a/src/main/java/com/bgsoftware/wildchests/Locale.java +++ b/src/main/java/com/bgsoftware/wildchests/Locale.java @@ -55,6 +55,7 @@ public final class Locale { public static Locale FORMAT_THOUSANDS = new Locale("FORMAT_THOUSANDS"); public static Locale MONEY_EARNED_OFFLINE = new Locale("MONEY_EARNED_OFFLINE"); public static Locale LEFTOVERS_ITEMS_WARNING = new Locale("LEFTOVERS_ITEMS_WARNING"); + public static Locale CHEST_LIMIT_REACHED = new Locale("CHEST_LIMIT_REACHED"); private Locale(String identifier) { diff --git a/src/main/java/com/bgsoftware/wildchests/handlers/ChestsHandler.java b/src/main/java/com/bgsoftware/wildchests/handlers/ChestsHandler.java index 4bf68ce1..392c9ff8 100644 --- a/src/main/java/com/bgsoftware/wildchests/handlers/ChestsHandler.java +++ b/src/main/java/com/bgsoftware/wildchests/handlers/ChestsHandler.java @@ -160,6 +160,14 @@ public List getChests(World world) { Collections.unmodifiableList(new LinkedList<>(chunkChests)); } + @Override + public int getChestCount(UUID placer, String chestType) { + return (int) chests.values().stream() + .filter(chest -> chest.getPlacer().equals(placer)) + .filter(chest -> chest.getData().getName().equals(chestType)) + .count(); + } + @Override public List getNearbyChests(Location location) { return getChests(location.getWorld()).stream() diff --git a/src/main/java/com/bgsoftware/wildchests/handlers/SettingsHandler.java b/src/main/java/com/bgsoftware/wildchests/handlers/SettingsHandler.java index abdbeec2..86ad797e 100644 --- a/src/main/java/com/bgsoftware/wildchests/handlers/SettingsHandler.java +++ b/src/main/java/com/bgsoftware/wildchests/handlers/SettingsHandler.java @@ -42,6 +42,8 @@ public final class SettingsHandler { public final boolean wildStackerHook; public final int maximumPickupDelay; public final int maxStacksOnDrop; + public final boolean enableChestLimits; + public final Map defaultChestLimits; public SettingsHandler(WildChestsPlugin plugin) { WildChestsPlugin.log("Loading configuration started..."); @@ -72,6 +74,16 @@ public SettingsHandler(WildChestsPlugin plugin) { wildStackerHook = cfg.getBoolean("hooks.wildstacker", true); maximumPickupDelay = cfg.getInt("maximum-pickup-delay", 32767); maxStacksOnDrop = cfg.getInt("max-stacks-on-drop", -1); + enableChestLimits = cfg.getBoolean("enable-chest-limits", false); + + defaultChestLimits = new HashMap<>(); + if (cfg.contains("default-chest-limits")) { + ConfigurationSection limitsSection = cfg.getConfigurationSection("default-chest-limits"); + for (String chestType : limitsSection.getKeys(false)) { + int limit = limitsSection.getInt(chestType, -1); + defaultChestLimits.put(chestType, limit); + } + } Map prices = new HashMap<>(); @@ -253,4 +265,4 @@ private static ChestData loadChestFromSection(WildChestsPlugin plugin, String ch return chestData; } -} +} \ No newline at end of file diff --git a/src/main/java/com/bgsoftware/wildchests/listeners/BlockListener.java b/src/main/java/com/bgsoftware/wildchests/listeners/BlockListener.java index 8372d4ef..4a8bd253 100644 --- a/src/main/java/com/bgsoftware/wildchests/listeners/BlockListener.java +++ b/src/main/java/com/bgsoftware/wildchests/listeners/BlockListener.java @@ -4,6 +4,7 @@ import com.bgsoftware.wildchests.WildChestsPlugin; import com.bgsoftware.wildchests.api.objects.chests.Chest; import com.bgsoftware.wildchests.api.objects.data.ChestData; +import com.bgsoftware.wildchests.utils.ChestLimitUtils; import com.bgsoftware.wildchests.utils.ItemUtils; import org.bukkit.GameMode; import org.bukkit.Material; @@ -79,6 +80,22 @@ public void onChestPlace(BlockPlaceEvent e) { if (chestData == null) return; + if (plugin.getSettings().enableChestLimits) { + String chestType = chestData.getName(); + Player player = e.getPlayer(); + + if (ChestLimitUtils.hasChestLimit(player, chestType)) { + int limit = ChestLimitUtils.getPlayerChestLimit(player, chestType); + int currentCount = plugin.getChestsManager().getChestCount(player.getUniqueId(), chestType); + + if (limit != Integer.MAX_VALUE && currentCount >= limit) { + e.setCancelled(true); + Locale.CHEST_LIMIT_REACHED.send(player, limit, chestType); + return; + } + } + } + Chest chest = plugin.getChestsManager().addChest(e.getPlayer().getUniqueId(), e.getBlockPlaced().getLocation(), chestData); plugin.getProviders().notifyChestPlaceListeners(chest); @@ -161,4 +178,4 @@ private static EntityType lookupEntityType(String name) { } } -} +} \ No newline at end of file diff --git a/src/main/java/com/bgsoftware/wildchests/utils/ChestLimitUtils.java b/src/main/java/com/bgsoftware/wildchests/utils/ChestLimitUtils.java new file mode 100644 index 00000000..c9d0e041 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildchests/utils/ChestLimitUtils.java @@ -0,0 +1,56 @@ +package com.bgsoftware.wildchests.utils; + +import com.bgsoftware.wildchests.WildChestsPlugin; +import org.bukkit.entity.Player; +import org.bukkit.permissions.PermissionAttachmentInfo; + +public final class ChestLimitUtils { + + private ChestLimitUtils() {} + + public static int getPlayerChestLimit(Player player, String chestType) { + String permissionPrefix = "wildchests.limit." + chestType + "."; + + int maxLimit = 0; + boolean hasPermission = false; + + for (PermissionAttachmentInfo permissionInfo : player.getEffectivePermissions()) { + String permission = permissionInfo.getPermission(); + if (permission.startsWith(permissionPrefix) && permissionInfo.getValue()) { + hasPermission = true; + try { + String limitStr = permission.substring(permissionPrefix.length()); + int limit = Integer.parseInt(limitStr); + if (limit > maxLimit) { + maxLimit = limit; + } + } catch (NumberFormatException ignored) { + } + } + } + + if (!hasPermission) { + Integer defaultLimit = WildChestsPlugin.getPlugin().getSettings().defaultChestLimits.get(chestType); + if (defaultLimit != null) { + return defaultLimit == 0 ? Integer.MAX_VALUE : defaultLimit; + } + return Integer.MAX_VALUE; + } + + return maxLimit; + } + + public static boolean hasChestLimit(Player player, String chestType) { + String permissionPrefix = "wildchests.limit." + chestType + "."; + + boolean hasPermission = player.getEffectivePermissions().stream() + .anyMatch(permissionInfo -> permissionInfo.getPermission().startsWith(permissionPrefix) && permissionInfo.getValue()); + + if (!hasPermission) { + Integer defaultLimit = WildChestsPlugin.getPlugin().getSettings().defaultChestLimits.get(chestType); + return defaultLimit != null && defaultLimit != -1; + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index f79b653e..654f6540 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -61,6 +61,24 @@ maximum-pickup-delay: 32767 # You can disable this feature by setting it to -1. max-stacks-on-drop: -1 +# Enable permission-based chest placement limits. +# When enabled, players can only place a limited number of each chest type based on permissions. +# Permission format: wildchests.limit.. +# For example: wildchests.limit.linked_chest.5 allows placing up to 5 linked chests +enable-chest-limits: false + +# Default limits for each chest type when no specific permission is given. +# Set to 0 for unlimited, or any positive number for the default limit. +# Set to -1 to disable this chest type entirely. +# These only apply when enable-chest-limits is true. +default-chest-limits: + linked_chest: 0 + large_chest: 0 + sell_chest: 0 + auto_crafter: 0 + storage_unit: 0 + chunk_collector: 0 + # The plugin brings tons of new custom and unique chests to your server. All the chests are configurable, and # you can create and mix between them. You can create chests that stores infinite amount of items, chests that # are connected to player vaults or factions, linked chests and many more! diff --git a/src/main/resources/lang.yml b/src/main/resources/lang.yml index 0b8d5158..84c4eb1d 100644 --- a/src/main/resources/lang.yml +++ b/src/main/resources/lang.yml @@ -77,4 +77,7 @@ RELOAD_SUCCESS: '&5&lWildChests &7Successfully reloaded the configuration files. # Called when selling items into sell-chest SOLD_CHEST_HEADER: '&5&lWildChests &7Your transactions in the last 10 minutes:' SOLD_CHEST_LINE: '&5&lWildChests &7x{0} {1} for ${2}' -SOLD_CHEST_FOOTER: '&5&lWildChests &7You earned a total of ${0}' \ No newline at end of file +SOLD_CHEST_FOOTER: '&5&lWildChests &7You earned a total of ${0}' + +# Called when a player tries to place a chest but has reached the limit for that type +CHEST_LIMIT_REACHED: '&5&lWildChests &7You have reached the maximum limit of {0} {1} chests!' \ No newline at end of file From d9e5615d47a01fef837f645c7da2998a3401e7f6 Mon Sep 17 00:00:00 2001 From: Cory Batchelor Date: Sat, 4 Oct 2025 11:43:15 +0100 Subject: [PATCH 2/3] - Update ChestLimitUtils parameter name from chestType to chestDataName - Chest placement limits functionality --- .../api/handlers/ChestsManager.java | 4 +- .../api/objects/data/ChestData.java | 2 +- NMS/v1_21_5/build.gradle | 2 +- .../nms/v1_21_5/NMSAdapterImpl.java | 17 ++-- .../inventory/WildChestBlockEntity.java | 8 +- settings.gradle | 2 +- .../wildchests/handlers/ChestsHandler.java | 8 +- .../wildchests/handlers/SettingsHandler.java | 7 +- .../wildchests/listeners/BlockListener.java | 11 ++- .../wildchests/utils/ChestLimitUtils.java | 94 +++++++++++-------- src/main/resources/config.yml | 36 +++---- 11 files changed, 105 insertions(+), 86 deletions(-) diff --git a/API/src/main/java/com/bgsoftware/wildchests/api/handlers/ChestsManager.java b/API/src/main/java/com/bgsoftware/wildchests/api/handlers/ChestsManager.java index 144ccbff..9cbb8032 100644 --- a/API/src/main/java/com/bgsoftware/wildchests/api/handlers/ChestsManager.java +++ b/API/src/main/java/com/bgsoftware/wildchests/api/handlers/ChestsManager.java @@ -117,9 +117,9 @@ public interface ChestsManager { * Get the count of chests placed by a specific player of a specific type. * * @param placer The UUID of the player who placed the chests - * @param chestType The name of the chest type to count + * @param chestDataName The name of the chest data to count * @return The number of chests of the specified type placed by the player */ - int getChestCount(UUID placer, String chestType); + int getChestCount(UUID placer, String chestDataName); } \ No newline at end of file diff --git a/API/src/main/java/com/bgsoftware/wildchests/api/objects/data/ChestData.java b/API/src/main/java/com/bgsoftware/wildchests/api/objects/data/ChestData.java index a59ae5dd..ed77b1b4 100644 --- a/API/src/main/java/com/bgsoftware/wildchests/api/objects/data/ChestData.java +++ b/API/src/main/java/com/bgsoftware/wildchests/api/objects/data/ChestData.java @@ -231,4 +231,4 @@ public interface ChestData { */ void setParticles(List particles); -} +} \ No newline at end of file diff --git a/NMS/v1_21_5/build.gradle b/NMS/v1_21_5/build.gradle index c2885e02..2be80123 100644 --- a/NMS/v1_21_5/build.gradle +++ b/NMS/v1_21_5/build.gradle @@ -11,7 +11,7 @@ java { group 'NMS:v1_21_5' dependencies { - paperweight.paperDevBundle("1.21.4-R0.1-SNAPSHOT") + paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT") compileOnly project(":API") compileOnly rootProject } diff --git a/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/NMSAdapterImpl.java b/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/NMSAdapterImpl.java index 8b61d866..0443b291 100644 --- a/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/NMSAdapterImpl.java +++ b/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/NMSAdapterImpl.java @@ -94,12 +94,12 @@ public InventoryHolder[] deserialze(String serialized) { try { CompoundTag compoundTag = NbtUtils.read(new DataInputStream(inputStream)); - int length = compoundTag.getInt("Length"); + int length = compoundTag.getIntOr("Length", 0); inventories = new InventoryHolder[length]; for (int i = 0; i < length; i++) { if (compoundTag.contains(i + "")) { - CompoundTag itemCompound = compoundTag.getCompound(i + ""); + CompoundTag itemCompound = compoundTag.getCompoundOrEmpty(i + ""); inventories[i] = deserializeInventory(itemCompound); } } @@ -174,8 +174,7 @@ public String getChestName(org.bukkit.inventory.ItemStack bukkitItem) { CustomData customData = itemStack.get(DataComponents.CUSTOM_DATA); if (customData != null) { CompoundTag compoundTag = customData.getUnsafe(); - if (compoundTag.contains("chest-name", 8)) - return compoundTag.getString("chest-name"); + return compoundTag.getString("chest-name").orElse(null); } return null; @@ -230,7 +229,7 @@ private static org.bukkit.inventory.ItemStack deserializeItemFromBytes(byte[] da throw new RuntimeException(ex); } - int itemVersion = compoundTag.getInt("DataVersion"); + int itemVersion = compoundTag.getIntOr("DataVersion", 0); if (itemVersion != DATA_VERSION) { compoundTag = (CompoundTag) DataFixers.getDataFixer().update(References.ITEM_STACK, new Dynamic<>(NbtOps.INSTANCE, compoundTag), itemVersion, DATA_VERSION).getValue(); @@ -267,12 +266,12 @@ private static CompoundTag serializeItemAsCompoundTag(org.bukkit.inventory.ItemS } private static InventoryHolder deserializeInventory(CompoundTag compoundTag) { - InventoryHolder inventory = new InventoryHolder(compoundTag.getInt("Size"), "Chest"); - ListTag itemsList = compoundTag.getList("Items", 10); + InventoryHolder inventory = new InventoryHolder(compoundTag.getIntOr("Size", 0), "Chest"); + ListTag itemsList = compoundTag.getListOrEmpty("Items"); for (int i = 0; i < itemsList.size(); i++) { - CompoundTag itemTag = itemsList.getCompound(i); - inventory.setItem(itemTag.getByte("Slot"), deserializeItemFromCompoundTag(itemTag)); + CompoundTag itemTag = itemsList.getCompoundOrEmpty(i); + inventory.setItem(itemTag.getIntOr("Slot", 0), deserializeItemFromCompoundTag(itemTag)); } return inventory; diff --git a/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/inventory/WildChestBlockEntity.java b/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/inventory/WildChestBlockEntity.java index e6ab2193..5bacb1aa 100644 --- a/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/inventory/WildChestBlockEntity.java +++ b/NMS/v1_21_5/src/main/java/com/bgsoftware/wildchests/nms/v1_21_5/inventory/WildChestBlockEntity.java @@ -188,9 +188,9 @@ public void tick(Level level, BlockPos blockPos, BlockState blockState, WildChes double z = blockPos.getZ() + level.getRandom().nextFloat(); for (String particle : chestData.getChestParticles()) { try { - this.serverLevel.sendParticles(null, + this.serverLevel.sendParticles( CraftParticle.createParticleParam(Particle.valueOf(particle), null), - false, x, y, z, 0, 0.0, 0.0, 0.0, 1.0); + false, false, x, y, z, 0, 0.0, 0.0, 0.0, 1.0); } catch (Exception ignored) { } } @@ -335,9 +335,9 @@ private void handleSuctionItems(ChestData chestData) { } private void handleItemSuctionRemoval(ItemEntity itemEntity) { - this.serverLevel.sendParticles(null, + this.serverLevel.sendParticles( CraftParticle.createParticleParam(Particle.CLOUD, null), - false, itemEntity.getX(), itemEntity.getY(), itemEntity.getZ(), + false, false, itemEntity.getX(), itemEntity.getY(), itemEntity.getZ(), 0, 0.0, 0.0, 0.0, 1.0); itemEntity.discard(); } diff --git a/settings.gradle b/settings.gradle index e9e0ae99..9a000f72 100644 --- a/settings.gradle +++ b/settings.gradle @@ -29,4 +29,4 @@ include 'NMS:v1_20_4' include 'NMS:v1_21' include 'NMS:v1_21_3' include 'NMS:v1_21_4' -// include 'NMS:v1_21_5' // Temporarily disabled due to API incompatibilities \ No newline at end of file +//include 'NMS:v1_21_5' \ No newline at end of file diff --git a/src/main/java/com/bgsoftware/wildchests/handlers/ChestsHandler.java b/src/main/java/com/bgsoftware/wildchests/handlers/ChestsHandler.java index 392c9ff8..8d6fa622 100644 --- a/src/main/java/com/bgsoftware/wildchests/handlers/ChestsHandler.java +++ b/src/main/java/com/bgsoftware/wildchests/handlers/ChestsHandler.java @@ -161,10 +161,12 @@ public List getChests(World world) { } @Override - public int getChestCount(UUID placer, String chestType) { + public int getChestCount(UUID placer, String chestDataName) { + ChestData chestData = getChestData(chestDataName); + if (chestData == null) + return 0; return (int) chests.values().stream() - .filter(chest -> chest.getPlacer().equals(placer)) - .filter(chest -> chest.getData().getName().equals(chestType)) + .filter(chest -> chest.getPlacer().equals(placer) && chest.getData() == chestData) .count(); } diff --git a/src/main/java/com/bgsoftware/wildchests/handlers/SettingsHandler.java b/src/main/java/com/bgsoftware/wildchests/handlers/SettingsHandler.java index 86ad797e..bdb95c56 100644 --- a/src/main/java/com/bgsoftware/wildchests/handlers/SettingsHandler.java +++ b/src/main/java/com/bgsoftware/wildchests/handlers/SettingsHandler.java @@ -74,11 +74,11 @@ public SettingsHandler(WildChestsPlugin plugin) { wildStackerHook = cfg.getBoolean("hooks.wildstacker", true); maximumPickupDelay = cfg.getInt("maximum-pickup-delay", 32767); maxStacksOnDrop = cfg.getInt("max-stacks-on-drop", -1); - enableChestLimits = cfg.getBoolean("enable-chest-limits", false); + enableChestLimits = cfg.getBoolean("chest-limits.enabled", false); defaultChestLimits = new HashMap<>(); - if (cfg.contains("default-chest-limits")) { - ConfigurationSection limitsSection = cfg.getConfigurationSection("default-chest-limits"); + if (cfg.contains("chest-limits.default")) { + ConfigurationSection limitsSection = cfg.getConfigurationSection("chest-limits.default"); for (String chestType : limitsSection.getKeys(false)) { int limit = limitsSection.getInt(chestType, -1); defaultChestLimits.put(chestType, limit); @@ -264,5 +264,4 @@ private static ChestData loadChestFromSection(WildChestsPlugin plugin, String ch return chestData; } - } \ No newline at end of file diff --git a/src/main/java/com/bgsoftware/wildchests/listeners/BlockListener.java b/src/main/java/com/bgsoftware/wildchests/listeners/BlockListener.java index 4a8bd253..565dfecb 100644 --- a/src/main/java/com/bgsoftware/wildchests/listeners/BlockListener.java +++ b/src/main/java/com/bgsoftware/wildchests/listeners/BlockListener.java @@ -81,16 +81,17 @@ public void onChestPlace(BlockPlaceEvent e) { return; if (plugin.getSettings().enableChestLimits) { - String chestType = chestData.getName(); + String chestDataName = chestData.getName(); Player player = e.getPlayer(); - if (ChestLimitUtils.hasChestLimit(player, chestType)) { - int limit = ChestLimitUtils.getPlayerChestLimit(player, chestType); - int currentCount = plugin.getChestsManager().getChestCount(player.getUniqueId(), chestType); + ChestLimitUtils.ChestLimitResult limitResult = ChestLimitUtils.getPlayerChestLimitResult(player, chestDataName); + if (limitResult.hasLimit()) { + int limit = limitResult.getLimit(); + int currentCount = plugin.getChestsManager().getChestCount(player.getUniqueId(), chestDataName); if (limit != Integer.MAX_VALUE && currentCount >= limit) { e.setCancelled(true); - Locale.CHEST_LIMIT_REACHED.send(player, limit, chestType); + Locale.CHEST_LIMIT_REACHED.send(player, limit, chestDataName); return; } } diff --git a/src/main/java/com/bgsoftware/wildchests/utils/ChestLimitUtils.java b/src/main/java/com/bgsoftware/wildchests/utils/ChestLimitUtils.java index c9d0e041..e9789e3c 100644 --- a/src/main/java/com/bgsoftware/wildchests/utils/ChestLimitUtils.java +++ b/src/main/java/com/bgsoftware/wildchests/utils/ChestLimitUtils.java @@ -4,53 +4,69 @@ import org.bukkit.entity.Player; import org.bukkit.permissions.PermissionAttachmentInfo; +import java.util.OptionalInt; + public final class ChestLimitUtils { private ChestLimitUtils() {} - public static int getPlayerChestLimit(Player player, String chestType) { - String permissionPrefix = "wildchests.limit." + chestType + "."; - - int maxLimit = 0; - boolean hasPermission = false; - - for (PermissionAttachmentInfo permissionInfo : player.getEffectivePermissions()) { - String permission = permissionInfo.getPermission(); - if (permission.startsWith(permissionPrefix) && permissionInfo.getValue()) { - hasPermission = true; - try { - String limitStr = permission.substring(permissionPrefix.length()); - int limit = Integer.parseInt(limitStr); - if (limit > maxLimit) { - maxLimit = limit; - } - } catch (NumberFormatException ignored) { - } - } + /** + * Result class to hold both limit existence and value information + */ + public static class ChestLimitResult { + private final boolean hasLimit; + private final int limit; + + private ChestLimitResult(boolean hasLimit, int limit) { + this.hasLimit = hasLimit; + this.limit = limit; + } + + public boolean hasLimit() { + return hasLimit; + } + + public int getLimit() { + return limit; + } + + public static ChestLimitResult noLimit() { + return new ChestLimitResult(false, Integer.MAX_VALUE); } - - if (!hasPermission) { - Integer defaultLimit = WildChestsPlugin.getPlugin().getSettings().defaultChestLimits.get(chestType); - if (defaultLimit != null) { - return defaultLimit == 0 ? Integer.MAX_VALUE : defaultLimit; - } - return Integer.MAX_VALUE; + + public static ChestLimitResult withLimit(int limit) { + return new ChestLimitResult(true, limit); } - - return maxLimit; } - public static boolean hasChestLimit(Player player, String chestType) { - String permissionPrefix = "wildchests.limit." + chestType + "."; - - boolean hasPermission = player.getEffectivePermissions().stream() - .anyMatch(permissionInfo -> permissionInfo.getPermission().startsWith(permissionPrefix) && permissionInfo.getValue()); - - if (!hasPermission) { - Integer defaultLimit = WildChestsPlugin.getPlugin().getSettings().defaultChestLimits.get(chestType); - return defaultLimit != null && defaultLimit != -1; + public static ChestLimitResult getPlayerChestLimitResult(Player player, String chestDataName) { + String permissionPrefix = "wildchests.limit." + chestDataName + "."; + + OptionalInt maxPermissionLimit = player.getEffectivePermissions().stream() + .filter(PermissionAttachmentInfo::getValue) + .map(PermissionAttachmentInfo::getPermission) + .filter(permission -> permission.startsWith(permissionPrefix)) + .map(permission -> permission.substring(permissionPrefix.length())) + .mapToInt(limitStr -> { + try { + return Integer.parseInt(limitStr); + } catch (NumberFormatException e) { + return -1; + } + }) + .filter(limit -> limit >= 0) + .max(); + + if (maxPermissionLimit.isPresent()) { + return ChestLimitResult.withLimit(maxPermissionLimit.getAsInt()); + } + + Integer defaultLimit = WildChestsPlugin.getPlugin().getSettings().defaultChestLimits.get(chestDataName); + if (defaultLimit != null && defaultLimit != -1) { + int actualLimit = defaultLimit == 0 ? Integer.MAX_VALUE : defaultLimit; + return ChestLimitResult.withLimit(actualLimit); } - - return true; + + return ChestLimitResult.noLimit(); } } \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 654f6540..9135d8bb 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -61,23 +61,25 @@ maximum-pickup-delay: 32767 # You can disable this feature by setting it to -1. max-stacks-on-drop: -1 -# Enable permission-based chest placement limits. -# When enabled, players can only place a limited number of each chest type based on permissions. -# Permission format: wildchests.limit.. -# For example: wildchests.limit.linked_chest.5 allows placing up to 5 linked chests -enable-chest-limits: false - -# Default limits for each chest type when no specific permission is given. -# Set to 0 for unlimited, or any positive number for the default limit. -# Set to -1 to disable this chest type entirely. -# These only apply when enable-chest-limits is true. -default-chest-limits: - linked_chest: 0 - large_chest: 0 - sell_chest: 0 - auto_crafter: 0 - storage_unit: 0 - chunk_collector: 0 +# Permission-based chest placement limits configuration +chest-limits: + # Enable chest placement limits + # When enabled, players can only place a limited number of each chest type based on permissions. + # Permission format: wildchests.limit.. + # For example: wildchests.limit.linked_chest.5 allows placing up to 5 linked chests + enabled: false + + # Default limits for each chest type when no specific permission is given. + # Set to 0 for unlimited, or any positive number for the default limit. + # Set to -1 to disable this chest type entirely. + # These only apply when chest-limits.enabled is true. + default: + linked_chest: 0 + large_chest: 0 + sell_chest: 0 + auto_crafter: 0 + storage_unit: 0 + chunk_collector: 0 # The plugin brings tons of new custom and unique chests to your server. All the chests are configurable, and # you can create and mix between them. You can create chests that stores infinite amount of items, chests that From 2374838098b9699e711d8da480c3c1e77ae05fb6 Mon Sep 17 00:00:00 2001 From: YouSeeMeRunning <61563491+YouSeeMeRunning2@users.noreply.github.com> Date: Sat, 4 Oct 2025 11:43:59 +0100 Subject: [PATCH 3/3] Update settings.gradle --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 9a000f72..865102cc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -29,4 +29,4 @@ include 'NMS:v1_20_4' include 'NMS:v1_21' include 'NMS:v1_21_3' include 'NMS:v1_21_4' -//include 'NMS:v1_21_5' \ No newline at end of file +include 'NMS:v1_21_5'