diff --git a/src/main/java/pw/yumc/YumCore/bukkit/Log.java b/src/main/java/pw/yumc/YumCore/bukkit/Log.java index 0e7d23c..91d191b 100644 --- a/src/main/java/pw/yumc/YumCore/bukkit/Log.java +++ b/src/main/java/pw/yumc/YumCore/bukkit/Log.java @@ -243,7 +243,7 @@ public class Log { * @param sender 命令发送者 * @param msg 消息 */ - public static void toSender(CommandSender sender, String msg) { + public static void sender(CommandSender sender, String msg) { sender.sendMessage(prefix + msg); } @@ -254,7 +254,7 @@ public class Log { * @param msg 消息 * @param objs 参数 */ - public static void toSender(CommandSender sender, String msg, Object... objs) { + public static void sender(CommandSender sender, String msg, Object... objs) { sender.sendMessage(prefix + String.format(msg, objs)); } @@ -264,9 +264,9 @@ public class Log { * @param sender 命令发送者 * @param msg 消息 */ - public static void toSender(CommandSender sender, String[] msg) { + public static void sender(CommandSender sender, String[] msg) { for (String str : msg) { - toSender(sender, str); + sender(sender, str); } } diff --git a/src/main/java/pw/yumc/YumCore/commands/CommandArgument.java b/src/main/java/pw/yumc/YumCore/commands/CommandArgument.java deleted file mode 100644 index 5a7cd19..0000000 --- a/src/main/java/pw/yumc/YumCore/commands/CommandArgument.java +++ /dev/null @@ -1,52 +0,0 @@ -package pw.yumc.YumCore.commands; - -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; - -/** - * 子命令参数类 - * - * @since 2015年8月22日上午8:29:44 - * @author 喵♂呜 - */ -public class CommandArgument { - private CommandSender sender; - private Command command; - private String alias; - private String[] args; - - public CommandArgument(CommandSender sender, Command command, String alias, String[] args) { - this.sender = sender; - this.command = command; - this.alias = alias; - this.args = args; - } - - /** - * @return 命令别名 - */ - public String getAlias() { - return alias; - } - - /** - * @return 命令参数 - */ - public String[] getArgs() { - return args; - } - - /** - * @return 命令实体 - */ - public Command getCommand() { - return command; - } - - /** - * @return 命令发送者 - */ - public CommandSender getSender() { - return sender; - } -} diff --git a/src/main/java/pw/yumc/YumCore/commands/CommandHelp.java b/src/main/java/pw/yumc/YumCore/commands/CommandHelp.java index e506a69..a85ec07 100644 --- a/src/main/java/pw/yumc/YumCore/commands/CommandHelp.java +++ b/src/main/java/pw/yumc/YumCore/commands/CommandHelp.java @@ -84,7 +84,7 @@ public class CommandHelp { */ public boolean send(CommandSender sender, Command command, String label, String[] args) { if (this.HELPPAGECOUNT == 0) { - Log.toSender(sender, commandNotFound); + Log.sender(sender, commandNotFound); return true; } int page = 1; @@ -175,7 +175,7 @@ public class CommandHelp { /** * 消息配置 */ - private static String helpTitle = String.format("§6========= %s §6帮助 §aBy §b喵♂呜 §6=========", Log.getPrefix()); + private static String helpTitle = String.format("§6========= %s§6帮助 §aBy §b喵♂呜 §6=========", Log.getPrefix()); private static String helpBody = "§6/%1$s §a%2$s §e%3$s §6- §b%4$s"; private static String helpFooter = "§6查看更多的帮助页面 §b请输入 /%s help §e1-%s"; private static String pageNotFound = "§c不存在的帮助页面 §b请输入 /%s help §e1-%s"; diff --git a/src/main/java/pw/yumc/YumCore/commands/CommandKit.java b/src/main/java/pw/yumc/YumCore/commands/CommandKit.java new file mode 100644 index 0000000..c5090f9 --- /dev/null +++ b/src/main/java/pw/yumc/YumCore/commands/CommandKit.java @@ -0,0 +1,63 @@ +package pw.yumc.YumCore.commands; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.PluginCommand; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; +import pw.yumc.YumCore.bukkit.Log; +import pw.yumc.YumCore.bukkit.P; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.Map; + +/** + * 命令工具类 + * + * @author 蒋天蓓 + * @since 2016/11/21 0021. + */ +public class CommandKit { + private static Constructor PluginCommandConstructor; + private static Map knownCommands; + private static Map lookupNames; + static { + try { + PluginManager pluginManager = Bukkit.getPluginManager(); + + Field lookupNamesField = pluginManager.getClass().getDeclaredField("lookupNames"); + lookupNamesField.setAccessible(true); + lookupNames = (Map) lookupNamesField.get(pluginManager); + + Field commandMapField = pluginManager.getClass().getDeclaredField("commandMap"); + commandMapField.setAccessible(true); + SimpleCommandMap commandMap = (SimpleCommandMap) commandMapField.get(pluginManager); + + Field knownCommandsField = commandMap.getClass().getDeclaredField("knownCommands"); + knownCommandsField.setAccessible(true); + knownCommands = (Map) knownCommandsField.get(commandMap); + + PluginCommandConstructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class); + } catch (NoSuchMethodException | SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { + Log.d("初始化命令管理器失败!"); + Log.debug(e); + } + } + + public static PluginCommand create(String name) { + return create(name, P.instance); + } + + public static PluginCommand create(String name, JavaPlugin plugin) { + try { + knownCommands.put(name, PluginCommandConstructor.newInstance(name, plugin)); + lookupNames.put(name, plugin); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ignored) { + } + return plugin.getCommand(name); + } +} diff --git a/src/main/java/pw/yumc/YumCore/commands/CommandMain.java b/src/main/java/pw/yumc/YumCore/commands/CommandMain.java index b24524c..3991e8f 100644 --- a/src/main/java/pw/yumc/YumCore/commands/CommandMain.java +++ b/src/main/java/pw/yumc/YumCore/commands/CommandMain.java @@ -1,22 +1,24 @@ package pw.yumc.YumCore.commands; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginCommand; +import pw.yumc.YumCore.bukkit.Log; +import pw.yumc.YumCore.bukkit.P; +import pw.yumc.YumCore.commands.info.CommandInfo; +import pw.yumc.YumCore.commands.interfaces.Executor; + import java.lang.reflect.Method; import java.util.*; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; - -import pw.yumc.YumCore.bukkit.Log; -import pw.yumc.YumCore.commands.info.CommandInfo; -import pw.yumc.YumCore.commands.interfaces.Executor; - /** * 主类命令管理 * * @author 喵♂呜 * @since 2016/11/18 0018 */ -public class CommandMain { +public class CommandMain implements CommandExecutor { private static String argumentTypeError = "注解命令方法 %s 位于 %s 的参数错误 第一个参数应实现 CommandSender 接口!"; /** * 命令列表 @@ -54,14 +56,10 @@ public class CommandMain { return this; } - public boolean execute(CommandSender sender, Command command, String label, String[] args) { - CommandInfo manager = getByCache(label); - return manager != null && manager.execute(new CommandArgument(sender, command, label, args)); - } - private boolean registerCommand(Method method, Executor clazz) { CommandInfo ci = CommandInfo.parse(method, clazz); if (ci != null) { + injectPluginCommand(ci); Class[] params = method.getParameterTypes(); Log.d("命令 %s 参数类型: %s", ci.getName(), Arrays.toString(params)); try { @@ -76,6 +74,15 @@ public class CommandMain { return false; } + private void injectPluginCommand(CommandInfo ci) { + PluginCommand cmd = P.getCommand(ci.getName()); + if (cmd == null) { + if ((cmd = CommandKit.create(ci.getName(), P.instance)) == null) { throw new IllegalStateException("未找到命令 必须在plugin.yml先注册 " + ci.getName() + " 命令!"); } + } + cmd.setAliases(ci.getAliases()); + cmd.setExecutor(this); + } + /** * 检查缓存并获得命令 * @@ -95,4 +102,10 @@ public class CommandMain { } return cmdCache.get(cmd); } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + CommandInfo manager = getByCache(label); + return manager != null && manager.execute(sender, label, args); + } } diff --git a/src/main/java/pw/yumc/YumCore/commands/CommandParse.java b/src/main/java/pw/yumc/YumCore/commands/CommandParse.java index 51f6f7b..26f5d59 100644 --- a/src/main/java/pw/yumc/YumCore/commands/CommandParse.java +++ b/src/main/java/pw/yumc/YumCore/commands/CommandParse.java @@ -5,9 +5,7 @@ import org.bukkit.Material; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import pw.yumc.YumCore.bukkit.Log; -import pw.yumc.YumCore.commands.annotation.Default; -import pw.yumc.YumCore.commands.annotation.KeyValue; -import pw.yumc.YumCore.commands.annotation.Limit; +import pw.yumc.YumCore.commands.annotation.Option; import pw.yumc.YumCore.commands.exception.ParseException; import java.io.File; @@ -47,7 +45,7 @@ public class CommandParse { parse = new EnumParse(clazz); } if (parse == null) { throw new ParseException(String.format("存在无法解析的参数类型 %s", clazz.getName())); } - this.parse.add(parse.clone().parseAnnotation(annotations)); + this.parse.add(parse.clone().parseAnnotation(annotations).handleAttrs()); } } @@ -77,10 +75,9 @@ public class CommandParse { allparses.put(clazz, parse); } - public Object[] parse(CommandArgument cmdArgs) { - String args[] = cmdArgs.getArgs(); + public Object[] parse(CommandSender sender, String label, String[] args) { List pobjs = new LinkedList<>(); - pobjs.add(cmdArgs.getSender()); + pobjs.add(sender); for (int i = 0; i < parse.size(); i++) { try { Parse p = parse.get(i); @@ -89,7 +86,7 @@ public class CommandParse { if (i + 1 == parse.size() && args.length >= parse.size()) { param = join(Arrays.copyOfRange(args, i, args.length), " "); } - pobjs.add(param == null ? null : p.parse(cmdArgs.getSender(), param)); + pobjs.add(param == null ? null : p.parse(sender, param)); } catch (Exception e) { throw new ParseException(String.format("第 %s 个参数 ", isMain ? 1 : 2 + i) + e.getMessage()); } @@ -244,19 +241,35 @@ public class CommandParse { public Parse parseAnnotation(Annotation[] annotations) { for (Annotation annotation : annotations) { - if (annotation.annotationType() == Default.class) { - def = ((Default) annotation).value(); - } else if (annotation.annotationType() == Limit.class) { - min = ((Limit) annotation).min(); - max = ((Limit) annotation).max(); - } else if (annotation.annotationType() == KeyValue.class) { - KeyValue kv = (KeyValue) annotation; - attrs.put(kv.key(), kv.value()); + if (annotation.annotationType() == Option.class) { + String value = ((Option) annotation).value(); + for (String str : value.split(" ")) { + if (str.isEmpty()) { + continue; + } + if (str.contains(":")) { + String[] args = str.split(":"); + attrs.put(args[0], args[1]); + } else { + attrs.put(str, null); + } + } } } return this; } + public Parse handleAttrs() { + if (attrs.containsKey("def")) { + def = String.valueOf(attrs.get("def")); + } else if (attrs.containsKey("min")) { + min = Integer.parseInt(String.valueOf(attrs.get("min"))); + } else if (attrs.containsKey("max")) { + max = Integer.parseInt(String.valueOf(attrs.get("max"))); + } + return this; + } + public void throwException(String str, Object... objects) { throw new ParseException(String.format(str, objects)); } @@ -285,8 +298,7 @@ public class CommandParse { } @Override - public Parse parseAnnotation(Annotation[] annotations) { - super.parseAnnotation(annotations); + public Parse handleAttrs() { check = attrs.containsKey("check"); return this; } @@ -311,8 +323,7 @@ public class CommandParse { } @Override - public Parse parseAnnotation(Annotation[] annotations) { - super.parseAnnotation(annotations); + public Parse handleAttrs() { if (attrs.containsKey("option")) { options = Arrays.asList(attrs.get("option").split(",")); } diff --git a/src/main/java/pw/yumc/YumCore/commands/CommandManager.java b/src/main/java/pw/yumc/YumCore/commands/CommandSub.java similarity index 71% rename from src/main/java/pw/yumc/YumCore/commands/CommandManager.java rename to src/main/java/pw/yumc/YumCore/commands/CommandSub.java index 7ee0d10..de65459 100644 --- a/src/main/java/pw/yumc/YumCore/commands/CommandManager.java +++ b/src/main/java/pw/yumc/YumCore/commands/CommandSub.java @@ -1,392 +1,356 @@ -package pw.yumc.YumCore.commands; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.*; - -import org.bukkit.Bukkit; -import org.bukkit.command.*; -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.util.StringUtil; - -import pw.yumc.YumCore.bukkit.Log; -import pw.yumc.YumCore.bukkit.P; -import pw.yumc.YumCore.bukkit.compatible.C; -import pw.yumc.YumCore.commands.exception.ArgumentException; -import pw.yumc.YumCore.commands.exception.CommandException; -import pw.yumc.YumCore.commands.exception.PermissionException; -import pw.yumc.YumCore.commands.exception.SenderException; -import pw.yumc.YumCore.commands.info.CommandInfo; -import pw.yumc.YumCore.commands.info.CommandTabInfo; -import pw.yumc.YumCore.commands.interfaces.ErrorHanlder; -import pw.yumc.YumCore.commands.interfaces.Executor; -import pw.yumc.YumCore.commands.interfaces.HelpGenerator; -import pw.yumc.YumCore.commands.interfaces.HelpParse; - -/** - * 命令管理类 - * - * @author 喵♂呜 - * @since 2016年7月23日 上午9:06:03 - */ -public class CommandManager implements TabExecutor { - private static String argumentTypeError = "注解命令方法 %s 位于 %s 的参数错误 第一个参数应实现 CommandSender 接口!"; - private static String returnTypeError = "注解命令补全 %s 位于 %s 的返回值错误 应实现 List 接口!"; - private static String onlyExecutor = "§c当前命令仅允许 §b%s §c执行!"; - private static String losePerm = "§c你需要有 %s 的权限才能执行此命令!"; - private static String cmdErr = "§6错误原因: §4命令参数不正确!"; - private static String cmdUse = "§6使用方法: §e/%s %s%s"; - private static String cmdDes = "§6命令描述: §3%s"; - private static Constructor PluginCommandConstructor; - private static Map knownCommands; - private static Map lookupNames; - - static { - try { - PluginManager pluginManager = Bukkit.getPluginManager(); - - Field lookupNamesField = pluginManager.getClass().getDeclaredField("lookupNames"); - lookupNamesField.setAccessible(true); - lookupNames = (Map) lookupNamesField.get(pluginManager); - - Field commandMapField = pluginManager.getClass().getDeclaredField("commandMap"); - commandMapField.setAccessible(true); - SimpleCommandMap commandMap = (SimpleCommandMap) commandMapField.get(pluginManager); - - Field knownCommandsField = commandMap.getClass().getDeclaredField("knownCommands"); - knownCommandsField.setAccessible(true); - knownCommands = (Map) knownCommandsField.get(commandMap); - - PluginCommandConstructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class); - } catch (NoSuchMethodException | SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { - Log.d("初始化命令管理器失败!"); - Log.debug(e); - } - } - - /** - * 命令帮助 - */ - private CommandHelp help; - /** - * 插件命令 - */ - private PluginCommand cmd; - /** - * 命令错误处理 - */ - private ErrorHanlder commandErrorHanlder = new ErrorHanlder() { - @Override - public void error(CommandException e, CommandSender sender, CommandInfo info, CommandArgument args) { - if (e instanceof SenderException) { - Log.toSender(sender, onlyExecutor, info.getExecutorStr()); - } else if (e instanceof PermissionException) { - Log.toSender(sender, losePerm, info.getCommand().permission()); - } else if (e instanceof ArgumentException) { - Log.toSender(sender, cmdErr); - Log.toSender(sender, cmdUse, args.getAlias(), info.isDefault() ? "" : info.getName() + " ", info.getHelp().possibleArguments()); - Log.toSender(sender, cmdDes, info.getHelp().value()); - } - } - }; - /** - * 插件实例类 - */ - private static JavaPlugin plugin = P.instance; - /** - * 默认命令 - */ - private CommandInfo defCmd = null; - /** - * 命令列表 - */ - private Set cmds = new HashSet<>(); - /** - * Tab列表 - */ - private Set tabs = new HashSet<>(); - /** - * 命令缓存列表 - */ - private Map cmdCache = new HashMap<>(); - /** - * 命令名称缓存 - */ - private List cmdNameCache = new ArrayList<>(); - - /** - * 命令管理器 用于主类 - * - * @param executor - * 命令执行类 - */ - public CommandManager(Executor... executor) { - register(executor); - } - - /** - * 命令管理器 - * - * @param name - * 注册的命令 - */ - public CommandManager(String name) { - cmd = plugin.getCommand(name); - if (cmd == null) { - try { - knownCommands.put(name, PluginCommandConstructor.newInstance(name, plugin)); - lookupNames.put(name, plugin); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ignored) { - } - if ((cmd = plugin.getCommand(name)) == null) { throw new IllegalStateException("未找到命令 必须在plugin.yml先注册 " + name + " 命令!"); } - } - cmd.setExecutor(this); - cmd.setTabCompleter(this); - } - - /** - * 命令管理器 - * - * @param name - * 注册的命令 - * @param executor - * 命令执行类 - */ - public CommandManager(String name, Executor... executor) { - this(name); - register(executor); - } - - /** - * 构建命令列表缓存 - */ - private void buildCmdNameCache() { - cmdNameCache.clear(); - for (CommandInfo cmd : cmds) { - cmdNameCache.add(cmd.getName()); - cmdNameCache.addAll(Arrays.asList(cmd.getCommand().aliases())); - } - cmdNameCache.add("help"); - } - - /** - * 检查缓存并获得命令 - * - * @param subcmd - * 子命令 - * @return 命令信息 - */ - private CommandInfo getByCache(String subcmd) { - if (!cmdCache.containsKey(subcmd)) { - for (CommandInfo cmdinfo : cmds) { - if (cmdinfo.isValid(subcmd)) { - cmdCache.put(subcmd, cmdinfo); - break; - } - } - if (!cmdCache.containsKey(subcmd)) { - cmdCache.put(subcmd, CommandInfo.Unknow); - } - } - return cmdCache.get(subcmd); - } - - /** - * 获取玩家命令补全 - * - * @param sender - * 命令发送者 - * @param command - * 命令 - * @param alias - * 别名 - * @param args - * 数组 - * @return 在线玩家数组 - */ - private List getPlayerTabComplete(CommandSender sender, Command command, String alias, String[] args) { - String lastWord = args[args.length - 1]; - Player senderPlayer = sender instanceof Player ? (Player) sender : null; - List matchedPlayers = new ArrayList<>(); - for (Player player : C.Player.getOnlinePlayers()) { - String name = player.getName(); - if ((senderPlayer == null || senderPlayer.canSee(player)) && StringUtil.startsWithIgnoreCase(name, lastWord)) { - matchedPlayers.add(name); - } - } - return matchedPlayers; - } - - /** - * 转移数组 - * - * @param args - * 原数组 - * @param start - * 数组开始位置 - * @return 转移后的数组字符串 - */ - private String[] moveStrings(String[] args, int start) { - String[] ret = new String[args.length - start]; - System.arraycopy(args, start, ret, 0, ret.length); - return ret; - } - - @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - if (args.length == 0) { - if (defCmd != null) { return defCmd.execute(new CommandArgument(sender, command, label, args)); } - return help.send(sender, command, label, args); - } - String subcmd = args[0].toLowerCase(); - if (subcmd.equalsIgnoreCase("help")) { return help.send(sender, command, label, args); } - CommandInfo cmd = getByCache(subcmd); - CommandArgument arg; - if (cmd.equals(CommandInfo.Unknow) && defCmd != null) { - cmd = defCmd; - arg = new CommandArgument(sender, command, label, args); - } else { - arg = new CommandArgument(sender, command, label, moveStrings(args, 1)); - } - try { - return cmd.execute(arg); - } catch (CommandException e) { - commandErrorHanlder.error(e, sender, cmd, arg); - } - return false; - } - - @Override - public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { - List completions = new ArrayList<>(); - String token = args[args.length - 1]; - if (args.length == 1) { - StringUtil.copyPartialMatches(token, cmdNameCache, completions); - } - for (CommandTabInfo tab : tabs) { - StringUtil.copyPartialMatches(token, tab.execute(sender, command, token, args), completions); - } - StringUtil.copyPartialMatches(token, getPlayerTabComplete(sender, command, alias, args), completions); - Collections.sort(completions, String.CASE_INSENSITIVE_ORDER); - return completions; - } - - /** - * 通过注解读取命令并注册 - * - * @param clazzs - * 子命令处理类 - * @return {@link CommandManager} - */ - public CommandManager register(Executor... clazzs) { - for (Executor clazz : clazzs) { - Method[] methods = clazz.getClass().getDeclaredMethods(); - for (Method method : methods) { - if (registerCommand(method, clazz)) { - continue; - } - registerTab(method, clazz); - } - } - help = new CommandHelp(defCmd, cmds); - buildCmdNameCache(); - return this; - } - - /** - * 注册命令 - * - * @param method - * 方法 - * @param clazz - * 调用对象 - * @return 是否成功 - */ - private boolean registerCommand(Method method, Executor clazz) { - CommandInfo ci = CommandInfo.parse(method, clazz); - if (ci != null) { - Class[] params = method.getParameterTypes(); - Log.d("命令 %s 参数类型: %s", ci.getName(), Arrays.toString(params)); - try { - Class sender = params[0]; - // 用于消除unuse警告 - if (!sender.getName().isEmpty() && method.getReturnType() == boolean.class) { - defCmd = ci; - } else { - cmds.add(ci); - cmdCache.put(ci.getName(), ci); - } - return true; - } catch (ArrayIndexOutOfBoundsException | ClassCastException ignored) { - } - Log.warning(String.format(argumentTypeError, method.getName(), clazz.getClass().getName())); - } - return false; - } - - /** - * 注册Tab补全 - * - * @param method - * 方法 - * @param clazz - * 调用对象 - * @return 是否成功 - */ - private boolean registerTab(Method method, Executor clazz) { - CommandTabInfo ti = CommandTabInfo.parse(method, clazz); - if (ti != null) { - if (method.getReturnType().equals(List.class)) { - tabs.add(ti); - return true; - } - Log.warning(String.format(returnTypeError, method.getName(), clazz.getClass().getName())); - } - return false; - } - - /** - * 设置命令错误处理器 - * - * @param commandErrorHanlder - * 命令错误处理器 - * @return {@link CommandManager} - */ - public CommandManager setCommandErrorHanlder(ErrorHanlder commandErrorHanlder) { - this.commandErrorHanlder = commandErrorHanlder; - return this; - } - - /** - * 设置帮助生成器 - * - * @param helpGenerator - * 帮助生成器 - * @return {@link CommandManager} - */ - public CommandManager setHelpGenerator(HelpGenerator helpGenerator) { - help.setHelpGenerator(helpGenerator); - return this; - } - - /** - * 设置帮助解析器 - * - * @param helpParse - * 帮助解析器 - * @return {@link CommandManager} - */ - public CommandManager setHelpParse(HelpParse helpParse) { - if (help.getHelpGenerator() instanceof CommandHelp.DefaultHelpGenerator) { - ((CommandHelp.DefaultHelpGenerator) help.getHelpGenerator()).setHelpParse(helpParse); - } else { - Log.w("已设置自定义帮助生成器 解析器设置将不会生效!"); - } - return this; - } -} +package pw.yumc.YumCore.commands; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.util.StringUtil; +import pw.yumc.YumCore.bukkit.Log; +import pw.yumc.YumCore.bukkit.P; +import pw.yumc.YumCore.bukkit.compatible.C; +import pw.yumc.YumCore.commands.exception.ArgumentException; +import pw.yumc.YumCore.commands.exception.CommandException; +import pw.yumc.YumCore.commands.exception.PermissionException; +import pw.yumc.YumCore.commands.exception.SenderException; +import pw.yumc.YumCore.commands.info.CommandInfo; +import pw.yumc.YumCore.commands.info.CommandTabInfo; +import pw.yumc.YumCore.commands.interfaces.ErrorHanlder; +import pw.yumc.YumCore.commands.interfaces.Executor; +import pw.yumc.YumCore.commands.interfaces.HelpGenerator; +import pw.yumc.YumCore.commands.interfaces.HelpParse; + +import java.lang.reflect.Method; +import java.util.*; + +/** + * 命令管理类 + * + * @author 喵♂呜 + * @since 2016年7月23日 上午9:06:03 + */ +public class CommandSub implements TabExecutor { + private static String argumentTypeError = "注解命令方法 %s 位于 %s 的参数错误 第一个参数应实现 CommandSender 接口!"; + private static String returnTypeError = "注解命令补全 %s 位于 %s 的返回值错误 应实现 List 接口!"; + private static String onlyExecutor = "§c当前命令仅允许 §b%s §c执行!"; + private static String losePerm = "§c你需要有 %s 的权限才能执行此命令!"; + private static String cmdErr = "§6错误原因: §4命令参数不正确!"; + private static String cmdUse = "§6使用方法: §e/%s %s%s"; + private static String cmdDes = "§6命令描述: §3%s"; + + /** + * 命令帮助 + */ + private CommandHelp help; + /** + * 插件命令 + */ + private PluginCommand cmd; + /** + * 命令错误处理 + */ + private ErrorHanlder commandErrorHanlder = new ErrorHanlder() { + @Override + public void error(CommandException e, CommandSender sender, CommandInfo info, String label, String[] args) { + if (e instanceof SenderException) { + Log.sender(sender, onlyExecutor, info.getExecutorStr()); + } else if (e instanceof PermissionException) { + Log.sender(sender, losePerm, info.getCommand().permission()); + } else if (e instanceof ArgumentException) { + Log.sender(sender, cmdErr); + Log.sender(sender, cmdUse, label, info.isDefault() ? "" : info.getName() + " ", info.getHelp().possibleArguments()); + Log.sender(sender, cmdDes, info.getHelp().value()); + } + } + }; + /** + * 插件实例类 + */ + private static JavaPlugin plugin = P.instance; + /** + * 默认命令 + */ + private CommandInfo defCmd = null; + /** + * 命令列表 + */ + private Set cmds = new HashSet<>(); + /** + * Tab列表 + */ + private Set tabs = new HashSet<>(); + /** + * 命令缓存列表 + */ + private Map cmdCache = new HashMap<>(); + /** + * 命令名称缓存 + */ + private List cmdNameCache = new ArrayList<>(); + + /** + * 命令管理器 用于主类 + * + * @param executor + * 命令执行类 + */ + public CommandSub(Executor... executor) { + register(executor); + } + + /** + * 命令管理器 + * + * @param name + * 注册的命令 + */ + public CommandSub(String name) { + cmd = plugin.getCommand(name); + if (cmd == null) { + if ((cmd = CommandKit.create(name, plugin)) == null) { throw new IllegalStateException("未找到命令 必须在plugin.yml先注册 " + name + " 命令!"); } + } + cmd.setExecutor(this); + cmd.setTabCompleter(this); + } + + /** + * 命令管理器 + * + * @param name + * 注册的命令 + * @param executor + * 命令执行类 + */ + public CommandSub(String name, Executor... executor) { + this(name); + register(executor); + } + + /** + * 构建命令列表缓存 + */ + private void buildCmdNameCache() { + cmdNameCache.clear(); + for (CommandInfo cmd : cmds) { + cmdNameCache.add(cmd.getName()); + cmdNameCache.addAll(Arrays.asList(cmd.getCommand().aliases())); + } + cmdNameCache.add("help"); + } + + /** + * 检查缓存并获得命令 + * + * @param subcmd + * 子命令 + * @return 命令信息 + */ + private CommandInfo getByCache(String subcmd) { + if (!cmdCache.containsKey(subcmd)) { + for (CommandInfo cmdinfo : cmds) { + if (cmdinfo.isValid(subcmd)) { + cmdCache.put(subcmd, cmdinfo); + break; + } + } + if (!cmdCache.containsKey(subcmd)) { + cmdCache.put(subcmd, CommandInfo.Unknow); + } + } + return cmdCache.get(subcmd); + } + + /** + * 获取玩家命令补全 + * + * @param sender + * 命令发送者 + * @param command + * 命令 + * @param alias + * 别名 + * @param args + * 数组 + * @return 在线玩家数组 + */ + private List getPlayerTabComplete(CommandSender sender, Command command, String alias, String[] args) { + String lastWord = args[args.length - 1]; + Player senderPlayer = sender instanceof Player ? (Player) sender : null; + List matchedPlayers = new ArrayList<>(); + for (Player player : C.Player.getOnlinePlayers()) { + String name = player.getName(); + if ((senderPlayer == null || senderPlayer.canSee(player)) && StringUtil.startsWithIgnoreCase(name, lastWord)) { + matchedPlayers.add(name); + } + } + return matchedPlayers; + } + + /** + * 转移数组 + * + * @param args + * 原数组 + * @param start + * 数组开始位置 + * @return 转移后的数组字符串 + */ + private String[] moveStrings(String[] args, int start) { + String[] ret = new String[args.length - start]; + System.arraycopy(args, start, ret, 0, ret.length); + return ret; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (args.length == 0) { + if (defCmd != null) { return defCmd.execute(sender, label, args); } + return help.send(sender, command, label, args); + } + String subcmd = args[0].toLowerCase(); + if (subcmd.equalsIgnoreCase("help")) { return help.send(sender, command, label, args); } + CommandInfo cmd = getByCache(subcmd); + String[] subargs = args; + if (cmd.equals(CommandInfo.Unknow) && defCmd != null) { + cmd = defCmd; + } else { + subargs = moveStrings(args, 1); + } + try { + return cmd.execute(sender, label, subargs); + } catch (CommandException e) { + commandErrorHanlder.error(e, sender, cmd, label, subargs); + } + return false; + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + List completions = new ArrayList<>(); + String token = args[args.length - 1]; + if (args.length == 1) { + StringUtil.copyPartialMatches(token, cmdNameCache, completions); + } + for (CommandTabInfo tab : tabs) { + StringUtil.copyPartialMatches(token, tab.execute(sender, token, args), completions); + } + StringUtil.copyPartialMatches(token, getPlayerTabComplete(sender, command, alias, args), completions); + Collections.sort(completions, String.CASE_INSENSITIVE_ORDER); + return completions; + } + + /** + * 通过注解读取命令并注册 + * + * @param clazzs + * 子命令处理类 + * @return {@link CommandSub} + */ + public CommandSub register(Executor... clazzs) { + for (Executor clazz : clazzs) { + Method[] methods = clazz.getClass().getDeclaredMethods(); + for (Method method : methods) { + if (registerCommand(method, clazz)) { + continue; + } + registerTab(method, clazz); + } + } + help = new CommandHelp(defCmd, cmds); + buildCmdNameCache(); + return this; + } + + /** + * 注册命令 + * + * @param method + * 方法 + * @param clazz + * 调用对象 + * @return 是否成功 + */ + private boolean registerCommand(Method method, Executor clazz) { + CommandInfo ci = CommandInfo.parse(method, clazz); + if (ci != null) { + Class[] params = method.getParameterTypes(); + Log.d("命令 %s 参数类型: %s", ci.getName(), Arrays.toString(params)); + try { + Class sender = params[0]; + // 用于消除unuse警告 + if (!sender.getName().isEmpty() && method.getReturnType() == boolean.class) { + defCmd = ci; + } else { + cmds.add(ci); + cmdCache.put(ci.getName(), ci); + } + return true; + } catch (ArrayIndexOutOfBoundsException | ClassCastException ignored) { + } + Log.warning(String.format(argumentTypeError, method.getName(), clazz.getClass().getName())); + } + return false; + } + + /** + * 注册Tab补全 + * + * @param method + * 方法 + * @param clazz + * 调用对象 + * @return 是否成功 + */ + private boolean registerTab(Method method, Executor clazz) { + CommandTabInfo ti = CommandTabInfo.parse(method, clazz); + if (ti != null) { + if (method.getReturnType().equals(List.class)) { + tabs.add(ti); + return true; + } + Log.warning(String.format(returnTypeError, method.getName(), clazz.getClass().getName())); + } + return false; + } + + /** + * 设置命令错误处理器 + * + * @param commandErrorHanlder + * 命令错误处理器 + * @return {@link CommandSub} + */ + public CommandSub setCommandErrorHanlder(ErrorHanlder commandErrorHanlder) { + this.commandErrorHanlder = commandErrorHanlder; + return this; + } + + /** + * 设置帮助生成器 + * + * @param helpGenerator + * 帮助生成器 + * @return {@link CommandSub} + */ + public CommandSub setHelpGenerator(HelpGenerator helpGenerator) { + help.setHelpGenerator(helpGenerator); + return this; + } + + /** + * 设置帮助解析器 + * + * @param helpParse + * 帮助解析器 + * @return {@link CommandSub} + */ + public CommandSub setHelpParse(HelpParse helpParse) { + if (help.getHelpGenerator() instanceof CommandHelp.DefaultHelpGenerator) { + ((CommandHelp.DefaultHelpGenerator) help.getHelpGenerator()).setHelpParse(helpParse); + } else { + Log.w("已设置自定义帮助生成器 解析器设置将不会生效!"); + } + return this; + } +} diff --git a/src/main/java/pw/yumc/YumCore/commands/annotation/KeyValue.java b/src/main/java/pw/yumc/YumCore/commands/annotation/KeyValue.java deleted file mode 100644 index 0439435..0000000 --- a/src/main/java/pw/yumc/YumCore/commands/annotation/KeyValue.java +++ /dev/null @@ -1,26 +0,0 @@ -package pw.yumc.YumCore.commands.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 自定义参数 - * - * @since 2016年7月23日 上午9:00:27 - * @author 喵♂呜 - */ -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -public @interface KeyValue { - /** - * @return 键 - */ - String key(); - - /** - * @return 值 - */ - String value() default ""; -} \ No newline at end of file diff --git a/src/main/java/pw/yumc/YumCore/commands/annotation/Limit.java b/src/main/java/pw/yumc/YumCore/commands/annotation/Limit.java deleted file mode 100644 index 83114e5..0000000 --- a/src/main/java/pw/yumc/YumCore/commands/annotation/Limit.java +++ /dev/null @@ -1,26 +0,0 @@ -package pw.yumc.YumCore.commands.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 参数限制 - * - * @since 2016年7月23日 上午9:00:27 - * @author 喵♂呜 - */ -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -public @interface Limit { - /** - * @return 最大长度(最大值) - */ - int max() default Integer.MAX_VALUE; - - /** - * @return 最小长度(或最小值) - */ - int min(); -} \ No newline at end of file diff --git a/src/main/java/pw/yumc/YumCore/commands/annotation/Default.java b/src/main/java/pw/yumc/YumCore/commands/annotation/Option.java similarity index 71% rename from src/main/java/pw/yumc/YumCore/commands/annotation/Default.java rename to src/main/java/pw/yumc/YumCore/commands/annotation/Option.java index b2c64c6..f232d8d 100644 --- a/src/main/java/pw/yumc/YumCore/commands/annotation/Default.java +++ b/src/main/java/pw/yumc/YumCore/commands/annotation/Option.java @@ -6,13 +6,13 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * 默认参数 + * 扩展选项注解 * - * @since 2016年7月23日 上午9:00:27 + * @since 2016年7月23日 上午9:00:07 * @author 喵♂呜 */ -@Target(ElementType.PARAMETER) +@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) -public @interface Default { +public @interface Option { String value(); -} \ No newline at end of file +} diff --git a/src/main/java/pw/yumc/YumCore/commands/info/CommandInfo.java b/src/main/java/pw/yumc/YumCore/commands/info/CommandInfo.java index 9eae2e1..b1563d9 100644 --- a/src/main/java/pw/yumc/YumCore/commands/info/CommandInfo.java +++ b/src/main/java/pw/yumc/YumCore/commands/info/CommandInfo.java @@ -4,7 +4,6 @@ import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import pw.yumc.YumCore.bukkit.Log; import pw.yumc.YumCore.bukkit.P; -import pw.yumc.YumCore.commands.CommandArgument; import pw.yumc.YumCore.commands.CommandParse; import pw.yumc.YumCore.commands.annotation.Async; import pw.yumc.YumCore.commands.annotation.Cmd; @@ -112,20 +111,24 @@ public class CommandInfo { /** * 执行命令 * - * @param cmdArgs - * 命令参数 + * @param sender + * 命令发送者 + * @param label + * 命令标签 + * @param args + * 参数 * @return 是否执行成功 */ - public boolean execute(final CommandArgument cmdArgs) { + public boolean execute(final CommandSender sender, final String label, final String[] args) { if (method == null) { return false; } - check(cmdArgs); + check(sender, label, args); Runnable runnable = new Runnable() { @Override public void run() { try { - method.invoke(origin, parse.parse(cmdArgs)); + method.invoke(origin, parse.parse(sender, label, args)); } catch (ParseException | ArgumentException e) { - Log.toSender(cmdArgs.getSender(), argErr, e.getMessage()); + Log.sender(sender, argErr, e.getMessage()); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new CommandException(e); } @@ -160,6 +163,13 @@ public class CommandInfo { return name; } + /** + * @return 命令别名 + */ + public List getAliases() { + return aliases; + } + /** * @return 命令排序 */ @@ -212,11 +222,10 @@ public class CommandInfo { return Objects.hash(origin, method, name); } - private void check(CommandArgument cmdArgs) { - CommandSender sender = cmdArgs.getSender(); + private void check(CommandSender sender, String label, String[] args) { if (!executors.contains(Executor.ALL) && !executors.contains(Executor.valueOf(sender))) { throw new SenderException(executorStr); } if (!"".equals(command.permission()) && !sender.hasPermission(command.permission())) { throw new PermissionException(command.permission()); } - if (cmdArgs.getArgs().length < command.minimumArguments()) { throw new ArgumentException(String.valueOf(command.minimumArguments())); } + if (args.length < command.minimumArguments()) { throw new ArgumentException(String.valueOf(command.minimumArguments())); } } private String eS(List executors) { diff --git a/src/main/java/pw/yumc/YumCore/commands/info/CommandTabInfo.java b/src/main/java/pw/yumc/YumCore/commands/info/CommandTabInfo.java index 8bee362..9ce68ab 100644 --- a/src/main/java/pw/yumc/YumCore/commands/info/CommandTabInfo.java +++ b/src/main/java/pw/yumc/YumCore/commands/info/CommandTabInfo.java @@ -2,7 +2,6 @@ package pw.yumc.YumCore.commands.info; import org.bukkit.command.CommandSender; import pw.yumc.YumCore.bukkit.P; -import pw.yumc.YumCore.commands.CommandArgument; import pw.yumc.YumCore.commands.annotation.Tab; import pw.yumc.YumCore.commands.exception.CommandException; @@ -47,8 +46,6 @@ public class CommandTabInfo { * * @param sender * 发送者 - * @param command - * 命令 * @param label * 命令 * @param args @@ -56,10 +53,9 @@ public class CommandTabInfo { * @return Tab补全信息 */ @SuppressWarnings("unchecked") - public List execute(CommandSender sender, org.bukkit.command.Command command, String label, String[] args) { - CommandArgument cmdArgs = new CommandArgument(sender, command, label, args); + public List execute(CommandSender sender, String label, String[] args) { try { - return (List) method.invoke(origin, cmdArgs); + return (List) method.invoke(origin, sender, label, args); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new CommandException("调用Tab自动补全发生错误 请反馈给开发者 " + Arrays.toString(P.getDescription().getAuthors().toArray()) + " !", e); } diff --git a/src/main/java/pw/yumc/YumCore/commands/interfaces/ErrorHanlder.java b/src/main/java/pw/yumc/YumCore/commands/interfaces/ErrorHanlder.java index 22f6a61..a764832 100644 --- a/src/main/java/pw/yumc/YumCore/commands/interfaces/ErrorHanlder.java +++ b/src/main/java/pw/yumc/YumCore/commands/interfaces/ErrorHanlder.java @@ -1,7 +1,6 @@ package pw.yumc.YumCore.commands.interfaces; import org.bukkit.command.CommandSender; -import pw.yumc.YumCore.commands.CommandArgument; import pw.yumc.YumCore.commands.exception.CommandException; import pw.yumc.YumCore.commands.info.CommandInfo; @@ -12,5 +11,5 @@ import pw.yumc.YumCore.commands.info.CommandInfo; * @author 喵♂呜 */ public interface ErrorHanlder { - void error(CommandException e, CommandSender sender, CommandInfo info, CommandArgument args); + void error(CommandException e, CommandSender sender, CommandInfo info, String label, String[] args); } diff --git a/src/main/java/pw/yumc/YumCore/config/inject/InjectParse.java b/src/main/java/pw/yumc/YumCore/config/inject/InjectParse.java index de70fea..ae39275 100644 --- a/src/main/java/pw/yumc/YumCore/config/inject/InjectParse.java +++ b/src/main/java/pw/yumc/YumCore/config/inject/InjectParse.java @@ -1,5 +1,9 @@ package pw.yumc.YumCore.config.inject; +import org.bukkit.configuration.ConfigurationSection; +import pw.yumc.YumCore.config.exception.ConfigParseException; + +import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; @@ -7,10 +11,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.bukkit.configuration.ConfigurationSection; - -import pw.yumc.YumCore.config.exception.ConfigParseException; - /** * 注入配置类解析 * @@ -20,7 +20,7 @@ import pw.yumc.YumCore.config.exception.ConfigParseException; public class InjectParse { private static String DATE_PARSE_ERROR = "配置节点 {0} 日期解析失败 格式应该为: {1} 但输入值为: {2}!"; private static String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; - private static SimpleDateFormat df = new SimpleDateFormat(DATE_FORMAT); + private static DateFormat df = new SimpleDateFormat(DATE_FORMAT); private static Map allparse = new HashMap<>(); static { @@ -62,6 +62,16 @@ public class InjectParse { } } + public static class DateFormatParse implements Parse { + public DateFormatParse() { + allparse.put(DateFormat.class, this); + } + + @Override + public DateFormat parse(ConfigurationSection config, String path) { + return new SimpleDateFormat(config.getString(path)); + } + } public static class ListParse implements Parse { public ListParse() { allparse.put(List.class, this); diff --git a/src/main/java/pw/yumc/YumCore/global/I18N.java b/src/main/java/pw/yumc/YumCore/global/I18N.java index 5f2e265..27ef6b0 100644 --- a/src/main/java/pw/yumc/YumCore/global/I18N.java +++ b/src/main/java/pw/yumc/YumCore/global/I18N.java @@ -45,7 +45,7 @@ public class I18N { } public static void send(CommandSender sender, String message, String def, Object... objs) { - Log.toSender(sender, $(message, def), objs); + Log.sender(sender, $(message, def), objs); } /** diff --git a/src/main/java/pw/yumc/YumCore/kit/LogKit.java b/src/main/java/pw/yumc/YumCore/kit/LogKit.java index 2356fde..d6dc881 100644 --- a/src/main/java/pw/yumc/YumCore/kit/LogKit.java +++ b/src/main/java/pw/yumc/YumCore/kit/LogKit.java @@ -79,7 +79,7 @@ public class LogKit implements Runnable { * 日志 */ public void send(final CommandSender sender, final String s) { - Log.toSender(sender, s); + Log.sender(sender, s); log(ChatColor.stripColor(s)); }