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:
Norbi Peti 2019-03-16 02:57:45 +01:00
parent 70cabfaa81
commit 0d5ca5a9af
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
5 changed files with 86 additions and 21 deletions

View file

@ -35,4 +35,18 @@ public final class ThorpeUtils {
*/
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;
}
}

View file

@ -1,6 +1,7 @@
package buttondevteam.lib.architecture;
import buttondevteam.core.MainPlugin;
import buttondevteam.lib.ThorpeUtils;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
@ -80,18 +81,9 @@ public class ConfigData<T> {
if (hmm == null) hmm = def; //Set if the getter returned null
return hmm;
}
if (val instanceof Number) {
if (def instanceof Long)
val = ((Number) val).longValue();
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 Number && def != null)
val = ThorpeUtils.convertNumber((Number) val,
(Class<? extends Number>) def.getClass());
if (val instanceof List && def != null && def.getClass().isArray())
val = ((List<T>) val).toArray((T[]) Array.newInstance(def.getClass().getComponentType(), 0));
return value = (T) val; //Always cache, if not cached yet

View file

@ -1,7 +1,9 @@
package buttondevteam.lib.chat;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.ThorpeUtils;
import buttondevteam.lib.player.ChromaGamerBase;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.experimental.var;
import lombok.val;
@ -13,6 +15,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@ -52,11 +56,11 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
public @interface OptionalArg {
}
@RequiredArgsConstructor
@AllArgsConstructor
protected static class SubcommandData<T extends ICommand2> {
public final Method method;
public final T command;
public final String[] helpText;
public String[] helpText;
}
@RequiredArgsConstructor
@ -136,6 +140,15 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
if (cl == String.class) {
params.add(param);
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);
if (conv == null)
@ -169,14 +182,16 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
val scmdHelpList = new ArrayList<String>();
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, String.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)
ht[0] = "§6---- " + ht[0] + " ----";
scmdHelpList.addAll(Arrays.asList(ht));
scmdHelpList.add("§6Subcommands:");
if (!isSubcommand)
scmdHelpList.add("§6Subcommands:");
if (!commandHelp.contains(mainPath))
commandHelp.add(mainPath);
} catch (Exception e) {
@ -199,8 +214,16 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
scmdHelpList.remove(scmdHelpList.size() - 1); //Remove Subcommands header
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])));
if (mainMethod != null && !subcommands.containsKey(mainPath)) //Main command, typically the same as the above
subcommands.put(mainPath, new SubcommandData<>(null, null, scmdHelpList.toArray(new String[0])));
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])));
}
}
private String[] getHelpText(Method method, String[] ht, String subcommand) {
@ -238,4 +261,10 @@ public abstract class Command2<TC extends ICommand2, TP extends Command2Sender>
public String[] getCommandsText() {
return commandHelp.toArray(new String[0]);
}
public String[] getHelpText(String path) {
val scmd = subcommands.get(path);
if (scmd == null) return null;
return scmd.helpText;
}
}

View file

@ -1,6 +1,7 @@
package buttondevteam.lib.chat;
import buttondevteam.core.MainPlugin;
import lombok.val;
import java.util.function.Function;
@ -12,11 +13,25 @@ public class Command2MC extends Command2<ICommand2MC, Command2MCSender> {
@Override
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(), "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.
* {@see super#addParamConverter}

View file

@ -4,6 +4,7 @@ import lombok.Getter;
import lombok.val;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.function.Function;
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() + "!");
Function<Class<?>, String> getFromClass = cl -> cl.getSimpleName().toLowerCase().replace("commandbase", "") // <-- ...
.replace("command", "");
String path = getClass().getAnnotation(CommandClass.class).path();
path = path.length() == 0 ? getFromClass.apply(getClass()) : path;
String path = getClass().getAnnotation(CommandClass.class).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;
}
}