diff --git a/pom.xml b/pom.xml index bf1a939..d113aaa 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 world.bentobox limits - 0.0.3-SNAPSHOT + 0.1.0-SNAPSHOT addon-limits An add-on for BentoBox that limits blocks and entities on islands. https://github.com/BentoBoxWorld/addon-level diff --git a/src/main/java/bentobox/addon/limits/Limits.java b/src/main/java/bentobox/addon/limits/Limits.java index b26c8c5..4d399c3 100644 --- a/src/main/java/bentobox/addon/limits/Limits.java +++ b/src/main/java/bentobox/addon/limits/Limits.java @@ -6,7 +6,7 @@ import java.util.stream.Collectors; import org.bukkit.World; import bentobox.addon.limits.listeners.BlockLimitsListener; -import bentobox.addon.limits.listeners.EntityLimitsListener; +import bentobox.addon.limits.listeners.JoinListener; import world.bentobox.bentobox.api.addons.Addon; import world.bentobox.bentobox.api.addons.GameModeAddon; @@ -19,15 +19,11 @@ import world.bentobox.bentobox.api.addons.GameModeAddon; public class Limits extends Addon { private Settings settings; - private EntityLimitsListener listener; - private List worlds; + private List gameModes; private BlockLimitsListener blockLimitListener; @Override public void onDisable(){ - if (listener != null) { - worlds.forEach(listener::disable); - } if (blockLimitListener != null) { blockLimitListener.save(); } @@ -40,16 +36,14 @@ public class Limits extends Addon { // Load settings settings = new Settings(this); // Register worlds from GameModes - worlds = getPlugin().getAddonsManager().getGameModeAddons().stream() + gameModes = getPlugin().getAddonsManager().getGameModeAddons().stream() .filter(gm -> settings.getGameModes().contains(gm.getDescription().getName())) - .map(GameModeAddon::getOverWorld) .collect(Collectors.toList()); - worlds.forEach(w -> log("Limits will apply to " + w.getName())); + gameModes.forEach(w -> log("Limits will apply to " + w.getDescription().getName())); // Register listener - //listener = new EntityLimitsListener(this); - //registerListener(listener); blockLimitListener = new BlockLimitsListener(this); registerListener(blockLimitListener); + registerListener(new JoinListener(this)); // Done } @@ -60,5 +54,44 @@ public class Limits extends Addon { return settings; } + /** + * @return the gameModes + */ + public List getGameModes() { + return gameModes; + } + /** + * @return the blockLimitListener + */ + public BlockLimitsListener getBlockLimitListener() { + return blockLimitListener; + } + + /** + * Checks if this world is covered by the activated game modes + * @param world - world + * @return true or false + */ + public boolean inGameModeWorld(World world) { + return gameModes.stream().anyMatch(gm -> gm.inWorld(world)); + } + + /** + * Get the name of the game mode for this world + * @param world - world + * @return game mode name or empty string if none + */ + public String getGameMode(World world) { + return gameModes.stream().filter(gm -> gm.inWorld(world)).findFirst().map(gm -> gm.getDescription().getName()).orElse(""); + } + + /** + * Check if any of the game modes covered have this name + * @param gameMode - name of game mode + * @return true or false + */ + public boolean isCoveredGameMode(String gameMode) { + return gameModes.stream().anyMatch(gm -> gm.getDescription().getName().equals(gameMode)); + } } diff --git a/src/main/java/bentobox/addon/limits/Settings.java b/src/main/java/bentobox/addon/limits/Settings.java index ba074d7..ca72557 100644 --- a/src/main/java/bentobox/addon/limits/Settings.java +++ b/src/main/java/bentobox/addon/limits/Settings.java @@ -17,7 +17,7 @@ public class Settings { public Settings(Limits addon) { // GameModes - gameModes = addon.getConfig().getStringList("game-modes"); + gameModes = addon.getConfig().getStringList("gamemodes"); ConfigurationSection el = addon.getConfig().getConfigurationSection("entitylimits"); if (el != null) { diff --git a/src/main/java/bentobox/addon/limits/listeners/BlockLimitsListener.java b/src/main/java/bentobox/addon/limits/listeners/BlockLimitsListener.java index 1451835..ecbbb44 100644 --- a/src/main/java/bentobox/addon/limits/listeners/BlockLimitsListener.java +++ b/src/main/java/bentobox/addon/limits/listeners/BlockLimitsListener.java @@ -3,7 +3,10 @@ */ package bentobox.addon.limits.listeners; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.bukkit.Bukkit; @@ -47,6 +50,7 @@ public class BlockLimitsListener implements Listener { * Save every 10 blocks of change */ private static final Integer CHANGE_LIMIT = 9; + private static final List DO_NOT_COUNT = Arrays.asList(Material.LAVA, Material.WATER, Material.AIR, Material.FIRE, Material.END_PORTAL, Material.NETHER_PORTAL); private Limits addon; private Map countMap = new HashMap<>(); private Map saveMap = new HashMap<>(); @@ -57,7 +61,18 @@ public class BlockLimitsListener implements Listener { public BlockLimitsListener(Limits addon) { this.addon = addon; handler = new Database<>(addon, IslandBlockCount.class); - handler.loadObjects().forEach(ibc -> countMap.put(ibc.getUniqueId(), ibc)); + List toBeDeleted = new ArrayList<>(); + handler.loadObjects().forEach(ibc -> { + // Clean up + if (addon.isCoveredGameMode(ibc.getGameMode())) { + ibc.getBlockCount().keySet().removeIf(DO_NOT_COUNT::contains); + // Store + countMap.put(ibc.getUniqueId(), ibc); + } else { + toBeDeleted.add(ibc.getUniqueId()); + } + }); + toBeDeleted.forEach(handler::deleteID); loadAllLimits(); } @@ -73,12 +88,11 @@ public class BlockLimitsListener implements Listener { } // Load specific worlds - if (addon.getConfig().isConfigurationSection("worlds")) { ConfigurationSection worlds = addon.getConfig().getConfigurationSection("worlds"); for (String worldName : worlds.getKeys(false)) { World world = Bukkit.getWorld(worldName); - if (world != null && addon.getPlugin().getIWM().inWorld(world)) { + if (world != null && addon.inGameModeWorld(world)) { addon.log("Loading limits for " + world.getName()); limitMap.putIfAbsent(world, new HashMap<>()); ConfigurationSection matsConfig = worlds.getConfigurationSection(worldName); @@ -98,7 +112,7 @@ public class BlockLimitsListener implements Listener { Map mats = new HashMap<>(); for (String material : cs.getKeys(false)) { Material mat = Material.getMaterial(material); - if (mat != null && mat.isBlock()) { + if (mat != null && mat.isBlock() && !DO_NOT_COUNT.contains(mat)) { mats.put(mat, cs.getInt(material)); addon.log("Limit " + mat + " to " + cs.getInt(material)); } else { @@ -203,10 +217,18 @@ public class BlockLimitsListener implements Listener { * @return limit amount if over limit, or -1 if no limitation */ private int process(Block b, boolean add, Material changeTo) { + if (DO_NOT_COUNT.contains(b.getType()) || !addon.inGameModeWorld(b.getWorld())) { + return -1; + } // Check if on island return addon.getIslands().getIslandAt(b.getLocation()).map(i -> { String id = i.getUniqueId(); - countMap.putIfAbsent(id, new IslandBlockCount(id)); + String gameMode = addon.getGameMode(b.getWorld()); + if (gameMode.isEmpty()) { + // Invalid world + return -1; + } + countMap.putIfAbsent(id, new IslandBlockCount(id, gameMode)); saveMap.putIfAbsent(id, 0); if (add) { // Check limit @@ -219,7 +241,7 @@ public class BlockLimitsListener implements Listener { } else { if (countMap.containsKey(id)) { // Check for changes - if (!changeTo.equals(b.getType()) && changeTo.isBlock()) { + if (!changeTo.equals(b.getType()) && changeTo.isBlock() && !DO_NOT_COUNT.contains(changeTo)) { // Check limit int limit = checkLimit(b.getWorld(), changeTo, id); if (limit > -1) { @@ -247,22 +269,18 @@ public class BlockLimitsListener implements Listener { * @return limit amount if at limit */ private int checkLimit(World w, Material m, String id) { - // Check specific world first + // Check island limits + IslandBlockCount island = countMap.get(id); + if (island.isBlockLimited(m)) { + return island.isAtLimit(m) ? island.getBlockLimit(m) : -1; + } + // Check specific world limits if (limitMap.containsKey(w) && limitMap.get(w).containsKey(m)) { // Material is overridden in world - if (countMap.get(id).isAtLimit(m, limitMap.get(w).get(m))) { - return limitMap.get(w).get(m); - } else { - // No limit - return -1; - } + return island.isAtLimit(m, limitMap.get(w).get(m)) ? limitMap.get(w).get(m) : -1; } // Check default limit map - if (defaultLimitMap.containsKey(m) - && countMap - .get(id) - .isAtLimit(m, - defaultLimitMap.get(m))) { + if (defaultLimitMap.containsKey(m) && island.isAtLimit(m, defaultLimitMap.get(m))) { return defaultLimitMap.get(m); } // No limit @@ -282,4 +300,24 @@ public class BlockLimitsListener implements Listener { } } + + /** + * Set the island block count values + * @param islandId - island unique id + * @param ibc - island block count + */ + public void setIsland(String islandId, IslandBlockCount ibc) { + countMap.put(islandId, ibc); + handler.saveObject(ibc); + } + + /** + * Get the island block count + * @param islandId - island unique id + * @return island block count or null if there is none yet + */ + public IslandBlockCount getIsland(String islandId) { + return countMap.get(islandId); + } + } diff --git a/src/main/java/bentobox/addon/limits/listeners/JoinListener.java b/src/main/java/bentobox/addon/limits/listeners/JoinListener.java new file mode 100644 index 0000000..7c1efdc --- /dev/null +++ b/src/main/java/bentobox/addon/limits/listeners/JoinListener.java @@ -0,0 +1,86 @@ +package bentobox.addon.limits.listeners; + +import java.util.Locale; + +import org.apache.commons.lang.math.NumberUtils; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.permissions.PermissionAttachmentInfo; + +import bentobox.addon.limits.Limits; +import bentobox.addon.limits.objects.IslandBlockCount; + +/** + * Sets block limits based on player permission + * @author tastybento + * + */ +public class JoinListener implements Listener { + + private Limits addon; + + public JoinListener(Limits addon) { + this.addon = addon; + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onPlayerJoin(PlayerJoinEvent e) { + // Check if player has any islands in the game modes + addon.getGameModes().forEach(gm -> { + if (addon.getIslands().hasIsland(gm.getOverWorld(), e.getPlayer().getUniqueId())) { + String islandId = addon.getIslands().getIsland(gm.getOverWorld(), e.getPlayer().getUniqueId()).getUniqueId(); + checkPerms(e.getPlayer(), gm.getPermissionPrefix() + "island.limit.", islandId, gm.getDescription().getName()); + } + }); + } + + private boolean checkPerms(Player player, String permissionPrefix, String islandId, String gameMode) { + IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(islandId); + int limit = -1; + for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) { + if (perms.getPermission().startsWith(permissionPrefix)) { + // Get the Material + String[] split = perms.getPermission().split("\\."); + if (split.length != 5) { + logError(player.getName(), perms.getPermission(), "format must be " + permissionPrefix + "MATERIAL.NUMBER"); + return false; + } + Material m = Material.getMaterial(split[3].toUpperCase(Locale.ENGLISH)); + if (m == null) { + logError(player.getName(), perms.getPermission(), split[3].toUpperCase(Locale.ENGLISH) + " is not a valid material"); + return false; + } + // Get the max value should there be more than one + if (perms.getPermission().contains(permissionPrefix + ".*")) { + logError(player.getName(), perms.getPermission(), "wildcards are not allowed"); + return false; + } + if (!NumberUtils.isDigits(split[4])) { + logError(player.getName(), perms.getPermission(), "the last part MUST be a number!"); + } else { + limit = Math.max(limit, Integer.valueOf(split[4])); + // Set the limit + if (ibc == null) { + ibc = new IslandBlockCount(islandId, gameMode); + } + ibc.setBlockLimit(m, limit); + } + } + } + // If any changes have been made then store it + if (ibc != null) { + addon.getBlockLimitListener().setIsland(islandId, ibc); + } + return true; + + } + + private void logError(String name, String perm, String error) { + addon.logError("Player " + name + " has permission: '" + perm + " but " + error + " Ignoring..."); + } + +} diff --git a/src/main/java/bentobox/addon/limits/objects/IslandBlockCount.java b/src/main/java/bentobox/addon/limits/objects/IslandBlockCount.java index 25e6f54..73ae1ba 100644 --- a/src/main/java/bentobox/addon/limits/objects/IslandBlockCount.java +++ b/src/main/java/bentobox/addon/limits/objects/IslandBlockCount.java @@ -21,14 +21,21 @@ public class IslandBlockCount implements DataObject { @Expose private String uniqueId = ""; + @Expose + private String gameMode = ""; + @Expose private Map blockCount = new HashMap<>(); + @Expose + private Map blockLimits = new HashMap<>(); + // Required for YAML database public IslandBlockCount() {} - public IslandBlockCount(String uniqueId2) { + public IslandBlockCount(String uniqueId2, String gameMode2) { this.uniqueId = uniqueId2; + this.gameMode = gameMode2; } /* (non-Javadoc) @@ -87,4 +94,68 @@ public class IslandBlockCount implements DataObject { public boolean isAtLimit(Material material, int limit) { return blockCount.getOrDefault(material, 0) >= limit; } + + /** + * Check if no more of this material can be added to this island + * @param m - material + * @return true if no more material can be added + */ + public boolean isAtLimit(Material m) { + // Check island limits first + return blockLimits.containsKey(m) ? blockCount.getOrDefault(m, 0) >= blockLimits.get(m) : false; + } + + public boolean isBlockLimited(Material m) { + return blockLimits.containsKey(m); + } + + /** + * @return the blockLimits + */ + public Map getBlockLimits() { + return blockLimits; + } + + /** + * @param blockLimits the blockLimits to set + */ + public void setBlockLimits(Map blockLimits) { + this.blockLimits = blockLimits; + } + + /** + * Get the block limit for this material for this island + * @param m - material + * @return limit or -1 for unlimited + */ + public Integer getBlockLimit(Material m) { + return blockLimits.getOrDefault(m, -1); + } + + /** + * Set the block limit for this material for this island + * @param m - material + * @param limit - maximum number allowed + */ + public void setBlockLimit(Material m, int limit) { + blockLimits.put(m, limit); + } + + /** + * @return the gameMode + */ + public String getGameMode() { + return gameMode; + } + + public boolean isGameMode(String gameMode) { + return this.gameMode.equals(gameMode); + } + + /** + * @param gameMode the gameMode to set + */ + public void setGameMode(String gameMode) { + this.gameMode = gameMode; + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 7fa00c8..036b1c2 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,3 +1,9 @@ +# Game Modes +gamemodes: +- AcidIsland +- BSkyBlock +- CaveBock + # General block limiting # Use this section to limit how many blocks can be added to an island. # 0 means the item will be blocked from placement completely.