Command improvements, number converter
Setting to modOnly if any of the superclasses are modOnly Re-added support for command path inheritance getHelpText() for a particular command Subcommands are properly printed across classes now Using a general number converter for both the config and commands
This commit is contained in:
parent
70cabfaa81
commit
0d5ca5a9af
5 changed files with 86 additions and 21 deletions
|
@ -35,4 +35,18 @@ public final class ThorpeUtils {
|
||||||
*/
|
*/
|
||||||
String getFancyFullName();
|
String getFancyFullName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Number convertNumber(Number number, Class<? extends Number> targetcl) {
|
||||||
|
if (targetcl == long.class || Long.class.isAssignableFrom(targetcl))
|
||||||
|
return number.longValue();
|
||||||
|
else if (targetcl == short.class || Short.class.isAssignableFrom(targetcl))
|
||||||
|
return number.shortValue();
|
||||||
|
else if (targetcl == byte.class || Byte.class.isAssignableFrom(targetcl))
|
||||||
|
return number.byteValue();
|
||||||
|
else if (targetcl == float.class || Float.class.isAssignableFrom(targetcl))
|
||||||
|
return number.floatValue();
|
||||||
|
else if (targetcl == double.class || Double.class.isAssignableFrom(targetcl))
|
||||||
|
return number.doubleValue();
|
||||||
|
return number;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package buttondevteam.lib.architecture;
|
package buttondevteam.lib.architecture;
|
||||||
|
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin;
|
||||||
|
import buttondevteam.lib.ThorpeUtils;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
@ -80,18 +81,9 @@ public class ConfigData<T> {
|
||||||
if (hmm == null) hmm = def; //Set if the getter returned null
|
if (hmm == null) hmm = def; //Set if the getter returned null
|
||||||
return hmm;
|
return hmm;
|
||||||
}
|
}
|
||||||
if (val instanceof Number) {
|
if (val instanceof Number && def != null)
|
||||||
if (def instanceof Long)
|
val = ThorpeUtils.convertNumber((Number) val,
|
||||||
val = ((Number) val).longValue();
|
(Class<? extends Number>) def.getClass());
|
||||||
else if (def instanceof Short)
|
|
||||||
val = ((Number) val).shortValue();
|
|
||||||
else if (def instanceof Byte)
|
|
||||||
val = ((Number) val).byteValue();
|
|
||||||
else if (def instanceof Float)
|
|
||||||
val = ((Number) val).floatValue();
|
|
||||||
else if (def instanceof Double)
|
|
||||||
val = ((Number) val).doubleValue();
|
|
||||||
}
|
|
||||||
if (val instanceof List && def != null && def.getClass().isArray())
|
if (val instanceof List && def != null && def.getClass().isArray())
|
||||||
val = ((List<T>) val).toArray((T[]) Array.newInstance(def.getClass().getComponentType(), 0));
|
val = ((List<T>) val).toArray((T[]) Array.newInstance(def.getClass().getComponentType(), 0));
|
||||||
return value = (T) val; //Always cache, if not cached yet
|
return value = (T) val; //Always cache, if not cached yet
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package buttondevteam.lib.chat;
|
package buttondevteam.lib.chat;
|
||||||
|
|
||||||
import buttondevteam.lib.TBMCCoreAPI;
|
import buttondevteam.lib.TBMCCoreAPI;
|
||||||
|
import buttondevteam.lib.ThorpeUtils;
|
||||||
import buttondevteam.lib.player.ChromaGamerBase;
|
import buttondevteam.lib.player.ChromaGamerBase;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.experimental.var;
|
import lombok.experimental.var;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
@ -13,6 +15,8 @@ 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;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.text.ParseException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -52,11 +56,11 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
|
||||||
public @interface OptionalArg {
|
public @interface OptionalArg {
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@AllArgsConstructor
|
||||||
protected static class SubcommandData<T extends ICommand2> {
|
protected static class SubcommandData<T extends ICommand2> {
|
||||||
public final Method method;
|
public final Method method;
|
||||||
public final T command;
|
public final T command;
|
||||||
public final String[] helpText;
|
public String[] helpText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
|
@ -136,6 +140,15 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
|
||||||
if (cl == String.class) {
|
if (cl == String.class) {
|
||||||
params.add(param);
|
params.add(param);
|
||||||
continue;
|
continue;
|
||||||
|
} else if (Number.class.isAssignableFrom(cl) || cl.isPrimitive()) {
|
||||||
|
try {
|
||||||
|
//noinspection unchecked
|
||||||
|
params.add(ThorpeUtils.convertNumber(NumberFormat.getInstance().parse(param), (Class<? extends Number>) cl));
|
||||||
|
} catch (ParseException e) {
|
||||||
|
sender.sendMessage("§c'" + param + "' is not a number.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
val conv = paramConverters.get(cl);
|
val conv = paramConverters.get(cl);
|
||||||
if (conv == null)
|
if (conv == null)
|
||||||
|
@ -169,13 +182,15 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
|
||||||
val scmdHelpList = new ArrayList<String>();
|
val scmdHelpList = new ArrayList<String>();
|
||||||
Method mainMethod = null;
|
Method mainMethod = null;
|
||||||
boolean nosubs = true;
|
boolean nosubs = true;
|
||||||
|
boolean isSubcommand = x != -1;
|
||||||
try { //Register the default handler first so it can be reliably overwritten
|
try { //Register the default handler first so it can be reliably overwritten
|
||||||
mainMethod = command.getClass().getMethod("def", Command2Sender.class, String.class);
|
mainMethod = command.getClass().getMethod("def", Command2Sender.class, String.class);
|
||||||
val cc = command.getClass().getAnnotation(CommandClass.class);
|
val cc = command.getClass().getAnnotation(CommandClass.class);
|
||||||
var ht = cc == null ? new String[0] : cc.helpText();
|
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)
|
if (ht.length > 0)
|
||||||
ht[0] = "§6---- " + ht[0] + " ----";
|
ht[0] = "§6---- " + ht[0] + " ----";
|
||||||
scmdHelpList.addAll(Arrays.asList(ht));
|
scmdHelpList.addAll(Arrays.asList(ht));
|
||||||
|
if (!isSubcommand)
|
||||||
scmdHelpList.add("§6Subcommands:");
|
scmdHelpList.add("§6Subcommands:");
|
||||||
if (!commandHelp.contains(mainPath))
|
if (!commandHelp.contains(mainPath))
|
||||||
commandHelp.add(mainPath);
|
commandHelp.add(mainPath);
|
||||||
|
@ -199,9 +214,17 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
|
||||||
scmdHelpList.remove(scmdHelpList.size() - 1); //Remove Subcommands header
|
scmdHelpList.remove(scmdHelpList.size() - 1); //Remove Subcommands header
|
||||||
if (mainMethod != null && !subcommands.containsKey(commandChar + path)) //Command specified by the class
|
if (mainMethod != null && !subcommands.containsKey(commandChar + path)) //Command specified by the class
|
||||||
subcommands.put(commandChar + path, new SubcommandData<>(mainMethod, command, scmdHelpList.toArray(new String[0])));
|
subcommands.put(commandChar + path, new SubcommandData<>(mainMethod, command, scmdHelpList.toArray(new String[0])));
|
||||||
if (mainMethod != null && !subcommands.containsKey(mainPath)) //Main command, typically the same as the above
|
if (mainMethod != null && !mainPath.equals(commandChar + path)) { //Main command, typically the same as the above
|
||||||
|
if (isSubcommand) { //The class itself is a subcommand
|
||||||
|
val scmd = subcommands.computeIfAbsent(mainPath, p -> new SubcommandData<>(null, null, 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;
|
||||||
|
} else if (!subcommands.containsKey(mainPath))
|
||||||
subcommands.put(mainPath, new SubcommandData<>(null, null, scmdHelpList.toArray(new String[0])));
|
subcommands.put(mainPath, new SubcommandData<>(null, null, scmdHelpList.toArray(new String[0])));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String[] getHelpText(Method method, String[] ht, String subcommand) {
|
private String[] getHelpText(Method method, String[] ht, String subcommand) {
|
||||||
val str = method.getDeclaringClass().getResourceAsStream("/commands.yml");
|
val str = method.getDeclaringClass().getResourceAsStream("/commands.yml");
|
||||||
|
@ -238,4 +261,10 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
|
||||||
public String[] getCommandsText() {
|
public String[] getCommandsText() {
|
||||||
return commandHelp.toArray(new String[0]);
|
return commandHelp.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String[] getHelpText(String path) {
|
||||||
|
val scmd = subcommands.get(path);
|
||||||
|
if (scmd == null) return null;
|
||||||
|
return scmd.helpText;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package buttondevteam.lib.chat;
|
package buttondevteam.lib.chat;
|
||||||
|
|
||||||
import buttondevteam.core.MainPlugin;
|
import buttondevteam.core.MainPlugin;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@ -12,11 +13,25 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPermission(Command2MCSender sender, ICommand2MC command) {
|
public boolean hasPermission(Command2MCSender sender, ICommand2MC command) {
|
||||||
return command.getClass().getAnnotation(CommandClass.class).modOnly()
|
return modOnly(command)
|
||||||
? MainPlugin.permission.has(sender.getSender(), "tbmc.admin") //TODO: Change when groups are implemented
|
? MainPlugin.permission.has(sender.getSender(), "tbmc.admin") //TODO: Change when groups are implemented
|
||||||
: MainPlugin.permission.has(sender.getSender(), "thorpe.command." + command.getCommandPath().replace(' ', '.'));
|
: MainPlugin.permission.has(sender.getSender(), "thorpe.command." + command.getCommandPath().replace(' ', '.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this class or <u>any</u> of the superclasses are mod only.
|
||||||
|
*
|
||||||
|
* @param command The command to check
|
||||||
|
* @return Whether the command is mod only
|
||||||
|
*/
|
||||||
|
private boolean modOnly(ICommand2MC command) {
|
||||||
|
for (Class<?> cl = command.getClass(); cl != null; cl = cl.getSuperclass()) {
|
||||||
|
val cc = command.getClass().getAnnotation(CommandClass.class);
|
||||||
|
if (cc != null && cc.modOnly()) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically colors the message red.
|
* Automatically colors the message red.
|
||||||
* {@see super#addParamConverter}
|
* {@see super#addParamConverter}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import lombok.Getter;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public abstract class ICommand2<TP extends Command2Sender> {
|
public abstract class ICommand2<TP extends Command2Sender> {
|
||||||
|
@ -69,8 +70,22 @@ public abstract class ICommand2<TP extends Command2Sender> {
|
||||||
"No @CommandClass annotation on command class " + getClass().getSimpleName() + "!");
|
"No @CommandClass annotation on command class " + getClass().getSimpleName() + "!");
|
||||||
Function<Class<?>, String> getFromClass = cl -> cl.getSimpleName().toLowerCase().replace("commandbase", "") // <-- ...
|
Function<Class<?>, String> getFromClass = cl -> cl.getSimpleName().toLowerCase().replace("commandbase", "") // <-- ...
|
||||||
.replace("command", "");
|
.replace("command", "");
|
||||||
String path = getClass().getAnnotation(CommandClass.class).path();
|
String path = getClass().getAnnotation(CommandClass.class).path(),
|
||||||
path = path.length() == 0 ? getFromClass.apply(getClass()) : path;
|
prevpath = path = path.length() == 0 ? getFromClass.apply(getClass()) : path;
|
||||||
|
for (Class<?> cl = getClass().getSuperclass(); cl != null
|
||||||
|
&& !cl.getPackage().getName().equals(TBMCCommandBase.class.getPackage().getName()); cl = cl
|
||||||
|
.getSuperclass()) { //
|
||||||
|
String newpath;
|
||||||
|
if (!cl.isAnnotationPresent(CommandClass.class)
|
||||||
|
|| (newpath = cl.getAnnotation(CommandClass.class).path()).length() == 0
|
||||||
|
|| newpath.equals(prevpath)) {
|
||||||
|
if ((Modifier.isAbstract(cl.getModifiers()) && !cl.isAnnotationPresent(CommandClass.class))
|
||||||
|
|| cl.getAnnotation(CommandClass.class).excludeFromPath()) // <--
|
||||||
|
continue;
|
||||||
|
newpath = getFromClass.apply(cl);
|
||||||
|
}
|
||||||
|
path = (prevpath = newpath) + " " + path;
|
||||||
|
}
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue