FINALLY revamped the commandhandler (but it still isn't fully complete, eventually I want there to be a hierarchy system where you can specify "parents" to comamnds for a sub-command system)

This commit is contained in:
batchprogrammer314
2021-09-02 15:46:00 -05:00
parent e7d8bf1b55
commit fadcc88946
14 changed files with 269 additions and 187 deletions

View File

@@ -86,7 +86,7 @@ public class CommandHandler {
} }
String methodName = cmd.name(); String methodName = cmd.name();
CommandMethod commandMethod = new CommandMethod(methodName, Sets.newHashSet(cmd.aliases()), cmd.desc(), cmd.usage(), perm, command, method, autofiller); CommandMethod commandMethod = new CommandMethod(methodName, Sets.newHashSet(cmd.aliases()), cmd.desc(), perm, command, method, autofiller);
command.addMethod(methodName, commandMethod); command.addMethod(methodName, commandMethod);
} }
@@ -112,16 +112,6 @@ public class CommandHandler {
help.put(commandInstance.getName(), getCommandInfo(commandInstance)); help.put(commandInstance.getName(), getCommandInfo(commandInstance));
} }
/*
* TODO
* Eventually, this will be a LOT better, basically not having to have this method
* at all (kind of like Drink), and even better, we won't even need a "usage" field
* in the @Command annotation, and can create the usage message from the method parameters.
*/
public void sendUsage(CommandSender sender, CommandInstance command, String usage) {
sender.sendMessage("Command Usage: " + ChatColor.YELLOW + "/" + command.getName() + " " + usage);
}
private List<String> getCommandInfo(CommandInstance commandInstance) { private List<String> getCommandInfo(CommandInstance commandInstance) {
List<String> output = new ArrayList<>(); List<String> output = new ArrayList<>();

View File

@@ -1,13 +1,23 @@
package net.nuggetmc.tplus.command; package net.nuggetmc.tplus.command;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.nuggetmc.tplus.command.annotation.Arg;
import net.nuggetmc.tplus.command.annotation.OptArg;
import net.nuggetmc.tplus.command.annotation.TextArg;
import net.nuggetmc.tplus.command.exception.ArgCountException;
import net.nuggetmc.tplus.command.exception.ArgParseException;
import net.nuggetmc.tplus.command.exception.NonPlayerException;
import net.nuggetmc.tplus.utils.ChatUtils;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.defaults.BukkitCommand; import org.bukkit.command.defaults.BukkitCommand;
import org.bukkit.entity.Player;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -61,8 +71,136 @@ public abstract class CommandInstance extends BukkitCommand {
arguments.remove(0); arguments.remove(0);
} }
List<Object> parsedArguments = new ArrayList<>();
int index = 0;
try { try {
method.getMethod().invoke(method.getHandler(), sender, arguments); for (Parameter parameter : method.getMethod().getParameters()) {
Class<?> type = parameter.getType();
boolean required = !parameter.isAnnotationPresent(OptArg.class);
if (type == CommandSender.class) {
parsedArguments.add(sender);
} else if (type == Player.class) {
if (!(sender instanceof Player)) {
throw new NonPlayerException();
}
parsedArguments.add(sender);
} else if (type == List.class) {
parsedArguments.add(arguments);
}
else {
if (parameter.isAnnotationPresent(TextArg.class)) {
if (index >= arguments.size()) {
parsedArguments.add("");
} else {
parsedArguments.add(StringUtils.join(arguments.subList(index, arguments.size()), " "));
}
continue;
}
if (index >= arguments.size() && required) {
throw new ArgCountException();
}
String arg;
if (index >= arguments.size()) {
arg = null;
} else {
arg = arguments.get(index);
}
index++;
if (type == String.class) {
parsedArguments.add(arg);
} else if (type == int.class) {
if (arg == null) {
parsedArguments.add(0);
continue;
}
try {
parsedArguments.add(Integer.parseInt(arg));
} catch (NumberFormatException e) {
throw new ArgParseException(parameter);
}
} else if (type == double.class) {
if (arg == null) {
parsedArguments.add(0);
continue;
}
try {
parsedArguments.add(Double.parseDouble(arg));
} catch (NumberFormatException e) {
throw new ArgParseException(parameter);
}
} else if (type == float.class) {
if (arg == null) {
parsedArguments.add(0);
continue;
}
try {
parsedArguments.add(Float.parseFloat(arg));
} catch (NumberFormatException e) {
throw new ArgParseException(parameter);
}
} else if (type == boolean.class) {
if (arg == null) {
parsedArguments.add(false);
continue;
}
if (arg.equalsIgnoreCase("true") || arg.equalsIgnoreCase("false")) {
parsedArguments.add(Boolean.parseBoolean(arg));
} else {
throw new ArgParseException(parameter);
}
} else {
parsedArguments.add(arg);
}
}
}
}
catch (NonPlayerException e) {
sender.sendMessage("This is a player-only command.");
return true;
}
catch (ArgParseException e) {
Parameter parameter = e.getParameter();
String name = getArgumentName(parameter);
sender.sendMessage("The parameter " + ChatColor.YELLOW + name + ChatColor.RESET + " must be of type " + ChatColor.YELLOW + parameter.getType().toString() + ChatColor.RESET + ".");
return true;
}
catch (ArgCountException e) {
List<String> usageArgs = new ArrayList<>();
Arrays.stream(method.getMethod().getParameters()).forEach(parameter -> {
Class<?> type = parameter.getType();
if (type != CommandSender.class && type != Player.class){
usageArgs.add(getArgumentName(parameter));
}
});
sender.sendMessage("Command Usage: " + org.bukkit.ChatColor.YELLOW + "/" + getName() + (method.getName().isEmpty() ? "" : " " + method.getName())
+ " " + StringUtils.join(usageArgs, " "));
return true;
}
try {
method.getMethod().invoke(method.getHandler(), parsedArguments.toArray());
} catch (InvocationTargetException | IllegalAccessException e) { } catch (InvocationTargetException | IllegalAccessException e) {
sender.sendMessage(ChatColor.RED + "Failed to perform command."); sender.sendMessage(ChatColor.RED + "Failed to perform command.");
e.printStackTrace(); e.printStackTrace();
@@ -71,6 +209,24 @@ public abstract class CommandInstance extends BukkitCommand {
return true; return true;
} }
public static String getArgumentName(Parameter parameter) {
if (parameter.isAnnotationPresent(OptArg.class)) {
OptArg arg = parameter.getAnnotation(OptArg.class);
if (!arg.value().isEmpty()) {
return "[" + ChatUtils.camelToDashed(arg.value()) + "]";
}
} else if (parameter.isAnnotationPresent(Arg.class)) {
Arg arg = parameter.getAnnotation(Arg.class);
if (!arg.value().isEmpty()) {
return "<" + ChatUtils.camelToDashed(arg.value()) + ">";
}
}
return "<" + ChatUtils.camelToDashed(parameter.getName()) + ">";
}
@Override @Override
@Nonnull @Nonnull
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@@ -8,7 +8,6 @@ public class CommandMethod {
private final String name; private final String name;
private final Set<String> aliases; private final Set<String> aliases;
private final String description; private final String description;
private final String usage;
private final String permission; private final String permission;
private final CommandInstance handler; private final CommandInstance handler;
@@ -16,11 +15,10 @@ public class CommandMethod {
private final Method method; private final Method method;
private final Method autofiller; private final Method autofiller;
public CommandMethod(String name, Set<String> aliases, String description, String usage, String permission, CommandInstance handler, Method method, Method autofiller) { public CommandMethod(String name, Set<String> aliases, String description, String permission, CommandInstance handler, Method method, Method autofiller) {
this.name = name; this.name = name;
this.aliases = aliases; this.aliases = aliases;
this.description = description; this.description = description;
this.usage = usage;
this.permission = permission; this.permission = permission;
this.handler = handler; this.handler = handler;
this.method = method; this.method = method;
@@ -39,10 +37,6 @@ public class CommandMethod {
return description; return description;
} }
public String getUsage() {
return usage;
}
public String getPermission() { public String getPermission() {
return permission; return permission;
} }
@@ -61,6 +55,6 @@ public class CommandMethod {
@Override @Override
public String toString() { public String toString() {
return getClass().getSimpleName() + "{name=\"" + name + "\",aliases=" + aliases + ",description=\"" + description + "\",usage=\"" + usage + "\",permission=\"" + permission + "\",method=" + method + ",autofiller=" + autofiller + "}"; return getClass().getSimpleName() + "{name=\"" + name + "\",aliases=" + aliases + ",description=\"" + description + "\",permission=\"" + permission + "\",method=" + method + ",autofiller=" + autofiller + "}";
} }
} }

View File

@@ -0,0 +1,12 @@
package net.nuggetmc.tplus.command.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Arg {
String value() default "";
}

View File

@@ -14,8 +14,6 @@ public @interface Command {
String desc() default "Blank description."; String desc() default "Blank description.";
String usage() default "";
String autofill() default ""; String autofill() default "";
boolean visible() default true; boolean visible() default true;

View File

@@ -0,0 +1,12 @@
package net.nuggetmc.tplus.command.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface OptArg {
String value() default "";
}

View File

@@ -0,0 +1,12 @@
package net.nuggetmc.tplus.command.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface TextArg {
String value() default "";
}

View File

@@ -7,8 +7,10 @@ import net.nuggetmc.tplus.bot.agent.legacyagent.ai.IntelligenceAgent;
import net.nuggetmc.tplus.bot.agent.legacyagent.ai.NeuralNetwork; import net.nuggetmc.tplus.bot.agent.legacyagent.ai.NeuralNetwork;
import net.nuggetmc.tplus.command.CommandHandler; import net.nuggetmc.tplus.command.CommandHandler;
import net.nuggetmc.tplus.command.CommandInstance; import net.nuggetmc.tplus.command.CommandInstance;
import net.nuggetmc.tplus.command.annotation.Arg;
import net.nuggetmc.tplus.command.annotation.Autofill; import net.nuggetmc.tplus.command.annotation.Autofill;
import net.nuggetmc.tplus.command.annotation.Command; import net.nuggetmc.tplus.command.annotation.Command;
import net.nuggetmc.tplus.command.annotation.OptArg;
import net.nuggetmc.tplus.utils.ChatUtils; import net.nuggetmc.tplus.utils.ChatUtils;
import net.nuggetmc.tplus.utils.MathUtils; import net.nuggetmc.tplus.utils.MathUtils;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@@ -49,82 +51,26 @@ public class AICommand extends CommandInstance {
@Command( @Command(
name = "random", name = "random",
desc = "Create bots with random neural networks, collecting feed data.", desc = "Create bots with random neural networks, collecting feed data."
usage = "<amount> <name> [skin]"
) )
public void random(CommandSender sender, List<String> args) { public void random(Player sender, @Arg("amount") int amount, @Arg("name") String name, @OptArg("skin") String skin) {
if (!(sender instanceof Player)) { manager.createBots(sender, name, skin, amount, NeuralNetwork.RANDOM);
return;
}
if (args.size() < 2) {
commandHandler.sendUsage(sender, this, "random <amount> <name> [skin]");
return;
}
String skin;
if (args.size() < 3) {
skin = null;
} else {
skin = args.get(2);
}
int n;
try {
n = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
sender.sendMessage("The amount must be an integer!");
return;
}
manager.createBots((Player) sender, args.get(1), skin, n, NeuralNetwork.RANDOM);
} }
@Command( @Command(
name = "reinforcement", name = "reinforcement",
desc = "Begin an AI training session.", desc = "Begin an AI training session."
usage = "<population-size> <name> [skin]"
) )
public void reinforcement(CommandSender sender, List<String> args) { public void reinforcement(Player sender, @Arg("population-size") int populationSize, @Arg("name") String name, @OptArg("skin") String skin) {
if (!(sender instanceof Player)) {
return;
}
Player player = (Player) sender;
if (args.size() < 2) {
commandHandler.sendUsage(player, this, "reinforcement <amount> <name> [skin]");
return;
}
String skin;
if (args.size() < 3) {
skin = null;
} else {
skin = args.get(2);
}
int populationSize;
try {
populationSize = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
player.sendMessage("The population size must be an integer!");
return;
}
if (agent != null) { if (agent != null) {
player.sendMessage("A session is already active."); sender.sendMessage("A session is already active.");
return; return;
} }
player.sendMessage("Starting a new session..."); sender.sendMessage("Starting a new session...");
agent = new IntelligenceAgent(this, populationSize, args.get(1), skin); agent = new IntelligenceAgent(this, populationSize, name, skin);
agent.addUser(player); agent.addUser(sender);
} }
public IntelligenceAgent getSession() { public IntelligenceAgent getSession() {
@@ -135,7 +81,7 @@ public class AICommand extends CommandInstance {
name = "stop", name = "stop",
desc = "End a currently running AI training session." desc = "End a currently running AI training session."
) )
public void stop(CommandSender sender, List<String> args) { public void stop(CommandSender sender) {
if (agent == null) { if (agent == null) {
sender.sendMessage("No session is currently active."); sender.sendMessage("No session is currently active.");
return; return;
@@ -162,17 +108,9 @@ public class AICommand extends CommandInstance {
@Command( @Command(
name = "info", name = "info",
desc = "Display neural network information about a bot.", desc = "Display neural network information about a bot.",
usage = "<name>",
autofill = "infoAutofill" autofill = "infoAutofill"
) )
public void info(CommandSender sender, List<String> args) { public void info(CommandSender sender, @Arg("bot-name") String name) {
if (args.isEmpty()) {
commandHandler.sendUsage(sender, this, "info <name>");
return;
}
String name = args.get(0);
sender.sendMessage("Processing request..."); sender.sendMessage("Processing request...");
scheduler.runTaskAsynchronously(plugin, () -> { scheduler.runTaskAsynchronously(plugin, () -> {

View File

@@ -8,8 +8,10 @@ import net.nuggetmc.tplus.bot.agent.legacyagent.EnumTargetGoal;
import net.nuggetmc.tplus.bot.agent.legacyagent.LegacyAgent; import net.nuggetmc.tplus.bot.agent.legacyagent.LegacyAgent;
import net.nuggetmc.tplus.command.CommandHandler; import net.nuggetmc.tplus.command.CommandHandler;
import net.nuggetmc.tplus.command.CommandInstance; import net.nuggetmc.tplus.command.CommandInstance;
import net.nuggetmc.tplus.command.annotation.Arg;
import net.nuggetmc.tplus.command.annotation.Autofill; import net.nuggetmc.tplus.command.annotation.Autofill;
import net.nuggetmc.tplus.command.annotation.Command; import net.nuggetmc.tplus.command.annotation.Command;
import net.nuggetmc.tplus.command.annotation.OptArg;
import net.nuggetmc.tplus.utils.ChatUtils; import net.nuggetmc.tplus.utils.ChatUtils;
import net.nuggetmc.tplus.utils.Debugger; import net.nuggetmc.tplus.utils.Debugger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@@ -51,83 +53,31 @@ public class BotCommand extends CommandInstance {
} }
@Command @Command
public void root(CommandSender sender, List<String> args) { public void root(CommandSender sender) {
commandHandler.sendRootInfo(this, sender); commandHandler.sendRootInfo(this, sender);
} }
@Command( @Command(
name = "create", name = "create",
desc = "Create a bot.", desc = "Create a bot."
usage = "<name> [skin]"
) )
public void create(CommandSender sender, List<String> args) { public void create(Player sender, @Arg("name") String name, @OptArg("skin") String skin) {
if (!(sender instanceof Player)) { manager.createBots(sender, name, skin, 1);
return;
}
if (args.isEmpty()) {
commandHandler.sendUsage(sender, this, "create <name> [skin]");
return;
}
String skin;
if (args.size() < 2) {
skin = null;
} else {
skin = args.get(1);
}
manager.createBots((Player) sender, args.get(0), skin, 1);
} }
@Command( @Command(
name = "multi", name = "multi",
desc = "Create multiple bots at once.", desc = "Create multiple bots at once."
usage = "<amount> <name> [skin]"
) )
public void multi(CommandSender sender, List<String> args) { public void multi(Player sender, @Arg("amount") int amount, @Arg("name") String name, @OptArg("skin") String skin) {
if (!(sender instanceof Player)) { manager.createBots(sender, name, skin, amount);
return;
}
if (args.size() < 2) {
commandHandler.sendUsage(sender, this, "multi <amount> <name> [skin]");
return;
}
String skin;
if (args.size() < 3) {
skin = null;
} else {
skin = args.get(2);
}
int n;
try {
n = Integer.parseInt(args.get(0));
} catch (NumberFormatException e) {
sender.sendMessage("The amount must be an integer!");
return;
}
manager.createBots((Player) sender, args.get(1), skin, n);
} }
@Command( @Command(
name = "give", name = "give",
desc = "Gives a specified item to all bots.", desc = "Gives a specified item to all bots."
usage = "<item>"
) )
public void give(CommandSender sender, List<String> args) { public void give(CommandSender sender, @Arg("item-name") String itemName) {
if (args.isEmpty()) {
commandHandler.sendUsage(sender, this, "give <item>");
return;
}
String itemName = args.get(0);
Material type = Material.matchMaterial(itemName); Material type = Material.matchMaterial(itemName);
if (type == null) { if (type == null) {
@@ -191,17 +141,11 @@ public class BotCommand extends CommandInstance {
@Command( @Command(
name = "armor", name = "armor",
desc = "Gives all bots an armor set.", desc = "Gives all bots an armor set.",
usage = "<armor-tier>",
autofill = "armorAutofill" autofill = "armorAutofill"
) )
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public void armor(CommandSender sender, List<String> args) { public void armor(CommandSender sender, @Arg("armor-tier") String armorTier) {
if (args.isEmpty()) { String tier = armorTier.toLowerCase();
commandHandler.sendUsage(sender, this, "armor <armor-tier>");
return;
}
String tier = args.get(0).toLowerCase();
if (!armorTiers.containsKey(tier)) { if (!armorTiers.containsKey(tier)) {
sender.sendMessage(ChatColor.YELLOW + tier + ChatColor.RESET + " is not a valid tier!"); sender.sendMessage(ChatColor.YELLOW + tier + ChatColor.RESET + " is not a valid tier!");
@@ -233,17 +177,9 @@ public class BotCommand extends CommandInstance {
@Command( @Command(
name = "info", name = "info",
desc = "Information about loaded bots.", desc = "Information about loaded bots.",
usage = "[name]",
autofill = "infoAutofill" autofill = "infoAutofill"
) )
public void info(CommandSender sender, List<String> args) { public void info(CommandSender sender, @Arg("bot-name") String name) {
if (args.isEmpty()) {
commandHandler.sendUsage(sender, this, "info <name>");
return;
}
String name = args.get(0);
if (name == null) { if (name == null) {
sender.sendMessage(ChatColor.YELLOW + "Bot GUI coming soon!"); sender.sendMessage(ChatColor.YELLOW + "Bot GUI coming soon!");
return; return;
@@ -301,7 +237,7 @@ public class BotCommand extends CommandInstance {
name = "reset", name = "reset",
desc = "Remove all loaded bots." desc = "Remove all loaded bots."
) )
public void reset(CommandSender sender, List<String> args) { public void reset(CommandSender sender) {
sender.sendMessage("Removing every bot..."); sender.sendMessage("Removing every bot...");
int size = manager.fetch().size(); int size = manager.fetch().size();
manager.reset(); manager.reset();
@@ -316,6 +252,10 @@ public class BotCommand extends CommandInstance {
} }
} }
/*
* EVENTUALLY, we should make a command parent hierarchy system soon too! (so we don't have to do this crap)
* basically, in the @Command annotation, you can include a "parent" for the command, so it will be a subcommand under the specified parent
*/
@Command( @Command(
name = "settings", name = "settings",
desc = "Make changes to the global configuration file and bot-specific settings.", desc = "Make changes to the global configuration file and bot-specific settings.",
@@ -381,15 +321,9 @@ public class BotCommand extends CommandInstance {
@Command( @Command(
name = "debug", name = "debug",
desc = "Debug plugin code.", desc = "Debug plugin code.",
usage = "<expression>",
visible = false visible = false
) )
public void debug(CommandSender sender, List<String> args) { public void debug(CommandSender sender, @Arg("expression") String expression) {
if (args.isEmpty()) { new Debugger(sender).execute(expression);
commandHandler.sendUsage(sender, this, "debug <expression>");
return;
}
new Debugger(sender).execute(args.get(0));
} }
} }

View File

@@ -13,8 +13,6 @@ import net.nuggetmc.tplus.utils.ChatUtils;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import java.util.List;
public class MainCommand extends CommandInstance { public class MainCommand extends CommandInstance {
private BaseComponent[] rootInfo; private BaseComponent[] rootInfo;
@@ -24,7 +22,7 @@ public class MainCommand extends CommandInstance {
} }
@Command @Command
public void root(CommandSender sender, List<String> args) { public void root(CommandSender sender) {
if (rootInfo == null) { if (rootInfo == null) {
rootInfoSetup(); rootInfoSetup();
} }

View File

@@ -0,0 +1,4 @@
package net.nuggetmc.tplus.command.exception;
public class ArgCountException extends Exception {
}

View File

@@ -0,0 +1,16 @@
package net.nuggetmc.tplus.command.exception;
import java.lang.reflect.Parameter;
public class ArgParseException extends Exception {
private final Parameter parameter;
public ArgParseException(Parameter parameter) {
this.parameter = parameter;
}
public Parameter getParameter() {
return parameter;
}
}

View File

@@ -0,0 +1,4 @@
package net.nuggetmc.tplus.command.exception;
public class NonPlayerException extends Exception {
}

View File

@@ -19,4 +19,18 @@ public class ChatUtils {
public static String trim16(String str) { public static String trim16(String str) {
return str.length() > 16 ? str.substring(0, 16) : str; return str.length() > 16 ? str.substring(0, 16) : str;
} }
public static String camelToDashed(String input) {
StringBuilder result = new StringBuilder();
for (char ch : input.toCharArray()) {
if (Character.isUpperCase(ch)) {
result.append("-").append(Character.toLowerCase(ch));
} else {
result.append(ch);
}
}
return result.toString();
}
} }