From 626dea07fa10d647546872856f2272f6b66328ff Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Sat, 3 Nov 2012 08:51:03 +0100 Subject: [PATCH] Move range parser out of CommandPacket. Make a common command base. --- .../com/comphenix/protocol/CommandBase.java | 51 ++++ .../com/comphenix/protocol/CommandPacket.java | 269 +++++------------- .../comphenix/protocol/CommandProtocol.java | 38 +-- .../com/comphenix/protocol/RangeParser.java | 142 +++++++++ 4 files changed, 284 insertions(+), 216 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/CommandBase.java create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/RangeParser.java diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/CommandBase.java b/ProtocolLib/src/main/java/com/comphenix/protocol/CommandBase.java new file mode 100644 index 00000000..be72d60f --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/CommandBase.java @@ -0,0 +1,51 @@ +package com.comphenix.protocol; + +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +/** + * Base class for all our commands. + * + * @author Kristian + */ +abstract class CommandBase implements CommandExecutor { + + public static final String PERMISSION_ADMIN = "protocol.admin"; + + private String permission; + private String name; + private int minimumArgumentCount; + + public CommandBase(String permission, String name) { + this(permission, name, 0); + } + + public CommandBase(String permission, String name, int minimumArgumentCount) { + this.name = name; + this.permission = permission; + this.minimumArgumentCount = minimumArgumentCount; + } + + @Override + public final boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + // Make sure we're dealing with the correct command + if (!command.getName().equalsIgnoreCase(name)) { + return false; + } + if (!sender.hasPermission(permission)) { + sender.sendMessage(ChatColor.RED + "You haven't got permission to run this command."); + return true; + } + + // Check argument length + if (args != null && args.length >= minimumArgumentCount) { + return handleCommand(sender, args); + } else { + return false; + } + } + + protected abstract boolean handleCommand(CommandSender sender, String[] args); +} diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java b/ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java index bee9c430..46a2715d 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/CommandPacket.java @@ -12,8 +12,6 @@ import net.minecraft.server.Packet; import net.sf.cglib.proxy.Factory; import org.bukkit.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; @@ -38,7 +36,8 @@ import com.google.common.collect.Ranges; * * @author Kristian */ -class CommandPacket implements CommandExecutor { +class CommandPacket extends CommandBase { + private interface DetailedPacketListener extends PacketListener { /** * Determine whether or not the given packet listener is detailed or not. @@ -73,6 +72,7 @@ class CommandPacket implements CommandExecutor { private AbstractIntervalTree serverListeners = createTree(ConnectionSide.SERVER_SIDE); public CommandPacket(Plugin plugin, Logger logger, ErrorReporter reporter, ProtocolManager manager) { + super(CommandBase.PERMISSION_ADMIN, NAME, 1); this.plugin = plugin; this.logger = logger; this.reporter = reporter; @@ -163,86 +163,87 @@ class CommandPacket implements CommandExecutor { Usage: / add|remove client|server|both [ID start] [ID stop] [detailed] */ @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - // Make sure we're dealing with the correct command - if (!command.getName().equalsIgnoreCase(NAME)) - return false; - - // We need at least one argument - if (args != null && args.length > 0) { - try { - SubCommand subCommand = parseCommand(args, 0); - ConnectionSide side = parseSide(args, 1, ConnectionSide.BOTH); - - Integer lastIndex = args.length - 1; - Boolean detailed = parseBoolean(args, lastIndex); + protected boolean handleCommand(CommandSender sender, String[] args) { + try { + SubCommand subCommand = parseCommand(args, 0); + ConnectionSide side = parseSide(args, 1, ConnectionSide.BOTH); + + Integer lastIndex = args.length - 1; + Boolean detailed = parseBoolean(args, lastIndex); - // See if the last element is a boolean - if (detailed == null) { - detailed = false; - } else { - lastIndex--; - } - - // Make sure the packet IDs are valid - List> ranges = getRanges(args, 2, lastIndex, Ranges.closed(0, 255)); - - if (ranges.isEmpty()) { - // Use every packet ID - ranges.add(Ranges.closed(0, 255)); - } - - // Perform command - if (subCommand == SubCommand.ADD) { - for (Range range : ranges) { - DetailedPacketListener listener = addPacketListeners(side, range.lowerEndpoint(), range.upperEndpoint(), detailed); - sendMessageSilently(sender, ChatColor.BLUE + "Added listener " + getWhitelistInfo(listener)); - } - - } else if (subCommand == SubCommand.REMOVE) { - int count = 0; - - // Remove each packet listener - for (Range range : ranges) { - count += removePacketListeners(side, range.lowerEndpoint(), range.upperEndpoint(), detailed).size(); - } - - sendMessageSilently(sender, ChatColor.BLUE + "Fully removed " + count + " listeners."); - } else if (subCommand == SubCommand.NAMES) { - - Set named = getNamedPackets(side); - List messages = new ArrayList(); - - // Print the equivalent name of every given ID - for (Range range : ranges) { - for (int id : range.asSet(DiscreteDomains.integers())) { - if (named.contains(id)) { - messages.add(ChatColor.BLUE + "" + id + ": " + Packets.getDeclaredName(id)); - } - } - } - - // Convert to two rows - messages = getMessagesInRows(messages, 2, CHAT_WIDTH); - - // Print that - for (String message : messages) { - sendMessageSilently(sender, message); - } - } - - } catch (NumberFormatException e) { - sendMessageSilently(sender, ChatColor.RED + "Cannot parse number: " + e.getMessage()); - } catch (IllegalArgumentException e) { - sendMessageSilently(sender, ChatColor.RED + e.getMessage()); + // See if the last element is a boolean + if (detailed == null) { + detailed = false; + } else { + lastIndex--; } - return true; + // Make sure the packet IDs are valid + List> ranges = RangeParser.getRanges(args, 2, lastIndex, Ranges.closed(0, 255)); + + if (ranges.isEmpty()) { + // Use every packet ID + ranges.add(Ranges.closed(0, 255)); + } + + // Perform commands + if (subCommand == SubCommand.ADD) { + executeAddCommand(sender, side, detailed, ranges); + } else if (subCommand == SubCommand.REMOVE) { + executeRemoveCommand(sender, side, detailed, ranges); + } else if (subCommand == SubCommand.NAMES) { + executeNamesCommand(sender, side, ranges); + } + + } catch (NumberFormatException e) { + sendMessageSilently(sender, ChatColor.RED + "Cannot parse number: " + e.getMessage()); + } catch (IllegalArgumentException e) { + sendMessageSilently(sender, ChatColor.RED + e.getMessage()); } - return false; + return true; + } + + private void executeAddCommand(CommandSender sender, ConnectionSide side, Boolean detailed, List> ranges) { + for (Range range : ranges) { + DetailedPacketListener listener = addPacketListeners(side, range.lowerEndpoint(), range.upperEndpoint(), detailed); + sendMessageSilently(sender, ChatColor.BLUE + "Added listener " + getWhitelistInfo(listener)); + } } + private void executeRemoveCommand(CommandSender sender, ConnectionSide side, Boolean detailed, List> ranges) { + int count = 0; + + // Remove each packet listener + for (Range range : ranges) { + count += removePacketListeners(side, range.lowerEndpoint(), range.upperEndpoint(), detailed).size(); + } + + sendMessageSilently(sender, ChatColor.BLUE + "Fully removed " + count + " listeners."); + } + + private void executeNamesCommand(CommandSender sender, ConnectionSide side, List> ranges) { + Set named = getNamedPackets(side); + List messages = new ArrayList(); + + // Print the equivalent name of every given ID + for (Range range : ranges) { + for (int id : range.asSet(DiscreteDomains.integers())) { + if (named.contains(id)) { + messages.add(ChatColor.BLUE + "" + id + ": " + Packets.getDeclaredName(id)); + } + } + } + + // Convert to two rows + messages = getMessagesInRows(messages, 2, CHAT_WIDTH); + + // Print that + for (String message : messages) { + sendMessageSilently(sender, message); + } + } + private List getMessagesInRows(List messages, int rows, int totalWidth) { List output = new ArrayList(); int columnWidth = totalWidth / rows; @@ -260,123 +261,7 @@ class CommandPacket implements CommandExecutor { return output; } - - /** - * Parse ranges from an array of tokens. - * @param args - array of tokens. - * @param offset - beginning offset. - * @param legalRange - range of legal values. - * @return The parsed ranges. - */ - public static List> getRanges(String[] args, int offset, int lastIndex, Range legalRange) { - List tokens = tokenizeInput(args, offset, lastIndex); - List> ranges = new ArrayList>(); - for (int i = 0; i < tokens.size(); i++) { - Range range; - String current = tokens.get(i); - String next = i + 1 < tokens.size() ? tokens.get(i + 1) : null; - - // Yoda equality is done for null-safety - if ("-".equals(current)) { - throw new IllegalArgumentException("A hyphen must appear between two numbers."); - } else if ("-".equals(next)) { - if (i + 2 >= tokens.size()) - throw new IllegalArgumentException("Cannot form a range without a upper limit."); - - // This is a proper range - range = Ranges.closed(Integer.parseInt(current), Integer.parseInt(tokens.get(i + 2))); - ranges.add(range); - - // Skip the two next tokens - i += 2; - - } else { - // Just a single number - range = Ranges.singleton(Integer.parseInt(current)); - ranges.add(range); - } - - // Validate ranges - if (!legalRange.encloses(range)) { - throw new IllegalArgumentException(range + " is not in the range " + range.toString()); - } - } - - return simplify(ranges, legalRange.upperEndpoint()); - } - - /** - * Simplify a list of ranges by assuming a maximum value. - * @param ranges - the list of ranges to simplify. - * @param maximum - the maximum value (minimum value is always 0). - * @return A simplified list of ranges. - */ - private static List> simplify(List> ranges, int maximum) { - List> result = new ArrayList>(); - boolean[] set = new boolean[maximum + 1]; - int start = -1; - - // Set every ID - for (Range range : ranges) { - for (int id : range.asSet(DiscreteDomains.integers())) { - set[id] = true; - } - } - - // Generate ranges from this set - for (int i = 0; i <= set.length; i++) { - if (i < set.length && set[i]) { - if (start < 0) { - start = i; - } - } else { - if (start >= 0) { - result.add(Ranges.closed(start, i - 1)); - start = -1; - } - } - } - - return result; - } - - private static List tokenizeInput(String[] args, int offset, int lastIndex) { - List tokens = new ArrayList(); - - // Tokenize the input - for (int i = offset; i <= lastIndex; i++) { - String text = args[i]; - StringBuilder number = new StringBuilder(); - - for (int j = 0; j < text.length(); j++) { - char current = text.charAt(j); - - if (Character.isDigit(current)) { - number.append(current); - } else if (Character.isWhitespace(current)) { - // That's ok - } else if (current == '-') { - // Add the number token first - if (number.length() > 0) { - tokens.add(number.toString()); - number.setLength(0); - } - - tokens.add(Character.toString(current)); - } else { - throw new IllegalArgumentException("Illegal character '" + current + "' found."); - } - } - - // Add the number token, if it hasn't already - if (number.length() > 0) - tokens.add(number.toString()); - } - - return tokens; - } - /** * Retrieve whitelist information about a given listener. * @param listener - the given listener. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/CommandProtocol.java b/ProtocolLib/src/main/java/com/comphenix/protocol/CommandProtocol.java index e4d504d7..9f5a0eb0 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/CommandProtocol.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/CommandProtocol.java @@ -1,8 +1,6 @@ package com.comphenix.protocol; import org.bukkit.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; @@ -15,7 +13,7 @@ import com.comphenix.protocol.metrics.Updater.UpdateType; * * @author Kristian */ -class CommandProtocol implements CommandExecutor { +class CommandProtocol extends CommandBase { /** * Name of this command. */ @@ -25,33 +23,25 @@ class CommandProtocol implements CommandExecutor { private Updater updater; public CommandProtocol(Plugin plugin, Updater updater) { + super(CommandBase.PERMISSION_ADMIN, NAME, 1); this.plugin = plugin; this.updater = updater; } @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - // Make sure we're dealing with the correct command - if (!command.getName().equalsIgnoreCase(NAME)) + protected boolean handleCommand(CommandSender sender, String[] args) { + String subCommand = args[0]; + + // Only return TRUE if we executed the correct command + if (subCommand.equalsIgnoreCase("config")) + reloadConfiguration(sender); + else if (subCommand.equalsIgnoreCase("check")) + checkVersion(sender); + else if (subCommand.equalsIgnoreCase("update")) + updateVersion(sender); + else return false; - - // We need one argument (the sub-command) - if (args != null && args.length == 1) { - String subCommand = args[0]; - - // Only return TRUE if we executed the correct command - if (subCommand.equalsIgnoreCase("config")) - reloadConfiguration(sender); - else if (subCommand.equalsIgnoreCase("check")) - checkVersion(sender); - else if (subCommand.equalsIgnoreCase("update")) - updateVersion(sender); - else - return false; - return true; - } - - return false; + return true; } public void checkVersion(final CommandSender sender) { diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/RangeParser.java b/ProtocolLib/src/main/java/com/comphenix/protocol/RangeParser.java new file mode 100644 index 00000000..d396be43 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/RangeParser.java @@ -0,0 +1,142 @@ +package com.comphenix.protocol; + +import java.util.ArrayList; +import java.util.List; + +import com.google.common.collect.DiscreteDomains; +import com.google.common.collect.Range; +import com.google.common.collect.Ranges; + +/** + * Used to parse ranges in CommandPacket. + * + * @author Kristian + */ +class RangeParser { + /** + * Parse a range from a given text. + * @param text - the text. + * @param legalRange - range of legal values. + * @return The parsed ranges. + */ + public static List> getRanges(String text, Range legalRange) { + return getRanges(new String[] { text }, 0, 0, legalRange); + } + + /** + * Parse ranges from an array of elements. + * @param args - array of elements. + * @param offset - beginning offset. + * @param lastIndex - the last index of the array to read. + * @param legalRange - range of legal values. + * @return The parsed ranges. + */ + public static List> getRanges(String[] args, int offset, int lastIndex, Range legalRange) { + List tokens = tokenizeInput(args, offset, lastIndex); + List> ranges = new ArrayList>(); + + for (int i = 0; i < tokens.size(); i++) { + Range range; + String current = tokens.get(i); + String next = i + 1 < tokens.size() ? tokens.get(i + 1) : null; + + // Yoda equality is done for null-safety + if ("-".equals(current)) { + throw new IllegalArgumentException("A hyphen must appear between two numbers."); + } else if ("-".equals(next)) { + if (i + 2 >= tokens.size()) + throw new IllegalArgumentException("Cannot form a range without a upper limit."); + + // This is a proper range + range = Ranges.closed(Integer.parseInt(current), Integer.parseInt(tokens.get(i + 2))); + ranges.add(range); + + // Skip the two next tokens + i += 2; + + } else { + // Just a single number + range = Ranges.singleton(Integer.parseInt(current)); + ranges.add(range); + } + + // Validate ranges + if (!legalRange.encloses(range)) { + throw new IllegalArgumentException(range + " is not in the range " + range.toString()); + } + } + + return simplify(ranges, legalRange.upperEndpoint()); + } + + /** + * Simplify a list of ranges by assuming a maximum value. + * @param ranges - the list of ranges to simplify. + * @param maximum - the maximum value (minimum value is always 0). + * @return A simplified list of ranges. + */ + private static List> simplify(List> ranges, int maximum) { + List> result = new ArrayList>(); + boolean[] set = new boolean[maximum + 1]; + int start = -1; + + // Set every ID + for (Range range : ranges) { + for (int id : range.asSet(DiscreteDomains.integers())) { + set[id] = true; + } + } + + // Generate ranges from this set + for (int i = 0; i <= set.length; i++) { + if (i < set.length && set[i]) { + if (start < 0) { + start = i; + } + } else { + if (start >= 0) { + result.add(Ranges.closed(start, i - 1)); + start = -1; + } + } + } + + return result; + } + + private static List tokenizeInput(String[] args, int offset, int lastIndex) { + List tokens = new ArrayList(); + + // Tokenize the input + for (int i = offset; i <= lastIndex; i++) { + String text = args[i]; + StringBuilder number = new StringBuilder(); + + for (int j = 0; j < text.length(); j++) { + char current = text.charAt(j); + + if (Character.isDigit(current)) { + number.append(current); + } else if (Character.isWhitespace(current)) { + // That's ok + } else if (current == '-') { + // Add the number token first + if (number.length() > 0) { + tokens.add(number.toString()); + number.setLength(0); + } + + tokens.add(Character.toString(current)); + } else { + throw new IllegalArgumentException("Illegal character '" + current + "' found."); + } + } + + // Add the number token, if it hasn't already + if (number.length() > 0) + tokens.add(number.toString()); + } + + return tokens; + } +}