diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommand.java b/SpigotCore_Main/src/de/steamwar/command/SWCommand.java index ab72fc5..4261cb1 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommand.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommand.java @@ -19,9 +19,11 @@ package de.steamwar.command; -import org.bukkit.Bukkit; +import de.steamwar.message.Message; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; import java.lang.annotation.*; import java.lang.reflect.Method; @@ -29,21 +31,33 @@ import java.lang.reflect.Parameter; import java.util.*; import java.util.function.BiConsumer; import java.util.function.IntPredicate; -import java.util.logging.Level; import java.util.stream.Collectors; public abstract class SWCommand { + private List help = null; + private final Command command; private final List commandList = new ArrayList<>(); private final List commandHelpList = new ArrayList<>(); private final Map> localTypeMapper = new HashMap<>(); + private final Message message; + protected SWCommand(String command) { this(command, new String[0]); } + protected SWCommand(String command, Message message) { + this(command, message, new String[0]); + } + protected SWCommand(String command, String... aliases) { + this(command, null, aliases); + } + + protected SWCommand(String command, Message message, String... aliases) { + this.message = message; this.command = new Command(command, "", "/" + command, Arrays.asList(aliases)) { @Override public boolean execute(CommandSender sender, String alias, String[] args) { @@ -67,7 +81,7 @@ public abstract class SWCommand { unregister(); register(); - Method[] methods = getClass().getDeclaredMethods(); + List methods = methods(); for (Method method : methods) { addMapper(Mapper.class, method, i -> i == 0, false, TypeMapper.class, (anno, typeMapper) -> { (anno.local() ? localTypeMapper : SWCommandUtils.MAPPER_FUNCTIONS).putIfAbsent(anno.value(), typeMapper); @@ -78,16 +92,15 @@ public abstract class SWCommand { add(Register.class, method, i -> i > 0, true, null, (anno, parameters) -> { if (!anno.help()) return; if (parameters.length != 2) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' is lacking parameters or has too many"); + throw new SecurityException("The method '" + method.toString() + "' is lacking parameters or has too many"); } if (!parameters[parameters.length - 1].isVarArgs()) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' is lacking the varArgs parameters as last Argument"); + throw new SecurityException("The method '" + method.toString() + "' is lacking the varArgs parameters as last Argument"); } if (parameters[parameters.length - 1].getType().getComponentType() != String.class) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' is lacking the varArgs parameters of type '" + String.class.getTypeName() + "' as last Argument"); - return; + throw new SecurityException("The method '" + method.toString() + "' is lacking the varArgs parameters of type '" + String.class.getTypeName() + "' as last Argument"); } - commandHelpList.add(new SubCommand(this, method, anno.value(), new HashMap<>())); + commandHelpList.add(new SubCommand(this, method, anno.description(), anno.value(), new HashMap<>())); }); } for (Method method : methods) { @@ -105,11 +118,10 @@ public abstract class SWCommand { } String name = mapper != null ? mapper.value() : clazz.getTypeName(); if (!SWCommandUtils.MAPPER_FUNCTIONS.containsKey(name) && !localTypeMapper.containsKey(name)) { - Bukkit.getLogger().log(Level.WARNING, "The parameter '" + parameter.toString() + "' is using an unsupported Mapper of type '" + name + "'"); - return; + throw new SecurityException("The parameter '" + parameter.toString() + "' is using an unsupported Mapper of type '" + name + "'"); } } - commandList.add(new SubCommand(this, method, anno.value(), localTypeMapper)); + commandList.add(new SubCommand(this, method, anno.description(), anno.value(), localTypeMapper)); }); this.commandList.sort((o1, o2) -> { @@ -121,7 +133,15 @@ public abstract class SWCommand { o2.varArgType != null ? Integer.MAX_VALUE : o2.arguments.length); } }); - commandHelpList.sort(Comparator.comparingInt(o -> -o.subCommand.length)); + commandHelpList.sort((o1, o2) -> { + int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length); + if (compare != 0) { + return compare; + } else { + return Integer.compare(o1.method.getDeclaringClass() == SWCommand.class ? 1 : 0, + o2.method.getDeclaringClass() == SWCommand.class ? 1 : 0); + } + }); } } @@ -131,16 +151,13 @@ public abstract class SWCommand { Parameter[] parameters = method.getParameters(); if (!parameterTester.test(parameters.length)) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' is lacking parameters or has too many"); - return; + throw new SecurityException("The method '" + method.toString() + "' is lacking parameters or has too many"); } if (firstParameter && !CommandSender.class.isAssignableFrom(parameters[0].getType())) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' is lacking the first parameter of type '" + CommandSender.class.getTypeName() + "'"); - return; + throw new SecurityException("The method '" + method.toString() + "' is lacking the first parameter of type '" + CommandSender.class.getTypeName() + "'"); } if (returnType != null && method.getReturnType() != returnType) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' is lacking the desired return type '" + returnType.getTypeName() + "'"); - return; + throw new SecurityException("The method '" + method.toString() + "' is lacking the desired return type '" + returnType.getTypeName() + "'"); } Arrays.stream(anno).forEach(t -> consumer.accept(t, parameters)); } @@ -156,6 +173,12 @@ public abstract class SWCommand { }); } + private List methods() { + List methods = new ArrayList<>(Arrays.asList(getClass().getDeclaredMethods())); + methods.addAll(Arrays.asList(SWCommand.class.getDeclaredMethods())); + return methods; + } + public void unregister() { SWCommandUtils.knownCommandMap.remove(command.getName()); command.getAliases().forEach(SWCommandUtils.knownCommandMap::remove); @@ -166,12 +189,68 @@ public abstract class SWCommand { SWCommandUtils.commandMap.register("steamwar", this.command); } + public void inject(Plugin plugin) { + new BukkitRunnable() { + @Override + public void run() { + SWCommand.this.unregister(); + SWCommand.this.register(); + } + }.runTask(plugin); + } + + @Register(help = true) + private void internalHelp(CommandSender sender, String... args) { + if (help == null) { + help = new ArrayList<>(); + commandList.forEach(subCommand -> { + StringBuilder st = new StringBuilder(); + st.append("§8/§7").append(command.getName()).append(" "); + st.append("§7").append(String.join(" ", subCommand.subCommand)); + String cmd = Arrays.stream(subCommand.parameters) + .skip(1) + .map(parameter -> { + Name name = parameter.getAnnotation(Name.class); + if (name != null) { + return name.name(); + } else { + return parameter.getName(); + } + }) + .map(param -> " §8[§e" + param + "§8]") + .collect(Collectors.joining("")); + st.append(cmd); + if (subCommand.varArgType != null) { + st.append("§7..."); + } + if (!subCommand.description.isEmpty()) { + st.append("§8 - §7").append(message != null ? message.parse(subCommand.description, sender) : subCommand.description); + } + help.add(st.toString()); + }); + } + String string = "/" + command.getName() + " " + String.join(" ", args); + sender.sendMessage("§7----==== §e" + command.getName() + " §7====----"); + sender.sendMessage("§7" + (message != null ? message.parse("COMMAND_ALIASES", sender) : "Aliases") + "§8:§e " + String.join("§8,§e ", command.getAliases())); + help.forEach(s -> { + if (s.replaceAll("§[0-9A-Z]", "").startsWith(string)) { + sender.sendMessage(s); + } + }); + } + + private String translate(String description, CommandSender sender) { + return message != null ? message.parse(description, sender) : description; + } + @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Repeatable(Register.Registeres.class) protected @interface Register { String[] value() default {}; + String description() default ""; + boolean help() default false; @Retention(RetentionPolicy.RUNTIME) @@ -196,4 +275,10 @@ public abstract class SWCommand { boolean local() default false; } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER}) + protected @interface Name { + String name(); + } } diff --git a/SpigotCore_Main/src/de/steamwar/command/SubCommand.java b/SpigotCore_Main/src/de/steamwar/command/SubCommand.java index 15dad9a..0ea3852 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SubCommand.java +++ b/SpigotCore_Main/src/de/steamwar/command/SubCommand.java @@ -33,18 +33,21 @@ import static de.steamwar.command.SWCommandUtils.*; class SubCommand { private SWCommand swCommand; - private Method method; + Method method; + String description; + Parameter[] parameters; String[] subCommand; TypeMapper[] arguments; private Predicate commandSenderPredicate; private Function commandSenderFunction; Class varArgType = null; - SubCommand(SWCommand swCommand, Method method, String[] subCommand, Map> localTypeMapper) { + SubCommand(SWCommand swCommand, Method method, String description, String[] subCommand, Map> localTypeMapper) { this.swCommand = swCommand; this.method = method; + this.description = description; - Parameter[] parameters = method.getParameters(); + parameters = method.getParameters(); commandSenderPredicate = sender -> parameters[0].getType().isAssignableFrom(sender.getClass()); commandSenderFunction = sender -> parameters[0].getType().cast(sender); this.subCommand = subCommand;