feat: 修复命令注册错误

Signed-off-by: 502647092 <admin@yumc.pw>
This commit is contained in:
502647092 2016-07-25 13:54:13 +08:00
parent 1a7e967826
commit ad4126a810
4 changed files with 484 additions and 452 deletions

View File

@ -1,130 +1,129 @@
package pw.yumc.YumCore.commands; package pw.yumc.YumCore.commands;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import pw.yumc.YumCore.bukkit.P; import pw.yumc.YumCore.bukkit.P;
import pw.yumc.YumCore.commands.annotation.Async; import pw.yumc.YumCore.commands.annotation.Async;
import pw.yumc.YumCore.commands.annotation.Cmd; import pw.yumc.YumCore.commands.annotation.Cmd;
import pw.yumc.YumCore.commands.annotation.Help; import pw.yumc.YumCore.commands.annotation.Help;
import pw.yumc.YumCore.commands.annotation.Sort; import pw.yumc.YumCore.commands.annotation.Sort;
import pw.yumc.YumCore.commands.exception.CommandException; import pw.yumc.YumCore.commands.exception.CommandException;
/** /**
* 命令信息存储类 * 命令信息存储类
* *
* @since 2016年7月23日 上午9:56:42 * @since 2016年7月23日 上午9:56:42
* @author * @author
*/ */
public class CommandInfo { public class CommandInfo {
public static CommandInfo Unknow = new CommandInfo(); public static CommandInfo Unknow = new CommandInfo();
private final Object origin; private final Object origin;
private final Method method; private final Method method;
private final String name; private final String name;
private final List<String> aliases; private final List<String> aliases;
private final boolean async; private final boolean async;
private final Cmd command; private final Cmd command;
private final Help help; private final Help help;
private final int sort; private final int sort;
public CommandInfo(final Method method, final Object origin, final Cmd command, final Help help, final boolean async, final int sort) { public CommandInfo(final Method method, final Object origin, final Cmd command, final Help help, final boolean async, final int sort) {
this.method = method; this.method = method;
this.origin = origin; this.origin = origin;
this.name = "".equals(command.name()) ? method.getName().toLowerCase() : command.name(); this.name = "".equals(command.name()) ? method.getName().toLowerCase() : command.name();
this.aliases = Arrays.asList(command.aliases()); this.aliases = Arrays.asList(command.aliases());
this.command = command; this.command = command;
this.help = help; this.help = help;
this.async = async; this.async = async;
this.sort = sort; this.sort = sort;
} }
private CommandInfo() { private CommandInfo() {
this.method = null; this.method = null;
this.origin = null; this.origin = null;
this.name = "unknow"; this.name = "unknow";
this.aliases = null; this.aliases = null;
this.command = null; this.command = null;
this.help = null; this.help = null;
this.async = false; this.async = false;
this.sort = 0; this.sort = 0;
} }
public static CommandInfo parse(final Method method, final Object origin) { public static CommandInfo parse(final Method method, final Object origin) {
final Class<?> clazz = method.getClass(); final Cmd command = method.getAnnotation(Cmd.class);
final Cmd command = clazz.getAnnotation(Cmd.class); if (command != null) {
if (command != null) { final Help help = method.getAnnotation(Help.class);
final Help help = clazz.getAnnotation(Help.class); final Async async = method.getAnnotation(Async.class);
final Async async = clazz.getAnnotation(Async.class); final Sort sort = method.getAnnotation(Sort.class);
final Sort sort = clazz.getAnnotation(Sort.class); return new CommandInfo(method, origin, command, help != null ? help : Help.DEFAULT, async != null, sort != null ? sort.sort() : 50);
return new CommandInfo(method, origin, command, help != null ? help : Help.DEFAULT, async != null, sort != null ? sort.sort() : 50); }
} return null;
return null; }
}
@Override
@Override public boolean equals(final Object obj) {
public boolean equals(final Object obj) { if (obj instanceof CommandInfo) {
if (obj instanceof CommandInfo) { return name.equalsIgnoreCase(((CommandInfo) obj).getName());
return name.equalsIgnoreCase(((CommandInfo) obj).getName()); }
} return super.equals(obj);
return super.equals(obj); }
}
public boolean execute(final CommandSender sender, final Command command, final String label, final String[] args) {
public boolean execute(final CommandSender sender, final Command command, final String label, final String[] args) { if (method == null) {
if (method == null) { return false;
return false; }
} final CommandArgument cmdArgs = new CommandArgument(sender, command, label, args);
final CommandArgument cmdArgs = new CommandArgument(sender, command, label, args); final Runnable runnable = new Runnable() {
final Runnable runnable = new Runnable() { @Override
@Override public void run() {
public void run() { try {
try { try {
try { cmdArgs.check(CommandInfo.this);
cmdArgs.check(CommandInfo.this); } catch (final CommandException e) {
} catch (final CommandException e) { sender.sendMessage(e.getMessage());
sender.sendMessage(e.getMessage()); return;
return; }
} method.invoke(origin, cmdArgs);
method.invoke(origin, cmdArgs); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new CommandException(e);
throw new CommandException(e); }
} }
} };
}; if (async) {
if (async) { Bukkit.getScheduler().runTaskAsynchronously(P.instance, runnable);
Bukkit.getScheduler().runTaskAsynchronously(P.instance, runnable); } else {
} else { runnable.run();
runnable.run(); }
} return true;
return true; }
}
public Cmd getCommand() {
public Cmd getCommand() { return command;
return command; }
}
public Help getHelp() {
public Help getHelp() { return help;
return help; }
}
public String getName() {
public String getName() { return name;
return name; }
}
public int getSort() {
public int getSort() { return sort;
return sort; }
}
public boolean isAsync() {
public boolean isAsync() { return async;
return async; }
}
public boolean isValid(final String cmd) {
public boolean isValid(final String cmd) { return name.equalsIgnoreCase(cmd) || aliases.contains(cmd);
return name.equalsIgnoreCase(cmd) || aliases.contains(cmd); }
} }
}

View File

@ -1,198 +1,232 @@
package pw.yumc.YumCore.commands; package pw.yumc.YumCore.commands;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand; import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.StringUtil; import org.bukkit.util.StringUtil;
import pw.yumc.YumCore.bukkit.Log; import pw.yumc.YumCore.bukkit.Log;
import pw.yumc.YumCore.bukkit.P; import pw.yumc.YumCore.bukkit.P;
import pw.yumc.YumCore.bukkit.compatible.C; import pw.yumc.YumCore.bukkit.compatible.C;
import pw.yumc.YumCore.commands.api.CommandExecutor; import pw.yumc.YumCore.commands.api.CommandExecutor;
/** /**
* 命令管理类 * 命令管理类
* *
* @since 2016年7月23日 上午9:06:03 * @since 2016年7月23日 上午9:06:03
* @author * @author
*/ */
public class CommandManager implements TabExecutor { public class CommandManager implements TabExecutor {
private final String argumentTypeError = "注解命令方法 %s 位于 %s 的参数错误 应只有 CommandArgument 参数!"; private final String argumentTypeError = "注解命令方法 %s 位于 %s 的参数错误 应只有 CommandArgument 参数!";
private final String returnTypeError = "注解命令补全 %s 位于 %s 的返回值错误 应实现 List 接口!"; private final String returnTypeError = "注解命令补全 %s 位于 %s 的返回值错误 应实现 List 接口!";
/** /**
* 插件实例类 * 插件实例类
*/ */
JavaPlugin plugin = P.instance; JavaPlugin plugin = P.instance;
/** /**
* 命令列表 * 命令列表
*/ */
Set<CommandInfo> cmdlist = new HashSet<>(); Set<CommandInfo> cmdlist = new HashSet<>();
/** /**
* Tab列表 * Tab列表
*/ */
Set<TabInfo> tablist = new HashSet<>(); Set<TabInfo> tablist = new HashSet<>();
/** /**
* 命令缓存列表 * 命令缓存列表
*/ */
Map<String, CommandInfo> cmdcache = new HashMap<>(); Map<String, CommandInfo> cmdcache = new HashMap<>();
/** /**
* 命令帮助 * 命令帮助
*/ */
CommandHelp help = new CommandHelp(cmdlist); CommandHelp help = new CommandHelp(cmdlist);
/** /**
* 命令管理器 * 插件命令
* */
* @param name PluginCommand cmd;
* 注册的命令
*/ /**
public CommandManager(final String name) { * 命令管理器
final PluginCommand cmd = plugin.getCommand(name); *
if (cmd == null) { * @param name
throw new IllegalStateException("未找到命令 必须在plugin.yml先注册 " + name + " 命令!"); * 注册的命令
} */
cmd.setExecutor(this); public CommandManager(final String name) {
cmd.setTabCompleter(this); cmd = plugin.getCommand(name);
} if (cmd == null) {
throw new IllegalStateException("未找到命令 必须在plugin.yml先注册 " + name + " 命令!");
/** }
* 命令管理器 cmd.setExecutor(this);
* cmd.setTabCompleter(this);
* @param name }
* 注册的命令
* @param executor /**
* 命令执行类 * 命令管理器
*/ *
public CommandManager(final String name, final CommandExecutor executor) { * @param name
this(name); * 注册的命令
registerCommands(executor); * @param executor
} * 命令执行类
*/
@Override public CommandManager(final String name, final CommandExecutor executor) {
public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args) { this(name);
if (args.length == 0) { register(executor);
help.send(sender, command, label, args); }
return true;
} @Override
final String subcmd = args[0].toLowerCase(); public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args) {
if (subcmd.equalsIgnoreCase("help")) { if (args.length == 0) {
help.send(sender, command, label, args); help.send(sender, command, label, args);
return true; return true;
} }
final String[] subargs = moveStrings(args, 1); final String subcmd = args[0].toLowerCase();
if (!cmdcache.containsKey(label)) { if (subcmd.equalsIgnoreCase("help")) {
for (final CommandInfo cmdinfo : cmdlist) { help.send(sender, command, label, args);
if (cmdinfo.isValid(label)) { return true;
cmdcache.put(label, cmdinfo); }
break; final String[] subargs = moveStrings(args, 1);
} if (!cmdcache.containsKey(label)) {
} for (final CommandInfo cmdinfo : cmdlist) {
cmdcache.put(label, CommandInfo.Unknow); if (cmdinfo.isValid(subcmd)) {
} cmdcache.put(subcmd, cmdinfo);
return cmdcache.get(label).execute(sender, command, label, subargs); break;
} }
}
@Override if (!cmdcache.containsKey(subcmd)) {
public List<String> onTabComplete(final CommandSender sender, final Command command, final String alias, final String[] args) { cmdcache.put(subcmd, CommandInfo.Unknow);
final List<String> completions = new ArrayList<>(); }
final String token = args[args.length - 1]; }
if (args.length == 1) { return cmdcache.get(subcmd).execute(sender, command, label, subargs);
final Set<String> commands = this.cmdcache.keySet(); }
StringUtil.copyPartialMatches(args[0], commands, completions);
} else if (args.length >= 2) { @Override
for (final TabInfo tab : tablist) { public List<String> onTabComplete(final CommandSender sender, final Command command, final String alias, final String[] args) {
StringUtil.copyPartialMatches(token, tab.execute(sender, command, token, args), completions); final List<String> completions = new ArrayList<>();
} final String token = args[args.length - 1];
StringUtil.copyPartialMatches(token, getPlayerTabComplete(sender, command, alias, args), completions); if (args.length == 1) {
} final Set<String> commands = this.cmdcache.keySet();
Collections.sort(completions, String.CASE_INSENSITIVE_ORDER); StringUtil.copyPartialMatches(args[0], commands, completions);
return completions; } else if (args.length >= 2) {
} for (final TabInfo tab : tablist) {
StringUtil.copyPartialMatches(token, tab.execute(sender, command, token, args), completions);
/** }
* 通过注解读取命令并注册 StringUtil.copyPartialMatches(token, getPlayerTabComplete(sender, command, alias, args), completions);
* }
* @param clazz Collections.sort(completions, String.CASE_INSENSITIVE_ORDER);
* 子命令处理类 return completions;
*/ }
public void registerCommands(final CommandExecutor clazz) {
final Method[] methods = clazz.getClass().getDeclaredMethods(); /**
for (final Method method : methods) { * 通过注解读取命令并注册
final CommandInfo ci = CommandInfo.parse(method, clazz); *
if (ci != null) { * @param clazz
final Class<?>[] params = method.getParameterTypes(); * 子命令处理类
if (params.length == 1 && params[0].equals(CommandArgument.class)) { */
if (cmdlist.add(ci)) { public void register(final CommandExecutor clazz) {
cmdcache.put(ci.getName(), ci); final Method[] methods = clazz.getClass().getDeclaredMethods();
continue; for (final Method method : methods) {
} if (registerCommand(method, clazz)) {
} else { continue;
Log.warning(String.format(argumentTypeError, method.getName(), clazz.getClass().getName())); }
} registerTab(method, clazz);
} }
final TabInfo ti = TabInfo.parse(method, clazz); help = new CommandHelp(cmdlist);
if (ti != null) { }
if (!method.getReturnType().equals(List.class)) {
Log.warning(String.format(returnTypeError, method.getName(), clazz.getClass().getName())); /**
continue; * 获取玩家命令补全
} *
tablist.add(ti); * @param sender
} * 命令发送者
} * @param command
help = new CommandHelp(cmdlist); * 命令
} * @param alias
* 别名
/** * @param args
* 获取玩家命令补全 * 数组
* * @return 在线玩家数组
* @param sender */
* 命令发送者 private List<String> getPlayerTabComplete(final CommandSender sender, final Command command, final String alias, final String[] args) {
* @param command final String lastWord = args[args.length - 1];
* 命令 final Player senderPlayer = sender instanceof Player ? (Player) sender : null;
* @param alias final ArrayList<String> matchedPlayers = new ArrayList<>();
* 别名 for (final Player player : C.Player.getOnlinePlayers()) {
* @param args final String name = player.getName();
* 数组 if ((senderPlayer == null || senderPlayer.canSee(player)) && StringUtil.startsWithIgnoreCase(name, lastWord)) {
* @return 在线玩家数组 matchedPlayers.add(name);
*/ }
private List<String> getPlayerTabComplete(final CommandSender sender, final Command command, final String alias, final String[] args) { }
final String lastWord = args[args.length - 1]; return matchedPlayers;
final Player senderPlayer = sender instanceof Player ? (Player) sender : null; }
final ArrayList<String> matchedPlayers = new ArrayList<>();
for (final Player player : C.Player.getOnlinePlayers()) { /**
final String name = player.getName(); * 转移数组
if ((senderPlayer == null || senderPlayer.canSee(player)) && StringUtil.startsWithIgnoreCase(name, lastWord)) { *
matchedPlayers.add(name); * @param args
} * 原数组
} * @param start
return matchedPlayers; * 数组开始位置
} * @return 转移后的数组字符串
*/
/** private String[] moveStrings(final String[] args, final int start) {
* 转移数组 final String[] ret = new String[args.length - start];
* System.arraycopy(args, start, ret, 0, ret.length);
* @param args return ret;
* 原数组 }
* @param start
* 数组开始位置 /**
* @return 转移后的数组字符串 * 注册命令
*/ *
private String[] moveStrings(final String[] args, final int start) { * @param method
final String[] ret = new String[args.length - start]; * 方法
System.arraycopy(args, start, ret, 0, ret.length); * @param clazz
return ret; * 调用对象
} * @return 是否成功
*/
} private boolean registerCommand(final Method method, final CommandExecutor clazz) {
final CommandInfo ci = CommandInfo.parse(method, clazz);
if (ci != null) {
final Class<?>[] params = method.getParameterTypes();
if (params.length == 1 && params[0].equals(CommandArgument.class)) {
cmdlist.add(ci);
cmdcache.put(ci.getName(), ci);
return true;
}
Log.warning(String.format(argumentTypeError, method.getName(), clazz.getClass().getName()));
}
return false;
}
/**
* 注册Tab补全
*
* @param method
* 方法
* @param clazz
* 调用对象
* @return 是否成功
*/
private boolean registerTab(final Method method, final CommandExecutor clazz) {
final TabInfo ti = TabInfo.parse(method, clazz);
if (ti != null) {
if (method.getReturnType().equals(List.class)) {
tablist.add(ti);
return true;
}
Log.warning(String.format(returnTypeError, method.getName(), clazz.getClass().getName()));
}
return false;
}
}

View File

@ -1,80 +1,79 @@
package pw.yumc.YumCore.commands; package pw.yumc.YumCore.commands;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import pw.yumc.YumCore.bukkit.P; import pw.yumc.YumCore.bukkit.P;
import pw.yumc.YumCore.commands.annotation.Tab; import pw.yumc.YumCore.commands.annotation.Tab;
/** /**
* Tab补全 * Tab补全
* *
* @since 2016年7月23日 上午9:56:42 * @since 2016年7月23日 上午9:56:42
* @author * @author
*/ */
public class TabInfo { public class TabInfo {
private final Object origin; private final Object origin;
private final Method method; private final Method method;
public TabInfo(final Method method, final Object origin) { public TabInfo(final Method method, final Object origin) {
this.method = method; this.method = method;
this.origin = origin; this.origin = origin;
} }
/** /**
* 解析TabInfo * 解析TabInfo
* *
* @param method * @param method
* 方法 * 方法
* @param origin * @param origin
* 对象 * 对象
* @return {@link TabInfo} * @return {@link TabInfo}
*/ */
public static TabInfo parse(final Method method, final Object origin) { public static TabInfo parse(final Method method, final Object origin) {
final Class<?> clazz = method.getClass(); final Tab tab = method.getAnnotation(Tab.class);
final Tab tab = clazz.getAnnotation(Tab.class); if (tab != null) {
if (tab != null) { return new TabInfo(method, origin);
return new TabInfo(method, origin); }
} return null;
return null; }
}
@Override
@Override public boolean equals(final Object obj) {
public boolean equals(final Object obj) { if (obj instanceof TabInfo) {
if (obj instanceof TabInfo) { return method.equals(((TabInfo) obj).getMethod());
return method.equals(((TabInfo) obj).getMethod()); }
} return super.equals(obj);
return super.equals(obj); }
}
/**
/** * 获得补全List
* 获得补全List *
* * @param sender
* @param sender * 发送者
* 发送者 * @param command
* @param command * 命令
* 命令 * @param label
* @param label * 命令
* 命令 * @param args
* @param args * 参数
* 参数 * @return Tab补全信息
* @return Tab补全信息 */
*/ @SuppressWarnings("unchecked")
@SuppressWarnings("unchecked") public List<String> execute(final CommandSender sender, final org.bukkit.command.Command command, final String label, final String[] args) {
public List<String> execute(final CommandSender sender, final org.bukkit.command.Command command, final String label, final String[] args) { final CommandArgument cmdArgs = new CommandArgument(sender, command, label, args);
final CommandArgument cmdArgs = new CommandArgument(sender, command, label, args); try {
try { return (List<String>) method.invoke(origin, cmdArgs);
return (List<String>) method.invoke(origin, cmdArgs); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException("调用Tab自动补全发生错误 请反馈给开发者 " + Arrays.toString(P.instance.getDescription().getAuthors().toArray()) + " !", e);
throw new RuntimeException("调用Tab自动补全发生错误 请反馈给开发者 " + Arrays.toString(P.instance.getDescription().getAuthors().toArray()) + " !", e); }
} }
}
public Method getMethod() {
public Method getMethod() { return method;
return method; }
} }
}

View File

@ -1,44 +1,44 @@
package pw.yumc.YumCore.commands.annotation; package pw.yumc.YumCore.commands.annotation;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
/** /**
* 命令帮助注解 * 命令帮助注解
* *
* @since 2016年7月23日 上午9:00:07 * @since 2016年7月23日 上午9:00:07
* @author * @author
*/ */
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface Help { public @interface Help {
public static Help DEFAULT = new Help() { public static Help DEFAULT = new Help() {
@Override @Override
public Class<? extends Annotation> annotationType() { public Class<? extends Annotation> annotationType() {
return getClass(); return getClass();
} }
@Override @Override
public String description() { public String description() {
return "这家伙很懒"; return "没写帮助信息";
} }
@Override @Override
public String possibleArguments() { public String possibleArguments() {
return "没有帮助信息"; return "这家伙很懒";
} }
}; };
/** /**
* @return 命令描述 * @return 命令描述
*/ */
String description() default ""; String description() default "";
/** /**
* @return 当前命令可能需要的参数 * @return 当前命令可能需要的参数
*/ */
String possibleArguments() default ""; String possibleArguments() default "";
} }