Plex-FAWE/src/com/sk89q/minecraft/util/commands/CommandsManager.java

343 lines
10 KiB
Java
Raw Normal View History

2011-01-29 10:05:22 +00:00
// $Id$
/*
* WorldEdit
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.minecraft.util.commands;
2011-01-29 10:05:22 +00:00
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
2011-01-29 10:05:22 +00:00
import java.util.Map;
import java.util.Set;
2011-02-01 10:03:18 +00:00
import com.sk89q.util.StringUtil;
2011-01-29 10:05:22 +00:00
/**
* Manager for handling commands.
*
* @author sk89q
*/
public class CommandsManager {
/**
2011-02-01 10:03:18 +00:00
* Mapping of nested commands (including aliases) with a description.
2011-01-29 10:05:22 +00:00
*/
2011-02-01 10:03:18 +00:00
public Map<Method, Map<String, Method>> commands
= new HashMap<Method, Map<String, Method>>();
2011-01-29 10:05:22 +00:00
/**
* Mapping of commands (not including aliases) with a description.
*/
public Map<String, String> descs = new HashMap<String, String>();
/**
* Register an object that contains commands (denoted by the
* <code>com.sk89q.util.commands.Command</code> annotation. The methods are
* cached into a map for later usage and it reduces the overhead of
* reflection (method lookup via reflection is relatively slow).
*
* @param cls
*/
public void register(Class<?> cls) {
2011-02-01 10:03:18 +00:00
registerMethods(cls, null);
}
/**
* Register the methods of a class.
*
* @param cls
* @param parent
*/
private void registerMethods(Class<?> cls, Method parent) {
Map<String, Method> map;
// Make a new hash map to cache the commands for this class
// as looking up methods via reflection is fairly slow
2011-02-01 10:03:18 +00:00
if (commands.containsKey(parent)) {
map = commands.get(parent);
} else {
map = new HashMap<String, Method>();
commands.put(parent, map);
}
2011-01-29 10:05:22 +00:00
for (Method method : cls.getMethods()) {
if (!method.isAnnotationPresent(Command.class)) {
continue;
}
Command cmd = method.getAnnotation(Command.class);
// Cache the aliases too
2011-01-29 10:05:22 +00:00
for (String alias : cmd.aliases()) {
2011-02-01 10:03:18 +00:00
map.put(alias, method);
2011-01-29 10:05:22 +00:00
}
// Build a list of commands and their usage details, at least for
// root level commands
if (parent == null) {
if (cmd.usage().length() == 0) {
descs.put(cmd.aliases()[0], cmd.desc());
} else {
descs.put(cmd.aliases()[0], cmd.usage() + " - " + cmd.desc());
}
2011-01-29 10:05:22 +00:00
}
2011-02-01 10:03:18 +00:00
// Look for nested commands -- if there are any, those have
// to be cached too so that they can be quickly looked
// up when processing commands
2011-02-01 10:03:18 +00:00
if (method.isAnnotationPresent(NestedCommand.class)) {
NestedCommand nestedCmd = method.getAnnotation(NestedCommand.class);
for (Class<?> nestedCls : nestedCmd.value()) {
registerMethods(nestedCls, method);
}
}
2011-01-29 10:05:22 +00:00
}
}
/**
* Checks to see whether there is a command.
*
* @param command
* @return
*/
public boolean hasCommand(String command) {
2011-02-01 10:03:18 +00:00
return commands.get(null).containsKey(command.toLowerCase());
2011-01-29 10:05:22 +00:00
}
/**
* Get a list of command descriptions.
*
* @return
*/
public Map<String, String> getCommands() {
return descs;
}
/**
* Get the usage string for a command.
*
2011-02-01 10:03:18 +00:00
* @param args
* @param level
* @param cmd
* @return
*/
2011-02-01 10:03:18 +00:00
private String getUsage(String[] args, int level, Command cmd) {
StringBuilder command = new StringBuilder();
command.append("/");
for (int i = 0; i <= level; i++) {
command.append(args[i] + " ");
}
command.append(cmd.flags().length() > 0 ? "[-" + cmd.flags() + "] " : "");
command.append(cmd.usage());
return command.toString();
}
/**
* Get the usage string for a nested command.
*
* @param args
* @param level
* @param method
* @param palyer
* @return
*/
private String getNestedUsage(String[] args, int level, Method method,
CommandsPlayer player) {
2011-02-01 10:03:18 +00:00
StringBuilder command = new StringBuilder();
command.append("/");
for (int i = 0; i <= level; i++) {
command.append(args[i] + " ");
}
Map<String, Method> map = commands.get(method);
command.append("<");
Set<String> allowedCommands = new HashSet<String>();
2011-02-01 10:03:18 +00:00
for (Map.Entry<String, Method> entry : map.entrySet()) {
Method childMethod = entry.getValue();
if (hasPermission(childMethod, player)) {
Command childCmd = childMethod.getAnnotation(Command.class);
allowedCommands.add(childCmd.aliases()[0]);
}
}
if (allowedCommands.size() > 0) {
command.append(StringUtil.joinString(allowedCommands, "|", 0));
} else {
command.append("action");
}
command.append(">");
return command.toString();
}
2011-01-29 10:05:22 +00:00
/**
* Attempt to execute a command.
*
* @param args
* @param player
* @param methodArgs
2011-01-29 10:05:22 +00:00
* @return
*/
public boolean execute(String[] args, CommandsPlayer player,
Object[] methodArgs) throws Throwable {
return executeMethod(null, args, player, methodArgs, 0);
2011-02-01 10:03:18 +00:00
}
/**
* Attempt to execute a command.
*
* @param parent
* @param args
* @param player
* @param methodArgs
2011-02-01 10:03:18 +00:00
* @param level
* @return
*/
public boolean executeMethod(Method parent, String[] args,
CommandsPlayer player, Object[] methodArgs, int level)
throws Throwable {
2011-02-01 10:03:18 +00:00
String cmdName = args[level];
Map<String, Method> map = commands.get(parent);
Method method = map.get(cmdName.toLowerCase());
2011-01-29 10:05:22 +00:00
if (method == null) {
2011-02-01 10:03:18 +00:00
if (parent == null) { // Root
return false;
} else {
player.printError(getNestedUsage(args, level - 1, parent, player));
2011-02-01 10:03:18 +00:00
return true;
}
2011-01-29 10:05:22 +00:00
}
if (!checkPermissions(method, player)) {
return true;
}
2011-02-01 10:03:18 +00:00
int argsCount = args.length - 1 - level;
if (method.isAnnotationPresent(NestedCommand.class)) {
if (argsCount == 0) {
player.printError(getNestedUsage(args, level, method, player));
return true;
} else {
return executeMethod(method, args, player, methodArgs, level + 1);
2011-02-01 10:03:18 +00:00
}
} else {
Command cmd = method.getAnnotation(Command.class);
String[] newArgs = new String[args.length - level];
System.arraycopy(args, level, newArgs, 0, args.length - level);
CommandContext context = new CommandContext(newArgs);
if (context.argsLength() < cmd.min()) {
player.printError("Too few arguments.");
player.printError(getUsage(args, level, cmd));
return true;
}
if (cmd.max() != -1 && context.argsLength() > cmd.max()) {
player.printError("Too many arguments.");
player.printError(getUsage(args, level, cmd));
return true;
}
for (char flag : context.getFlags()) {
if (cmd.flags().indexOf(String.valueOf(flag)) == -1) {
player.printError("Unknown flag: " + flag);
player.printError(getUsage(args, level, cmd));
return true;
}
}
methodArgs[0] = context;
2011-02-01 10:03:18 +00:00
try {
method.invoke(null, methodArgs);
2011-02-01 10:03:18 +00:00
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
throw e.getCause();
2011-02-01 10:03:18 +00:00
}
2011-01-29 10:05:22 +00:00
}
2011-02-01 10:03:18 +00:00
return true;
}
/**
* Checks permissions, prints an error if needed.
*
* @param method
* @param player
* @return
*/
private boolean checkPermissions(Method method, CommandsPlayer player) {
2011-02-01 10:03:18 +00:00
if (!method.isAnnotationPresent(CommandPermissions.class)) {
2011-01-29 10:05:22 +00:00
return true;
}
2011-02-01 10:03:18 +00:00
CommandPermissions perms = method.getAnnotation(CommandPermissions.class);
2011-01-29 10:05:22 +00:00
2011-02-01 10:03:18 +00:00
for (String perm : perms.value()) {
if (player.hasPermission(perm)) {
return true;
}
}
2011-02-01 10:03:18 +00:00
player.printError("You don't have permission for this command.");
return false;
2011-01-29 10:05:22 +00:00
}
2011-02-01 10:03:18 +00:00
/**
* Returns whether a player has access to a command.
*
* @param method
* @param player
* @return
*/
private boolean hasPermission(Method method, CommandsPlayer player) {
2011-01-29 10:05:22 +00:00
CommandPermissions perms = method.getAnnotation(CommandPermissions.class);
if (perms == null) {
return true;
}
for (String perm : perms.value()) {
if (player.hasPermission(perm)) {
return true;
}
}
return false;
}
}