Fix Command2MC command registration
- Tab completion still needs to be fixed - Fixed usage check existence check - Fixed parameter number limits - Added commands to get and remove registered commands
This commit is contained in:
parent
c0c3fc68dc
commit
47178e7f7c
3 changed files with 83 additions and 87 deletions
|
@ -29,7 +29,9 @@ import java.lang.annotation.Target;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -276,27 +278,21 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
throw new RuntimeException("No sender parameter for method '" + method + "'");
|
throw new RuntimeException("No sender parameter for method '" + method + "'");
|
||||||
val ret = new CommandArgument[parameters.length];
|
val ret = new CommandArgument[parameters.length];
|
||||||
val usage = getParameterHelp(method);
|
val usage = getParameterHelp(method);
|
||||||
if (usage == null) {
|
val paramNames = usage != null ? usage.split(" ") : null;
|
||||||
for (int i = 1; i < parameters.length; i++) {
|
|
||||||
ret[i - 1] = new CommandArgument("param" + i, parameters[i].getType(), false, null, false, "param" + i);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val paramNames = usage.split(" ");
|
|
||||||
for (int i = 1; i < parameters.length; i++) {
|
for (int i = 1; i < parameters.length; i++) {
|
||||||
val numAnn = parameters[i].getAnnotation(NumberArg.class);
|
val numAnn = parameters[i].getAnnotation(NumberArg.class);
|
||||||
ret[i - 1] = new CommandArgument(paramNames[i], parameters[i].getType(),
|
ret[i - 1] = new CommandArgument(paramNames == null ? "param" + i : paramNames[i], parameters[i].getType(),
|
||||||
parameters[i].isVarArgs() || parameters[i].isAnnotationPresent(TextArg.class),
|
parameters[i].isVarArgs() || parameters[i].isAnnotationPresent(TextArg.class),
|
||||||
numAnn == null ? null : new Pair<>(numAnn.lowerLimit(), numAnn.upperLimit()),
|
numAnn == null ? null : new Pair<>(numAnn.lowerLimit(), numAnn.upperLimit()),
|
||||||
parameters[i].isAnnotationPresent(OptionalArg.class),
|
parameters[i].isAnnotationPresent(OptionalArg.class),
|
||||||
paramNames[i]); // TODO: Description (JavaDoc?)
|
paramNames == null ? "param" + i : paramNames[i]); // TODO: Description (JavaDoc?)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return new Pair<>(ret, parameters[0].getType());
|
return new Pair<>(ret, parameters[0].getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArgumentType<?> getParameterType(CommandArgument arg) {
|
private ArgumentType<?> getParameterType(CommandArgument arg) {
|
||||||
final Class<?> ptype = arg.type;
|
final Class<?> ptype = arg.type;
|
||||||
Number lowerLimit = Double.NEGATIVE_INFINITY, upperLimit = Double.POSITIVE_INFINITY;
|
Number lowerLimit = arg.limits.getValue0(), upperLimit = arg.limits.getValue1();
|
||||||
if (arg.greedy)
|
if (arg.greedy)
|
||||||
return StringArgumentType.greedyString();
|
return StringArgumentType.greedyString();
|
||||||
else if (ptype == String.class)
|
else if (ptype == String.class)
|
||||||
|
@ -382,61 +378,6 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*protected List<SubcommandData<TC>> registerCommand(TC command, @SuppressWarnings("SameParameterValue") char commandChar) {
|
|
||||||
this.commandChar = commandChar;
|
|
||||||
Method mainMethod = null;
|
|
||||||
boolean nosubs = true;
|
|
||||||
boolean isSubcommand = x != -1;
|
|
||||||
try { //Register the default handler first so it can be reliably overwritten
|
|
||||||
mainMethod = command.getClass().getMethod("def", Command2Sender.class);
|
|
||||||
val cc = command.getClass().getAnnotation(CommandClass.class);
|
|
||||||
var ht = cc == null || isSubcommand ? new String[0] : cc.helpText(); //If it's not the main command, don't add it
|
|
||||||
if (ht.length > 0)
|
|
||||||
ht[0] = "§6---- " + ht[0] + " ----";
|
|
||||||
scmdHelpList.addAll(Arrays.asList(ht));
|
|
||||||
if (!isSubcommand)
|
|
||||||
scmdHelpList.add("§6Subcommands:");
|
|
||||||
if (!commandHelp.contains(mainPath))
|
|
||||||
commandHelp.add(mainPath);
|
|
||||||
} catch (Exception e) {
|
|
||||||
TBMCCoreAPI.SendException("Could not register default handler for command /" + path, e, MainPlugin.Instance);
|
|
||||||
}
|
|
||||||
var addedSubcommands = new ArrayList<SubcommandData<TC>>();
|
|
||||||
for (val method : command.getClass().getMethods()) {
|
|
||||||
val ann = method.getAnnotation(Subcommand.class);
|
|
||||||
if (ann == null) continue; //Don't call the method on non-subcommands because they're not in the yaml
|
|
||||||
var ht = command.getHelpText(method, ann);
|
|
||||||
if (ht != null) { //The method is a subcommand
|
|
||||||
val subcommand = commandChar + path + //Add command path (class name by default)
|
|
||||||
getCommandPath(method.getName(), ' '); //Add method name, unless it's 'def'
|
|
||||||
var params = new String[method.getParameterCount() - 1];
|
|
||||||
ht = getParameterHelp(method, ht, subcommand, params);
|
|
||||||
var sd = new SubcommandData<>(method, command, params, ht);
|
|
||||||
registerCommand(path, method.getName(), ann, sd);
|
|
||||||
for (String p : command.getCommandPaths())
|
|
||||||
registerCommand(p, method.getName(), ann, sd);
|
|
||||||
addedSubcommands.add(sd);
|
|
||||||
scmdHelpList.add(subcommand);
|
|
||||||
nosubs = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nosubs && scmdHelpList.size() > 0)
|
|
||||||
scmdHelpList.remove(scmdHelpList.size() - 1); //Remove Subcommands header
|
|
||||||
if (mainMethod != null && !subcommands.containsKey(commandChar + path)) { //Command specified by the class
|
|
||||||
var sd = new SubcommandData<>(mainMethod, command, null, scmdHelpList.toArray(new String[0]));
|
|
||||||
subcommands.put(commandChar + path, sd);
|
|
||||||
addedSubcommands.add(sd);
|
|
||||||
}
|
|
||||||
if (isSubcommand) { //The class itself is a subcommand
|
|
||||||
val scmd = subcommands.computeIfAbsent(mainPath, p -> new SubcommandData<>(null, null, new String[0], new String[]{"§6---- Subcommands ----"}));
|
|
||||||
val scmdHelp = Arrays.copyOf(scmd.helpText, scmd.helpText.length + scmdHelpList.size());
|
|
||||||
for (int i = 0; i < scmdHelpList.size(); i++)
|
|
||||||
scmdHelp[scmd.helpText.length + i] = scmdHelpList.get(i);
|
|
||||||
scmd.helpText = scmdHelp;
|
|
||||||
}
|
|
||||||
return addedSubcommands;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
private String getParameterHelp(Method method) {
|
private String getParameterHelp(Method method) {
|
||||||
val str = method.getDeclaringClass().getResourceAsStream("/commands.yml");
|
val str = method.getDeclaringClass().getResourceAsStream("/commands.yml");
|
||||||
if (str == null)
|
if (str == null)
|
||||||
|
@ -469,7 +410,7 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It will start with the given replace char.
|
* Returns the path of the given subcommand excluding the class' path. It will start with the given replace char.
|
||||||
*
|
*
|
||||||
* @param methodName The method's name, method.getName()
|
* @param methodName The method's name, method.getName()
|
||||||
* @param replaceChar The character to use between subcommands
|
* @param replaceChar The character to use between subcommands
|
||||||
|
@ -479,4 +420,51 @@ public abstract class Command2<TC extends ICommand2<TP>, TP extends Command2Send
|
||||||
public String getCommandPath(String methodName, char replaceChar) {
|
public String getCommandPath(String methodName, char replaceChar) {
|
||||||
return methodName.equals("def") ? "" : replaceChar + methodName.replace('_', replaceChar).toLowerCase();
|
return methodName.equals("def") ? "" : replaceChar + methodName.replace('_', replaceChar).toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all registered command nodes. This returns all registered Chroma commands with all the information about them.
|
||||||
|
*
|
||||||
|
* @return A set of command node objects containing the commands
|
||||||
|
*/
|
||||||
|
public Set<CoreCommandNode<TP, TC>> getCommandNodes() {
|
||||||
|
return dispatcher.getRoot().getChildren().stream().map(node -> (CoreCommandNode<TP, TC>) node).collect(Collectors.toUnmodifiableSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a node that belongs to the given command.
|
||||||
|
*
|
||||||
|
* @param command The exact name of the command
|
||||||
|
* @return A command node
|
||||||
|
*/
|
||||||
|
public CoreCommandNode<TP, TC> getCommandNode(String command) {
|
||||||
|
return (CoreCommandNode<TP, TC>) dispatcher.getRoot().getChild(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister all subcommands that were registered with the given command class.
|
||||||
|
*
|
||||||
|
* @param command The command class (object) to unregister
|
||||||
|
*/
|
||||||
|
public void unregisterCommand(ICommand2<TP> command) {
|
||||||
|
dispatcher.getRoot().getChildren().removeIf(node -> ((CoreCommandNode<TP, TC>) node).getData().command == command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters all commands that match the given predicate.
|
||||||
|
*
|
||||||
|
* @param condition The condition for removing a given command
|
||||||
|
*/
|
||||||
|
public void unregisterCommandIf(Predicate<CoreCommandNode<TP, TC>> condition, boolean nested) {
|
||||||
|
dispatcher.getRoot().getChildren().removeIf(node -> condition.test((CoreCommandNode<TP, TC>) node));
|
||||||
|
if (nested)
|
||||||
|
for (var child : dispatcher.getRoot().getChildren())
|
||||||
|
unregisterCommandIf(condition, (CoreCommandNode<TP, TC>) child);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unregisterCommandIf(Predicate<CoreCommandNode<TP, TC>> condition, CoreCommandNode<TP, TC> root) {
|
||||||
|
// Can't use getCoreChildren() here because the collection needs to be modifiable
|
||||||
|
root.getChildren().removeIf(node -> condition.test((CoreCommandNode<TP, TC>) node));
|
||||||
|
for (var child : root.getCoreChildren())
|
||||||
|
unregisterCommandIf(condition, child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ import java.util.Optional;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@ -59,9 +58,8 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
}*/
|
}*/
|
||||||
var commandNode = super.registerCommandSuper(command);
|
var commandNode = super.registerCommandSuper(command);
|
||||||
var bcmd = registerOfficially(command, commandNode);
|
var bcmd = registerOfficially(command, commandNode);
|
||||||
if (bcmd != null)
|
if (bcmd != null) // TODO: Support aliases
|
||||||
for (String alias : bcmd.getAliases())
|
super.registerCommandSuper(command);
|
||||||
super.registerCommand(command, command.getCommandPath().replaceFirst("^" + bcmd.getName(), Matcher.quoteReplacement(alias)), '/');
|
|
||||||
|
|
||||||
var perm = "chroma.command." + command.getCommandPath().replace(' ', '.');
|
var perm = "chroma.command." + command.getCommandPath().replace(' ', '.');
|
||||||
if (Bukkit.getPluginManager().getPermission(perm) == null) //Check needed for plugin reset
|
if (Bukkit.getPluginManager().getPermission(perm) == null) //Check needed for plugin reset
|
||||||
|
@ -164,18 +162,12 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> implemen
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterCommands(ButtonPlugin plugin) {
|
public void unregisterCommands(ButtonPlugin plugin) {
|
||||||
/*var cmds = subcommands.values().stream().map(sd -> sd.command).filter(cmd -> plugin.equals(cmd.getPlugin())).toArray(ICommand2MC[]::new);
|
unregisterCommandIf(node -> Optional.ofNullable(node.getData().command).map(ICommand2MC::getPlugin).map(plugin::equals).orElse(false), true);
|
||||||
for (var cmd : cmds)
|
|
||||||
unregisterCommand(cmd);*/
|
|
||||||
subcommands.values().removeIf(sd -> Optional.ofNullable(sd.command).map(ICommand2MC::getPlugin).map(plugin::equals).orElse(false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterCommands(Component<?> component) {
|
public void unregisterCommands(Component<?> component) {
|
||||||
/*var cmds = subcommands.values().stream().map(sd -> sd.command).filter(cmd -> component.equals(cmd.getComponent())).toArray(ICommand2MC[]::new);
|
unregisterCommandIf(node -> Optional.ofNullable(node.getData().command).map(ICommand2MC::getPlugin)
|
||||||
for (var cmd : cmds)
|
.map(comp -> component.getClass().getSimpleName().equals(comp.getClass().getSimpleName())).orElse(false), true);
|
||||||
unregisterCommand(cmd);*/
|
|
||||||
subcommands.values().removeIf(sd -> Optional.ofNullable(sd.command).map(ICommand2MC::getComponent)
|
|
||||||
.map(comp -> component.getClass().getSimpleName().equals(comp.getClass().getSimpleName())).orElse(false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@EventHandler
|
/*@EventHandler
|
||||||
|
|
|
@ -7,14 +7,30 @@ import com.mojang.brigadier.tree.CommandNode;
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class CoreCommandNode<T, TC extends ICommand2<?>> extends LiteralCommandNode<T> {
|
public class CoreCommandNode<T extends Command2Sender, TC extends ICommand2<?>> extends LiteralCommandNode<T> {
|
||||||
@Getter
|
@Getter
|
||||||
private final SubcommandData<TC> data;
|
private final SubcommandData<TC, T> data;
|
||||||
|
|
||||||
public CoreCommandNode(String literal, Command<T> command, Predicate<T> requirement, CommandNode<T> redirect, RedirectModifier<T> modifier, boolean forks, SubcommandData<TC> data) {
|
public CoreCommandNode(String literal, Command<T> command, Predicate<T> requirement, CommandNode<T> redirect, RedirectModifier<T> modifier, boolean forks, SubcommandData<TC, T> data) {
|
||||||
super(literal, command, requirement, redirect, modifier, forks);
|
super(literal, command, requirement, redirect, modifier, forks);
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #getChildren()
|
||||||
|
*/
|
||||||
|
public Collection<CoreCommandNode<T, TC>> getCoreChildren() {
|
||||||
|
return super.getChildren().stream().map(node -> (CoreCommandNode<T, TC>) node).collect(Collectors.toUnmodifiableSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #getChild(String)
|
||||||
|
*/
|
||||||
|
public CoreCommandNode<T, TC> getCoreChild(String name) {
|
||||||
|
return (CoreCommandNode<T, TC>) super.getChild(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue