From ff813e3e539b94cb0e7785e31ba54b595402a767 Mon Sep 17 00:00:00 2001 From: tastybento Date: Fri, 14 Apr 2023 15:37:34 -0700 Subject: [PATCH] #176 adds support for custom blocks with ItemsAdder --- pom.xml | 10 ++ .../limits/listeners/BlockLimitsListener.java | 138 ++++++++++++++++-- .../limits/objects/IslandBlockCount.java | 109 ++++++++++++++ src/main/resources/config.yml | 7 + .../limits/listeners/JoinListenerTest.java | 2 +- 5 files changed, 251 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index a35d5b3..a6ed8b8 100644 --- a/pom.xml +++ b/pom.xml @@ -125,6 +125,10 @@ codemc-repo https://repo.codemc.org/repository/maven-public/ + + jitpack-repo + https://jitpack.io + @@ -160,6 +164,12 @@ ${bentobox.version} provided + + com.github.LoneDev6 + api-itemsadder + 3.4.1-r4 + provided + diff --git a/src/main/java/world/bentobox/limits/listeners/BlockLimitsListener.java b/src/main/java/world/bentobox/limits/listeners/BlockLimitsListener.java index 5d64bd4..8507ab9 100644 --- a/src/main/java/world/bentobox/limits/listeners/BlockLimitsListener.java +++ b/src/main/java/world/bentobox/limits/listeners/BlockLimitsListener.java @@ -1,6 +1,14 @@ package world.bentobox.limits.listeners; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -15,13 +23,27 @@ import org.bukkit.event.Event; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.event.block.*; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockBurnEvent; +import org.bukkit.event.block.BlockExplodeEvent; +import org.bukkit.event.block.BlockFadeEvent; +import org.bukkit.event.block.BlockFormEvent; +import org.bukkit.event.block.BlockFromToEvent; +import org.bukkit.event.block.BlockGrowEvent; +import org.bukkit.event.block.BlockMultiPlaceEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.block.BlockSpreadEvent; +import org.bukkit.event.block.EntityBlockFormEvent; +import org.bukkit.event.block.LeavesDecayEvent; import org.bukkit.event.entity.EntityChangeBlockEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.plugin.Plugin; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import dev.lone.itemsadder.api.CustomBlock; import world.bentobox.bentobox.api.events.island.IslandDeleteEvent; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; @@ -59,7 +81,10 @@ public class BlockLimitsListener implements Listener { private final Map saveMap = new HashMap<>(); private final Database handler; private final Map> worldLimitMap = new HashMap<>(); + //private final Map> customWorldLimitMap = new HashMap<>(); private Map defaultLimitMap = new EnumMap<>(Material.class); + private Map defaultCustomLimitMap = new HashMap<>(); + private Plugin itemsAdder; public BlockLimitsListener(Limits addon) { this.addon = addon; @@ -89,6 +114,11 @@ public class BlockLimitsListener implements Listener { ConfigurationSection limitConfig = addon.getConfig().getConfigurationSection("blocklimits"); defaultLimitMap = loadLimits(Objects.requireNonNull(limitConfig)); } + // Load custom blocks + if (addon.getConfig().isConfigurationSection("customblocklimits")) { + ConfigurationSection limitConfig = addon.getConfig().getConfigurationSection("customblocklimits"); + defaultCustomLimitMap = loadCustomLimits(Objects.requireNonNull(limitConfig)); + } // Load specific worlds if (addon.getConfig().isConfigurationSection("worlds")) { @@ -126,6 +156,21 @@ public class BlockLimitsListener implements Listener { return mats; } + /** + * Loads custom limit map from configuration section + * + * @param cs - configuration section + * @return limit map + */ + private Map loadCustomLimits(ConfigurationSection cs) { + Map mats = new HashMap<>(); + for (String material : cs.getKeys(false)) { + mats.put(material, cs.getInt(material)); + addon.log("Limit " + material + " to " + cs.getInt(material)); + } + return mats; + } + /** * Save the count database completely @@ -137,7 +182,7 @@ public class BlockLimitsListener implements Listener { // Player-related events @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onBlock(BlockPlaceEvent e) { - notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), true), e.getBlock().getType()); + notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), true), e.getBlock()); } @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @@ -187,7 +232,7 @@ public class BlockLimitsListener implements Listener { @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onBlock(BlockMultiPlaceEvent e) { - notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), true), e.getBlock().getType()); + notify(e, User.getInstance(e.getPlayer()), process(e.getBlock(), true), e.getBlock()); } /** @@ -197,10 +242,15 @@ public class BlockLimitsListener implements Listener { * @param limit maximum limit allowed * @param m material */ - private void notify(Cancellable e, User user, int limit, Material m) { + private void notify(Cancellable e, User user, int limit, Block block) { if (limit > -1) { + String name = Util.prettifyText(block.getType().toString()); + if (this.itemsAdder != null) { + CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); + name = customBlock.getDisplayName(); + } user.notify("block-limits.hit-limit", - "[material]", Util.prettifyText(m.toString()), + "[material]", name, TextVariables.NUMBER, String.valueOf(limit)); e.setCancelled(true); } @@ -318,6 +368,8 @@ public class BlockLimitsListener implements Listener { * @return limit amount if over limit, or -1 if no limitation */ private int process(Block b, boolean add) { + // Check for custom block handlers + checkCustom(); if (DO_NOT_COUNT.contains(fixMaterial(b.getBlockData())) || !addon.inGameModeWorld(b.getWorld())) { return -1; } @@ -335,12 +387,27 @@ public class BlockLimitsListener implements Listener { } islandCountMap.putIfAbsent(id, new IslandBlockCount(id, gameMode)); if (add) { - // Check limit - int limit = checkLimit(b.getWorld(), fixMaterial(b.getBlockData()), id); - if (limit > -1) { - return limit; + // Check if custom block + if (itemsAdder != null ) { + CustomBlock customBlock = CustomBlock.byAlreadyPlaced(b); + if(customBlock != null) { + // Custom block + // Check limit + int limit = checkCustomLimit(b.getWorld(), b, id); + if (limit > -1) { + return limit; + } + islandCountMap.get(id).add(b); + } + + } else { + // Check limit + int limit = checkLimit(b.getWorld(), fixMaterial(b.getBlockData()), id); + if (limit > -1) { + return limit; + } + islandCountMap.get(id).add(fixMaterial(b.getBlockData())); } - islandCountMap.get(id).add(fixMaterial(b.getBlockData())); } else { if (islandCountMap.containsKey(id)) { islandCountMap.get(id).remove(fixMaterial(b.getBlockData())); @@ -351,6 +418,11 @@ public class BlockLimitsListener implements Listener { }).orElse(-1); } + private void checkCustom() { + itemsAdder = Bukkit.getPluginManager().getPlugin("ItemsAdder"); + + } + /** * Removed a block from any island limit count * @param b - block to remove @@ -364,10 +436,17 @@ public class BlockLimitsListener implements Listener { // Invalid world return; } - islandCountMap.computeIfAbsent(id, k -> new IslandBlockCount(id, gameMode)).remove(fixMaterial(b.getBlockData())); - updateSaveMap(id); + // Check for custom block + if (this.itemsAdder != null && CustomBlock.byAlreadyPlaced(b) != null) { + islandCountMap.computeIfAbsent(id, k -> new IslandBlockCount(id, gameMode)).remove(b); + updateSaveMap(id); + } else { + islandCountMap.computeIfAbsent(id, k -> new IslandBlockCount(id, gameMode)).remove(fixMaterial(b.getBlockData())); + updateSaveMap(id); + } }); } + private void updateSaveMap(String id) { saveMap.putIfAbsent(id, 0); if (saveMap.merge(id, 1, Integer::sum) > CHANGE_LIMIT) { @@ -404,6 +483,37 @@ public class BlockLimitsListener implements Listener { return -1; } + /** + * Check if this custom block is at its limit for world on this island + * + * @param w - world + * @param block - custom block + * @param id - island id + * @return limit amount if at limit or -1 if no limit + */ + private int checkCustomLimit(World w, Block block, String islandId) { + // Check island limits + IslandBlockCount ibc = islandCountMap.get(islandId); + if (ibc.isCustomBlockLimited(block)) { + return ibc.isAtLimit(block) ? ibc.getBlockLimit(block) : -1; + } + CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); + String id = Objects.requireNonNull(customBlock).getNamespacedID(); + /* NOT SUPPORTED YET + // Check specific world limits + if (customWorldLimitMap.containsKey(w) && customWorldLimitMap.get(w).containsKey(id)) { + // Material is overridden in world + return ibc.isAtLimit(block, worldLimitMap.get(w).get(id)) ? worldLimitMap.get(w).get(id) : -1; // TODO Add perm offset + } + */ + // Check default limit map + if (defaultCustomLimitMap.containsKey(id) && ibc.isAtLimit(block, defaultCustomLimitMap.get(id))) { + return defaultCustomLimitMap.get(id);// TODO add perm offset + } + // No limit + return -1; + } + /** * Gets an aggregate map of the limits for this island * @@ -427,7 +537,7 @@ public class BlockLimitsListener implements Listener { // Add offsets to the every limit. islandBlockCount.getBlockLimitsOffset().forEach((material, offset) -> - result.put(material, result.getOrDefault(material, 0) + offset)); + result.put(material, result.getOrDefault(material, 0) + offset)); } return result; } diff --git a/src/main/java/world/bentobox/limits/objects/IslandBlockCount.java b/src/main/java/world/bentobox/limits/objects/IslandBlockCount.java index 8cc137e..2cb8751 100644 --- a/src/main/java/world/bentobox/limits/objects/IslandBlockCount.java +++ b/src/main/java/world/bentobox/limits/objects/IslandBlockCount.java @@ -3,12 +3,15 @@ package world.bentobox.limits.objects; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import org.bukkit.Material; +import org.bukkit.block.Block; import org.bukkit.entity.EntityType; import com.google.gson.annotations.Expose; +import dev.lone.itemsadder.api.CustomBlock; import world.bentobox.bentobox.database.objects.DataObject; import world.bentobox.bentobox.database.objects.Table; @@ -28,6 +31,9 @@ public class IslandBlockCount implements DataObject { @Expose private Map blockCounts = new EnumMap<>(Material.class); + @Expose + private Map customBlockCounts = new HashMap<>(); + private boolean changed; /** @@ -36,6 +42,8 @@ public class IslandBlockCount implements DataObject { @Expose private Map blockLimits = new EnumMap<>(Material.class); @Expose + private Map customBlockLimits = new HashMap<>(); + @Expose private Map entityLimits = new EnumMap<>(EntityType.class); @Expose private Map entityGroupLimits = new HashMap<>(); @@ -120,6 +128,28 @@ public class IslandBlockCount implements DataObject { setChanged(); } + /** + * Add a custom block to the count + * @param block - custom block + */ + public void add(Block block) { + CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); + customBlockCounts.merge(Objects.requireNonNull(customBlock).getNamespacedID(), 1, Integer::sum); + setChanged(); + } + + /** + * Remove a custom block from the count + * @param block - block + */ + public void remove(Block block) { + CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); + String id = Objects.requireNonNull(customBlock).getNamespacedID(); + customBlockCounts.put(id, blockCounts.getOrDefault(id, 0) - 1); + blockCounts.values().removeIf(v -> v <= 0); + setChanged(); + } + /** * Check if this material is at or over a limit * @param material - block material @@ -129,6 +159,18 @@ public class IslandBlockCount implements DataObject { public boolean isAtLimit(Material material, int limit) { return blockCounts.getOrDefault(material, 0) >= limit + this.getBlockLimitOffset(material); } + + /** + * Check if this custom block is at or over a limit + * @param block - block + * @param limit - limit to check + * @return true if count is >= limit + */ + public boolean isAtLimit(Block block, int limit) { + CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); + String id = Objects.requireNonNull(customBlock).getNamespacedID(); + return customBlockCounts.getOrDefault(id, 0) >= limit; // TODO Add a permission offset + } /** * Check if no more of this material can be added to this island @@ -139,10 +181,28 @@ public class IslandBlockCount implements DataObject { // Check island limits first return blockLimits.containsKey(m) && blockCounts.getOrDefault(m, 0) >= getBlockLimit(m) + this.getBlockLimitOffset(m); } + + /** + * Check if no more of this custom block can be added to this island + * @param block - block + * @return true if no more block can be added + */ + public boolean isAtLimit(Block block) { + CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); + String id = Objects.requireNonNull(customBlock).getNamespacedID(); + // Check island limits first + return customBlockLimits.containsKey(id) && customBlockCounts.getOrDefault(id, 0) >= getBlockLimit(block); // TODO add perm offset + } public boolean isBlockLimited(Material m) { return blockLimits.containsKey(m); } + + public boolean isCustomBlockLimited(Block block) { + CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); + String id = Objects.requireNonNull(customBlock).getNamespacedID(); + return customBlockLimits.containsKey(id); + } /** * @return the blockLimits @@ -167,6 +227,17 @@ public class IslandBlockCount implements DataObject { public int getBlockLimit(Material m) { return blockLimits.getOrDefault(m, -1); } + + /** + * Get the custom block limit for this material for this island + * @param block - block + * @return limit or -1 for unlimited + */ + public int getBlockLimit(Block block) { + CustomBlock customBlock = CustomBlock.byAlreadyPlaced(block); + String id = Objects.requireNonNull(customBlock).getNamespacedID(); + return customBlockLimits.getOrDefault(id, -1); + } /** * Get the block offset for this material for this island @@ -186,6 +257,16 @@ public class IslandBlockCount implements DataObject { blockLimits.put(m, limit); setChanged(); } + + /** + * Set the custom block limit for this island + * @param id - namespaced id + * @param limit - maximum number allowed + */ + public void setCustomBlockLimit(String id, int limit) { + customBlockLimits.put(id, limit); + setChanged(); + } /** * @return the gameMode @@ -385,4 +466,32 @@ public class IslandBlockCount implements DataObject { public void setEntityGroupLimitsOffset(String name, Integer entityGroupLimitsOffset) { getEntityGroupLimitsOffset().put(name, entityGroupLimitsOffset); } + + /** + * @return the customBlockCounts + */ + public Map getCustomBlockCounts() { + return customBlockCounts; + } + + /** + * @param customBlockCounts the customBlockCounts to set + */ + public void setCustomBlockCounts(Map customBlockCounts) { + this.customBlockCounts = customBlockCounts; + } + + /** + * @return the customBlockLimits + */ + public Map getCustomBlockLimits() { + return customBlockLimits; + } + + /** + * @param customBlockLimits the customBlockLimits to set + */ + public void setCustomBlockLimits(Map customBlockLimits) { + this.customBlockLimits = customBlockLimits; + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 30f8c61..f872393 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -26,6 +26,13 @@ async-golums: true # These limits apply to every game mode world blocklimits: HOPPER: 10 + +# Custom block limits +# Requires use of the ItemsAdder plugin. Custom blocks are currently only supported globally and not on a per-world basis. +# Limits require the full namespace name of the custom block material +customblocklimits: + # "iafestivities:christmas/christmas_tree/green_orb": 5 + # This section is for world-specific limits and overrides the general limit # Specify each world you want to limit individually (including nether and end worlds) # If these worlds are not covered by the game modes above, nothing will happen diff --git a/src/test/java/bentobox/addon/limits/listeners/JoinListenerTest.java b/src/test/java/bentobox/addon/limits/listeners/JoinListenerTest.java index c0ca01a..109d1b1 100644 --- a/src/test/java/bentobox/addon/limits/listeners/JoinListenerTest.java +++ b/src/test/java/bentobox/addon/limits/listeners/JoinListenerTest.java @@ -400,7 +400,7 @@ public class JoinListenerTest { @Test public void testOnPlayerJoinWithPermLimitsMultiPermsSameMaterial() { // IBC - set the block limit for STONE to be 25 already - when(ibc.getBlockLimit(any())).thenReturn(25); + when(ibc.getBlockLimit(any(Material.class))).thenReturn(25); Set perms = new HashSet<>(); PermissionAttachmentInfo permAtt = mock(PermissionAttachmentInfo.class); when(permAtt.getPermission()).thenReturn("bskyblock.island.limit.STONE.24");