From 0d5ca5a9af21a85401a6f5b6eb2cb6d7d281d2b6 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sat, 16 Mar 2019 02:57:45 +0100 Subject: [PATCH] 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 --- .../java/buttondevteam/lib/ThorpeUtils.java | 14 +++++++ .../lib/architecture/ConfigData.java | 16 ++------ .../java/buttondevteam/lib/chat/Command2.java | 41 ++++++++++++++++--- .../buttondevteam/lib/chat/Command2MC.java | 17 +++++++- .../buttondevteam/lib/chat/ICommand2.java | 19 ++++++++- 5 files changed, 86 insertions(+), 21 deletions(-) diff --git a/ButtonCore/src/main/java/buttondevteam/lib/ThorpeUtils.java b/ButtonCore/src/main/java/buttondevteam/lib/ThorpeUtils.java index 4f8560c..8c464b0 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/ThorpeUtils.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/ThorpeUtils.java @@ -35,4 +35,18 @@ public final class ThorpeUtils { */ String getFancyFullName(); } + + public static Number convertNumber(Number number, Class 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; + } } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java index f055717..6374db0 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/architecture/ConfigData.java @@ -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 { 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) def.getClass()); if (val instanceof List && def != null && def.getClass().isArray()) val = ((List) val).toArray((T[]) Array.newInstance(def.getClass().getComponentType(), 0)); return value = (T) val; //Always cache, if not cached yet diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2.java index 03e5dd4..5e10530 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2.java @@ -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 public @interface OptionalArg { } - @RequiredArgsConstructor + @AllArgsConstructor protected static class SubcommandData { public final Method method; public final T command; - public final String[] helpText; + public String[] helpText; } @RequiredArgsConstructor @@ -136,6 +140,15 @@ public abstract class Command2 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) 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 val scmdHelpList = new ArrayList(); 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 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 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; + } } diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2MC.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2MC.java index b7a736c..9761ea5 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2MC.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/Command2MC.java @@ -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 { @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 any 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} diff --git a/ButtonCore/src/main/java/buttondevteam/lib/chat/ICommand2.java b/ButtonCore/src/main/java/buttondevteam/lib/chat/ICommand2.java index dba5664..b55a85a 100644 --- a/ButtonCore/src/main/java/buttondevteam/lib/chat/ICommand2.java +++ b/ButtonCore/src/main/java/buttondevteam/lib/chat/ICommand2.java @@ -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 { @@ -69,8 +70,22 @@ public abstract class ICommand2 { "No @CommandClass annotation on command class " + getClass().getSimpleName() + "!"); Function, 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; } }