diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..4bc3533d --- /dev/null +++ b/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + + co.aikar + command + 1.0-SNAPSHOT + + + UTF-8 + + + + + aikar + http://ci.emc.gs/nexus/content/groups/aikar/ + + + paper + https://repo.destroystokyo.com/repository/maven-public/ + + + + + clean install + ${project.artifactId} + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.2 + + 1.8 + 1.8 + false + false + + -parameters + + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + + jar + + + + + + + + + + org.jetbrains + annotations + 13.0 + + + com.destroystokyo.paper + paper-api + 1.10-R0.1-SNAPSHOT + provided + + + + diff --git a/src/main/java/co/aikar/commands/BaseCommand.java b/src/main/java/co/aikar/commands/BaseCommand.java new file mode 100644 index 00000000..ff617783 --- /dev/null +++ b/src/main/java/co/aikar/commands/BaseCommand.java @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Default; +import co.aikar.commands.annotation.Subcommand; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.SetMultimap; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.util.StringUtil; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +public abstract class BaseCommand extends Command { + + private final SetMultimap subCommands = HashMultimap.create(); + + protected String execLabel; + protected String execSubcommand; + protected String[] origArgs; + + public BaseCommand() { + this(null); + } + + public BaseCommand(String cmd) { + super(cmd); + final Class self = this.getClass(); + CommandAlias rootCmdAlias = self.getAnnotation(CommandAlias.class); + if (cmd == null) { + if (rootCmdAlias == null) { + cmd = "__" + self.getSimpleName(); + } else { + cmd = Patterns.PIPE.split(rootCmdAlias.value())[0]; + } + cmd = cmd.toLowerCase(); + setName(cmd); + setLabel(cmd); + } + this.description = cmd + " commands"; + this.usageMessage = "/" + cmd; + + final CommandPermission perm = self.getAnnotation(CommandPermission.class); + if (perm != null) { + this.setPermission(perm.value()); + } + + boolean foundDefault = false; + for (Method method : self.getDeclaredMethods()) { + method.setAccessible(true); + String sublist = null; + final Subcommand sub = method.getAnnotation(Subcommand.class); + final Default def = method.getAnnotation(Default.class); + + final CommandAlias commandAliases = method.getAnnotation(CommandAlias.class); + + if (def != null) { + if (!foundDefault) { + registerSubcommand(method, "__default"); + foundDefault = true; + } else { + SneakyThrow.sneaky(new InvalidConfigurationException("Multiple @Default commands")); + } + } + if (sub != null) { + sublist = sub.value(); + } else if (commandAliases != null) { + sublist = commandAliases.value(); + } + if (sublist != null) { + registerSubcommand(method, sublist); + } + } + + try { + Method unknown = self.getMethod("onUnknown", CommandSender.class, String.class, String[].class); + unknown.setAccessible(true); + registerSubcommand(unknown, "__unknown"); + } catch (NoSuchMethodException ignored) {} + + + if (rootCmdAlias != null) { + List cmdList = new ArrayList<>(); + Collections.addAll(cmdList, Patterns.PIPE.split(rootCmdAlias.value().toLowerCase())); + cmdList.remove(cmd); + for (String cmdAlias : cmdList) { + register(cmdAlias, new ForwardingCommand(this)); + } + } + + register(getName(), this); + } + + private boolean register(String name, Command cmd) { + return Bukkit.getServer().getCommandMap().register(name.toLowerCase(), "empire", cmd); + } + + private void registerSubcommand(Method method, String subCommand) { + subCommand = subCommand.toLowerCase(); + final String[] subCommandParts = Patterns.SPACE.split(subCommand); + // Must run getSubcommandPossibility BEFORE we rewrite it just after this. + List cmdList = getSubCommandPossibilityList(subCommandParts); + + // Strip pipes off for auto complete addition + for (int i = 0; i < subCommandParts.length; i++) { + subCommandParts[i] = Patterns.PIPE.split(subCommandParts[i])[0]; + } + String prefSubCommand = StringUtils.join(subCommandParts, " "); + final CommandAlias cmdAlias = method.getAnnotation(CommandAlias.class); + + final String[] aliasNames = cmdAlias != null ? Patterns.PIPE.split(cmdAlias.value().toLowerCase()) : null; + String cmdName = aliasNames != null ? aliasNames[0] : getLabel() + " "; + RegisteredCommand cmd = new RegisteredCommand(this, cmdName, method, prefSubCommand); + + for (String subcmd : cmdList) { + subCommands.put(subcmd, cmd); + } + + if (aliasNames != null) { + for (String name : aliasNames) { + register(name, new ForwardingCommand(this, subCommandParts)); + } + } + } + + /** + * Takes a string like "foo|bar baz|qux" and generates a list of + * - foo baz + * - foo qux + * - bar baz + * - bar qux + * + * For every possible sub command combination + * + * @param subCommandParts + * @return List of all sub command possibilities + */ + private static List getSubCommandPossibilityList(String[] subCommandParts) { + int i = 0; + ArrayList current = null; + while (true) { + ArrayList newList = new ArrayList<>(); + + if (i < subCommandParts.length) { + for (String s1 : Patterns.PIPE.split(subCommandParts[i])) { + if (current != null) { + newList.addAll(current.stream().map(s -> s + " " + s1).collect(Collectors.toList())); + } else { + newList.add(s1); + } + } + } + + if (i + 1 < subCommandParts.length) { + current = newList; + i = i + 1; + continue; + } + + return newList; + } + } + + @Override + public final boolean execute(CommandSender sender, String commandLabel, String[] args) { + if (!testPermission(sender)) { + return true; + } + commandLabel = commandLabel.toLowerCase(); + if (preCommand(sender, commandLabel, args)) { + return true; + } + + execSubcommand = null; + execLabel = commandLabel; + origArgs = args; + + if (args.length == 0) { + onDefault(sender, commandLabel); + return true; + } + + CommandSearch cmd = findSubCommand(args); + if (cmd != null) { + execSubcommand = cmd.getCheckSub(); + final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length); + executeCommand(sender, execargs, cmd.cmd); + return true; + } + + if (!onUnknown(sender, commandLabel, args)) { + help(sender, args); + } + return true; + } + + private CommandSearch findSubCommand(String[] args) { + return findSubCommand(args, false); + } + private CommandSearch findSubCommand(String[] args, boolean completion) { + for (int i = args.length; i >= 0; i--) { + String checkSub = StringUtils.join(args, " ", 0, i).toLowerCase(); + Set cmds = subCommands.get(checkSub); + + final int extraArgs = args.length - i; + if (!cmds.isEmpty()) { + RegisteredCommand cmd = null; + if (cmds.size() == 1) { + cmd = Iterables.getOnlyElement(cmds); + } else { + Optional optCmd = cmds.stream().filter(c -> { + int nonSender = c.nonSenderAwareResolvers; + int partialSender = c.optionalResolvers; + return extraArgs <= nonSender + partialSender && (completion || extraArgs >= nonSender); + }).sorted((c1, c2) -> { + int a = c1.nonSenderAwareResolvers + c1.optionalResolvers; + int b = c2.nonSenderAwareResolvers + c2.optionalResolvers; + + if (a == b) { + return 0; + } + return a < b ? 1 : -1; + }).findFirst(); + if (optCmd.isPresent()) { + cmd = optCmd.get(); + } + } + if (cmd != null) { + return new CommandSearch(cmd, i, checkSub); + } + } + } + return null; + } + + private static void executeCommand(CommandSender sender, String[] args, RegisteredCommand cmd) { + if (cmd.hasPermission(sender)) { + List sargs = Lists.newArrayList(args); + cmd.invoke(sender, sargs); + } else { + CommandUtil.sendMsg(sender, "&cI'm sorry, but you do not have permission to perform this command."); + } + } + + public boolean canExecute(CommandSender sender, RegisteredCommand cmd) { + return true; + } + + @Override + public List tabComplete(CommandSender sender, String commandLabel, String[] args) + throws IllegalArgumentException { + + commandLabel = commandLabel.toLowerCase(); + + + final CommandSearch search = findSubCommand(args, true); + + if (search != null) { + args = Arrays.copyOfRange(args, search.argIndex, args.length); + return completeCommand(sender, search.cmd, args, commandLabel); + } + + String argString = StringUtils.join(args, " ").toLowerCase(); + + final List cmds = new ArrayList<>(); + + for (Map.Entry entry : subCommands.entries()) { + final String key = entry.getKey(); + if (key.startsWith(argString) && !"__unknown".equals(key)) { + final RegisteredCommand value = entry.getValue(); + if (!value.hasPermission(sender)) { + continue; + } + String prefCommand = value.prefSubCommand; + + final String[] psplit = Patterns.SPACE.split(prefCommand); + cmds.add(psplit[args.length - 1]); + } + } + + final Set unknownCmds = subCommands.get("__unknown"); + if (cmds.isEmpty() && !unknownCmds.isEmpty()) { + RegisteredCommand unknownCommand = null; + if (unknownCmds.size() == 1) { + unknownCommand = Iterables.getOnlyElement(unknownCmds); + } + if (unknownCommand != null) { + return completeCommand(sender, unknownCommand, args, commandLabel); + } + } + + return filterTabComplete(args[args.length-1], cmds); + } + + private List completeCommand(CommandSender sender, RegisteredCommand cmd, String[] args, String commandLabel) { + if (args.length > cmd.nonSenderAwareResolvers + cmd.optionalResolvers) { + return ImmutableList.of(); + } + if (args.length == 0 || cmd.complete == null) { + return args.length < 2 ? super.tabComplete(sender, commandLabel, args) : ImmutableList.of(); + } + + String[] completions = Patterns.SPACE.split(cmd.complete.value()); + final int argIndex = args.length - 1; + + String input = args[argIndex]; + final String completion = argIndex < completions.length ? completions[argIndex] : null; + + if ("@players".equals(completion)) { + return super.tabComplete(sender, commandLabel, args); + } + List cmds = CommandCompletions.of(sender, completion, input); + if (cmds.isEmpty()) { + cmds = ImmutableList.of(input); + } + return filterTabComplete(args[(argIndex)], cmds); + } + + private static List filterTabComplete(String arg, List cmds) { + return cmds.stream() + .distinct() + .filter(cmd -> cmd != null && (arg.isEmpty() || StringUtil.startsWithIgnoreCase(cmd, arg))) + .collect(Collectors.toList()); + } + + public void help(CommandSender sender, String[] args) { + CommandUtil.sendMsg(sender, "&cUnknown Command, please type &f/help"); + } + + public void onDefault(CommandSender sender, String commandLabel) { + executeDefault(sender); + } + public boolean onUnknown(CommandSender sender, String commandLabel, String[] args) { + help(sender, args); + return true; + } + public boolean executeDefault(CommandSender sender, String... args) { + final Set defs = subCommands.get("__default"); + RegisteredCommand def = null; + if (!defs.isEmpty()) { + if (defs.size() == 1) { + def = Iterables.getOnlyElement(defs); + } + if (def != null) { + executeCommand(sender, args, def); + return true; + } + } + return false; + } + + public boolean preCommand(CommandSender sender, String commandLabel, String[] args) { + return false; + } + + public void doHelp(CommandSender sender, String... args) { + help(sender, args); + } + + public void showSyntax(CommandSender sender, RegisteredCommand cmd) { + CommandUtil.sendMsg(sender, "&cUsage: /" + cmd.command + " " + cmd.syntax); + } + + /*@Data*/ /*@AllArgsConstructor*/ + private static class CommandSearch { RegisteredCommand cmd; int argIndex; String checkSub; + + public CommandSearch(RegisteredCommand cmd, int argIndex, String checkSub) { + this.cmd = cmd; + this.argIndex = argIndex; + this.checkSub = checkSub; + } + + public RegisteredCommand getCmd() { + return this.cmd; + } + + public int getArgIndex() { + return this.argIndex; + } + + public String getCheckSub() { + return this.checkSub; + } + + public void setCmd(RegisteredCommand cmd) { + this.cmd = cmd; + } + + public void setArgIndex(int argIndex) { + this.argIndex = argIndex; + } + + public void setCheckSub(String checkSub) { + this.checkSub = checkSub; + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof CommandSearch)) { + return false; + } + final CommandSearch other = (CommandSearch) o; + if (!other.canEqual(this)) { + return false; + } + final Object this$cmd = this.getCmd(); + final Object other$cmd = other.getCmd(); + if (this$cmd == null ? other$cmd != null : !this$cmd.equals(other$cmd)) { + return false; + } + if (this.getArgIndex() != other.getArgIndex()) { + return false; + } + final Object this$checkSub = this.getCheckSub(); + final Object other$checkSub = other.getCheckSub(); + if (this$checkSub == null ? other$checkSub != null : !this$checkSub.equals(other$checkSub)) { + return false; + } + return true; + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $cmd = this.getCmd(); + result = result * PRIME + ($cmd == null ? 43 : $cmd.hashCode()); + result = result * PRIME + this.getArgIndex(); + final Object $checkSub = this.getCheckSub(); + result = result * PRIME + ($checkSub == null ? 43 : $checkSub.hashCode()); + return result; + } + + protected boolean canEqual(Object other) { + return other instanceof CommandSearch; + } + + public String toString() { + return "com.empireminecraft.commands.EmpireCommand.CommandSearch(cmd=" + this.getCmd() + ", argIndex=" + this.getArgIndex() + ", checkSub=" + + this.getCheckSub() + ")"; + } + } +} diff --git a/src/main/java/co/aikar/commands/CommandCompletions.java b/src/main/java/co/aikar/commands/CommandCompletions.java new file mode 100644 index 00000000..b6deeaeb --- /dev/null +++ b/src/main/java/co/aikar/commands/CommandCompletions.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + + +public final class CommandCompletions { + private CommandCompletions() {} + + private static final List EMPTY = ImmutableList.of(); + public static List of(CommandSender sender, String completion, String input) { + if (completion == null) { + return ImmutableList.of(); + } + String[] complete = Patterns.COLON.split(completion, 2); + + switch (complete[0]) { + case "@range": + if (complete.length == 1) { + return ImmutableList.of(); + } + final String[] ranges = Patterns.DASH.split(complete[1]); + int start; + int end; + if (ranges.length != 2) { + start = 0; + end = CommandUtil.parseInt(ranges[0], 0); + } else { + start = CommandUtil.parseInt(ranges[0], 0); + end = CommandUtil.parseInt(ranges[1], 0); + } + return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList()); + case "@timeunits": + return ImmutableList.of("hours", "days", "weeks", "months", "minutes"); + + case "@mobs": + final Stream normal = Stream.of(EntityType.values()) + .map(entityType -> CommandUtil.simplifyString(entityType.getName())); + return normal.collect(Collectors.toList()); + default: + return Lists.newArrayList(Patterns.PIPE.split(completion)); + } + } +} diff --git a/src/main/java/co/aikar/commands/CommandUtil.java b/src/main/java/co/aikar/commands/CommandUtil.java new file mode 100644 index 00000000..6b01beb3 --- /dev/null +++ b/src/main/java/co/aikar/commands/CommandUtil.java @@ -0,0 +1,684 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.WordUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.math.BigDecimal; +import java.text.Normalizer; +import java.text.Normalizer.Form; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@SuppressWarnings("WeakerAccess") +public final class CommandUtil { + + public static final Random RANDOM = new Random(); + + private CommandUtil() {} + + public static String padRight(String s, int n) { + return String.format("%1$-" + n + "s", s); + } + + public static String padLeft(String s, int n) { + return String.format("%1$" + n + "s", s); + } + + public static String formatNumber(Integer balance) { + return NumberFormat.getInstance().format(balance); + } + + public static String formatLocation(Location loc) { + if (loc == null) { + return null; + } + return loc.getWorld().getName() + + ":" + + loc.getBlockX() + + "," + + loc.getBlockY() + + "," + + loc.getBlockZ(); + } + + public static String color(String message) { + return ChatColor.translateAlternateColorCodes('&', message); + } + + public static void sendMsg(CommandSender player, String message) { + message = color(message); + if (player == null) { + for (String msg : Patterns.NEWLINE.split(message)) { + Log.info(msg); + } + } else { + for (String msg : Patterns.NEWLINE.split(message)) { + player.sendMessage(msg); + } + } + } + + public static Location stringToLocation(String storedLoc) { + return stringToLocation(storedLoc, null); + } + public static Location stringToLocation(String storedLoc, World forcedWorld) { + if (storedLoc == null) { + return null; + } + String[] args = Patterns.COLON.split(storedLoc); + if (args.length >= 4 || (args.length == 3 && forcedWorld != null)) { + String world = forcedWorld != null ? forcedWorld.getName() : args[0]; + int i = args.length == 3 ? 0 : 1; + double x = Double.parseDouble(args[i]); + double y = Double.parseDouble(args[i + 1]); + double z = Double.parseDouble(args[i + 2]); + Location loc = new Location(Bukkit.getWorld(world), x, y, z); + if (args.length >= 6) { + loc.setPitch(Float.parseFloat(args[4])); + loc.setYaw(Float.parseFloat(args[5])); + } + return loc; + } else if (args.length == 2) { + String[] args2 = Patterns.COMMA.split(args[1]); + if (args2.length == 3) { + String world = forcedWorld != null ? forcedWorld.getName() : args[0]; + double x = Double.parseDouble(args2[0]); + double y = Double.parseDouble(args2[1]); + double z = Double.parseDouble(args2[2]); + return new Location(Bukkit.getWorld(world), x, y, z); + } + } + return null; + } + + public static String fullLocationToString(Location loc) { + if (loc == null) { + return null; + } + return (new StringBuilder(64)) + .append(loc.getWorld().getName()) + .append(':') + .append(NumUtil.precision(loc.getX(), 4)) + .append(':') + .append(NumUtil.precision(loc.getY(), 4)) + .append(':') + .append(NumUtil.precision(loc.getZ(), 4)) + .append(':') + .append(NumUtil.precision(loc.getPitch(), 4)) + .append(':') + .append(NumUtil.precision(loc.getYaw(), 4)) + .toString(); + } + + public static String fullBlockLocationToString(Location loc) { + if (loc == null) { + return null; + } + return (new StringBuilder(64)) + .append(loc.getWorld().getName()) + .append(':') + .append(loc.getBlockX()) + .append(':') + .append(loc.getBlockY()) + .append(':') + .append(loc.getBlockZ()) + .append(':') + .append(NumUtil.precision(loc.getPitch(), 4)) + .append(':') + .append(NumUtil.precision(loc.getYaw(), 4)) + .toString(); + } + + public static String blockLocationToString(Location loc) { + if (loc == null) { + return null; + } + + return (new StringBuilder(32)) + .append(loc.getWorld().getName()) + .append(':') + .append(loc.getBlockX()) + .append(':') + .append(loc.getBlockY()) + .append(':') + .append(loc.getBlockZ()) + .toString(); + } + + public static T getEnumFromName(T[] types, String name) { + return getEnumFromName(types, name, null); + } + public static T getEnumFromName(T[] types, String name, T def) { + for (T type : types) { + if (type.name().equalsIgnoreCase(name)) { + return type; + } + } + return def; + } + public static T getEnumFromOrdinal(T[] types, int ordinal) { + for (T type : types) { + if (type.ordinal() == ordinal) { + return type; + } + } + return null; + } + + public static String ucfirst(String str) { + return WordUtils.capitalizeFully(str); + } + + public static Double parseDouble(String var) { + return parseDouble(var, null); + } + + public static Double parseDouble(String var, Double def) { + if (var == null) { + return def; + } + try { + return Double.parseDouble(var); + } catch (NumberFormatException ignored) {} + return def; + } + + public static Float parseFloat(String var) { + return parseFloat(var, null); + } + public static Float parseFloat(String var, Float def) { + if (var == null) { + return def; + } + try { + return Float.parseFloat(var); + } catch (NumberFormatException ignored) {} + return def; + } + public static Long parseLong(String var) { + return parseLong(var, null); + } + public static Long parseLong(String var, Long def) { + if (var == null) { + return def; + } + try { + return Long.parseLong(var); + } catch (NumberFormatException ignored) {} + return def; + } + + public static Integer parseInt(String var) { + return parseInt(var, null); + } + public static Integer parseInt(String var, Integer def) { + if (var == null) { + return def; + } + try { + return Integer.parseInt(var); + } catch (NumberFormatException ignored) {} + return def; + } + + public static boolean randBool() { + return RANDOM.nextBoolean(); + } + + public static T nullDefault(Object val, Object def) { + return (T) (val != null ? val : def); + } + + public static String join(Collection args) { + return StringUtils.join(args, " "); + } + public static String join(Collection args, String sep) { + return StringUtils.join(args, sep); + } + public static String join(String[] args) { + return join(args, 0, ' '); + } + + public static String join(String[] args, String sep) { + return StringUtils.join(args, sep); + } + public static String join(String[] args, char sep) { + return join(args, 0, sep); + } + + public static String join(String[] args, int index) { + return join(args, index, ' '); + } + + public static String join(String[] args, int index, char sep) { + return StringUtils.join(args, sep, index, args.length); + } + + public static String simplifyString(String str) { + if (str == null) { + return null; + } + return Patterns.NON_ALPHA_NUMERIC.matcher(str.toLowerCase()).replaceAll(""); + } + + public static double round(double x, int scale) { + try { + return (new BigDecimal + (Double.toString(x)) + .setScale(scale, BigDecimal.ROUND_HALF_UP)) + .doubleValue(); + } catch (NumberFormatException ex) { + if (Double.isInfinite(x)) { + return x; + } else { + return Double.NaN; + } + } + } + public static int roundUp(int num, int multiple) { + if(multiple == 0) { + return num; + } + + int remainder = num % multiple; + if (remainder == 0) { + return num; + } + return num + multiple - remainder; + + } + + public static String removeColors(String msg) { + return ChatColor.stripColor(CommandUtil.color(msg)); + + } + + public static String replaceChatString(String message, String replace, String with) { + return replaceChatString(message, Pattern.compile(Pattern.quote(replace), Pattern.CASE_INSENSITIVE), with); + } + public static String replaceChatString(String message, Pattern replace, String with) { + final String[] split = replace.split(message + "1"); + + if (split.length < 2) { + return replace.matcher(message).replaceAll(with); + } + message = split[0]; + + for (int i = 1; i < split.length; i++) { + final String prev = ChatColor.getLastColors(message); + message += with + prev + split[i]; + } + return message.substring(0, message.length() - 1); + } + + public static boolean isWithinDistance(@NotNull Player p1, @NotNull Player p2, int dist) { + return isWithinDistance(p1.getLocation(), p2.getLocation(), dist); + } + public static boolean isWithinDistance(@NotNull Location loc1, @NotNull Location loc2, int dist) { + return loc1.getWorld() == loc2.getWorld() && loc1.distance(loc2) <= dist; + } + + public static String limit(String str, int limit) { + return str.length() > limit ? str.substring(0, limit) : str; + } + + /** + * Plain string replacement, escapes replace value. + * @param string + * @param pattern + * @param repl + * @return + */ + public static String replace(String string, Pattern pattern, String repl) { + return pattern.matcher(string).replaceAll(Matcher.quoteReplacement(repl)); + } + + /** + * Regex version of {@link #replace(String, Pattern, String)} + * @param string + * @param pattern + * @param repl + * @return + */ + public static String replacePattern(String string, Pattern pattern, String repl) { + return pattern.matcher(string).replaceAll(repl); + } + + /** + * Plain String replacement. If you need regex patterns, see {@link #replacePattern(String, String, String)} + * @param string + * @param pattern + * @param repl + * @return + */ + public static String replace(String string, String pattern, String repl) { + return replace(string, Patterns.getPattern(Pattern.quote(pattern)), repl); + } + + /** + * Regex version of {@link #replace(String, String, String)} + * @param string + * @param pattern + * @param repl + * @return + */ + public static String replacePattern(String string, String pattern, String repl) { + return replace(string, Patterns.getPattern(pattern), repl); + } + /** + * Pure Regex Pattern matching and replacement, no escaping + * @param string + * @param pattern + * @param repl + * @return + */ + public static String replacePatternMatch(String string, Pattern pattern, String repl) { + return pattern.matcher(string).replaceAll(repl); + } + + /** + * Pure Regex Pattern matching and replacement, no escaping + * @param string + * @param pattern + * @param repl + * @return + */ + public static String replacePatternMatch(String string, String pattern, String repl) { + return replacePatternMatch(string, Patterns.getPattern(pattern), repl); + } + + public static String replaceStrings(String string, String... replacements) { + if (replacements.length < 2 || replacements.length % 2 != 0) { + throw new IllegalArgumentException("Invalid Replacements"); + } + for (int i = 0; i < replacements.length; i += 2) { + String key = replacements[i]; + String value = replacements[i+1]; + if (value == null) value = ""; + string = replace(string, key, value); + } + return string; + } + public static String replacePatterns(String string, String... replacements) { + if (replacements.length < 2 || replacements.length % 2 != 0) { + throw new IllegalArgumentException("Invalid Replacements"); + } + for (int i = 0; i < replacements.length; i += 2) { + String key = replacements[i]; + String value = replacements[i+1]; + if (value == null) value = ""; + string = replacePattern(string, key, value); + } + return string; + } + + + /** + * Copied from Apache Commons WordUtils, with an exception to skip spaces after delimiters. + * + * @see org.apache.commons.lang.WordUtils#capitalize(String, char[]) + * @param str + * @param delimiters + * @return + */ + public static String capitalize(String str, char[] delimiters) { + int delimLen = (delimiters == null ? -1 : delimiters.length); + if (str == null || str.isEmpty() || delimLen == 0) { + return str; + } + int strLen = str.length(); + StringBuilder builder = new StringBuilder(strLen); + boolean capitalizeNext = true; + for (int i = 0; i < strLen; i++) { + char ch = str.charAt(i); + + if (isDelimiter(ch, delimiters)) { + builder.append(ch); + capitalizeNext = true; + } else if (ch != ' ' && capitalizeNext) { + builder.append(Character.toTitleCase(ch)); + capitalizeNext = false; + } else { + builder.append(ch); + } + } + return builder.toString(); + } + private static boolean isDelimiter(char ch, char[] delimiters) { + if (delimiters == null) { + return Character.isWhitespace(ch); + } + for (char delimiter : delimiters) { + if (ch == delimiter) { + return true; + } + } + return false; + } + + public static T random(List arr) { + if (arr == null || arr.isEmpty()) { + return null; + } + return arr.get(RANDOM.nextInt(arr.size())); + } + public static T random(T[] arr) { + if (arr == null || arr.length == 0) { + return null; + } + return arr[RANDOM.nextInt(arr.length)]; + } + + /** + * Added as im sure we will try to "Find this" again. This is no different than Enum.values() passed to above method logically + * but the array version is slightly faster. + * @param enm + * @param + * @return + */ + @Deprecated + public static > T random(Class enm) { + return random(enm.getEnumConstants()); + } + + public static String normalize(String s) { + if (s == null) { + return null; + } + return Patterns.NON_PRINTABLE_CHARACTERS.matcher(Normalizer.normalize(s, Form.NFD)).replaceAll(""); + } + + public static int indexOf(String arg, String[] split) { + for (int i = 0; i < split.length; i++) { + if (arg == null) { + if (split[i] == null) { + return i; + } + } else if (arg.equals(split[i])) { + return i; + } + } + return -1; + } + + public static String capitalizeFirst(String name) { + return capitalizeFirst(name, '_'); + } + + public static String capitalizeFirst(String name, char separator) { + name = name.toLowerCase(); + String[] split = name.split(Character.toString(separator)); + StringBuilder total = new StringBuilder(3); + for (String s : split) { + total.append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).append(' '); + } + + return total.toString().trim(); + } + + public static List enumNames(Enum[] values) { + return Stream.of(values).map(Enum::name).collect(Collectors.toList()); + } + + public static List enumNames(Class> cls) { + return enumNames(cls.getEnumConstants()); + } + + public static String combine(String[] args) { + return combine(args, 0); + } + public static String combine(String[] args, int start) { + int size = 0; + for (int i = start; i < args.length; i++) { + size += args[i].length(); + } + StringBuilder sb = new StringBuilder(size); + for (int i = start; i < args.length; i++) { + sb.append(args[i]); + } + return sb.toString(); + } + + public static double distance(@NotNull Entity e1, @NotNull Entity e2) { + return distance(e1.getLocation(), e2.getLocation()); + } + public static double distance2d(@NotNull Entity e1, @NotNull Entity e2) { + return distance2d(e1.getLocation(), e2.getLocation()); + } + public static double distance2d(@NotNull Location loc1, @NotNull Location loc2) { + loc1 = loc1.clone(); + loc1.setY(loc2.getY()); + return distance(loc1, loc2); + } + public static double distance(@NotNull Location loc1, @NotNull Location loc2) { + if (loc1.getWorld() != loc2.getWorld()) { + return 0; + } + return loc1.distance(loc2); + } + + public static Location getTargetLoc(Player player) { + return getTargetLoc(player, 128); + } + public static Location getTargetLoc(Player player, int maxDist) { + return getTargetLoc(player, maxDist, 1.5); + } + public static Location getTargetLoc(Player player, int maxDist, double addY) { + try { + Location target = player.getTargetBlock((Set) null, maxDist).getLocation(); + target.setY(target.getY() + addY); + return target; + } catch (Exception ignored) { + return null; + } + } + + public static Location getRandLoc(Location loc, int radius) { + return getRandLoc(loc, radius, radius, radius); + } + public static Location getRandLoc(Location loc, int xzRadius, int yRadius) { + return getRandLoc(loc, xzRadius, yRadius, xzRadius); + } + @NotNull public static Location getRandLoc(Location loc, int xRadius, int yRadius, int zRadius) { + Location newLoc = loc.clone(); + newLoc.setX(NumUtil.rand(loc.getX()-xRadius, loc.getX()+xRadius)); + newLoc.setY(NumUtil.rand(loc.getY()-yRadius, loc.getY()+yRadius)); + newLoc.setZ(NumUtil.rand(loc.getZ()-zRadius, loc.getZ()+zRadius)); + return newLoc; + } + + @Nullable public static Enum simpleMatch(Class> list, String item) { + if (item == null) { + return null; + } + item = CommandUtil.simplifyString(item); + for (Enum s : list.getEnumConstants()) { + String simple = CommandUtil.simplifyString(s.name()); + if (item.equals(simple)) { + return s; + } + } + + return null; + } + + @NotNull public static Boolean isTruthy(String test) { + switch (test) { + case "t": + case "true": + case "on": + case "y": + case "yes": + case "1": + return true; + } + return false; + } + + + public static Number parseNumber(String num, boolean suffixes) { + double mod = 1; + if (suffixes) { + switch (num.charAt(num.length()-1)) { + case 'M': + case 'm': + mod = 1000000D; + num = num.substring(0, num.length()-1); + break; + case 'K': + case 'k': + mod = 1000D; + num = num.substring(0, num.length()-1); + } + } + + return Double.parseDouble(num) * mod; + } + + public static boolean hasIntersection(Collection list1, Collection list2) { + for (T t : list1) { + if (list2.contains(t)) { + return true; + } + } + + return false; + } + + public static Collection intersection(Collection list1, Collection list2) { + List list = new ArrayList<>(); + + for (T t : list1) { + if(list2.contains(t)) { + list.add(t); + } + } + + return list; + } +} diff --git a/src/main/java/co/aikar/commands/ForwardingCommand.java b/src/main/java/co/aikar/commands/ForwardingCommand.java new file mode 100644 index 00000000..7433a4d7 --- /dev/null +++ b/src/main/java/co/aikar/commands/ForwardingCommand.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands; + +import org.apache.commons.lang.ArrayUtils; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; + +import java.util.List; + +public class ForwardingCommand extends Command { + private final Command command; + private final String[] baseArgs; + private static final String[] NO_ARGS = new String[0]; + + public ForwardingCommand(Command command) { + this(command, NO_ARGS); + } + + public ForwardingCommand(Command command, String[] baseArgs) { + super(command.getName()); + this.command = command; + this.baseArgs = baseArgs; + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + return command.tabComplete(sender, alias, (String[]) ArrayUtils.addAll(baseArgs, args)); + } + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) { + return command.execute(sender, commandLabel, (String[]) ArrayUtils.addAll(baseArgs, args)); + } +} diff --git a/src/main/java/co/aikar/commands/InvalidCommandArgument.java b/src/main/java/co/aikar/commands/InvalidCommandArgument.java new file mode 100644 index 00000000..806e1960 --- /dev/null +++ b/src/main/java/co/aikar/commands/InvalidCommandArgument.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands; + +public class InvalidCommandArgument extends Exception { + public boolean showSyntax = true; + public InvalidCommandArgument() { + this(null); + } + public InvalidCommandArgument(boolean showSyntax) { + this(null, showSyntax); + } + public InvalidCommandArgument(String message) { + this(message, true); + } + public InvalidCommandArgument(String message, boolean showSyntax) { + super(message, null, false, false); + this.showSyntax = showSyntax; + } +} diff --git a/src/main/java/co/aikar/commands/Log.java b/src/main/java/co/aikar/commands/Log.java new file mode 100644 index 00000000..24f277c2 --- /dev/null +++ b/src/main/java/co/aikar/commands/Log.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands; + +import org.apache.commons.lang.exception.ExceptionUtils; + +import java.util.logging.Logger; + +public final class Log { + public static Logger LOGGER; + + private Log() {} + + + public static void log(String message) { + info(message); + } + + + public static void info(String message) { + for (String s : Patterns.NEWLINE.split(message)) { + LOGGER.info(s); + } + } + + public static void warn(String message) { + for (String s : Patterns.NEWLINE.split(message)) { + LOGGER.warning(s); + } + } + + public static void severe(String message) { + for (String s : Patterns.NEWLINE.split(message)) { + LOGGER.severe(s); + } + } + + public static void error(String message) { + severe(message); + } + + + public static void exception(String msg) { + exception(new Throwable(msg)); + } + + public static void exception(Throwable e) { + exception(e.getMessage(), e); + } + + public static void exception(String msg, Throwable e) { + if (msg != null) { + severe(msg); + } + severe(ExceptionUtils.getFullStackTrace(e)); + } + + public static void exception(Throwable dbg, int lines) { + if (dbg == null) { + return; + } + severe(dbg.getMessage()); + final StackTraceElement current = new Throwable().getStackTrace()[1]; + severe("c: "+ current.getClassName()+":" + current.getLineNumber()); + + final StackTraceElement[] stack = dbg.getStackTrace(); + for (int i = 0; i < lines && i < stack.length; i++) { + final StackTraceElement cur = stack[i]; + Logger.getGlobal().severe(" " + cur); + } + } +} diff --git a/src/main/java/co/aikar/commands/NumUtil.java b/src/main/java/co/aikar/commands/NumUtil.java new file mode 100644 index 00000000..c640fc26 --- /dev/null +++ b/src/main/java/co/aikar/commands/NumUtil.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands; + +import org.apache.commons.lang.StringUtils; + +public final class NumUtil { + private NumUtil() {} + + public static int rand(int min, int max) { + return min + CommandUtil.RANDOM.nextInt(max - min + 1); + } + + /** + * Calculate random between 2 points, excluding a center + * ex: Util.rand(-12, -6, 6, 12) would not return -5 to 5 + * @param min1 + * @param max1 + * @param min2 + * @param max2 + * @return + */ + public static int rand(int min1, int max1, int min2, int max2) { + return CommandUtil.randBool() ? rand(min1, max1) : rand(min2, max2); + } + + public static double rand(double min, double max) { + return CommandUtil.RANDOM.nextDouble() * (max - min) + min; + } + + public static boolean isNumber(String str) { + return StringUtils.isNumeric(str); + } + + public static String intToRoman(int integer) { + if (integer == 1) { + return "I"; + } + if (integer == 2) { + return "II"; + } + if (integer == 3) { + return "III"; + } + if (integer == 4) { + return "IV"; + } + if (integer == 5) { + return "V"; + } + if (integer == 6) { + return "VI"; + } + if (integer == 7) { + return "VII"; + } + if (integer == 8) { + return "VIII"; + } + if (integer == 9) { + return "IX"; + } + if (integer == 10) { + return "X"; + } + return null; + } + + public static boolean isInteger(String string) { + if (!Patterns.INTEGER.matcher(string).matches()) { + return false; + } + return true; + } + + public static boolean isFloat(String string) { + try { + Float.parseFloat(string); + return true; + } catch (Exception e) { + return false; + } + } + + public static boolean isDouble(String string) { + try { + Double.parseDouble(string); + return true; + } catch (Exception e) { + return false; + } + } + + public static boolean isBetween(float num, double min, double max) { + if (num >= min && num <= max){ + return true; + } else { + return false; + } + } + + public static double precision(double x, int p) { + double pow = Math.pow(10, p); + return Math.round(x * pow) / pow; + } +} diff --git a/src/main/java/co/aikar/commands/Patterns.java b/src/main/java/co/aikar/commands/Patterns.java new file mode 100644 index 00000000..c0257024 --- /dev/null +++ b/src/main/java/co/aikar/commands/Patterns.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; + +@SuppressWarnings("WeakerAccess") +public final class Patterns { + public static final Pattern COMMA = Pattern.compile(","); + public static final Pattern NEWLINE = Pattern.compile("\n"); + public static final Pattern DASH = Pattern.compile("-"); + public static final Pattern SPACE = Pattern.compile(" "); + public static final Pattern SEMICOLON = Pattern.compile(";"); + public static final Pattern COLON = Pattern.compile(":"); + public static final Pattern PIPE = Pattern.compile("\\|"); + public static final Pattern NON_ALPHA_NUMERIC = Pattern.compile("[^a-zA-Z0-9]"); + public static final Pattern INTEGER = Pattern.compile("^[0-9]+$"); + public static final Pattern VALID_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]{2,16}$"); + public static final Pattern NON_PRINTABLE_CHARACTERS = Pattern.compile("[^\\x20-\\x7F]"); + + public static final Pattern EQUALS = Pattern.compile("="); + + + + private Patterns() {} + @SuppressWarnings("Convert2MethodRef") + static final LoadingCache patternCache = + CacheBuilder.newBuilder() + .expireAfterAccess(90, TimeUnit.DAYS) + .maximumSize(1024) + // has to be this or fails to compile + .build(CacheLoader.from((pattern) -> Pattern.compile(pattern))); + public static Pattern getPattern(String pattern) { + try { + return patternCache.get(pattern); + } catch (ExecutionException e) { + e.printStackTrace(); + return Pattern.compile(pattern); + } + } +} diff --git a/src/main/java/co/aikar/commands/RegisteredCommand.java b/src/main/java/co/aikar/commands/RegisteredCommand.java new file mode 100644 index 00000000..f90170d6 --- /dev/null +++ b/src/main/java/co/aikar/commands/RegisteredCommand.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands; + +import co.aikar.commands.annotation.CommandAlias; +import co.aikar.commands.annotation.CommandCompletion; +import co.aikar.commands.annotation.CommandPermission; +import co.aikar.commands.annotation.Default; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Syntax; +import co.aikar.commands.annotation.Values; +import co.aikar.commands.contexts.CommandContexts; +import co.aikar.commands.contexts.CommandExecutionContext; +import co.aikar.commands.contexts.ContextResolver; +import co.aikar.commands.contexts.SenderAwareContextResolver; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.entity.Player; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class RegisteredCommand { + public final BaseCommand scope; + public final String command; + public final Method method; + public final String prefSubCommand; + public final Parameter[] parameters; + public final ContextResolver[] resolvers; + public final String syntax; + + public final CommandPermission perm; + public final CommandCompletion complete; + public final int nonSenderAwareResolvers; + public final int optionalResolvers; + + RegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) { + this.scope = scope; + if ("__unknown".equals(prefSubCommand)) { + prefSubCommand = ""; + } + this.command = command + (method.getAnnotation(CommandAlias.class) == null && !prefSubCommand.isEmpty() ? prefSubCommand : ""); + this.method = method; + this.prefSubCommand = prefSubCommand; + this.perm = method.getAnnotation(CommandPermission.class); + this.complete = method.getAnnotation(CommandCompletion.class); + this.parameters = method.getParameters(); + this.resolvers = new ContextResolver[this.parameters.length]; + final Syntax syntaxStr = method.getAnnotation(Syntax.class); + + int nonSenderAwareResolvers = 0; + int optionalResolvers = 0; + StringBuilder syntaxB = new StringBuilder(64); + for (int i = 0; i < parameters.length; i++) { + final Parameter parameter = parameters[i]; + final Class type = parameter.getType(); + final ContextResolver resolver = CommandContexts.getResolver(type); + if (resolver != null) { + resolvers[i] = resolver; + + if (type == World.class || resolver instanceof SenderAwareContextResolver || parameter.getAnnotation(Optional.class) != null + || parameter.getAnnotation(Default.class) != null) { + optionalResolvers++; + } else { + nonSenderAwareResolvers++; + } + if (!CommandSender.class.isAssignableFrom(parameter.getType())) { + if (parameter.getAnnotation(Default.class) != null || + parameter.getAnnotation(Optional.class) != null || + resolver instanceof SenderAwareContextResolver) { + syntaxB.append('[').append(parameter.getName()).append("] "); + } else { + syntaxB.append('<').append(parameter.getName()).append("> "); + } + } + } else { + SneakyThrow.sneaky(new InvalidConfigurationException( + "Parameter " + type.getSimpleName() + " of " + this.command + " has no resolver" + )); + } + } + if (syntaxStr != null) { + this.syntax = syntaxStr.value(); + } else { + this.syntax = syntaxB.toString(); + } + this.nonSenderAwareResolvers = nonSenderAwareResolvers; + this.optionalResolvers = optionalResolvers; + } + + public void invoke(CommandSender sender, List args) { + if (!scope.canExecute(sender, this)) { + return; + } + try { + Map passedArgs = Maps.newLinkedHashMap(); + for (int i = 0; i < parameters.length; i++) { + boolean isLast = i == parameters.length - 1; + final Parameter parameter = parameters[i]; + final String parameterName = parameter.getName(); + final Class type = parameter.getType(); + final ContextResolver resolver = resolvers[i]; + if (resolver != null) { + CommandExecutionContext context = new CommandExecutionContext(this, parameter, sender, args, i, passedArgs); + if (args.isEmpty() && !(isLast && type == String[].class)) { + Default def = parameter.getAnnotation(Default.class); + Optional opt = parameter.getAnnotation(Optional.class); + if (isLast && def != null) { + args.add(def.value()); + } else if (isLast && opt != null) { + passedArgs.put(parameterName, resolver instanceof SenderAwareContextResolver ? resolver.getContext(context) : null); + continue; + } else if (!(resolver instanceof SenderAwareContextResolver)) { + scope.showSyntax(sender, this); + return; + } + } else { + final Values values = parameter.getAnnotation(Values.class); + if (values != null) { + String arg = args.get(0); + + final String[] split = Patterns.PIPE.split(values.value()); + Set possible = Sets.newHashSet(); + for (String s : split) { + List check = CommandCompletions.of(sender, s, arg); + if (!check.isEmpty()) { + possible.addAll(check.stream().map(String::toLowerCase).collect(Collectors.toList())); + } else { + possible.add(s.toLowerCase()); + } + } + + if (!possible.contains(arg.toLowerCase())) { + throw new InvalidCommandArgument("Must be one of: " + CommandUtil.join(possible, ", ")); + } + } + } + passedArgs.put(parameterName, resolver.getContext(context)); + } else { + CommandUtil.sendMsg(sender, "&cUnexpected Error. Staff have been notified of the bug."); + return; + } + } + + method.invoke(scope, passedArgs.values().toArray()); + } catch (Exception e) { + if (e instanceof InvocationTargetException && e.getCause() instanceof InvalidCommandArgument) { + e = (Exception) e.getCause(); + } + if (e instanceof InvalidCommandArgument) { + + if (e.getMessage() != null && !e.getMessage().isEmpty()) { + CommandUtil.sendMsg(sender, "&cError: " + e.getMessage()); + } + if (((InvalidCommandArgument) e).showSyntax) { + scope.showSyntax(sender, this); + } + } else { + CommandUtil.sendMsg(sender, "&cI'm sorry, but there was an error performing this command."); + Log.exception("Exception in command: " + command + " " + CommandUtil.join(args), e.getCause()); + } + } + } + + public boolean hasPermission(CommandSender check) { + return perm == null || !(check instanceof Player) || check.hasPermission(perm.value()); + } +} diff --git a/src/main/java/co/aikar/commands/SneakyThrow.java b/src/main/java/co/aikar/commands/SneakyThrow.java new file mode 100644 index 00000000..1bdcb284 --- /dev/null +++ b/src/main/java/co/aikar/commands/SneakyThrow.java @@ -0,0 +1,16 @@ +package co.aikar.commands; + +public class SneakyThrow +{ + + public static void sneaky(Throwable t) + { + throw SneakyThrow.superSneaky( t ); + } + + private static T superSneaky(Throwable t) throws T + { + throw (T) t; + } +} + diff --git a/src/main/java/co/aikar/commands/UserUtil.java b/src/main/java/co/aikar/commands/UserUtil.java new file mode 100644 index 00000000..71bcad12 --- /dev/null +++ b/src/main/java/co/aikar/commands/UserUtil.java @@ -0,0 +1,68 @@ +package co.aikar.commands; + +import com.google.common.collect.Iterables; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class UserUtil { + public static Player findPlayerSmart(CommandSender requester, String origname) { + String name = CommandUtil.replace(origname, ":confirm", ""); + if (name.length() < 3) { + requester.sendMessage("§cUsername too short, must be at least three characters"); + return null; + } + if (!isValidName(name)) { + requester.sendMessage("§c'" + name + "' is not a valid username"); + return null; + } + + List matches = Bukkit.getServer().matchPlayer(name); + List confirmList = new ArrayList<>(); + + // Remove confirmList players from smart matching. + Iterator iter = matches.iterator(); + while (iter.hasNext()) { + Player player = iter.next(); + if (requester instanceof Player && !((Player) requester).canSee(player)) { + if (requester.hasPermission("command.seevanish")) { + if (!origname.endsWith(":confirm")) { + confirmList.add(player); + iter.remove(); + } + } else { + iter.remove(); + } + } + } + + if (matches.size() > 1 || confirmList.size() > 1) { + requester.sendMessage("§cMultiple players matched '" + name + "', please be more specific"); + return null; + } + + if (matches.isEmpty()) { + if (confirmList.isEmpty()) { + requester.sendMessage("§cNo player matching '" + name + "' is connected to this server"); + return null; + } else { + Player player = Iterables.getOnlyElement(confirmList); + CommandUtil.sendMsg(requester, + "&cWarning: " + player.getDisplayName() + "&c is confirmList. Do not blow their cover!\n" + + "&cTo confirm your action, add &f:confirm&c to the end of their name. \n" + + "&bEx: &e/g " + player.getName() + ":confirm"); + return null; + } + } + + return matches.get(0); + } + + public static boolean isValidName(String name) { + return name != null && !name.isEmpty() && Patterns.VALID_NAME_PATTERN.matcher(name).matches(); + } +} diff --git a/src/main/java/co/aikar/commands/annotation/CommandAlias.java b/src/main/java/co/aikar/commands/annotation/CommandAlias.java new file mode 100644 index 00000000..69d2a690 --- /dev/null +++ b/src/main/java/co/aikar/commands/annotation/CommandAlias.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface CommandAlias { + String value(); +} diff --git a/src/main/java/co/aikar/commands/annotation/CommandCompletion.java b/src/main/java/co/aikar/commands/annotation/CommandCompletion.java new file mode 100644 index 00000000..faacd0ba --- /dev/null +++ b/src/main/java/co/aikar/commands/annotation/CommandCompletion.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface CommandCompletion { + String value(); +} diff --git a/src/main/java/co/aikar/commands/annotation/CommandPermission.java b/src/main/java/co/aikar/commands/annotation/CommandPermission.java new file mode 100644 index 00000000..1de1db6f --- /dev/null +++ b/src/main/java/co/aikar/commands/annotation/CommandPermission.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface CommandPermission { + String value(); +} diff --git a/src/main/java/co/aikar/commands/annotation/Default.java b/src/main/java/co/aikar/commands/annotation/Default.java new file mode 100644 index 00000000..38252e47 --- /dev/null +++ b/src/main/java/co/aikar/commands/annotation/Default.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Default { + String value() default ""; +} diff --git a/src/main/java/co/aikar/commands/annotation/Flags.java b/src/main/java/co/aikar/commands/annotation/Flags.java new file mode 100644 index 00000000..e2c8dcae --- /dev/null +++ b/src/main/java/co/aikar/commands/annotation/Flags.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Flags { + String value(); +} diff --git a/src/main/java/co/aikar/commands/annotation/Optional.java b/src/main/java/co/aikar/commands/annotation/Optional.java new file mode 100644 index 00000000..098c892a --- /dev/null +++ b/src/main/java/co/aikar/commands/annotation/Optional.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Optional { +} diff --git a/src/main/java/co/aikar/commands/annotation/Single.java b/src/main/java/co/aikar/commands/annotation/Single.java new file mode 100644 index 00000000..21f026b0 --- /dev/null +++ b/src/main/java/co/aikar/commands/annotation/Single.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Don't join remaining arguments + */ + +@Retention(RetentionPolicy.RUNTIME) +public @interface Single {} diff --git a/src/main/java/co/aikar/commands/annotation/Split.java b/src/main/java/co/aikar/commands/annotation/Split.java new file mode 100644 index 00000000..35975336 --- /dev/null +++ b/src/main/java/co/aikar/commands/annotation/Split.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.annotation; + + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Split { + String value() default ","; +} diff --git a/src/main/java/co/aikar/commands/annotation/Subcommand.java b/src/main/java/co/aikar/commands/annotation/Subcommand.java new file mode 100644 index 00000000..0b961aa6 --- /dev/null +++ b/src/main/java/co/aikar/commands/annotation/Subcommand.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Subcommand { + String value(); +} diff --git a/src/main/java/co/aikar/commands/annotation/Syntax.java b/src/main/java/co/aikar/commands/annotation/Syntax.java new file mode 100644 index 00000000..1f740d7a --- /dev/null +++ b/src/main/java/co/aikar/commands/annotation/Syntax.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Syntax { + String value(); +} + diff --git a/src/main/java/co/aikar/commands/annotation/Values.java b/src/main/java/co/aikar/commands/annotation/Values.java new file mode 100644 index 00000000..7ec6073d --- /dev/null +++ b/src/main/java/co/aikar/commands/annotation/Values.java @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Values { + String value(); +} diff --git a/src/main/java/co/aikar/commands/contexts/CommandContexts.java b/src/main/java/co/aikar/commands/contexts/CommandContexts.java new file mode 100644 index 00000000..86fdcac7 --- /dev/null +++ b/src/main/java/co/aikar/commands/contexts/CommandContexts.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.contexts; + +import co.aikar.commands.InvalidCommandArgument; +import co.aikar.commands.annotation.Single; +import co.aikar.commands.annotation.Split; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.annotation.Values; +import co.aikar.commands.Log; +import co.aikar.commands.Patterns; +import co.aikar.commands.SneakyThrow; +import co.aikar.commands.UserUtil; +import co.aikar.commands.CommandUtil; +import com.google.common.collect.Maps; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Map; + +public final class CommandContexts { + private static final Map, ContextResolver> contextMap = Maps.newHashMap(); + + private CommandContexts() {} + + public static void initialize() { + registerContext(Integer.class, (c) -> { + try { + return CommandUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).intValue(); + } catch (NumberFormatException e) { + throw new InvalidCommandArgument("Must be a number"); + } + }); + registerContext(Long.class, (c) -> { + try { + return CommandUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).longValue(); + } catch (NumberFormatException e) { + throw new InvalidCommandArgument("Must be a number"); + } + + }); + registerContext(Float.class, (c) -> { + try { + return CommandUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).floatValue(); + } catch (NumberFormatException e) { + throw new InvalidCommandArgument("Must be a number"); + } + }); + registerContext(Double.class, (c) -> { + try { + return CommandUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")).doubleValue(); + } catch (NumberFormatException e) { + throw new InvalidCommandArgument("Must be a number"); + } + }); + registerContext(Number.class, (c) -> { + try { + return CommandUtil.parseNumber(c.popFirstArg(), c.hasFlag("suffixes")); + } catch (NumberFormatException e) { + throw new InvalidCommandArgument("Must be a number"); + } + }); + registerContext(Boolean.class, (c) -> CommandUtil.isTruthy(c.popFirstArg())); + registerContext(String.class, (c) -> { + final Values values = c.getParam().getAnnotation(Values.class); + if (values != null) { + return c.popFirstArg(); + } + if (c.isLastArg() && c.getParam().getAnnotation(Single.class) == null) { + return CommandUtil.join(c.getArgs()); + } + return c.popFirstArg(); + }); + registerContext(String[].class, (c) -> { + String val; + if (c.isLastArg() && c.getParam().getAnnotation(Single.class) == null) { + val = CommandUtil.join(c.getArgs()); + } else { + val = c.popFirstArg(); + } + Split split = c.getParam().getAnnotation(Split.class); + if (split != null) { + if (val.isEmpty()) { + throw new InvalidCommandArgument(); + } + return Patterns.getPattern(split.value()).split(val); + } else if (!c.isLastArg()) { + SneakyThrow.sneaky(new InvalidConfigurationException("Weird Command signature... String[] should be last or @Split")); + } + + String[] result = c.getArgs().toArray(new String[c.getArgs().size()]); + c.getArgs().clear(); + return result; + }); + + registerContext(OnlinePlayer.class, (c) -> { + final String playercheck = c.popFirstArg(); + Player player = UserUtil.findPlayerSmart(c.getSender(), playercheck); + if (player == null) { + CommandUtil.sendMsg(c.getSender(), "&cCould not find a player by the name " + playercheck); + throw new InvalidCommandArgument(false); + } + return new OnlinePlayer(player); + }); + registerSenderAwareContext(World.class, (c) -> { + String firstArg = c.getFirstArg(); + World world = firstArg != null ? Bukkit.getWorld(firstArg) : null; + if (world != null) { + c.popFirstArg(); + } + if (world == null && c.getSender() instanceof Player) { + world = ((Entity) c.getSender()).getWorld(); + } + if (world == null) { + throw new InvalidCommandArgument("Invalid World"); + } + return world; + }); + registerSenderAwareContext(CommandSender.class, CommandExecutionContext::getSender); + registerSenderAwareContext(Player.class, (c) -> { + Player player = c.getSender() instanceof Player ? (Player) c.getSender() : null; + if (player == null && !c.hasAnnotation(Optional.class)) { + throw new InvalidCommandArgument("Requires a player to run this command", false); + } + if (player != null && c.hasFlag("itemheld") && !isValidItem(player.getInventory().getItemInMainHand())) { + throw new InvalidCommandArgument("You must be holding an item in your main hand.", false); + } + return player; + }); + registerContext(Enum.class, (c) -> { + final String first = c.popFirstArg(); + Class> enumCls = (Class>) c.getParam().getType(); + Enum match = CommandUtil.simpleMatch(enumCls, first); + if (match == null) { + List names = CommandUtil.enumNames(enumCls); + throw new InvalidCommandArgument("Please specify one of: " + CommandUtil.join(names)); + } + return match; + }); + } + + public static void registerSenderAwareContext(Class context, SenderAwareContextResolver supplier) { + contextMap.put(context, supplier); + } + public static void registerContext(Class context, ContextResolver supplier) { + contextMap.put(context, supplier); + } + + public static ContextResolver getResolver(Class type) { + Class rootType = type; + do { + if (type == Object.class) { + break; + } + + final ContextResolver resolver = contextMap.get(type); + if (resolver != null) { + return resolver; + } + } while ((type = type.getSuperclass()) != null); + + Log.exception(new InvalidConfigurationException("No context resolver defined for " + rootType.getName())); + return null; + } + private static boolean isValidItem(ItemStack item) { + return item != null && item.getType() != Material.AIR && item.getAmount() > 0; + } +} diff --git a/src/main/java/co/aikar/commands/contexts/CommandExecutionContext.java b/src/main/java/co/aikar/commands/contexts/CommandExecutionContext.java new file mode 100644 index 00000000..d02c9ebf --- /dev/null +++ b/src/main/java/co/aikar/commands/contexts/CommandExecutionContext.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.contexts; + +import co.aikar.commands.RegisteredCommand; +import co.aikar.commands.annotation.Default; +import co.aikar.commands.annotation.Flags; +import co.aikar.commands.annotation.Optional; +import co.aikar.commands.Patterns; +import co.aikar.commands.CommandUtil; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; +import org.bukkit.command.CommandSender; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Parameter; +import java.util.List; +import java.util.Map; + +/*@Data*/ public class CommandExecutionContext { + private final RegisteredCommand cmd; + private final Parameter param; + private final CommandSender sender; + private final List args; + private final int index; + private final Map passedArgs; + private final Map flags; + + public CommandExecutionContext(RegisteredCommand cmd, Parameter param, CommandSender sender, List args, + int index, Map passedArgs) { + this.cmd = cmd; + this.param = param; + this.sender = sender; + this.args = args; + this.index = index; + this.passedArgs = passedArgs; + Flags flags = param.getAnnotation(Flags.class); + if (flags != null) { + this.flags = Maps.newHashMap(); + for (String s : Patterns.COMMA.split(flags.value())) { + String[] v = Patterns.EQUALS.split(s, 2); + this.flags.put(v[0], v.length > 1 ? v[1] : null); + } + } else { + this.flags = ImmutableMap.of(); + } + } + + public String popFirstArg() { + return !args.isEmpty() ? args.remove(0) : null; + } + + public String getFirstArg() { + return !args.isEmpty() ? args.get(0) : null; + } + + public boolean isLastArg() { + return cmd.parameters.length -1 == index; + } + + public int getNumParams() { + return cmd.parameters.length; + } + + public boolean canOverridePlayerContext() { + int numRequired = getNumParams(); + for (int i = 0; i < cmd.resolvers.length; i++) { + Parameter parameter = cmd.parameters[i]; + ContextResolver resolver = cmd.resolvers[i]; + if (parameter.getAnnotation(Optional.class) != null || parameter.getAnnotation(Default.class) != null) { + numRequired--; + } else if (resolver instanceof SenderAwareContextResolver) { + numRequired--; + } + } + + return numRequired >= args.size(); + } + + public Object getResolvedArg(String arg) { + return passedArgs.get(arg); + } + + public Object getResolvedArg(Class... classes) { + for (Class clazz : classes) { + for (Object passedArg : passedArgs.values()) { + if (clazz.isInstance(passedArg)) { + return passedArg; + } + } + } + + return null; + } + + public T getResolvedArg(String key, Class... classes) { + final Object o = passedArgs.get(key); + for (Class clazz : classes) { + if (clazz.isInstance(o)) { + return (T) o; + } + } + + return null; + } + + public boolean isOptional() { + return param.getAnnotation(Optional.class) != null; + } + public boolean hasFlag(String flag) { + return flags.containsKey(flag); + } + + public String getFlagValue(String flag, String def) { + return flags.containsKey(flag) ? flags.get(flag) : def; + } + + public Integer getFlagValue(String flag, Integer def) { + return CommandUtil.parseInt(this.flags.get(flag), def); + } + + public T getAnnotation(Class cls) { + return param.getAnnotation(cls); + } + + public boolean hasAnnotation(Class cls) { + return param.getAnnotation(cls) != null; + } + + public RegisteredCommand getCmd() { + return this.cmd; + } + + public Parameter getParam() { + return this.param; + } + + public CommandSender getSender() { + return this.sender; + } + + public List getArgs() { + return this.args; + } + + public int getIndex() { + return this.index; + } + + public Map getPassedArgs() { + return this.passedArgs; + } + + public Map getFlags() { + return this.flags; + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof CommandExecutionContext)) { + return false; + } + final CommandExecutionContext other = (CommandExecutionContext) o; + if (!other.canEqual(this)) { + return false; + } + final Object this$cmd = this.getCmd(); + final Object other$cmd = other.getCmd(); + if (this$cmd == null ? other$cmd != null : !this$cmd.equals(other$cmd)) { + return false; + } + final Object this$param = this.getParam(); + final Object other$param = other.getParam(); + if (this$param == null ? other$param != null : !this$param.equals(other$param)) { + return false; + } + final Object this$sender = this.getSender(); + final Object other$sender = other.getSender(); + if (this$sender == null ? other$sender != null : !this$sender.equals(other$sender)) { + return false; + } + final Object this$args = this.getArgs(); + final Object other$args = other.getArgs(); + if (this$args == null ? other$args != null : !this$args.equals(other$args)) { + return false; + } + if (this.getIndex() != other.getIndex()) { + return false; + } + final Object this$passedArgs = this.getPassedArgs(); + final Object other$passedArgs = other.getPassedArgs(); + if (this$passedArgs == null ? other$passedArgs != null : !this$passedArgs.equals(other$passedArgs)) { + return false; + } + final Object this$flags = this.getFlags(); + final Object other$flags = other.getFlags(); + if (this$flags == null ? other$flags != null : !this$flags.equals(other$flags)) { + return false; + } + return true; + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $cmd = this.getCmd(); + result = result * PRIME + ($cmd == null ? 43 : $cmd.hashCode()); + final Object $param = this.getParam(); + result = result * PRIME + ($param == null ? 43 : $param.hashCode()); + final Object $sender = this.getSender(); + result = result * PRIME + ($sender == null ? 43 : $sender.hashCode()); + final Object $args = this.getArgs(); + result = result * PRIME + ($args == null ? 43 : $args.hashCode()); + result = result * PRIME + this.getIndex(); + final Object $passedArgs = this.getPassedArgs(); + result = result * PRIME + ($passedArgs == null ? 43 : $passedArgs.hashCode()); + final Object $flags = this.getFlags(); + result = result * PRIME + ($flags == null ? 43 : $flags.hashCode()); + return result; + } + + protected boolean canEqual(Object other) { + return other instanceof CommandExecutionContext; + } + + public String toString() { + return "com.empireminecraft.commands.contexts.CommandExecutionContext(cmd=" + this.getCmd() + ", param=" + this.getParam() + ", sender=" + + this.getSender() + ", args=" + this.getArgs() + ", index=" + this.getIndex() + ", passedArgs=" + this.getPassedArgs() + ", flags=" + + this.getFlags() + ")"; + } +} diff --git a/src/main/java/co/aikar/commands/contexts/ContextResolver.java b/src/main/java/co/aikar/commands/contexts/ContextResolver.java new file mode 100644 index 00000000..48946ec7 --- /dev/null +++ b/src/main/java/co/aikar/commands/contexts/ContextResolver.java @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.contexts; + +import co.aikar.commands.InvalidCommandArgument; + +@FunctionalInterface +public interface ContextResolver { + C getContext(CommandExecutionContext c) throws InvalidCommandArgument; +} diff --git a/src/main/java/co/aikar/commands/contexts/OnlinePlayer.java b/src/main/java/co/aikar/commands/contexts/OnlinePlayer.java new file mode 100644 index 00000000..d8d69e96 --- /dev/null +++ b/src/main/java/co/aikar/commands/contexts/OnlinePlayer.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.contexts; + +import org.bukkit.entity.Player; + +/*@Data*/ public class OnlinePlayer { + public final Player player; + + public OnlinePlayer(Player player) { + this.player = player; + } + + public Player getPlayer() { + return this.player; + } + + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof OnlinePlayer)) { + return false; + } + final OnlinePlayer other = (OnlinePlayer) o; + if (!other.canEqual(this)) { + return false; + } + final Object this$player = this.getPlayer(); + final Object other$player = other.getPlayer(); + if (this$player == null ? other$player != null : !this$player.equals(other$player)) { + return false; + } + return true; + } + + public int hashCode() { + final int PRIME = 59; + int result = 1; + final Object $player = this.getPlayer(); + result = result * PRIME + ($player == null ? 43 : $player.hashCode()); + return result; + } + + protected boolean canEqual(Object other) { + return other instanceof OnlinePlayer; + } + + public String toString() { + return "com.empireminecraft.commands.contexts.OnlinePlayer(player=" + this.getPlayer() + ")"; + } +} diff --git a/src/main/java/co/aikar/commands/contexts/SenderAwareContextResolver.java b/src/main/java/co/aikar/commands/contexts/SenderAwareContextResolver.java new file mode 100644 index 00000000..3edfa4b3 --- /dev/null +++ b/src/main/java/co/aikar/commands/contexts/SenderAwareContextResolver.java @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2016. Starlis LLC / dba Empire Minecraft + * + * This source code is proprietary software and must not be redistributed without Starlis LLC's approval + * + */ + +package co.aikar.commands.contexts; + +public interface SenderAwareContextResolver extends ContextResolver {}