diff --git a/SpigotCore_Main/pom.xml b/SpigotCore_Main/pom.xml index 81c3416..9405988 100644 --- a/SpigotCore_Main/pom.xml +++ b/SpigotCore_Main/pom.xml @@ -55,14 +55,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - - 9 - 9 - - spigotcore diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommand.java b/SpigotCore_Main/src/de/steamwar/command/SWCommand.java index e9f9914..fba9c11 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommand.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommand.java @@ -60,6 +60,11 @@ public abstract class SWCommand { swCommandInterface.register(); } + interface SWCommandInterface { + void unregister(); + void register(); + } + @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Repeatable(Register.Registeres.class) diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java index 16ba87b..9e31c8b 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java @@ -24,90 +24,36 @@ import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import org.bukkit.Bukkit; +import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.*; import java.util.function.BiConsumer; -import java.util.function.Function; import java.util.function.IntPredicate; -import java.util.function.Predicate; import java.util.logging.Level; +import java.util.stream.Collectors; +import java.util.stream.Stream; -import static de.steamwar.command.SWCommandUtils.*; +class SWCommandBrigadier implements SWCommand.SWCommandInterface { -class SWCommandBrigadier implements SWCommandInterface { - - private SWCommandNormal swCommandNormal = null; private final SWCommand swCommand; + private Command command; private final List commandList = new ArrayList<>(); private final List commandHelpList = new ArrayList<>(); private final Map> localTypeMapper = new HashMap<>(); protected SWCommandBrigadier(SWCommand swCommand, String command, String... aliases) { this.swCommand = swCommand; - - Method[] methods = swCommand.getClass().getDeclaredMethods(); - for (Method method : methods) { - addMapper(SWCommand.Mapper.class, method, i -> i == 0, false, TypeMapper.class, (anno, typeMapper) -> { - (anno.local() ? localTypeMapper : SWCommandUtils.MAPPER_FUNCTIONS).putIfAbsent(anno.value(), typeMapper); - }); - addMapper(SWCommand.ClassMapper.class, method, i -> i == 0, false, TypeMapper.class, (anno, typeMapper) -> { - (anno.local() ? localTypeMapper : SWCommandUtils.MAPPER_FUNCTIONS).putIfAbsent(anno.value().getTypeName(), typeMapper); - }); - add(SWCommand.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"); - } - if (!parameters[parameters.length - 1].isVarArgs()) { - Bukkit.getLogger().log(Level.WARNING, "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; - } - commandList.add(createSubCommand(method, anno.value())); - }); - } - for (Method method : methods) { - add(SWCommand.Register.class, method, i -> i > 0, true, null, (anno, parameters) -> { - if (anno.help()) return; - for (int i = 1; i < parameters.length; i++) { - Parameter parameter = parameters[i]; - Class clazz = parameter.getType(); - if (parameter.isVarArgs() && i == parameters.length - 1) { - clazz = parameter.getType().getComponentType(); - } - SWCommand.Mapper mapper = parameter.getAnnotation(SWCommand.Mapper.class); - if (clazz.isEnum() && mapper == null && !SWCommandUtils.MAPPER_FUNCTIONS.containsKey(clazz.getTypeName())) { - continue; - } - 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 + "' is using an unsupported Mapper of type '" + name + "'"); - return; - } - } - commandList.add(createSubCommand(method, anno.value())); - }); - } - - for (SubCommand subCommand : commandList) { + SWCommandUtils.createList(swCommand, commandList, commandHelpList, localTypeMapper, this::createSubCommand); + Stream.of(commandList, commandHelpList).flatMap(List::stream).forEach(subCommand -> { register(command, subCommand, command, aliases); for (String s : aliases) { register(s, subCommand, command, aliases); } - } - for (SubCommand subCommand : commandHelpList) { - register(command, subCommand, command, aliases); - for (String s : aliases) { - register(s, subCommand, command, aliases); - } - } + }); } private SubCommand createSubCommand(Method method, String[] strings) { @@ -171,8 +117,27 @@ class SWCommandBrigadier implements SWCommandInterface { if (subCommand.argumentNode == null) { return; } - if (subCommand.normalTabCompleteNeeded) { - swCommandNormal = new SWCommandNormal(swCommand, command, aliases); + if (subCommand.normalTabCompleteNeeded && command == null) { + this.command = new Command(command, "", "/" + command, Arrays.asList(aliases)) { + @Override + public boolean execute(CommandSender sender, String alias, String[] args) { + if (commandList.stream().anyMatch(s -> s.invoke(sender, args))) return false; + commandHelpList.stream().anyMatch(s -> s.invoke(sender, args)); + return false; + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + String string = args[args.length - 1].toLowerCase(); + return commandList.stream() + .map(s -> s.tabComplete(sender, args)) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .filter(s -> !s.isEmpty()) + .filter(s -> s.toLowerCase().startsWith(string)) + .collect(Collectors.toList()); + } + }; } literalArgumentBuilder.then(subCommand.argumentNode); SWCommand.dispatcher.register(literalArgumentBuilder); @@ -211,14 +176,16 @@ class SWCommandBrigadier implements SWCommandInterface { @Override public void unregister() { - if (swCommandNormal == null) return; - swCommandNormal.unregister(); + if (command == null) return; + SWCommandUtils.knownCommandMap.remove(command.getName()); + command.getAliases().forEach(SWCommandUtils.knownCommandMap::remove); + command.unregister(SWCommandUtils.commandMap); } @Override public void register() { - if (swCommandNormal == null) return; - swCommandNormal.register(); + if (command == null) return; + SWCommandUtils.commandMap.register("steamwar", this.command); } private ArgumentType getArgumentType(Parameter parameter, Class parameterType, SubCommand subCommand) { @@ -227,32 +194,20 @@ class SWCommandBrigadier implements SWCommandInterface { argumentType = BoolArgumentType.bool(); } else if (parameterType == int.class || parameterType == Integer.class) { SWCommand.IntRange intRange = parameter.getAnnotation(SWCommand.IntRange.class); - if (intRange != null) { - argumentType = IntegerArgumentType.integer(intRange.min(), intRange.max()); - } else { - argumentType = IntegerArgumentType.integer(); - } + if (intRange != null) argumentType = IntegerArgumentType.integer(intRange.min(), intRange.max()); + else argumentType = IntegerArgumentType.integer(); } else if (parameterType == float.class || parameterType == Float.class) { SWCommand.FloatRange floatRange = parameter.getAnnotation(SWCommand.FloatRange.class); - if (floatRange != null) { - argumentType = FloatArgumentType.floatArg(floatRange.min(), floatRange.max()); - } else { - argumentType = FloatArgumentType.floatArg(); - } + if (floatRange != null) argumentType = FloatArgumentType.floatArg(floatRange.min(), floatRange.max()); + else argumentType = FloatArgumentType.floatArg(); } else if (parameterType == long.class || parameterType == Long.class) { SWCommand.LongRange longRange = parameter.getAnnotation(SWCommand.LongRange.class); - if (longRange != null) { - argumentType = LongArgumentType.longArg(longRange.min(), longRange.max()); - } else { - argumentType = LongArgumentType.longArg(); - } + if (longRange != null) argumentType = LongArgumentType.longArg(longRange.min(), longRange.max()); + else argumentType = LongArgumentType.longArg(); } else if (parameterType == double.class || parameterType == Double.class) { SWCommand.DoubleRange doubleRange = parameter.getAnnotation(SWCommand.DoubleRange.class); - if (doubleRange != null) { - argumentType = DoubleArgumentType.doubleArg(doubleRange.min(), doubleRange.max()); - } else { - argumentType = DoubleArgumentType.doubleArg(); - } + if (doubleRange != null) argumentType = DoubleArgumentType.doubleArg(doubleRange.min(), doubleRange.max()); + else argumentType = DoubleArgumentType.doubleArg(); } else { argumentType = StringArgumentType.string(); subCommand.normalTabCompleteNeeded = true; diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandInterface.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandInterface.java deleted file mode 100644 index 4ba0844..0000000 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandInterface.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2020 SteamWar.de-Serverteam - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package de.steamwar.command; - -public interface SWCommandInterface { - void unregister(); - void register(); -} diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandNormal.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandNormal.java index b325a81..4017ec5 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandNormal.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandNormal.java @@ -32,7 +32,7 @@ import java.util.function.IntPredicate; import java.util.logging.Level; import java.util.stream.Collectors; -class SWCommandNormal implements SWCommandInterface { +class SWCommandNormal implements SWCommand.SWCommandInterface { private final SWCommand swCommand; private final Command command; @@ -65,100 +65,13 @@ class SWCommandNormal implements SWCommandInterface { }; unregister(); register(); - - Method[] methods = swCommand.getClass().getDeclaredMethods(); - for (Method method : methods) { - addMapper(SWCommand.Mapper.class, method, i -> i == 0, false, TypeMapper.class, (anno, typeMapper) -> { - (anno.local() ? localTypeMapper : SWCommandUtils.MAPPER_FUNCTIONS).putIfAbsent(anno.value(), typeMapper); - }); - addMapper(SWCommand.ClassMapper.class, method, i -> i == 0, false, TypeMapper.class, (anno, typeMapper) -> { - (anno.local() ? localTypeMapper : SWCommandUtils.MAPPER_FUNCTIONS).putIfAbsent(anno.value().getTypeName(), typeMapper); - }); - add(SWCommand.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"); - } - if (!parameters[parameters.length - 1].isVarArgs()) { - Bukkit.getLogger().log(Level.WARNING, "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; - } - commandHelpList.add(createSubCommand(method, anno.value())); - }); - } - for (Method method : methods) { - add(SWCommand.Register.class, method, i -> i > 0, true, null, (anno, parameters) -> { - if (anno.help()) return; - for (int i = 1; i < parameters.length; i++) { - Parameter parameter = parameters[i]; - Class clazz = parameter.getType(); - if (parameter.isVarArgs() && i == parameters.length - 1) { - clazz = parameter.getType().getComponentType(); - } - SWCommand.Mapper mapper = parameter.getAnnotation(SWCommand.Mapper.class); - if (clazz.isEnum() && mapper == null && !SWCommandUtils.MAPPER_FUNCTIONS.containsKey(clazz.getTypeName())) { - continue; - } - 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 + "' is using an unsupported Mapper of type '" + name + "'"); - return; - } - } - commandList.add(createSubCommand(method, anno.value())); - }); - - this.commandList.sort((o1, o2) -> { - int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length); - if (compare != 0) { - return compare; - } else { - return Integer.compare(o1.varArgType != null ? Integer.MAX_VALUE : o1.arguments.length, - o2.varArgType != null ? Integer.MAX_VALUE : o2.arguments.length); - } - }); - commandHelpList.sort(Comparator.comparingInt(o -> -o.subCommand.length)); - } + SWCommandUtils.createList(swCommand, commandList, commandHelpList, localTypeMapper, this::createSubCommand); } - private de.steamwar.command.SubCommand createSubCommand(Method method, String[] strings) { + private SubCommand createSubCommand(Method method, String[] strings) { return new de.steamwar.command.SubCommand(swCommand, method, strings, localTypeMapper, current -> {}); } - private void add(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer consumer) { - T[] anno = SWCommandUtils.getAnnotation(method, annotation); - if (anno == null || anno.length == 0) return; - - Parameter[] parameters = method.getParameters(); - if (!parameterTester.test(parameters.length)) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method + "' is lacking parameters or has too many"); - return; - } - if (firstParameter && !CommandSender.class.isAssignableFrom(parameters[0].getType())) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method + "' is lacking the first parameter of type '" + CommandSender.class.getTypeName() + "'"); - return; - } - if (returnType != null && method.getReturnType() != returnType) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method + "' is lacking the desired return type '" + returnType.getTypeName() + "'"); - return; - } - Arrays.stream(anno).forEach(t -> consumer.accept(t, parameters)); - } - - private void addMapper(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer> consumer) { - add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> { - try { - method.setAccessible(true); - consumer.accept(anno, (TypeMapper) method.invoke(swCommand)); - } catch (Exception e) { - throw new SecurityException(e.getMessage(), e); - } - }); - } - public void unregister() { SWCommandUtils.knownCommandMap.remove(command.getName()); command.getAliases().forEach(SWCommandUtils.knownCommandMap::remove); diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java index e6e0ee4..5b932b3 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java @@ -31,9 +31,13 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import java.util.*; +import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.IntPredicate; +import java.util.logging.Level; import java.util.stream.Collectors; public class SWCommandUtils { @@ -178,4 +182,83 @@ public class SWCommandUtils { if (method.getAnnotations().length != 1) return null; return method.getDeclaredAnnotationsByType(annotation); } + + static void createList(SWCommand swCommand, List commandList, List commandHelpList, Map> localTypeMapper, BiFunction creator) { + Method[] methods = swCommand.getClass().getDeclaredMethods(); + for (Method method : methods) { + addMapper(swCommand, SWCommand.Mapper.class, method, i -> i == 0, false, TypeMapper.class, (anno, typeMapper) -> { + (anno.local() ? localTypeMapper : SWCommandUtils.MAPPER_FUNCTIONS).putIfAbsent(anno.value(), typeMapper); + }); + addMapper(swCommand, SWCommand.ClassMapper.class, method, i -> i == 0, false, TypeMapper.class, (anno, typeMapper) -> { + (anno.local() ? localTypeMapper : SWCommandUtils.MAPPER_FUNCTIONS).putIfAbsent(anno.value().getTypeName(), typeMapper); + }); + add(SWCommand.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"); + } + if (!parameters[parameters.length - 1].isVarArgs()) { + Bukkit.getLogger().log(Level.WARNING, "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; + } + commandHelpList.add(creator.apply(method, anno.value())); + }); + } + for (Method method : methods) { + add(SWCommand.Register.class, method, i -> i > 0, true, null, (anno, parameters) -> { + if (anno.help()) return; + for (int i = 1; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + Class clazz = parameter.getType(); + if (parameter.isVarArgs() && i == parameters.length - 1) { + clazz = parameter.getType().getComponentType(); + } + SWCommand.Mapper mapper = parameter.getAnnotation(SWCommand.Mapper.class); + if (clazz.isEnum() && mapper == null && !SWCommandUtils.MAPPER_FUNCTIONS.containsKey(clazz.getTypeName())) { + continue; + } + 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 + "' is using an unsupported Mapper of type '" + name + "'"); + return; + } + } + commandList.add(creator.apply(method, anno.value())); + }); + } + } + + private static void add(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer consumer) { + T[] anno = SWCommandUtils.getAnnotation(method, annotation); + if (anno == null || anno.length == 0) return; + + Parameter[] parameters = method.getParameters(); + if (!parameterTester.test(parameters.length)) { + Bukkit.getLogger().log(Level.WARNING, "The method '" + method + "' is lacking parameters or has too many"); + return; + } + if (firstParameter && !CommandSender.class.isAssignableFrom(parameters[0].getType())) { + Bukkit.getLogger().log(Level.WARNING, "The method '" + method + "' is lacking the first parameter of type '" + CommandSender.class.getTypeName() + "'"); + return; + } + if (returnType != null && method.getReturnType() != returnType) { + Bukkit.getLogger().log(Level.WARNING, "The method '" + method + "' is lacking the desired return type '" + returnType.getTypeName() + "'"); + return; + } + Arrays.stream(anno).forEach(t -> consumer.accept(t, parameters)); + } + + private static void addMapper(SWCommand swCommand, Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer> consumer) { + add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> { + try { + method.setAccessible(true); + consumer.accept(anno, (TypeMapper) method.invoke(swCommand)); + } catch (Exception e) { + throw new SecurityException(e.getMessage(), e); + } + }); + } }