Created
February 11, 2016 15:36
-
-
Save Stumblinbear/c27f1fe584c130690b8f to your computer and use it in GitHub Desktop.
An annotation-based command manager that handles arguments, argument type handling, predictions, tab completion, (sub-)*commands, error handling, help pages, and more!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.PrintWriter; | |
import java.io.StringWriter; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
import java.lang.reflect.Constructor; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map.Entry; | |
import java.util.TreeMap; | |
import java.util.regex.Matcher; | |
import java.util.regex.Pattern; | |
import org.apache.commons.lang.StringUtils; | |
import org.apache.commons.lang.time.StopWatch; | |
import org.bukkit.Bukkit; | |
import org.bukkit.ChatColor; | |
import org.bukkit.OfflinePlayer; | |
import org.bukkit.command.Command; | |
import org.bukkit.command.CommandExecutor; | |
import org.bukkit.command.CommandMap; | |
import org.bukkit.command.CommandSender; | |
import org.bukkit.command.PluginCommand; | |
import org.bukkit.command.TabCompleter; | |
import org.bukkit.entity.Player; | |
import org.bukkit.plugin.Plugin; | |
import org.bukkit.plugin.java.JavaPlugin; | |
import net.md_5.bungee.api.chat.ClickEvent; | |
import net.md_5.bungee.api.chat.ComponentBuilder; | |
import net.md_5.bungee.api.chat.HoverEvent; | |
/** | |
* Register a command with the command system! | |
* <br><br> | |
* Handles arguments, argument type handling, predictions, tab completion, sub-sub-sub-commands, error handling, help pages, and more! | |
* <br><br> | |
* CommandManager manager = new CommandManager(plugin, "Help Tag", "command", "permission node"); | |
* | |
* @author Stumblinbear | |
*/ | |
public class CommandManager implements TabCompleter, CommandExecutor { | |
static boolean GLOBAL_DEBUG = false; | |
@Cmd(cmd = "debug", | |
args = "[bool]", | |
argTypes = { Arg.ArgBoolean.class }, | |
help = "Toggle debug mode.", | |
longhelp = "Toggle debug mode. Shows information on command usage.", | |
only = CommandOnly.OP, | |
permission = "debug") | |
public static CommandFinished cmdToggleDebugMode(CommandSender sender, Object[] args) { | |
GLOBAL_DEBUG = (args.length != 0 ? (Boolean)args[0] : !GLOBAL_DEBUG); | |
sender.sendMessage(ChatColor.YELLOW + "Debug mode is now: " + (GLOBAL_DEBUG ? ChatColor.GREEN + "ON" : ChatColor.RED + "OFF")); | |
return CommandFinished.DONE; | |
} | |
private static HashMap<Class<? extends AbstractArg<?>>, AbstractArg<?>> argInstances = new HashMap<Class<? extends AbstractArg<?>>, AbstractArg<?>>(); | |
private ArrayList<Cmd> commands = new ArrayList<Cmd>(); | |
private ArrayList<Method> commandMethods = new ArrayList<Method>(); | |
String tag; | |
String command; | |
String permissionScheme; | |
public CommandManager(JavaPlugin plugin, String tag, String permissionScheme, String command, String... aliases) { | |
this.tag = tag; | |
this.command = command; | |
this.permissionScheme = permissionScheme; | |
// Used to inject the command without using plugin.yml | |
try { | |
final Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); | |
bukkitCommandMap.setAccessible(true); | |
CommandMap commandMap = (CommandMap)bukkitCommandMap.get(Bukkit.getServer()); | |
Constructor<PluginCommand> c = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class); | |
c.setAccessible(true); | |
PluginCommand pluginCommand = c.newInstance(command, plugin); | |
pluginCommand.setTabCompleter(this); | |
pluginCommand.setExecutor(this); | |
if(aliases.length > 0) | |
pluginCommand.setAliases(Arrays.asList(aliases)); | |
commandMap.register(command, pluginCommand); | |
loadCommandClass(this.getClass()); | |
} catch(Exception e) { e.printStackTrace(); } | |
} | |
/** | |
* Parses classes for @Cmd annotations. | |
*/ | |
public CommandManager loadCommandClass(Class<?> commandClass) { | |
try { | |
for(Method method : commandClass.getMethods()) { | |
if(method.isAnnotationPresent(Cmd.class)) { | |
Cmd cmd = method.getAnnotation(Cmd.class); | |
commands.add(cmd); | |
commandMethods.add(method); | |
} | |
} | |
} catch(SecurityException e) { | |
e.printStackTrace(); | |
} | |
return this; | |
} | |
@Override | |
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) { | |
List<String> predictions = new ArrayList<String>(); | |
String token = (args.length == 0 ? "" : args[args.length - 1]); | |
for(Cmd c : commands) { | |
List<String> cmdPredictions = getPredicted(c, token, args.length - 1); | |
// Prevent duplicate entries | |
if(cmdPredictions != null) { | |
for(String str : cmdPredictions) { | |
if(!predictions.contains(str)) | |
predictions.add(str); | |
} | |
} | |
} | |
return predictions; | |
} | |
/** | |
* Get a prediction of the next command argument. | |
*/ | |
private List<String> getPredicted(Cmd c, String token, int i) { | |
String[] cmdArg = c.cmd().split(" "); | |
// If no token, return all possible commands. | |
if(token == "") | |
return Arrays.asList(new String[] { cmdArg[0] }); | |
// If the amount of args is more than available, or it doesn't start with the token. | |
if(i >= cmdArg.length) { | |
int argNum = i - cmdArg.length; | |
if(argNum >= c.argTypes().length) | |
return null; | |
else{ | |
if(!argInstances.containsKey(c.argTypes()[argNum])) | |
try { | |
argInstances.put(c.argTypes()[argNum], c.argTypes()[argNum].newInstance()); | |
} catch (Exception e) { } | |
AbstractArg<?> absArg = argInstances.get(c.argTypes()[argNum]); | |
return absArg.getPredictions(); | |
} | |
// If it doesn't start with the token. | |
}else if(!cmdArg[i].startsWith(token)) | |
return null; | |
// It must be a match! | |
return Arrays.asList(new String[] { cmdArg[i] }); | |
} | |
@Override | |
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { | |
StopWatch sw = null; | |
if(GLOBAL_DEBUG) { | |
sw = new StopWatch(); | |
sw.start(); | |
} | |
CommandFinished finishedType = runCommand(sender, args); | |
if(finishedType.shouldPrint()) { | |
sender.sendMessage(ChatColor.RED + finishedType.getErrorString()); | |
// Do our best to predict which command was going to be used. | |
if(finishedType == CommandFinished.COMMAND) { | |
// TreeMaps automatically sort by numbers. | |
TreeMap<Double, Cmd> possible = new TreeMap<Double, Cmd>(); | |
for(Cmd c : commands) { | |
// Reduce arg array to the shortest one. | |
String[] fixedArgs = new String[c.cmd().split(" ").length]; | |
for(int i = 0; i < (args.length > fixedArgs.length ? fixedArgs.length : args.length); i++) | |
fixedArgs[i] = args[i]; | |
// Combine the arguments. | |
String cmdArgs = StringUtils.join(fixedArgs, " "); | |
// Use Levenshtein Distance to get how similar the two strings are to each other. Calculate percentage with the value returned. | |
possible.put((1D - (StringUtils.getLevenshteinDistance(cmdArgs, c.cmd()) / (Math.max(cmdArgs.length(), c.cmd().length()) * 1D))) * 100D, c); | |
} | |
// Are there even any predictions? | |
if(possible.size() > 0) { | |
// Get the last entry. (The one with the highest possibility) | |
Entry<Double, Cmd> entry = possible.pollLastEntry(); | |
sender.sendMessage(""); | |
// Allow players to click the command in chat to add it to their chat input. | |
if(sender instanceof Player) { | |
((Player)sender).spigot().sendMessage(new ComponentBuilder(" Did you mean: ").color(net.md_5.bungee.api.ChatColor.GOLD) | |
.append("/" + label + " " + entry.getValue().cmd()).color(net.md_5.bungee.api.ChatColor.GRAY) | |
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(StringUtils.join((entry.getValue().longhelp().equals("") ? entry.getValue().help() : entry.getValue().longhelp()).split("(?<=\\G.........................)"), "\n")).create())) | |
.event(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, "/" + label + " " + entry.getValue().cmd())) | |
.append("? We're " + ((int)(entry.getKey() * 10) / 10D) + "% sure.").reset().color(net.md_5.bungee.api.ChatColor.GOLD).create()); | |
}else | |
sender.sendMessage(ChatColor.GOLD + " Did you mean: " + ChatColor.GRAY + "/" + label + " " + entry.getValue().cmd() + ChatColor.GOLD + "? We're " + ((int)(entry.getKey() * 10) / 10D) + "% sure."); | |
sender.sendMessage(""); | |
} | |
} | |
} | |
if(GLOBAL_DEBUG && sw != null) { | |
sw.stop(); | |
sender.sendMessage(ChatColor.YELLOW + "Command took " + sw.getTime() + " milliseconds to complete."); | |
} | |
return true; | |
} | |
/** | |
* Find the best command to run and do so. | |
*/ | |
public CommandFinished runCommand(CommandSender sender, String[] args) { | |
try { | |
// Display help if no args, or they use the help command | |
if(args.length == 0 || args[0].equalsIgnoreCase("help")) | |
return displayHelp(sender, args.length == 0 ? null : args); | |
else{ | |
Cmd bestFit = null; | |
int bestFit_i = 0; | |
int bestFit_args = -1; | |
// Loop through commands until a suitable one is found | |
for(int i = 0; i < commands.size(); i++) { | |
Cmd cmd = commands.get(i); | |
// Split the base command and check for a match | |
String[] cmds = cmd.cmd().split(" "); | |
if(args.length >= cmds.length) { | |
boolean valid = true; | |
for(int j = 0; j < cmds.length; j++) { | |
if(!cmds[j].equalsIgnoreCase(args[j])) { | |
valid = false; | |
break; | |
} | |
} | |
if(!valid) | |
continue; | |
}else | |
continue; | |
// Check if it's better than the best fit. | |
if(cmd.cmd().split(" ").length > bestFit_args) { | |
bestFit = cmd; | |
bestFit_i = i; | |
bestFit_args = cmd.cmd().split(" ").length; | |
}else | |
continue; | |
} | |
if(bestFit != null) { | |
// Check the "only" argument | |
if(sender instanceof Player) { | |
if(bestFit.only() == CommandOnly.CONSOLE) | |
return CommandFinished.NOPLAYER; | |
}else if(bestFit.only() == CommandOnly.PLAYER) | |
return CommandFinished.NOCONSOLE; | |
// Check for the "op" argument and permission argument | |
if((bestFit.only() == CommandOnly.OP ? !sender.isOp() : false) || | |
(bestFit.permission() != "" ? !sender.hasPermission(permissionScheme + "." + bestFit.permission()) : false)) | |
return CommandFinished.PERMISSION; | |
// Split up the args; arguments in quotes count as a single argument. | |
List<Object> cmdArgList = new ArrayList<Object>(); | |
Matcher m = Pattern.compile("(?:([^\"]\\S*)|\"(.+?)\")\\s*").matcher(StringUtils.join(args, " ").replaceFirst(bestFit.cmd(), "").trim()); | |
for(int j = 0; m.find(); j++) { | |
// Apply the requested argument type. | |
Class<? extends AbstractArg<?>> requestedType = (j < bestFit.argTypes().length ? bestFit.argTypes()[j] : Arg.ArgString.class); | |
// Cache the instance. | |
if(!argInstances.containsKey(requestedType)) | |
argInstances.put(requestedType, requestedType.newInstance()); | |
AbstractArg<?> absArg = argInstances.get(requestedType); | |
try { | |
Object arg = absArg.parseArg(m.group(1) != null ? m.group(1) : m.group(2)); | |
if(arg == null) | |
// Some argument parsers don't throw an exception. Just an extra precaution. | |
throw new CommandException(absArg.getFailure() + " (" + m.group(1) != null ? m.group(1) : m.group(2) + ")"); | |
cmdArgList.add(arg); | |
} catch(Exception e) { | |
return CommandFinished.CUSTOM.replace(absArg.getFailure() + " (" + (m.group(1) != null ? m.group(1) : m.group(2)) + ")"); | |
} | |
} | |
// Check that all the required arguments have been fulfilled. | |
Object[] cmdArgsPassed = cmdArgList.toArray(new Object[cmdArgList.size()]); | |
if(StringUtils.countMatches(bestFit.args(), "<") > cmdArgsPassed.length) | |
return CommandFinished.BADCOMMAND.replace(command + " " + bestFit.cmd() + " " + bestFit.args()); | |
// Run the command :D | |
return (CommandFinished)commandMethods.get(bestFit_i).invoke(null, new Object[] { sender, (cmdArgsPassed != null ? cmdArgsPassed : null) }); | |
} | |
} | |
} catch(InvocationTargetException e) { | |
e.getCause().printStackTrace(); | |
if(e.getCause() instanceof CommandException) | |
return CommandFinished.CUSTOM.replace(e.getCause().getMessage()); | |
if(GLOBAL_DEBUG) | |
Bukkit.broadcastMessage(ChatColor.RED + "Error: " + getTrace(e)); | |
return CommandFinished.EXCEPTION; | |
} catch (Exception e) { | |
e.printStackTrace(); | |
if(GLOBAL_DEBUG) | |
Bukkit.broadcastMessage(ChatColor.RED + "Error: " + getTrace(e)); | |
return CommandFinished.EXCEPTION; | |
} | |
return CommandFinished.COMMAND.replace(command); | |
} | |
/** | |
* Display the help menu. | |
*/ | |
public CommandFinished displayHelp(CommandSender sender, String[] args) { | |
ArrayList<String> helpList = new ArrayList<String>(); // The help message buffer | |
boolean specific = false; // If "help <command>" | |
int perPage = 8; // How many commands to show per page | |
int page = 0; // Which page | |
if(args != null && args.length != 1) { | |
try { | |
page = Integer.parseInt(args[1]) - 1; | |
if(page < 0) // Negative pages are bad juju. | |
page = 0; | |
} catch(Exception e) { specific = true; } // If this fails, it's probably a string. Check for a specific command. | |
} | |
String cmdLabel = null; // The label of the specific command. | |
if(specific && args.length != 1) { | |
perPage = 4; // Reduce the amount to show per page. | |
cmdLabel = StringUtils.join(args, " ").split(" ", 2)[1]; // Because args = "help <command>" cut out "help". | |
} | |
for(Cmd cmd : commands) { | |
// If looking for specific commands and it isn't the one we're looking for | |
if(specific && !cmd.cmd().startsWith(cmdLabel)) | |
continue; | |
// Should it even show? | |
if(cmd.showInHelp()) { | |
// If it can't be used, don't show it! Simple! :D | |
boolean canUse = cmd.permission().equals("") ? true : (sender.hasPermission(permissionScheme + "." + cmd.permission())); | |
// Is it op-only? | |
if(cmd.only() == CommandOnly.OP) | |
canUse = (cmd.permission().equals("") ? sender.isOp() : canUse); | |
if(canUse) | |
helpList.add(ChatColor.GOLD + "/" + command + " " + cmd.cmd() + (cmd.args() != "" ? " " + cmd.args() : "") + ": " + ChatColor.WHITE + (specific ? (cmd.longhelp().equals("") ? cmd.help() : cmd.longhelp()) : cmd.help())); | |
} | |
} | |
// Make sure there's something to show. | |
boolean badPage = true; | |
if(helpList.size() >= page * perPage) { | |
for(int j = 0; j < perPage; j++) { | |
if(helpList.size() > (j + page * perPage)) { | |
if(j == 0) | |
sender.sendMessage(ChatColor.YELLOW + "--------- " + ChatColor.WHITE + tag + " Help (" + (page + 1) + "/" + (int)Math.ceil(helpList.size() / (perPage * 1F)) + ")" + ChatColor.YELLOW + " ---------------------"); | |
sender.sendMessage(helpList.get((j + page * perPage))); | |
badPage = false; | |
} | |
} | |
} | |
if(badPage) { | |
if(specific) | |
return CommandFinished.CUSTOM.replace("Command unrecognized."); | |
else | |
return CommandFinished.CUSTOM.replace("Page " + (page + 1) + " does not exist in help."); | |
}else if(helpList.size() > (page + 1) * perPage) | |
sender.sendMessage(ChatColor.WHITE + "Use " + ChatColor.YELLOW + "/" + command + " help " + (page + 2) + ChatColor.WHITE + " to see more help."); | |
return CommandFinished.DONE; | |
} | |
/** | |
* Returns the string version of an exception. Helps with in-game error checking. | |
*/ | |
private String getTrace(Exception e) { | |
StringWriter sw = new StringWriter(); | |
PrintWriter pw = new PrintWriter(sw); | |
e.printStackTrace(pw); | |
return sw.toString(); | |
} | |
@Target(ElementType.METHOD) | |
@Retention(RetentionPolicy.RUNTIME) | |
/** | |
* Attach to a function to denote it as a command. You must register the class it is in as a command class before it will be used. | |
*/ | |
public static @interface Cmd { | |
/** | |
* Takes in any amount of subcommands. The command handler always chooses the best-fit command. | |
* <br><br> | |
* Example: | |
* <br> | |
* cmd = "sub1 sub2 sub3 sub4 sub5" | |
*/ | |
String cmd() default ""; | |
/** | |
* The arguments for the command. Required arguments must be enclosed in <>'s | |
* <br><br> | |
* Example: | |
* <br> | |
* args = "<arg1> <arg2> [arg3]" | |
* <br> | |
* Argument 1 and 2 are required; the third is optional. | |
*/ | |
String args() default ""; | |
/** | |
* Specifies the type that an argument should be. Default is Arg.ArgString. | |
* <br><br> | |
* Example: | |
* <br> | |
* argTypes = { Arg.ArgInteger, Arg.ArgString, Arg.ArgPlayer } | |
* <ul> | |
* <li>Argument 1 <i>must</i> be an integer.</li> | |
* <li>Argument 2 <i>must</i> be a string(anything).</li> | |
* <li>Argument 3 <i>must</i> be an online player.</li> | |
* </ul> | |
*/ | |
Class<? extends AbstractArg<?>>[] argTypes() default { }; | |
/** | |
* The text to show next to a command when a user does /cmd help. | |
*/ | |
String help() default "Default help thingy... :("; | |
/** | |
* The text to show next to a command when a user does /cmd help <command>. | |
* <br><br> | |
* Use to give more information about the command. | |
*/ | |
String longhelp() default ""; | |
/** | |
* Should the command be shown in help at all? | |
*/ | |
boolean showInHelp() default true; | |
/** | |
* Specifies if the command should be restricted to CONSOLE, OP, or PLAYERS. Otherwise, ALL. | |
*/ | |
CommandOnly only() default CommandOnly.ALL; | |
/** | |
* The permission node to use. | |
* <br><br> | |
* Example: | |
* <br> | |
* new CommandManager(plugin, "Test", "testnode", "test"); | |
* <br> | |
* permission = "edit" | |
* <br> | |
* This setup would require a player to have testnode.edit to use the command. | |
*/ | |
String permission() default ""; | |
} | |
/** | |
* Used to define who is allowed to use a command. | |
*/ | |
public static enum CommandOnly { | |
/** | |
* Only players can use the command. | |
*/ | |
PLAYER, | |
/** | |
* Only op players can use the command. Can be overridden by a matched permission. | |
*/ | |
OP, | |
/** | |
* Only the console can use the command. | |
*/ | |
CONSOLE, | |
/** | |
* Anyone can use the command, given their permissions match. | |
*/ | |
ALL | |
} | |
/** | |
* This can be used to immediately throw an error without returning a <code>CommandFinished</code>. | |
* It'll display the specified error. | |
*/ | |
public static class CommandException extends Exception { | |
private static final long serialVersionUID = 1L; | |
public CommandException(String message) { | |
super(message); | |
} | |
} | |
/** | |
* A set of command errors. | |
*/ | |
public static enum CommandFinished { | |
/** Finished correctly */ | |
DONE(false, "Done"), | |
/** Command does not exist */ | |
COMMAND(true, "Command does not exist. Use /%s for help."), | |
/** Command does not exist */ | |
BADCOMMAND(true, "Bad command usage: /%s "), | |
/** Console not allowed to use */ | |
NOCONSOLE(true, "This command cannot be run from the console."), | |
/** Player not allowed to use */ | |
NOPLAYER(true, "This command cannot be run by players."), | |
/** Player does not exist */ | |
EXISTPLAYER(true, "That player does not exist."), | |
/** Incorrect permissions */ | |
PERMISSION(true, "Insufficient permissions."), | |
HOLDBLOCK(true, "You must be holding a block."), | |
HOLDITEM(true, "You must be holding an item."), | |
LONGSTRING(true, "String cannot be longer than %s!"), | |
/** Custom error */ | |
CUSTOM(true, "%s"), | |
EXCEPTION(true, "An exception occured. Please contact a member of staff and tell them!"); | |
private boolean shouldPrint; | |
private String errorString; | |
private String extraString; | |
CommandFinished(boolean par1ShouldPrint, String par1Error) { | |
shouldPrint = par1ShouldPrint; | |
errorString = par1Error; | |
} | |
public boolean shouldPrint() { | |
return shouldPrint; | |
} | |
public String getErrorString() { | |
if(extraString != null) | |
return errorString.replace("%s", extraString); | |
else | |
return errorString; | |
} | |
public CommandFinished replace(String theString) { | |
extraString = theString; | |
return this; | |
} | |
} | |
/* | |
* Arguments | |
*/ | |
public static interface IArgParse<T> { | |
T parseArg(String arg); | |
List<String> getPredictions(); | |
String getFailure(); | |
} | |
public static abstract class AbstractArg<T> implements IArgParse<T> { | |
public AbstractArg() { } | |
} | |
public static class Arg { | |
public static class ArgArray extends AbstractArg<List<String>> { | |
public List<String> parseArg(String arg) { | |
List<String> list = new ArrayList<String>(); | |
for(String str : arg.split(",")) | |
list.add(str); | |
return list; | |
} | |
public String getFailure() { | |
return "Argument failure."; | |
} | |
public List<String> getPredictions() { return null; } | |
} | |
public static class ArgBoolean extends AbstractArg<Boolean> { | |
public Boolean parseArg(String arg) { | |
if(arg.equalsIgnoreCase("true") || arg.equalsIgnoreCase("yes") || arg.equalsIgnoreCase("on") || arg.equalsIgnoreCase("1")) | |
return true; | |
if(arg.equalsIgnoreCase("false") || arg.equalsIgnoreCase("no") || arg.equalsIgnoreCase("off") || arg.equalsIgnoreCase("0")) | |
return false; | |
return null; | |
} | |
public String getFailure() { | |
return "Argument not a valid boolean."; | |
} | |
public List<String> getPredictions() { return Arrays.asList(new String[] { "true", "false" }); } | |
} | |
public static class ArgByte extends AbstractArg<Byte> { | |
public Byte parseArg(String arg) { | |
return Byte.valueOf(arg); | |
} | |
public String getFailure() { | |
return "Argument not a valid byte."; | |
} | |
public List<String> getPredictions() { return Arrays.asList(new String[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" }); } | |
} | |
public static class ArgDouble extends AbstractArg<Double> { | |
public Double parseArg(String arg) { | |
return Double.valueOf(arg); | |
} | |
public String getFailure() { | |
return "Argument not a real number."; | |
} | |
public List<String> getPredictions() { return null; } | |
} | |
public static class ArgFloat extends AbstractArg<Float> { | |
public Float parseArg(String arg) { | |
return Float.valueOf(arg); | |
} | |
public String getFailure() { | |
return "Argument not a floating point number."; | |
} | |
public List<String> getPredictions() { return null; } | |
} | |
public static class ArgInteger extends AbstractArg<Integer> { | |
public Integer parseArg(String arg) { | |
return Integer.valueOf(arg); | |
} | |
public String getFailure() { | |
return "Argument not an integer."; | |
} | |
public List<String> getPredictions() { return null; } | |
} | |
public static class ArgPlayer extends AbstractArg<OfflinePlayer> { | |
public Player parseArg(String arg) { | |
return Bukkit.getPlayer(arg); | |
} | |
public String getFailure() { | |
return CommandFinished.EXISTPLAYER.getErrorString(); | |
} | |
public List<String> getPredictions() { | |
List<String> players = new ArrayList<String>(); | |
for(Player p : Bukkit.getOnlinePlayers()) | |
players.add(p.getName()); | |
return players; | |
} | |
} | |
public static class ArgOfflinePlayer extends AbstractArg<OfflinePlayer> { | |
@SuppressWarnings("deprecation") | |
public OfflinePlayer parseArg(String arg) { | |
return Bukkit.getOfflinePlayer(arg); | |
} | |
public String getFailure() { | |
return CommandFinished.EXISTPLAYER.getErrorString(); | |
} | |
public List<String> getPredictions() { | |
List<String> players = new ArrayList<String>(); | |
for(Player p : Bukkit.getOnlinePlayers()) | |
players.add(p.getName()); | |
return players; | |
} | |
} | |
public static class ArgString extends AbstractArg<String> { | |
public String parseArg(String arg) { | |
return arg; | |
} | |
public String getFailure() { | |
return "Could not parse string."; | |
} | |
public List<String> getPredictions() { return null; } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment