diff --git a/bukkit/pom.xml b/bukkit/pom.xml index c984cfb6..be07b11e 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -30,12 +30,12 @@ co.aikar acf-parent - 0.5.0 + 0.5.0-SNAPSHOT ../pom.xml acf-bukkit - 0.5.0 + 0.5.0-SNAPSHOT ACF (Bukkit) @@ -43,7 +43,8 @@ co.aikar acf-core - 0.5.0 + 0.5.0-SNAPSHOT + compile org.bukkit diff --git a/bukkit/src/main/java/co/aikar/commands/ACFBukkitUtil.java b/bukkit/src/main/java/co/aikar/commands/ACFBukkitUtil.java index f402f014..7827e731 100644 --- a/bukkit/src/main/java/co/aikar/commands/ACFBukkitUtil.java +++ b/bukkit/src/main/java/co/aikar/commands/ACFBukkitUtil.java @@ -31,6 +31,7 @@ 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.bukkit.inventory.ItemStack; @@ -61,7 +62,7 @@ public class ACFBukkitUtil { return ChatColor.translateAlternateColorCodes('&', message); } - public static void sendMsg(CommandIssuer player, String message) { + public static void sendMsg(CommandSender player, String message) { message = color(message); if (player == null) { for (String msg : ACFPatterns.NEWLINE.split(message)) { @@ -239,7 +240,7 @@ public class ACFBukkitUtil { return loc1.getWorld() == loc2.getWorld() && loc1.distance(loc2) <= dist; } - public static Player findPlayerSmart(CommandIssuer requester, String origName) { + public static Player findPlayerSmart(CommandSender requester, String origName) { String name = ACFUtil.replace(origName, ":confirm", ""); if (name.length() < 3) { requester.sendMessage("§cUsername too short, must be at least three characters"); diff --git a/bukkit/src/main/java/co/aikar/commands/BukkitCommandCompletionContext.java b/bukkit/src/main/java/co/aikar/commands/BukkitCommandCompletionContext.java new file mode 100644 index 00000000..df78c6bb --- /dev/null +++ b/bukkit/src/main/java/co/aikar/commands/BukkitCommandCompletionContext.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package co.aikar.commands; + +import org.bukkit.command.CommandSender; + +public class BukkitCommandCompletionContext extends CommandCompletionContext { + BukkitCommandCompletionContext(RegisteredCommand command, CommandIssuer issuer, String input, String config, String[] args) { + super(command, issuer, input, config, args); + } + + public CommandSender getSender() { + return this.getIssuer().getIssuer(); + } +} diff --git a/bukkit/src/main/java/co/aikar/commands/BukkitCommandCompletions.java b/bukkit/src/main/java/co/aikar/commands/BukkitCommandCompletions.java index 7b49d28b..53a8d8ca 100644 --- a/bukkit/src/main/java/co/aikar/commands/BukkitCommandCompletions.java +++ b/bukkit/src/main/java/co/aikar/commands/BukkitCommandCompletions.java @@ -27,21 +27,19 @@ import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.World; +import org.bukkit.command.CommandSender; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.util.StringUtil; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @SuppressWarnings("WeakerAccess") -public class BukkitCommandCompletions extends CommandCompletions { +public class BukkitCommandCompletions extends CommandCompletions { public BukkitCommandCompletions(BukkitCommandManager manager) { super(manager); registerCompletion("mobs", (sender, config, input, c) -> { @@ -74,7 +72,7 @@ public class BukkitCommandCompletions extends CommandCompletions { Player senderPlayer = sender instanceof Player ? (Player) sender : null; ArrayList matchedPlayers = new ArrayList(); - for (Player player : sender.getServer().getOnlinePlayers()) { + for (Player player : Bukkit.getOnlinePlayers()) { String name = player.getName(); if ((senderPlayer == null || senderPlayer.canSee(player)) && StringUtil.startsWithIgnoreCase(name, input)) { matchedPlayers.add(name); diff --git a/bukkit/src/main/java/co/aikar/commands/BukkitCommandContexts.java b/bukkit/src/main/java/co/aikar/commands/BukkitCommandContexts.java index 5e9a452b..85f9add4 100644 --- a/bukkit/src/main/java/co/aikar/commands/BukkitCommandContexts.java +++ b/bukkit/src/main/java/co/aikar/commands/BukkitCommandContexts.java @@ -46,12 +46,12 @@ public class BukkitCommandContexts extends CommandContexts { final String playercheck = c.popFirstArg(); - Player player = ACFBukkitUtil.findPlayerSmart(c.getIssuer(), playercheck); + Player player = ACFBukkitUtil.findPlayerSmart(c.getSender(), playercheck); if (player == null) { if (c.hasAnnotation(Optional.class)) { return null; } - ACFBukkitUtil.sendMsg(c.getIssuer(), "&cCould not find a player by the name " + playercheck); + ACFBukkitUtil.sendMsg(c.getSender(), "&cCould not find a player by the name " + playercheck); throw new InvalidCommandArgument(false); } return new OnlinePlayer(player); @@ -62,17 +62,17 @@ public class BukkitCommandContexts extends CommandContexts bukkitCommandExecutionContext.getSender()); + registerSenderAwareContext(CommandSender.class, BukkitCommandExecutionContext::getSender); registerSenderAwareContext(Player.class, (c) -> { - Player player = c.getIssuer() instanceof Player ? (Player) c.getIssuer() : null; + 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); } diff --git a/bukkit/src/main/java/co/aikar/commands/BukkitCommandManager.java b/bukkit/src/main/java/co/aikar/commands/BukkitCommandManager.java index 272c8627..72c0ba2d 100644 --- a/bukkit/src/main/java/co/aikar/commands/BukkitCommandManager.java +++ b/bukkit/src/main/java/co/aikar/commands/BukkitCommandManager.java @@ -31,6 +31,7 @@ import org.bukkit.command.CommandException; import org.bukkit.command.CommandMap; import org.bukkit.command.CommandSender; import org.bukkit.command.SimpleCommandMap; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.server.PluginDisableEvent; @@ -39,7 +40,10 @@ import org.bukkit.plugin.Plugin; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Parameter; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; @SuppressWarnings("WeakerAccess") public class BukkitCommandManager extends CommandManager { @@ -50,8 +54,8 @@ public class BukkitCommandManager extends CommandManager { private final TimingManager timingManager; protected Map knownCommands = new HashMap<>(); protected Map registeredCommands = new HashMap<>(); - protected CommandContexts contexts; - protected CommandCompletions completions; + protected BukkitCommandContexts contexts; + protected BukkitCommandCompletions completions; public BukkitCommandManager(Plugin plugin) { this.plugin = plugin; @@ -87,7 +91,12 @@ public class BukkitCommandManager extends CommandManager { } @Override - public synchronized CommandContexts getCommandContexts() { + public boolean isCommandIssuer(Class type) { + return CommandSender.class.isAssignableFrom(type); + } + + @Override + public synchronized CommandContexts getCommandContexts() { if (this.contexts == null) { this.contexts = new BukkitCommandContexts(this); } @@ -95,7 +104,7 @@ public class BukkitCommandManager extends CommandManager { } @Override - public synchronized CommandCompletions getCommandCompletions() { + public synchronized CommandCompletions getCommandCompletions() { if (this.completions == null) { this.completions = new BukkitCommandCompletions(this); } @@ -155,7 +164,6 @@ public class BukkitCommandManager extends CommandManager { } } - @Override public TimingManager getTimings() { return timingManager; } @@ -166,8 +174,21 @@ public class BukkitCommandManager extends CommandManager { } @Override - public CommandExecutionContext createCommandContext(RegisteredCommand command, Parameter parameter, CommandIssuer sender, List args, int i, Map passedArgs) { - return new BukkitCommandExecutionContext(command, parameter, sender, args, i, passedArgs); + public CommandIssuer getCommandIssuer(Object issuer) { + if (!(issuer instanceof CommandSender)) { + throw new IllegalArgumentException(issuer.getClass().getName() + " is not a Command Issuer."); + } + return new BukkitCommandIssuer((CommandSender) issuer); + } + + @Override + public R createCommandContext(RegisteredCommand command, Parameter parameter, CommandIssuer sender, List args, int i, Map passedArgs) { + return (R) new BukkitCommandExecutionContext(command, parameter, sender, args, i, passedArgs); + } + + @Override + public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) { + return new RegisteredCommand(command, cmdName, method, prefSubCommand); } class ProxyCommandMap extends SimpleCommandMap { diff --git a/bukkit/src/main/java/co/aikar/commands/BukkitRegisteredCommand.java b/bukkit/src/main/java/co/aikar/commands/BukkitRegisteredCommand.java new file mode 100644 index 00000000..7a46daa5 --- /dev/null +++ b/bukkit/src/main/java/co/aikar/commands/BukkitRegisteredCommand.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package co.aikar.commands; + +import co.aikar.timings.lib.MCTiming; + +import java.lang.reflect.Method; + +public class BukkitRegisteredCommand extends RegisteredCommand { + private final MCTiming timing; + BukkitRegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) { + super(scope, command, method, prefSubCommand); + this.timing = ((BukkitCommandManager) scope.manager).getTimings().of("Command: " + command); + } + + + @Override + public void preCommand() { + timing.startTiming(); + super.preCommand(); + } + + @Override + public void postCommand() { + super.postCommand(); + timing.stopTiming(); + } +} diff --git a/core/acf-core.iml b/core/acf-core.iml index e937ca62..5882babb 100644 --- a/core/acf-core.iml +++ b/core/acf-core.iml @@ -17,7 +17,7 @@ - + \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml index ed5fdfc0..3e55f8d1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -30,12 +30,12 @@ co.aikar acf-parent - 0.5.0 + 0.5.0-SNAPSHOT ../pom.xml acf-core - 0.5.0 + 0.5.0-SNAPSHOT ACF (Core) diff --git a/core/src/main/java/co/aikar/commands/BaseCommand.java b/core/src/main/java/co/aikar/commands/BaseCommand.java index 8b314bde..5fa49ad4 100644 --- a/core/src/main/java/co/aikar/commands/BaseCommand.java +++ b/core/src/main/java/co/aikar/commands/BaseCommand.java @@ -26,10 +26,10 @@ 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.PreCommand; import co.aikar.commands.annotation.Subcommand; import co.aikar.commands.annotation.UnknownHandler; import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil; -import co.aikar.timings.lib.MCTiming; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @@ -59,6 +59,7 @@ public abstract class BaseCommand { public static final String UNKNOWN = "__unknown"; public static final String DEFAULT = "__default"; final SetMultimap subCommands = HashMultimap.create(); + Method preCommandHandler; @SuppressWarnings("WeakerAccess") private String execLabel; @@ -73,6 +74,11 @@ public abstract class BaseCommand { String usageMessage; String permission; + public BaseCommand() {} + public BaseCommand(String cmd) { + this.commandName = cmd; + } + /** * Gets the root command name that the user actually typed * @return Name @@ -98,7 +104,7 @@ public abstract class BaseCommand { } void onRegister(CommandManager manager) { - onRegister(manager, null); + onRegister(manager, this.commandName); } void onRegister(CommandManager manager, String cmd) { this.manager = manager; @@ -141,18 +147,21 @@ public abstract class BaseCommand { ACFUtil.sneaky(new IllegalStateException("Multiple @Default commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); } } + + + + if (sub != null) { sublist = sub; } else if (commandAliases != null) { sublist = commandAliases.value(); } - if (sublist != null) { - registerSubcommand(method, sublist); - } //CommandIssuer.class, String.class, String[].class UnknownHandler unknown = method.getAnnotation(UnknownHandler.class); + //CommandIssuer.class, String.class, String[].class + PreCommand preCommand = method.getAnnotation(PreCommand.class); if (unknown != null) { if (!foundUnknown) { registerSubcommand(method, UNKNOWN); @@ -160,6 +169,14 @@ public abstract class BaseCommand { } else { ACFUtil.sneaky(new IllegalStateException("Multiple @UnknownHandler commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); } + } else if (preCommand != null) { + if (this.preCommandHandler == null) { + this.preCommandHandler = method; + } else { + ACFUtil.sneaky(new IllegalStateException("Multiple @PreCommand commands, duplicate on " + method.getDeclaringClass().getName() + "#" + method.getName())); + } + } else if (sublist != null) { + registerSubcommand(method, sublist); } } @@ -246,7 +263,7 @@ public abstract class BaseCommand { final String[] aliasNames = cmdAlias != null ? ACFPatterns.PIPE.split(manager.getCommandReplacements().replace(cmdAlias.value().toLowerCase())) : null; String cmdName = aliasNames != null ? aliasNames[0] : this.commandName + " "; - RegisteredCommand cmd = new RegisteredCommand(this, cmdName, method, prefSubCommand); + RegisteredCommand cmd = manager.createRegisteredCommand(this, cmdName, method, prefSubCommand); for (String subcmd : cmdList) { subCommands.put(subcmd, cmd); @@ -305,7 +322,7 @@ public abstract class BaseCommand { origArgs = args; if (args.length == 0) { - if (preCommand(sender, commandLabel, args)) { + if (checkPrecommand(sender, args)) { return true; } executeSubcommand(DEFAULT, sender); @@ -316,7 +333,7 @@ public abstract class BaseCommand { if (cmd != null) { execSubcommand = cmd.getCheckSub(); final String[] execargs = Arrays.copyOfRange(args, cmd.argIndex, args.length); - if (preCommand(sender, commandLabel, execargs)) { + if (checkPrecommand(sender, execargs)) { return true; } executeCommand(sender, execargs, cmd.cmd); @@ -371,15 +388,13 @@ public abstract class BaseCommand { private static void executeCommand(CommandIssuer sender, String[] args, RegisteredCommand cmd) { if (cmd.hasPermission(sender)) { List sargs = Lists.newArrayList(args); - try (MCTiming timing = cmd.getTiming().startTiming()) { - cmd.invoke(sender, sargs); - } + cmd.invoke(sender, sargs); } else { sender.sendMessage("&cI'm sorry, but you do not have permission to perform this command."); } } - public boolean canExecute(CommandIssuer sender, RegisteredCommand cmd) { + public boolean canExecute(CommandIssuer sender, RegisteredCommand cmd) { return true; } @@ -416,12 +431,9 @@ public abstract class BaseCommand { } private List completeCommand(CommandIssuer sender, RegisteredCommand cmd, String[] args, String commandLabel) { - if (args.length > cmd.requiredResolvers + cmd.optionalResolvers) { + if (args.length > cmd.requiredResolvers + cmd.optionalResolvers || args.length == 0 || cmd.complete == null) { return ImmutableList.of(); } - if (args.length == 0 || cmd.complete == null) { - return args.length < 2 ? null : ImmutableList.of(); - } String[] completions = ACFPatterns.SPACE.split(cmd.complete); @@ -436,9 +448,6 @@ public abstract class BaseCommand { .collect(Collectors.toList()); } - public void help(CommandIssuer sender, String[] args) { - sender.sendMessage("&cUnknown Command, please type &f/help"); - } private boolean executeSubcommand(String subcommand, CommandIssuer sender, String... args) { final Set defs = subCommands.get(subcommand); @@ -455,24 +464,44 @@ public abstract class BaseCommand { return false; } - public boolean preCommand(CommandIssuer sender, String commandLabel, String[] args) { + private boolean checkPrecommand(CommandIssuer sender, String[] args) { + if (this.preCommandHandler != null) { + try { + if (this.preCommandHandler.getParameterCount() == 1) { + return (boolean) preCommandHandler.invoke(this, new Object[]{ sender.getIssuer() }); + } else { + return (boolean) preCommandHandler.invoke(this, sender.getIssuer(), args); + } + } catch (IllegalAccessException | InvocationTargetException e) { + ACFLog.exception(e); + } + } return false; } + public void help(Object sender, String[] args) { + help(manager.getCommandIssuer(sender), args); + } + public void help(CommandIssuer sender, String[] args) { + sender.sendMessage("&cUnknown Command, please type &f/help"); + } + public void doHelp(Object sender, String... args) { + doHelp(manager.getCommandIssuer(sender), args); + } public void doHelp(CommandIssuer sender, String... args) { help(sender, args); } - public void showSyntax(CommandIssuer sender, RegisteredCommand cmd) { + public void showSyntax(CommandIssuer sender, RegisteredCommand cmd) { sender.sendMessage("&cUsage: /" + cmd.command + " " + cmd.syntaxText); } + public boolean testPermission(Object sender) { + return testPermission(manager.getCommandIssuer(sender)); + } + public boolean testPermission(CommandIssuer sender) { - if (permission != null && !permission.isEmpty() && !sender.hasPermission(permission)) { - // TODO: Msg - return false; - } - return true; + return permission == null || permission.isEmpty() || sender.hasPermission(permission); } public String getName() { diff --git a/core/src/main/java/co/aikar/commands/CommandCompletionContext.java b/core/src/main/java/co/aikar/commands/CommandCompletionContext.java new file mode 100644 index 00000000..b647a5de --- /dev/null +++ b/core/src/main/java/co/aikar/commands/CommandCompletionContext.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package co.aikar.commands; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import java.lang.reflect.Parameter; +import java.util.List; +import java.util.Map; + +public class CommandCompletionContext { + private final RegisteredCommand command; + private final CommandIssuer issuer; + private final String input; + private final String config; + private final Map configs = Maps.newHashMap(); + private final List args; + + CommandCompletionContext(RegisteredCommand command, CommandIssuer issuer, String input, String config, String[] args) { + this.command = command; + this.issuer = issuer; + this.input = input; + if (config != null) { + String[] configs = ACFPatterns.COMMA.split(config); + for (String conf : configs) { + String[] confsplit = ACFPatterns.EQUALS.split(conf, 2); + this.configs.put(confsplit[0].toLowerCase(), confsplit.length > 1 ? confsplit[1] : null); + } + this.config = configs[0]; + } else { + this.config = null; + } + + this.args = Lists.newArrayList(args); + } + + public Map getConfigs() { + return configs; + } + + public String getConfig(String key) { + return getConfig(key, null); + } + + public String getConfig(String key, String def) { + return this.configs.getOrDefault(key.toLowerCase(), def); + } + + public boolean hasConfig(String key) { + return this.configs.containsKey(key.toLowerCase()); + } + + public T getContextValue(Class clazz) throws InvalidCommandArgument { + return getContextValue(clazz, null); + } + + public T getContextValue(Class clazz, Integer paramIdx) throws InvalidCommandArgument { + String name = null; + if (paramIdx != null) { + if (paramIdx >= command.parameters.length) { + throw new IllegalArgumentException("Param index is higher than number of parameters"); + } + Parameter param = command.parameters[paramIdx]; + Class paramType = param.getType(); + if (!clazz.isAssignableFrom(paramType)) { + throw new IllegalArgumentException(param.getName() +":" + paramType.getName() + " can not satisfy " + clazz.getName()); + } + name = param.getName(); + } else { + Parameter[] parameters = command.parameters; + for (int i = 0; i < parameters.length; i++) { + Parameter param = parameters[i]; + if (clazz.isAssignableFrom(param.getType())) { + paramIdx = i; + name = param.getName(); + break; + } + } + if (paramIdx == null) { + throw new IllegalStateException("Can not find any parameter that can satisfy " + clazz.getName()); + } + } + //noinspection unchecked + Map resolved = command.resolveContexts(issuer, args, args.size()); + if (resolved == null || paramIdx > resolved.size()) { + ACFLog.error("resolved: " + resolved + " paramIdx: " + paramIdx + " - size: " + (resolved != null ? resolved.size() : null )); + ACFUtil.sneaky(new CommandCompletionTextLookupException()); + } + + //noinspection unchecked + return (T) resolved.get(name); + } + + public CommandIssuer getIssuer() { + return issuer; + } + + public String getInput() { + return input; + } + + public String getConfig() { + return config; + } +} diff --git a/core/src/main/java/co/aikar/commands/CommandCompletionTextLookupException.java b/core/src/main/java/co/aikar/commands/CommandCompletionTextLookupException.java new file mode 100644 index 00000000..c9d37b7b --- /dev/null +++ b/core/src/main/java/co/aikar/commands/CommandCompletionTextLookupException.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package co.aikar.commands; + +class CommandCompletionTextLookupException extends Throwable { +} diff --git a/core/src/main/java/co/aikar/commands/CommandCompletions.java b/core/src/main/java/co/aikar/commands/CommandCompletions.java index e4b70bf5..ded728d4 100644 --- a/core/src/main/java/co/aikar/commands/CommandCompletions.java +++ b/core/src/main/java/co/aikar/commands/CommandCompletions.java @@ -23,13 +23,10 @@ package co.aikar.commands; -import co.aikar.commands.annotation.Split; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.Parameter; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -39,7 +36,7 @@ import java.util.stream.IntStream; @SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) -public class CommandCompletions { +public class CommandCompletions { private final CommandManager manager; private Map completionMap = new HashMap<>(); @@ -64,7 +61,7 @@ public class CommandCompletions { registerCompletion("timeunits", (sender, config, input, c) -> ImmutableList.of("minutes", "hours", "days", "weeks", "months", "years")); } - public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler handler) { + public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler handler) { return this.completionMap.put("@" + id.toLowerCase(), handler); } @@ -121,105 +118,8 @@ public class CommandCompletions { return allCompletions; } - public interface CommandCompletionHandler { - Collection getCompletions(CommandIssuer sender, String config, String input, CommandCompletionContext context) throws InvalidCommandArgument; + public interface CommandCompletionHandler { + Collection getCompletions(I sender, String config, String input, C context) throws InvalidCommandArgument; } - public class CommandCompletionContext { - private final RegisteredCommand command; - private final CommandIssuer sender; - private final String input; - private final String config; - private final Map configs = Maps.newHashMap(); - private final List args; - - CommandCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args) { - this.command = command; - this.sender = sender; - this.input = input; - if (config != null) { - String[] configs = ACFPatterns.COMMA.split(config); - for (String conf : configs) { - String[] confsplit = ACFPatterns.EQUALS.split(conf, 2); - this.configs.put(confsplit[0].toLowerCase(), confsplit.length > 1 ? confsplit[1] : null); - } - this.config = configs[0]; - } else { - this.config = null; - } - - this.args = Lists.newArrayList(args); - } - - public Map getConfigs() { - return configs; - } - - public String getConfig(String key) { - return getConfig(key, null); - } - - public String getConfig(String key, String def) { - return this.configs.getOrDefault(key.toLowerCase(), def); - } - - public boolean hasConfig(String key) { - return this.configs.containsKey(key.toLowerCase()); - } - - public T getContextValue(Class clazz) throws InvalidCommandArgument { - return getContextValue(clazz, null); - } - - public T getContextValue(Class clazz, Integer paramIdx) throws InvalidCommandArgument { - String name = null; - if (paramIdx != null) { - if (paramIdx >= command.parameters.length) { - throw new IllegalArgumentException("Param index is higher than number of parameters"); - } - Parameter param = command.parameters[paramIdx]; - Class paramType = param.getType(); - if (!clazz.isAssignableFrom(paramType)) { - throw new IllegalArgumentException(param.getName() +":" + paramType.getName() + " can not satisfy " + clazz.getName()); - } - name = param.getName(); - } else { - Parameter[] parameters = command.parameters; - for (int i = 0; i < parameters.length; i++) { - Parameter param = parameters[i]; - if (clazz.isAssignableFrom(param.getType())) { - paramIdx = i; - name = param.getName(); - break; - } - } - if (paramIdx == null) { - throw new IllegalStateException("Can not find any parameter that can satisfy " + clazz.getName()); - } - } - Map resolved = command.resolveContexts(sender, args, args.size()); - if (resolved == null || paramIdx > resolved.size()) { - ACFLog.error("resolved: " + resolved + " paramIdx: " + paramIdx + " - size: " + (resolved != null ? resolved.size() : null )); - ACFUtil.sneaky(new CommandCompletionTextLookupException()); - } - - //noinspection unchecked - return (T) resolved.get(name); - } - - public CommandIssuer getSender() { - return sender; - } - - public String getInput() { - return input; - } - - public String getConfig() { - return config; - } - } - - private class CommandCompletionTextLookupException extends Throwable { - } } diff --git a/core/src/main/java/co/aikar/commands/CommandContexts.java b/core/src/main/java/co/aikar/commands/CommandContexts.java index 171d3dc8..04f9273b 100644 --- a/core/src/main/java/co/aikar/commands/CommandContexts.java +++ b/core/src/main/java/co/aikar/commands/CommandContexts.java @@ -34,7 +34,7 @@ import java.util.List; import java.util.Map; @SuppressWarnings("WeakerAccess") -public class CommandContexts { +public class CommandContexts > { protected final Map, ContextResolver> contextMap = Maps.newHashMap(); private final CommandManager manager; @@ -140,10 +140,10 @@ public class CommandContexts { }); } - void registerSenderAwareContext(Class context, SenderAwareContextResolver supplier) { + public void registerSenderAwareContext(Class context, SenderAwareContextResolver supplier) { contextMap.put(context, supplier); } - void registerContext(Class context, ContextResolver supplier) { + public void registerContext(Class context, ContextResolver supplier) { contextMap.put(context, supplier); } diff --git a/core/src/main/java/co/aikar/commands/CommandManager.java b/core/src/main/java/co/aikar/commands/CommandManager.java index 028cb8f5..0e8b9b51 100644 --- a/core/src/main/java/co/aikar/commands/CommandManager.java +++ b/core/src/main/java/co/aikar/commands/CommandManager.java @@ -23,15 +23,14 @@ package co.aikar.commands; -import co.aikar.timings.lib.TimingManager; - +import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.HashMap; import java.util.List; import java.util.Map; @SuppressWarnings("WeakerAccess") -public abstract class CommandManager { +abstract class CommandManager { protected Map rootCommands = new HashMap<>(); protected CommandReplacements replacements = new CommandReplacements(this); @@ -40,13 +39,13 @@ public abstract class CommandManager { * Gets the command contexts manager * @return Command Contexts */ - public abstract CommandContexts getCommandContexts(); + public abstract CommandContexts getCommandContexts(); /** * Gets the command completions manager * @return Command Completions */ - public abstract CommandCompletions getCommandCompletions(); + public abstract CommandCompletions getCommandCompletions(); /** * Lets you add custom string replacements that can be applied to annotation values, @@ -69,13 +68,18 @@ public abstract class CommandManager { */ public abstract void registerCommand(BaseCommand command); public abstract boolean hasRegisteredCommands(); - - public abstract TimingManager getTimings(); + public abstract boolean isCommandIssuer(Class type); + public abstract CommandIssuer getCommandIssuer(Object issuer); public abstract RootCommand createRootCommand(String cmd); + public synchronized RootCommand obtainRootCommand(String cmd) { return rootCommands.computeIfAbsent(cmd.toLowerCase(), this::createRootCommand); } - public abstract CommandExecutionContext createCommandContext(RegisteredCommand command, Parameter parameter, CommandIssuer sender, List args, int i, Map passedArgs); + public abstract R createCommandContext(RegisteredCommand command, Parameter parameter, CommandIssuer sender, List args, int i, Map passedArgs); + + public RegisteredCommand createRegisteredCommand(BaseCommand command, String cmdName, Method method, String prefSubCommand) { + return new RegisteredCommand(command, cmdName, method, prefSubCommand); + } } diff --git a/core/src/main/java/co/aikar/commands/RegisteredCommand.java b/core/src/main/java/co/aikar/commands/RegisteredCommand.java index febced29..056c97cd 100644 --- a/core/src/main/java/co/aikar/commands/RegisteredCommand.java +++ b/core/src/main/java/co/aikar/commands/RegisteredCommand.java @@ -32,7 +32,6 @@ import co.aikar.commands.annotation.Syntax; import co.aikar.commands.annotation.Values; import co.aikar.commands.contexts.ContextResolver; import co.aikar.commands.contexts.SenderAwareContextResolver; -import co.aikar.timings.lib.MCTiming; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -47,20 +46,19 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -public class RegisteredCommand { +public class RegisteredCommand > { final BaseCommand scope; public final String command; private final Method method; final String prefSubCommand; final Parameter[] parameters; - final ContextResolver[] resolvers; + final ContextResolver[] resolvers; final String syntaxText; private final String permission; final String complete; final int requiredResolvers; final int optionalResolvers; - private MCTiming timing; RegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) { this.scope = scope; @@ -75,6 +73,7 @@ public class RegisteredCommand { CommandCompletion completionAnno = method.getAnnotation(CommandCompletion.class); this.complete = completionAnno != null ? scope.manager.getCommandReplacements().replace(completionAnno.value()) : null; this.parameters = method.getParameters(); + //noinspection unchecked this.resolvers = new ContextResolver[this.parameters.length]; final Syntax syntaxStr = method.getAnnotation(Syntax.class); final CommandManager manager = scope.manager; @@ -88,17 +87,19 @@ public class RegisteredCommand { final Parameter parameter = parameters[i]; final Class type = parameter.getType(); - final ContextResolver resolver = commandContexts.getResolver(type); + //noinspection unchecked + final ContextResolver resolver = commandContexts.getResolver(type); if (resolver != null) { resolvers[i] = resolver; - if (!CommandIssuer.class.isAssignableFrom(parameter.getType())) { + if (!scope.manager.isCommandIssuer(type)) { + String name = parameter.getName(); if (isOptionalResolver(resolver, parameter)) { optionalResolvers++; - syntaxB.append('[').append(parameter.getName()).append("] "); + syntaxB.append('[').append(name).append("] "); } else { requiredResolvers++; - syntaxB.append('<').append(parameter.getName()).append("> "); + syntaxB.append('<').append(name).append("> "); } } } else { @@ -116,7 +117,7 @@ public class RegisteredCommand { this.optionalResolvers = optionalResolvers; } - static boolean isOptionalResolver(ContextResolver resolver, Parameter parameter) { + private boolean isOptionalResolver(ContextResolver resolver, Parameter parameter) { return resolver instanceof SenderAwareContextResolver || parameter.getAnnotation(Optional.class) != null || parameter.getAnnotation(Default.class) != null; @@ -126,6 +127,7 @@ public class RegisteredCommand { if (!scope.canExecute(sender, this)) { return; } + preCommand(); try { Map passedArgs = resolveContexts(sender, args); if (passedArgs == null) return; @@ -134,7 +136,10 @@ public class RegisteredCommand { } catch (Exception e) { handleException(sender, args, e); } + postCommand(); } + public void preCommand() {} + public void postCommand() {} void handleException(CommandIssuer sender, List args, Exception e) { if (e instanceof InvocationTargetException && e.getCause() instanceof InvalidCommandArgument) { @@ -169,8 +174,9 @@ public class RegisteredCommand { final Parameter parameter = parameters[i]; final String parameterName = parameter.getName(); final Class type = parameter.getType(); - final ContextResolver resolver = resolvers[i]; - CommandExecutionContext context = this.scope.manager.createCommandContext(this, parameter, sender, args, i, passedArgs); + //noinspection unchecked + final ContextResolver resolver = resolvers[i]; + R context = this.scope.manager.createCommandContext(this, parameter, sender, args, i, passedArgs); if (!isOptionalResolver(resolver, parameter)) { remainingRequired--; } @@ -212,13 +218,6 @@ public class RegisteredCommand { return passedArgs; } - MCTiming getTiming() { - if (this.timing == null) { - this.timing = scope.manager.getTimings().of("Command: " + command); - } - return this.timing; - } - boolean hasPermission(CommandIssuer check) { return permission == null || !check.isPlayer() || check.hasPermission(permission); } diff --git a/core/src/main/java/co/aikar/commands/annotation/PreCommand.java b/core/src/main/java/co/aikar/commands/annotation/PreCommand.java new file mode 100644 index 00000000..04c41aa0 --- /dev/null +++ b/core/src/main/java/co/aikar/commands/annotation/PreCommand.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package co.aikar.commands.annotation; + + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface PreCommand {} diff --git a/core/src/main/java/co/aikar/commands/contexts/ContextResolver.java b/core/src/main/java/co/aikar/commands/contexts/ContextResolver.java index 9f194889..f2f22e37 100644 --- a/core/src/main/java/co/aikar/commands/contexts/ContextResolver.java +++ b/core/src/main/java/co/aikar/commands/contexts/ContextResolver.java @@ -27,6 +27,6 @@ import co.aikar.commands.CommandExecutionContext; import co.aikar.commands.InvalidCommandArgument; @FunctionalInterface -public interface ContextResolver { - C getContext(CommandExecutionContext c) throws InvalidCommandArgument; +public interface ContextResolver > { + T getContext(C c) throws InvalidCommandArgument; } diff --git a/core/src/main/java/co/aikar/commands/contexts/SenderAwareContextResolver.java b/core/src/main/java/co/aikar/commands/contexts/SenderAwareContextResolver.java index f9f533b2..8644d08a 100644 --- a/core/src/main/java/co/aikar/commands/contexts/SenderAwareContextResolver.java +++ b/core/src/main/java/co/aikar/commands/contexts/SenderAwareContextResolver.java @@ -25,4 +25,4 @@ package co.aikar.commands.contexts; import co.aikar.commands.CommandExecutionContext; -public interface SenderAwareContextResolver extends ContextResolver {} +public interface SenderAwareContextResolver> extends ContextResolver {} diff --git a/example/pom.xml b/example/pom.xml index ac0fa778..339fc165 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -112,7 +112,8 @@ co.aikar acf-bukkit - 0.5.0 + 0.5.0-SNAPSHOT + compile diff --git a/example/src/main/java/co/aikar/acfexample/ACFExample.java b/example/src/main/java/co/aikar/acfexample/ACFExample.java index 66d7f027..6ed8c990 100644 --- a/example/src/main/java/co/aikar/acfexample/ACFExample.java +++ b/example/src/main/java/co/aikar/acfexample/ACFExample.java @@ -24,14 +24,13 @@ package co.aikar.acfexample; import co.aikar.commands.BukkitCommandManager; -import co.aikar.commands.CommandManager; import com.google.common.collect.Lists; import org.bukkit.plugin.java.JavaPlugin; public final class ACFExample extends JavaPlugin { private static ACFExample plugin; - private static CommandManager commandManager; + private static BukkitCommandManager commandManager; @Override public void onEnable() { plugin = this; @@ -54,7 +53,7 @@ public final class ACFExample extends JavaPlugin { return plugin; } - public static CommandManager getCommandManager() { + public static BukkitCommandManager getCommandManager() { return commandManager; } } diff --git a/example/src/main/java/co/aikar/acfexample/SomeCommand_ExtraSubs.java b/example/src/main/java/co/aikar/acfexample/SomeCommand_ExtraSubs.java index 25db164d..ce200bd8 100644 --- a/example/src/main/java/co/aikar/acfexample/SomeCommand_ExtraSubs.java +++ b/example/src/main/java/co/aikar/acfexample/SomeCommand_ExtraSubs.java @@ -27,14 +27,14 @@ import co.aikar.commands.BaseCommand; import co.aikar.commands.annotation.CommandAlias; import co.aikar.commands.annotation.CommandCompletion; import co.aikar.commands.annotation.Subcommand; -import org.bukkit.command.CommandIssuer; +import org.bukkit.command.CommandSender; @CommandAlias("acf") public class SomeCommand_ExtraSubs extends BaseCommand { @Subcommand("testsub test2") @CommandCompletion("Foo2") - public void onTestSub2(CommandIssuer sender, String hi) { + public void onTestSub2(CommandSender sender, String hi) { sender.sendMessage(hi); } } diff --git a/example/src/main/java/co/aikar/acfexample/SomeObject.java b/example/src/main/java/co/aikar/acfexample/SomeObject.java index e2dd2113..2077309c 100644 --- a/example/src/main/java/co/aikar/acfexample/SomeObject.java +++ b/example/src/main/java/co/aikar/acfexample/SomeObject.java @@ -23,6 +23,7 @@ package co.aikar.acfexample; +import co.aikar.commands.BukkitCommandExecutionContext; import co.aikar.commands.InvalidCommandArgument; import co.aikar.commands.contexts.ContextResolver; @@ -37,7 +38,7 @@ public abstract class SomeObject { return this.thisValue; } - public static ContextResolver getContextResolver() { + public static ContextResolver getContextResolver() { return (c) -> { String first = c.popFirstArg(); if (first == null) { diff --git a/paper/acf-paper.iml b/paper/acf-paper.iml index f67f0b96..8245c319 100644 --- a/paper/acf-paper.iml +++ b/paper/acf-paper.iml @@ -18,9 +18,9 @@ + - diff --git a/paper/pom.xml b/paper/pom.xml index 7edd0ffb..bcb0fb26 100644 --- a/paper/pom.xml +++ b/paper/pom.xml @@ -30,25 +30,21 @@ co.aikar acf-parent - 0.5.0 + 0.5.0-SNAPSHOT ../pom.xml acf-paper - 0.5.0 + 0.5.0-SNAPSHOT ACF (Paper) - - co.aikar - acf-core - 0.5.0 - co.aikar acf-bukkit - 0.5.0 + 0.5.0-SNAPSHOT + compile org.bukkit diff --git a/paper/src/main/java/co/aikar/commands/PaperCommandManager.java b/paper/src/main/java/co/aikar/commands/PaperCommandManager.java index c664a846..28d0e1f8 100644 --- a/paper/src/main/java/co/aikar/commands/PaperCommandManager.java +++ b/paper/src/main/java/co/aikar/commands/PaperCommandManager.java @@ -23,6 +23,7 @@ package co.aikar.commands; +import org.bukkit.command.CommandSender; import org.bukkit.plugin.Plugin; @SuppressWarnings("WeakerAccess") @@ -34,7 +35,7 @@ public class PaperCommandManager extends BukkitCommandManager { } @Override - public synchronized CommandContexts getCommandContexts() { + public synchronized CommandContexts getCommandContexts() { if (this.contexts == null) { this.contexts = new PaperCommandContexts(this); } @@ -42,7 +43,7 @@ public class PaperCommandManager extends BukkitCommandManager { } @Override - public synchronized CommandCompletions getCommandCompletions() { + public synchronized CommandCompletions getCommandCompletions() { if (this.completions == null) { this.completions = new PaperCommandCompletions(this); } diff --git a/pom.xml b/pom.xml index 238e5fde..a26409f3 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ 4.0.0 co.aikar acf-parent - 0.5.0 + 0.5.0-SNAPSHOT pom ACF (All) https://acf.emc.gs