From aa63b412c866cbaa9d764da9fcd4500aa71b0ea2 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 9 Feb 2019 10:07:16 -0800 Subject: [PATCH] Added limits command. https://github.com/BentoBoxWorld/addon-limits/issues/3 --- .../java/bentobox/addon/limits/Limits.java | 12 ++- .../addon/limits/commands/AdminCommand.java | 78 +++++++++++++++++++ .../addon/limits/commands/LimitPanel.java | 67 ++++++++++++++++ .../addon/limits/commands/PlayerCommand.java | 55 +++++++++++++ .../limits/listeners/BlockLimitsListener.java | 68 +++++++++++----- src/main/resources/addon.yml | 7 ++ src/main/resources/locales/en-US.yml | 16 ++++ 7 files changed, 281 insertions(+), 22 deletions(-) create mode 100644 src/main/java/bentobox/addon/limits/commands/AdminCommand.java create mode 100644 src/main/java/bentobox/addon/limits/commands/LimitPanel.java create mode 100644 src/main/java/bentobox/addon/limits/commands/PlayerCommand.java diff --git a/src/main/java/bentobox/addon/limits/Limits.java b/src/main/java/bentobox/addon/limits/Limits.java index 4d399c3..ac265ad 100644 --- a/src/main/java/bentobox/addon/limits/Limits.java +++ b/src/main/java/bentobox/addon/limits/Limits.java @@ -5,6 +5,8 @@ import java.util.stream.Collectors; import org.bukkit.World; +import bentobox.addon.limits.commands.AdminCommand; +import bentobox.addon.limits.commands.PlayerCommand; import bentobox.addon.limits.listeners.BlockLimitsListener; import bentobox.addon.limits.listeners.JoinListener; import world.bentobox.bentobox.api.addons.Addon; @@ -39,7 +41,14 @@ public class Limits extends Addon { gameModes = getPlugin().getAddonsManager().getGameModeAddons().stream() .filter(gm -> settings.getGameModes().contains(gm.getDescription().getName())) .collect(Collectors.toList()); - gameModes.forEach(w -> log("Limits will apply to " + w.getDescription().getName())); + gameModes.forEach(gm -> + { + // Register commands + gm.getAdminCommand().ifPresent(a -> new AdminCommand(this, a)); + gm.getPlayerCommand().ifPresent(a -> new PlayerCommand(this, a)); + log("Limits will apply to " + gm.getDescription().getName()); + } + ); // Register listener blockLimitListener = new BlockLimitsListener(this); registerListener(blockLimitListener); @@ -94,4 +103,5 @@ public class Limits extends Addon { public boolean isCoveredGameMode(String gameMode) { return gameModes.stream().anyMatch(gm -> gm.getDescription().getName().equals(gameMode)); } + } diff --git a/src/main/java/bentobox/addon/limits/commands/AdminCommand.java b/src/main/java/bentobox/addon/limits/commands/AdminCommand.java new file mode 100644 index 0000000..a143b08 --- /dev/null +++ b/src/main/java/bentobox/addon/limits/commands/AdminCommand.java @@ -0,0 +1,78 @@ +/** + * + */ +package bentobox.addon.limits.commands; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import bentobox.addon.limits.Limits; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.util.Util; + +/** + * Admin command for limits + * @author tastybento + * + */ +public class AdminCommand extends CompositeCommand { + + private Limits addon; + + /** + * Top level command + * @param addon + */ + public AdminCommand(Limits addon, CompositeCommand parent) { + super(parent, "limits"); + this.addon = addon; + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup() + */ + @Override + public void setup() { + this.setPermission("limits.admin.limits"); + this.setOnlyPlayer(true); + this.setParametersHelp("admin.limits.parameters"); + this.setDescription("admin.limits.description"); + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + if (args.size() == 1) { + // Asking for another player's limits + // Convert name to a UUID + final UUID playerUUID = getPlugin().getPlayers().getUUID(args.get(0)); + if (playerUUID == null) { + user.sendMessage("general.errors.unknown-player", args.get(0)); + return true; + } else { + new LimitPanel(addon).showLimits(getWorld(), user, playerUUID); + } + return true; + } else { + showHelp(this, user); + return false; + } + } + + @Override + public Optional> tabComplete(User user, String alias, List args) { + String lastArg = !args.isEmpty() ? args.get(args.size()-1) : ""; + if (args.isEmpty()) { + // Don't show every player on the server. Require at least the first letter + return Optional.empty(); + } + List options = new ArrayList<>(Util.getOnlinePlayerList(user)); + return Optional.of(Util.tabLimit(options, lastArg)); + } + +} diff --git a/src/main/java/bentobox/addon/limits/commands/LimitPanel.java b/src/main/java/bentobox/addon/limits/commands/LimitPanel.java new file mode 100644 index 0000000..370a7b0 --- /dev/null +++ b/src/main/java/bentobox/addon/limits/commands/LimitPanel.java @@ -0,0 +1,67 @@ +/** + * + */ +package bentobox.addon.limits.commands; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; + +import org.bukkit.Material; +import org.bukkit.World; + +import bentobox.addon.limits.Limits; +import bentobox.addon.limits.objects.IslandBlockCount; +import world.bentobox.bentobox.api.localization.TextVariables; +import world.bentobox.bentobox.api.panels.builders.PanelBuilder; +import world.bentobox.bentobox.api.panels.builders.PanelItemBuilder; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.bentobox.database.objects.Island; +import world.bentobox.bentobox.util.Util; + +/** + * Shows a panel of the blocks that are limited and their status + * @author tastybento + * + */ +public class LimitPanel { + + private Limits addon; + + /** + * @param addon - limit addon + */ + public LimitPanel(Limits addon) { + this.addon = addon; + } + + public void showLimits(World world, User user, UUID target) { + PanelBuilder pb = new PanelBuilder().name(user.getTranslation(world, "limits.panel-title")).user(user); + // Get the island for the target + Island island = addon.getIslands().getIsland(world, target); + if (island == null) { + user.sendMessage("general.errors.player-has-no-island"); + return; + } + IslandBlockCount ibc = addon.getBlockLimitListener().getIsland(island.getUniqueId()); + Map matLimits = addon.getBlockLimitListener().getMaterialLimits(world, island.getUniqueId()); + if (matLimits.isEmpty()) { + user.sendMessage("island.limits.no-limits"); + return; + } + for (Entry en : matLimits.entrySet()) { + PanelItemBuilder pib = new PanelItemBuilder(); + pib.name(Util.prettifyText(en.getKey().toString())); + pib.icon(en.getKey()); + int count = ibc == null ? 0 : ibc.getBlockCount().getOrDefault(en.getKey(), 0); + String color = count >= en.getValue() ? user.getTranslation("island.limits.max-color") : user.getTranslation("island.limits.regular-color"); + pib.description(color + + user.getTranslation("island.limits.block-limit-syntax", + TextVariables.NUMBER, String.valueOf(count), + "[limit]", String.valueOf(en.getValue()))); + pb.item(pib.build()); + } + pb.build(); + } + +} diff --git a/src/main/java/bentobox/addon/limits/commands/PlayerCommand.java b/src/main/java/bentobox/addon/limits/commands/PlayerCommand.java new file mode 100644 index 0000000..6a47ebc --- /dev/null +++ b/src/main/java/bentobox/addon/limits/commands/PlayerCommand.java @@ -0,0 +1,55 @@ +/** + * + */ +package bentobox.addon.limits.commands; + +import java.util.List; + +import bentobox.addon.limits.Limits; +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; + +/** + * User command for limits + * @author tastybento + * + */ +public class PlayerCommand extends CompositeCommand { + + private Limits addon; + + /** + * Top level command + * @param addon + */ + public PlayerCommand(Limits addon, CompositeCommand parent) { + super(parent, "limits"); + this.addon = addon; + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup() + */ + @Override + public void setup() { + this.setPermission("limits.player.limits"); + this.setOnlyPlayer(true); + this.setParametersHelp("island.limits.parameters"); + this.setDescription("island.limits.description"); + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + if (!args.isEmpty()) { + showHelp(this, user); + return false; + } else { + new LimitPanel(addon).showLimits(getWorld(), user, user.getUniqueId()); + return true; + } + } + +} diff --git a/src/main/java/bentobox/addon/limits/listeners/BlockLimitsListener.java b/src/main/java/bentobox/addon/limits/listeners/BlockLimitsListener.java index ecbbb44..eb364b5 100644 --- a/src/main/java/bentobox/addon/limits/listeners/BlockLimitsListener.java +++ b/src/main/java/bentobox/addon/limits/listeners/BlockLimitsListener.java @@ -46,16 +46,20 @@ import world.bentobox.bentobox.util.Util; */ public class BlockLimitsListener implements Listener { + /** + * Blocks that are not counted + */ + public static final List DO_NOT_COUNT = Arrays.asList(Material.LAVA, Material.WATER, Material.AIR, Material.FIRE, Material.END_PORTAL, Material.NETHER_PORTAL); + /** * 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 islandCountMap = new HashMap<>(); private Map saveMap = new HashMap<>(); private Database handler; - private Map> limitMap = new HashMap<>(); + private Map> worldLimitMap = new HashMap<>(); private Map defaultLimitMap = new HashMap<>(); public BlockLimitsListener(Limits addon) { @@ -67,7 +71,7 @@ public class BlockLimitsListener implements Listener { if (addon.isCoveredGameMode(ibc.getGameMode())) { ibc.getBlockCount().keySet().removeIf(DO_NOT_COUNT::contains); // Store - countMap.put(ibc.getUniqueId(), ibc); + islandCountMap.put(ibc.getUniqueId(), ibc); } else { toBeDeleted.add(ibc.getUniqueId()); } @@ -94,9 +98,9 @@ public class BlockLimitsListener implements Listener { World world = Bukkit.getWorld(worldName); if (world != null && addon.inGameModeWorld(world)) { addon.log("Loading limits for " + world.getName()); - limitMap.putIfAbsent(world, new HashMap<>()); + worldLimitMap.putIfAbsent(world, new HashMap<>()); ConfigurationSection matsConfig = worlds.getConfigurationSection(worldName); - limitMap.put(world, loadLimits(matsConfig)); + worldLimitMap.put(world, loadLimits(matsConfig)); } } } @@ -126,7 +130,7 @@ public class BlockLimitsListener implements Listener { * Save the count database completely */ public void save() { - countMap.values().forEach(handler::saveObject); + islandCountMap.values().forEach(handler::saveObject); } // Player-related events @@ -228,7 +232,7 @@ public class BlockLimitsListener implements Listener { // Invalid world return -1; } - countMap.putIfAbsent(id, new IslandBlockCount(id, gameMode)); + islandCountMap.putIfAbsent(id, new IslandBlockCount(id, gameMode)); saveMap.putIfAbsent(id, 0); if (add) { // Check limit @@ -236,10 +240,10 @@ public class BlockLimitsListener implements Listener { if (limit > -1) { return limit; } - countMap.get(id).add(b.getType()); + islandCountMap.get(id).add(b.getType()); saveMap.merge(id, 1, Integer::sum); } else { - if (countMap.containsKey(id)) { + if (islandCountMap.containsKey(id)) { // Check for changes if (!changeTo.equals(b.getType()) && changeTo.isBlock() && !DO_NOT_COUNT.contains(changeTo)) { // Check limit @@ -247,14 +251,14 @@ public class BlockLimitsListener implements Listener { if (limit > -1) { return limit; } - countMap.get(id).add(changeTo); + islandCountMap.get(id).add(changeTo); } - countMap.get(id).remove(b.getType()); + islandCountMap.get(id).remove(b.getType()); saveMap.merge(id, 1, Integer::sum); } } if (saveMap.get(id) > CHANGE_LIMIT) { - handler.saveObject(countMap.get(id)); + handler.saveObject(islandCountMap.get(id)); saveMap.remove(id); } return -1; @@ -266,18 +270,18 @@ public class BlockLimitsListener implements Listener { * @param w - world * @param m - material * @param id - island id - * @return limit amount if at limit + * @return limit amount if at limit or -1 if no limit */ - private int checkLimit(World w, Material m, String id) { + public int checkLimit(World w, Material m, String id) { // Check island limits - IslandBlockCount island = countMap.get(id); + IslandBlockCount island = islandCountMap.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)) { + if (worldLimitMap.containsKey(w) && worldLimitMap.get(w).containsKey(m)) { // Material is overridden in world - return island.isAtLimit(m, limitMap.get(w).get(m)) ? limitMap.get(w).get(m) : -1; + return island.isAtLimit(m, worldLimitMap.get(w).get(m)) ? worldLimitMap.get(w).get(m) : -1; } // Check default limit map if (defaultLimitMap.containsKey(m) && island.isAtLimit(m, defaultLimitMap.get(m))) { @@ -287,13 +291,35 @@ public class BlockLimitsListener implements Listener { return -1; } + /** + * Gets an aggregate map of the limits for this island + * @param w - world + * @param id - island id + * @return map of limits for materials + */ + public Map getMaterialLimits(World w, String id) { + // Merge limits + Map result = new HashMap<>(); + // Default + defaultLimitMap.forEach((k,v) -> result.put(k, v)); + // World + if (worldLimitMap.containsKey(w)) { + worldLimitMap.get(w).forEach((k,v) -> result.put(k, v)); + } + // Island + if (islandCountMap.containsKey(id)) { + islandCountMap.get(id).getBlockLimits().forEach((k,v) -> result.put(k, v)); + } + return result; + } + /** * Removes island from the database * @param e - island delete event */ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onIslandDelete(IslandDeleteEvent e) { - countMap.remove(e.getIsland().getUniqueId()); + islandCountMap.remove(e.getIsland().getUniqueId()); saveMap.remove(e.getIsland().getUniqueId()); if (handler.objectExists(e.getIsland().getUniqueId())) { handler.deleteID(e.getIsland().getUniqueId()); @@ -307,7 +333,7 @@ public class BlockLimitsListener implements Listener { * @param ibc - island block count */ public void setIsland(String islandId, IslandBlockCount ibc) { - countMap.put(islandId, ibc); + islandCountMap.put(islandId, ibc); handler.saveObject(ibc); } @@ -317,7 +343,7 @@ public class BlockLimitsListener implements Listener { * @return island block count or null if there is none yet */ public IslandBlockCount getIsland(String islandId) { - return countMap.get(islandId); + return islandCountMap.get(islandId); } } diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml index b948674..4c8178b 100755 --- a/src/main/resources/addon.yml +++ b/src/main/resources/addon.yml @@ -6,3 +6,10 @@ authors: tastybento softdepend: AcidIsland, BSkyBlock, CaveBlock, SkyGrid +permissions: + limits.player.limits: + description: Player can use limits command + default: true + limits.admin.limits: + description: Player can use admin limits command + default: op diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 46a1013..ce2900f 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -5,4 +5,20 @@ limits: hit-limit: "&c[material] limited to [number]!" + panel-title: "Island limits" + +admin: + limits: + parameters: "" + description: "show the island limits for player" + +island: + limits: + parameters: "" + description: "show your island limits" + max-color: "&c" + regular-color: "&a" + block-limit-syntax: "[number]/[limit]" + no-limits: "&cNo limits set in this world" + \ No newline at end of file