From 45ddd8cb5a8ae785b16f8c0a68aea1509ff605a5 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 8 Jul 2021 22:07:11 +0200 Subject: [PATCH 01/12] CMDoS because Brigadier --- .../de/steamwar/command/Dispatcher_14.java | 31 ++ .../de/steamwar/command/Dispatcher_15.java | 32 ++ SpigotCore_Main/pom.xml | 18 + .../src/de/steamwar/command/SWCommand.java | 178 +++------- .../steamwar/command/SWCommandBrigadier.java | 333 ++++++++++++++++++ .../steamwar/command/SWCommandInterface.java | 25 ++ .../de/steamwar/command/SWCommandNormal.java | 290 +++++++++++++++ .../src/de/steamwar/command/SubCommand.java | 149 -------- 8 files changed, 780 insertions(+), 276 deletions(-) create mode 100644 SpigotCore_14/src/de/steamwar/command/Dispatcher_14.java create mode 100644 SpigotCore_15/src/de/steamwar/command/Dispatcher_15.java create mode 100644 SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java create mode 100644 SpigotCore_Main/src/de/steamwar/command/SWCommandInterface.java create mode 100644 SpigotCore_Main/src/de/steamwar/command/SWCommandNormal.java delete mode 100644 SpigotCore_Main/src/de/steamwar/command/SubCommand.java diff --git a/SpigotCore_14/src/de/steamwar/command/Dispatcher_14.java b/SpigotCore_14/src/de/steamwar/command/Dispatcher_14.java new file mode 100644 index 0000000..c2935b9 --- /dev/null +++ b/SpigotCore_14/src/de/steamwar/command/Dispatcher_14.java @@ -0,0 +1,31 @@ +/* + * 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; + +import com.mojang.brigadier.CommandDispatcher; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_14_R1.CraftServer; + +public class Dispatcher_14 { + + static CommandDispatcher getDispatcher() { + return ((CraftServer) Bukkit.getServer()).getServer().getCommandDispatcher().a(); + } +} diff --git a/SpigotCore_15/src/de/steamwar/command/Dispatcher_15.java b/SpigotCore_15/src/de/steamwar/command/Dispatcher_15.java new file mode 100644 index 0000000..363ae82 --- /dev/null +++ b/SpigotCore_15/src/de/steamwar/command/Dispatcher_15.java @@ -0,0 +1,32 @@ +/* + * 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; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.server.v1_15_R1.CommandListenerWrapper; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_15_R1.CraftServer; + +public class Dispatcher_15 { + + static CommandDispatcher getDispatcher() { + return ((CraftServer) Bukkit.getServer()).getServer().getCommandDispatcher().a(); + } +} diff --git a/SpigotCore_Main/pom.xml b/SpigotCore_Main/pom.xml index 0d23f6f..81c3416 100644 --- a/SpigotCore_Main/pom.xml +++ b/SpigotCore_Main/pom.xml @@ -19,6 +19,11 @@ codemc-snapshots https://repo.codemc.io/repository/maven-snapshots/ + + minecraft-libraries + Minecraft Libraries + https://libraries.minecraft.net + SpigotCore_Main @@ -50,6 +55,14 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + 9 + 9 + + spigotcore @@ -124,5 +137,10 @@ 2.0 compile + + com.mojang + brigadier + 1.0.17 + \ No newline at end of file diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommand.java b/SpigotCore_Main/src/de/steamwar/command/SWCommand.java index ab72fc5..e9f9914 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommand.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommand.java @@ -19,151 +19,45 @@ package de.steamwar.command; -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; +import com.mojang.brigadier.CommandDispatcher; +import de.steamwar.core.VersionedCallable; import java.lang.annotation.*; -import java.lang.reflect.Method; -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 final Command command; - private final List commandList = new ArrayList<>(); - private final List commandHelpList = new ArrayList<>(); - private final Map> localTypeMapper = new HashMap<>(); + static final CommandDispatcher dispatcher; - protected SWCommand(String command) { - this(command, new String[0]); + static { + dispatcher = VersionedCallable.call(new VersionedCallable<>(() -> null, 12), + new VersionedCallable<>(Dispatcher_14::getDispatcher, 14), + new VersionedCallable<>(Dispatcher_15::getDispatcher, 15)); + } + + private SWCommandInterface swCommandInterface; + + protected SWCommand(String command, boolean noBrigadier) { + this(command, noBrigadier, new String[0]); } protected SWCommand(String command, String... aliases) { - 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()); - } - }; - unregister(); - register(); - - Method[] methods = getClass().getDeclaredMethods(); - 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); - }); - addMapper(ClassMapper.class, method, i -> i == 0, false, TypeMapper.class, (anno, typeMapper) -> { - (anno.local() ? localTypeMapper : SWCommandUtils.MAPPER_FUNCTIONS).putIfAbsent(anno.value().getTypeName(), typeMapper); - }); - 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"); - } - 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(new SubCommand(this, method, anno.value(), new HashMap<>())); - }); - } - for (Method method : methods) { - add(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(); - } - Mapper mapper = parameter.getAnnotation(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.toString() + "' is using an unsupported Mapper of type '" + name + "'"); - return; - } - } - commandList.add(new SubCommand(this, method, anno.value(), localTypeMapper)); - }); - - 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)); - } + this(command, false, aliases); } - 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.toString() + "' is lacking parameters or has too many"); - return; + protected SWCommand(String command, boolean noBrigadier, String... aliases) { + if (dispatcher != null && !noBrigadier) { + swCommandInterface = new SWCommandBrigadier(this, command, aliases); + } else { + swCommandInterface = new SWCommandNormal(this, command, aliases); } - 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; - } - if (returnType != null && method.getReturnType() != returnType) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' 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(this)); - } catch (Exception e) { - throw new SecurityException(e.getMessage(), e); - } - }); } public void unregister() { - SWCommandUtils.knownCommandMap.remove(command.getName()); - command.getAliases().forEach(SWCommandUtils.knownCommandMap::remove); - command.unregister(SWCommandUtils.commandMap); + swCommandInterface.unregister(); } public void register() { - SWCommandUtils.commandMap.register("steamwar", this.command); + swCommandInterface.register(); } @Retention(RetentionPolicy.RUNTIME) @@ -196,4 +90,34 @@ public abstract class SWCommand { boolean local() default false; } + + + // Used for Brigadier, as parse hints and TabComplete hints + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER}) + protected @interface IntRange { + int min(); + int max(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER}) + protected @interface LongRange { + long min(); + long max(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER}) + protected @interface FloatRange { + float min(); + float max(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER}) + protected @interface DoubleRange { + double min(); + double max(); + } } diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java new file mode 100644 index 0000000..ecad6b3 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java @@ -0,0 +1,333 @@ +/* + * 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; + +import com.mojang.brigadier.arguments.*; +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.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 static de.steamwar.command.SWCommandUtils.*; + +class SWCommandBrigadier implements SWCommandInterface { + + private SWCommandNormal swCommandNormal = null; + private final SWCommand swCommand; + 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; + } + commandHelpList.add(new SubCommand(swCommand, method, anno.value(), new HashMap<>())); + }); + } + 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(new SubCommand(swCommand, method, anno.value(), new HashMap<>())); + }); + } + + for (SubCommand subCommand : commandList) { + 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 void register(String name, SubCommand subCommand, String command, String... aliases) { + LiteralArgumentBuilder literalArgumentBuilder = LiteralArgumentBuilder.literal(name); + if (subCommand.argumentNode == null) { + return; + } + if (subCommand.normalTabCompleteNeeded) { + swCommandNormal = new SWCommandNormal(swCommand, command, aliases); + } + literalArgumentBuilder.then(subCommand.argumentNode); + SWCommand.dispatcher.register(literalArgumentBuilder); + } + + 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); + } + }); + } + + @Override + public void unregister() { + if (swCommandNormal == null) return; + swCommandNormal.unregister(); + } + + @Override + public void register() { + if (swCommandNormal == null) return; + swCommandNormal.register(); + } + + static class SubCommand { + + ArgumentBuilder argumentNode = null; + boolean normalTabCompleteNeeded = false; + + private SWCommand swCommand; + private Method method; + String[] subCommand; + TypeMapper[] arguments; + private Predicate commandSenderPredicate; + private Function commandSenderFunction; + Class varArgType = null; + + SubCommand(SWCommand swCommand, Method method, String[] subCommand, Map> localTypeMapper) { + this.swCommand = swCommand; + this.method = method; + + Parameter[] parameters = method.getParameters(); + commandSenderPredicate = sender -> parameters[0].getType().isAssignableFrom(sender.getClass()); + commandSenderFunction = sender -> parameters[0].getType().cast(sender); + this.subCommand = subCommand; + + arguments = new TypeMapper[parameters.length - 1]; + for (int i = 1; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + Class clazz = parameter.getType(); + if (parameter.isVarArgs()) { + clazz = clazz.getComponentType(); + varArgType = clazz; + } + + SWCommand.Mapper mapper = parameter.getAnnotation(SWCommand.Mapper.class); + if (clazz.isEnum() && mapper == null && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) { + Class> enumClass = (Class>) clazz; + List tabCompletes = new ArrayList<>(); + for (Enum enumConstant : enumClass.getEnumConstants()) { + tabCompletes.add(enumConstant.name().toLowerCase()); + } + arguments[i - 1] = SWCommandUtils.createMapper(s -> ENUM_MAPPER.apply(enumClass, s), s -> tabCompletes); + continue; + } + + String name = clazz.getTypeName(); + if (mapper != null) { + name = mapper.value(); + } + arguments[i - 1] = localTypeMapper.containsKey(name) + ? localTypeMapper.get(name) + : MAPPER_FUNCTIONS.getOrDefault(name, ERROR_FUNCTION); + } + + + ArgumentBuilder argumentBuilder = null; + for (String s : subCommand) { + LiteralArgumentBuilder literalArgumentBuilder = LiteralArgumentBuilder.literal(s); + if (argumentBuilder != null) { + argumentBuilder.then(literalArgumentBuilder); + } else { + argumentNode = literalArgumentBuilder; + } + argumentBuilder = literalArgumentBuilder; + } + + for (int i = 0; i < arguments.length - (varArgType != null ? 1 : 0); i++) { + Parameter parameter = parameters[i + 1]; + Class parameterType = parameter.getType(); + ArgumentType argumentType = getArgumentType(parameter, parameterType, arguments[i]); + + RequiredArgumentBuilder requiredArgumentBuilder = RequiredArgumentBuilder.argument(parameter.getName(), argumentType); + if (argumentBuilder != null) { + argumentBuilder.then(requiredArgumentBuilder); + } else { + argumentNode = requiredArgumentBuilder; + } + argumentBuilder = requiredArgumentBuilder; + if (i == arguments.length - 1) { + argumentBuilder.executes(commandContext -> { + invoke((CommandSender) commandContext.getCommand(), commandContext.getInput().split(" ")); + return 0; + }); + } + } + if (varArgType != null) { + // TODO: UNSUPORTED + /*Parameter parameter = parameters[parameters.length - 1]; + Class parameterType = parameter.getType(); + TypeMapper typeMapper = arguments[arguments.length - 1]; + ArgumentType argumentType = getArgumentType(parameter, parameterType, typeMapper); + + RequiredArgumentBuilder requiredArgumentBuilder = RequiredArgumentBuilder.argument(parameter.getName(), argumentType); + if (argumentBuilder != null) { + argumentBuilder.then(requiredArgumentBuilder); + } else { + argumentNode = requiredArgumentBuilder; + } + argumentBuilder = requiredArgumentBuilder; + argumentBuilder.executes(commandContext -> { + invoke((CommandSender) commandContext.getCommand(), commandContext.getInput().split(" ")); + return 0; + }); + CommandNode commandNode = argumentBuilder.build(); + argumentBuilder.redirect(commandNode);*/ + } + } + + boolean invoke(CommandSender commandSender, String[] args) { + if (args.length < arguments.length + subCommand.length - (varArgType != null ? 1 : 0)) { + return false; + } + if (varArgType == null && args.length > arguments.length + subCommand.length) { + return false; + } + try { + if (!commandSenderPredicate.test(commandSender)) { + return false; + } + Object[] objects = SWCommandUtils.generateArgumentArray(commandSender, arguments, args, varArgType, subCommand); + objects[0] = commandSenderFunction.apply(commandSender); + method.setAccessible(true); + method.invoke(swCommand, objects); + } catch (IllegalAccessException | RuntimeException | InvocationTargetException e) { + throw new SecurityException(e.getMessage(), e); + } catch (CommandParseException e) { + return false; + } + return true; + } + + private ArgumentType getArgumentType(Parameter parameter, Class parameterType, TypeMapper typeMapper) { + ArgumentType argumentType; + 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(); + } + } 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(); + } + } 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(); + } + } 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(); + } + } else { + argumentType = StringArgumentType.string(); + normalTabCompleteNeeded = true; + } + return argumentType; + } + } +} diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandInterface.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandInterface.java new file mode 100644 index 0000000..4ba0844 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandInterface.java @@ -0,0 +1,25 @@ +/* + * 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 new file mode 100644 index 0000000..144f867 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandNormal.java @@ -0,0 +1,290 @@ +/* + * 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; + +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 static de.steamwar.command.SWCommandUtils.*; + +class SWCommandNormal implements SWCommandInterface { + + private final SWCommand swCommand; + private final Command command; + private final List commandList = new ArrayList<>(); + private final List commandHelpList = new ArrayList<>(); + private final Map> localTypeMapper = new HashMap<>(); + + protected SWCommandNormal(SWCommand swCommand, String command, String... aliases) { + this.swCommand = swCommand; + + 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()); + } + }; + 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(new SubCommand(swCommand, method, anno.value(), new HashMap<>())); + }); + } + 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(new SubCommand(swCommand, method, anno.value(), localTypeMapper)); + }); + + 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)); + } + } + + 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); + command.unregister(SWCommandUtils.commandMap); + } + + public void register() { + SWCommandUtils.commandMap.register("steamwar", this.command); + } + + static class SubCommand { + + private SWCommand swCommand; + private Method method; + String[] subCommand; + TypeMapper[] arguments; + private Predicate commandSenderPredicate; + private Function commandSenderFunction; + Class varArgType = null; + + SubCommand(SWCommand swCommand, Method method, String[] subCommand, Map> localTypeMapper) { + this.swCommand = swCommand; + this.method = method; + + Parameter[] parameters = method.getParameters(); + commandSenderPredicate = sender -> parameters[0].getType().isAssignableFrom(sender.getClass()); + commandSenderFunction = sender -> parameters[0].getType().cast(sender); + this.subCommand = subCommand; + + arguments = new TypeMapper[parameters.length - 1]; + for (int i = 1; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + Class clazz = parameter.getType(); + if (parameter.isVarArgs()) { + clazz = clazz.getComponentType(); + varArgType = clazz; + } + + SWCommand.Mapper mapper = parameter.getAnnotation(SWCommand.Mapper.class); + if (clazz.isEnum() && mapper == null && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) { + Class> enumClass = (Class>) clazz; + List tabCompletes = new ArrayList<>(); + for (Enum enumConstant : enumClass.getEnumConstants()) { + tabCompletes.add(enumConstant.name().toLowerCase()); + } + arguments[i - 1] = SWCommandUtils.createMapper(s -> ENUM_MAPPER.apply(enumClass, s), s -> tabCompletes); + continue; + } + + String name = clazz.getTypeName(); + if (mapper != null) { + name = mapper.value(); + } + arguments[i - 1] = localTypeMapper.containsKey(name) + ? localTypeMapper.get(name) + : MAPPER_FUNCTIONS.getOrDefault(name, ERROR_FUNCTION); + } + } + + boolean invoke(CommandSender commandSender, String[] args) { + if (args.length < arguments.length + subCommand.length - (varArgType != null ? 1 : 0)) { + return false; + } + if (varArgType == null && args.length > arguments.length + subCommand.length) { + return false; + } + try { + if (!commandSenderPredicate.test(commandSender)) { + return false; + } + Object[] objects = SWCommandUtils.generateArgumentArray(commandSender, arguments, args, varArgType, subCommand); + objects[0] = commandSenderFunction.apply(commandSender); + method.setAccessible(true); + method.invoke(swCommand, objects); + } catch (IllegalAccessException | RuntimeException | InvocationTargetException e) { + throw new SecurityException(e.getMessage(), e); + } catch (CommandParseException e) { + return false; + } + return true; + } + + List tabComplete(CommandSender commandSender, String[] args) { + if (varArgType == null && args.length > arguments.length + subCommand.length) { + return null; + } + int index = 0; + List argsList = new LinkedList<>(Arrays.asList(args)); + for (String value : subCommand) { + String s = argsList.remove(0); + if (argsList.isEmpty()) return Collections.singletonList(value); + if (!value.equalsIgnoreCase(s)) return null; + index++; + } + for (TypeMapper argument : arguments) { + String s = argsList.remove(0); + if (argsList.isEmpty()) { + return argument.tabCompletes(commandSender, Arrays.copyOf(args, args.length - 1), s); + } + try { + if (argument.map(commandSender, Arrays.copyOf(args, index), s) == null) { + return null; + } + } catch (Exception e) { + return null; + } + index++; + } + if (varArgType != null && !argsList.isEmpty()) { + while (!argsList.isEmpty()) { + String s = argsList.remove(0); + if (argsList.isEmpty()) { + return arguments[arguments.length - 1].tabCompletes(commandSender, Arrays.copyOf(args, args.length - 1), s); + } + try { + if (arguments[arguments.length - 1].map(commandSender, Arrays.copyOf(args, index), s) == null) { + return null; + } + } catch (Exception e) { + return null; + } + index++; + } + } + return null; + } + } +} diff --git a/SpigotCore_Main/src/de/steamwar/command/SubCommand.java b/SpigotCore_Main/src/de/steamwar/command/SubCommand.java deleted file mode 100644 index 15dad9a..0000000 --- a/SpigotCore_Main/src/de/steamwar/command/SubCommand.java +++ /dev/null @@ -1,149 +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; - -import org.bukkit.command.CommandSender; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Parameter; -import java.util.*; -import java.util.function.Function; -import java.util.function.Predicate; - -import static de.steamwar.command.SWCommandUtils.*; - -class SubCommand { - - private SWCommand swCommand; - private Method method; - String[] subCommand; - TypeMapper[] arguments; - private Predicate commandSenderPredicate; - private Function commandSenderFunction; - Class varArgType = null; - - SubCommand(SWCommand swCommand, Method method, String[] subCommand, Map> localTypeMapper) { - this.swCommand = swCommand; - this.method = method; - - Parameter[] parameters = method.getParameters(); - commandSenderPredicate = sender -> parameters[0].getType().isAssignableFrom(sender.getClass()); - commandSenderFunction = sender -> parameters[0].getType().cast(sender); - this.subCommand = subCommand; - - arguments = new TypeMapper[parameters.length - 1]; - for (int i = 1; i < parameters.length; i++) { - Parameter parameter = parameters[i]; - Class clazz = parameter.getType(); - if (parameter.isVarArgs()) { - clazz = clazz.getComponentType(); - varArgType = clazz; - } - - SWCommand.Mapper mapper = parameter.getAnnotation(SWCommand.Mapper.class); - if (clazz.isEnum() && mapper == null && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) { - Class> enumClass = (Class>) clazz; - List tabCompletes = new ArrayList<>(); - for (Enum enumConstant : enumClass.getEnumConstants()) { - tabCompletes.add(enumConstant.name().toLowerCase()); - } - arguments[i - 1] = SWCommandUtils.createMapper(s -> ENUM_MAPPER.apply(enumClass, s), s -> tabCompletes); - continue; - } - - String name = clazz.getTypeName(); - if (mapper != null) { - name = mapper.value(); - } - arguments[i - 1] = localTypeMapper.containsKey(name) - ? localTypeMapper.get(name) - : MAPPER_FUNCTIONS.getOrDefault(name, ERROR_FUNCTION); - } - } - - boolean invoke(CommandSender commandSender, String[] args) { - if (args.length < arguments.length + subCommand.length - (varArgType != null ? 1 : 0)) { - return false; - } - if (varArgType == null && args.length > arguments.length + subCommand.length) { - return false; - } - try { - if (!commandSenderPredicate.test(commandSender)) { - return false; - } - Object[] objects = SWCommandUtils.generateArgumentArray(commandSender, arguments, args, varArgType, subCommand); - objects[0] = commandSenderFunction.apply(commandSender); - method.setAccessible(true); - method.invoke(swCommand, objects); - } catch (IllegalAccessException | RuntimeException | InvocationTargetException e) { - throw new SecurityException(e.getMessage(), e); - } catch (CommandParseException e) { - return false; - } - return true; - } - - List tabComplete(CommandSender commandSender, String[] args) { - if (varArgType == null && args.length > arguments.length + subCommand.length) { - return null; - } - int index = 0; - List argsList = new LinkedList<>(Arrays.asList(args)); - for (String value : subCommand) { - String s = argsList.remove(0); - if (argsList.isEmpty()) return Collections.singletonList(value); - if (!value.equalsIgnoreCase(s)) return null; - index++; - } - for (TypeMapper argument : arguments) { - String s = argsList.remove(0); - if (argsList.isEmpty()) { - return argument.tabCompletes(commandSender, Arrays.copyOf(args, args.length - 1), s); - } - try { - if (argument.map(commandSender, Arrays.copyOf(args, index), s) == null) { - return null; - } - } catch (Exception e) { - return null; - } - index++; - } - if (varArgType != null && !argsList.isEmpty()) { - while (!argsList.isEmpty()) { - String s = argsList.remove(0); - if (argsList.isEmpty()) { - return arguments[arguments.length - 1].tabCompletes(commandSender, Arrays.copyOf(args, args.length - 1), s); - } - try { - if (arguments[arguments.length - 1].map(commandSender, Arrays.copyOf(args, index), s) == null) { - return null; - } - } catch (Exception e) { - return null; - } - index++; - } - } - return null; - } -} -- 2.39.2 From 5178f1f48f2262cdbe60a9422b0d6d37b4677848 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 8 Jul 2021 22:09:52 +0200 Subject: [PATCH 02/12] CMDoS because Brigadier --- .../src/de/steamwar/command/SWCommandBrigadier.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java index ecad6b3..92e131a 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java @@ -295,7 +295,9 @@ class SWCommandBrigadier implements SWCommandInterface { private ArgumentType getArgumentType(Parameter parameter, Class parameterType, TypeMapper typeMapper) { ArgumentType argumentType; - if (parameterType == int.class || parameterType == Integer.class) { + if (parameterType == boolean.class || parameterType == Boolean.class) { + 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()); -- 2.39.2 From df6974ad04d63ff50dcb7bab5662103bf8143e18 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 8 Jul 2021 22:21:53 +0200 Subject: [PATCH 03/12] CMDoS because Brigadier --- .../steamwar/command/SWCommandBrigadier.java | 257 +++++++----------- .../de/steamwar/command/SWCommandNormal.java | 131 +-------- .../src/de/steamwar/command/SubCommand.java | 157 +++++++++++ 3 files changed, 255 insertions(+), 290 deletions(-) create mode 100644 SpigotCore_Main/src/de/steamwar/command/SubCommand.java diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java index 92e131a..16ba87b 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java @@ -70,7 +70,7 @@ class SWCommandBrigadier implements SWCommandInterface { 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(new SubCommand(swCommand, method, anno.value(), new HashMap<>())); + commandList.add(createSubCommand(method, anno.value())); }); } for (Method method : methods) { @@ -92,7 +92,7 @@ class SWCommandBrigadier implements SWCommandInterface { return; } } - commandList.add(new SubCommand(swCommand, method, anno.value(), new HashMap<>())); + commandList.add(createSubCommand(method, anno.value())); }); } @@ -110,6 +110,62 @@ class SWCommandBrigadier implements SWCommandInterface { } } + private SubCommand createSubCommand(Method method, String[] strings) { + return new SubCommand(swCommand, method, strings, localTypeMapper, current -> { + ArgumentBuilder argumentBuilder = null; + for (String s : current.subCommand) { + LiteralArgumentBuilder literalArgumentBuilder = LiteralArgumentBuilder.literal(s); + if (argumentBuilder != null) { + argumentBuilder.then(literalArgumentBuilder); + } else { + current.argumentNode = literalArgumentBuilder; + } + argumentBuilder = literalArgumentBuilder; + } + + for (int i = 0; i < current.arguments.length - (current.varArgType != null ? 1 : 0); i++) { + Parameter parameter = current.parameters[i + 1]; + Class parameterType = parameter.getType(); + ArgumentType argumentType = getArgumentType(parameter, parameterType, current); + + RequiredArgumentBuilder requiredArgumentBuilder = RequiredArgumentBuilder.argument(parameter.getName(), argumentType); + if (argumentBuilder != null) { + argumentBuilder.then(requiredArgumentBuilder); + } else { + current.argumentNode = requiredArgumentBuilder; + } + argumentBuilder = requiredArgumentBuilder; + if (i == current.arguments.length - 1) { + argumentBuilder.executes(commandContext -> { + current.invoke((CommandSender) commandContext.getCommand(), commandContext.getInput().split(" ")); + return 0; + }); + } + } + if (current.varArgType != null) { + // TODO: UNSUPORTED + /*Parameter parameter = parameters[parameters.length - 1]; + Class parameterType = parameter.getType(); + TypeMapper typeMapper = arguments[arguments.length - 1]; + ArgumentType argumentType = getArgumentType(parameter, parameterType, typeMapper); + + RequiredArgumentBuilder requiredArgumentBuilder = RequiredArgumentBuilder.argument(parameter.getName(), argumentType); + if (argumentBuilder != null) { + argumentBuilder.then(requiredArgumentBuilder); + } else { + argumentNode = requiredArgumentBuilder; + } + argumentBuilder = requiredArgumentBuilder; + argumentBuilder.executes(commandContext -> { + invoke((CommandSender) commandContext.getCommand(), commandContext.getInput().split(" ")); + return 0; + }); + CommandNode commandNode = argumentBuilder.build(); + argumentBuilder.redirect(commandNode);*/ + } + }); + } + private void register(String name, SubCommand subCommand, String command, String... aliases) { LiteralArgumentBuilder literalArgumentBuilder = LiteralArgumentBuilder.literal(name); if (subCommand.argumentNode == null) { @@ -165,171 +221,42 @@ class SWCommandBrigadier implements SWCommandInterface { swCommandNormal.register(); } - static class SubCommand { - - ArgumentBuilder argumentNode = null; - boolean normalTabCompleteNeeded = false; - - private SWCommand swCommand; - private Method method; - String[] subCommand; - TypeMapper[] arguments; - private Predicate commandSenderPredicate; - private Function commandSenderFunction; - Class varArgType = null; - - SubCommand(SWCommand swCommand, Method method, String[] subCommand, Map> localTypeMapper) { - this.swCommand = swCommand; - this.method = method; - - Parameter[] parameters = method.getParameters(); - commandSenderPredicate = sender -> parameters[0].getType().isAssignableFrom(sender.getClass()); - commandSenderFunction = sender -> parameters[0].getType().cast(sender); - this.subCommand = subCommand; - - arguments = new TypeMapper[parameters.length - 1]; - for (int i = 1; i < parameters.length; i++) { - Parameter parameter = parameters[i]; - Class clazz = parameter.getType(); - if (parameter.isVarArgs()) { - clazz = clazz.getComponentType(); - varArgType = clazz; - } - - SWCommand.Mapper mapper = parameter.getAnnotation(SWCommand.Mapper.class); - if (clazz.isEnum() && mapper == null && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) { - Class> enumClass = (Class>) clazz; - List tabCompletes = new ArrayList<>(); - for (Enum enumConstant : enumClass.getEnumConstants()) { - tabCompletes.add(enumConstant.name().toLowerCase()); - } - arguments[i - 1] = SWCommandUtils.createMapper(s -> ENUM_MAPPER.apply(enumClass, s), s -> tabCompletes); - continue; - } - - String name = clazz.getTypeName(); - if (mapper != null) { - name = mapper.value(); - } - arguments[i - 1] = localTypeMapper.containsKey(name) - ? localTypeMapper.get(name) - : MAPPER_FUNCTIONS.getOrDefault(name, ERROR_FUNCTION); - } - - - ArgumentBuilder argumentBuilder = null; - for (String s : subCommand) { - LiteralArgumentBuilder literalArgumentBuilder = LiteralArgumentBuilder.literal(s); - if (argumentBuilder != null) { - argumentBuilder.then(literalArgumentBuilder); - } else { - argumentNode = literalArgumentBuilder; - } - argumentBuilder = literalArgumentBuilder; - } - - for (int i = 0; i < arguments.length - (varArgType != null ? 1 : 0); i++) { - Parameter parameter = parameters[i + 1]; - Class parameterType = parameter.getType(); - ArgumentType argumentType = getArgumentType(parameter, parameterType, arguments[i]); - - RequiredArgumentBuilder requiredArgumentBuilder = RequiredArgumentBuilder.argument(parameter.getName(), argumentType); - if (argumentBuilder != null) { - argumentBuilder.then(requiredArgumentBuilder); - } else { - argumentNode = requiredArgumentBuilder; - } - argumentBuilder = requiredArgumentBuilder; - if (i == arguments.length - 1) { - argumentBuilder.executes(commandContext -> { - invoke((CommandSender) commandContext.getCommand(), commandContext.getInput().split(" ")); - return 0; - }); - } - } - if (varArgType != null) { - // TODO: UNSUPORTED - /*Parameter parameter = parameters[parameters.length - 1]; - Class parameterType = parameter.getType(); - TypeMapper typeMapper = arguments[arguments.length - 1]; - ArgumentType argumentType = getArgumentType(parameter, parameterType, typeMapper); - - RequiredArgumentBuilder requiredArgumentBuilder = RequiredArgumentBuilder.argument(parameter.getName(), argumentType); - if (argumentBuilder != null) { - argumentBuilder.then(requiredArgumentBuilder); - } else { - argumentNode = requiredArgumentBuilder; - } - argumentBuilder = requiredArgumentBuilder; - argumentBuilder.executes(commandContext -> { - invoke((CommandSender) commandContext.getCommand(), commandContext.getInput().split(" ")); - return 0; - }); - CommandNode commandNode = argumentBuilder.build(); - argumentBuilder.redirect(commandNode);*/ - } - } - - boolean invoke(CommandSender commandSender, String[] args) { - if (args.length < arguments.length + subCommand.length - (varArgType != null ? 1 : 0)) { - return false; - } - if (varArgType == null && args.length > arguments.length + subCommand.length) { - return false; - } - try { - if (!commandSenderPredicate.test(commandSender)) { - return false; - } - Object[] objects = SWCommandUtils.generateArgumentArray(commandSender, arguments, args, varArgType, subCommand); - objects[0] = commandSenderFunction.apply(commandSender); - method.setAccessible(true); - method.invoke(swCommand, objects); - } catch (IllegalAccessException | RuntimeException | InvocationTargetException e) { - throw new SecurityException(e.getMessage(), e); - } catch (CommandParseException e) { - return false; - } - return true; - } - - private ArgumentType getArgumentType(Parameter parameter, Class parameterType, TypeMapper typeMapper) { - ArgumentType argumentType; - if (parameterType == boolean.class || parameterType == Boolean.class) { - 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(); - } - } 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(); - } - } 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(); - } - } 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(); - } + private ArgumentType getArgumentType(Parameter parameter, Class parameterType, SubCommand subCommand) { + ArgumentType argumentType; + if (parameterType == boolean.class || parameterType == Boolean.class) { + 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 = StringArgumentType.string(); - normalTabCompleteNeeded = true; + argumentType = IntegerArgumentType.integer(); } - return argumentType; + } 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(); + } + } 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(); + } + } 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(); + } + } else { + argumentType = StringArgumentType.string(); + subCommand.normalTabCompleteNeeded = true; } + return argumentType; } } diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandNormal.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandNormal.java index 144f867..b325a81 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandNormal.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandNormal.java @@ -24,19 +24,14 @@ 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 static de.steamwar.command.SWCommandUtils.*; - class SWCommandNormal implements SWCommandInterface { private final SWCommand swCommand; @@ -91,7 +86,7 @@ class SWCommandNormal implements SWCommandInterface { 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(new SubCommand(swCommand, method, anno.value(), new HashMap<>())); + commandHelpList.add(createSubCommand(method, anno.value())); }); } for (Method method : methods) { @@ -113,7 +108,7 @@ class SWCommandNormal implements SWCommandInterface { return; } } - commandList.add(new SubCommand(swCommand, method, anno.value(), localTypeMapper)); + commandList.add(createSubCommand(method, anno.value())); }); this.commandList.sort((o1, o2) -> { @@ -129,6 +124,10 @@ class SWCommandNormal implements SWCommandInterface { } } + private de.steamwar.command.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; @@ -169,122 +168,4 @@ class SWCommandNormal implements SWCommandInterface { public void register() { SWCommandUtils.commandMap.register("steamwar", this.command); } - - static class SubCommand { - - private SWCommand swCommand; - private Method method; - String[] subCommand; - TypeMapper[] arguments; - private Predicate commandSenderPredicate; - private Function commandSenderFunction; - Class varArgType = null; - - SubCommand(SWCommand swCommand, Method method, String[] subCommand, Map> localTypeMapper) { - this.swCommand = swCommand; - this.method = method; - - Parameter[] parameters = method.getParameters(); - commandSenderPredicate = sender -> parameters[0].getType().isAssignableFrom(sender.getClass()); - commandSenderFunction = sender -> parameters[0].getType().cast(sender); - this.subCommand = subCommand; - - arguments = new TypeMapper[parameters.length - 1]; - for (int i = 1; i < parameters.length; i++) { - Parameter parameter = parameters[i]; - Class clazz = parameter.getType(); - if (parameter.isVarArgs()) { - clazz = clazz.getComponentType(); - varArgType = clazz; - } - - SWCommand.Mapper mapper = parameter.getAnnotation(SWCommand.Mapper.class); - if (clazz.isEnum() && mapper == null && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) { - Class> enumClass = (Class>) clazz; - List tabCompletes = new ArrayList<>(); - for (Enum enumConstant : enumClass.getEnumConstants()) { - tabCompletes.add(enumConstant.name().toLowerCase()); - } - arguments[i - 1] = SWCommandUtils.createMapper(s -> ENUM_MAPPER.apply(enumClass, s), s -> tabCompletes); - continue; - } - - String name = clazz.getTypeName(); - if (mapper != null) { - name = mapper.value(); - } - arguments[i - 1] = localTypeMapper.containsKey(name) - ? localTypeMapper.get(name) - : MAPPER_FUNCTIONS.getOrDefault(name, ERROR_FUNCTION); - } - } - - boolean invoke(CommandSender commandSender, String[] args) { - if (args.length < arguments.length + subCommand.length - (varArgType != null ? 1 : 0)) { - return false; - } - if (varArgType == null && args.length > arguments.length + subCommand.length) { - return false; - } - try { - if (!commandSenderPredicate.test(commandSender)) { - return false; - } - Object[] objects = SWCommandUtils.generateArgumentArray(commandSender, arguments, args, varArgType, subCommand); - objects[0] = commandSenderFunction.apply(commandSender); - method.setAccessible(true); - method.invoke(swCommand, objects); - } catch (IllegalAccessException | RuntimeException | InvocationTargetException e) { - throw new SecurityException(e.getMessage(), e); - } catch (CommandParseException e) { - return false; - } - return true; - } - - List tabComplete(CommandSender commandSender, String[] args) { - if (varArgType == null && args.length > arguments.length + subCommand.length) { - return null; - } - int index = 0; - List argsList = new LinkedList<>(Arrays.asList(args)); - for (String value : subCommand) { - String s = argsList.remove(0); - if (argsList.isEmpty()) return Collections.singletonList(value); - if (!value.equalsIgnoreCase(s)) return null; - index++; - } - for (TypeMapper argument : arguments) { - String s = argsList.remove(0); - if (argsList.isEmpty()) { - return argument.tabCompletes(commandSender, Arrays.copyOf(args, args.length - 1), s); - } - try { - if (argument.map(commandSender, Arrays.copyOf(args, index), s) == null) { - return null; - } - } catch (Exception e) { - return null; - } - index++; - } - if (varArgType != null && !argsList.isEmpty()) { - while (!argsList.isEmpty()) { - String s = argsList.remove(0); - if (argsList.isEmpty()) { - return arguments[arguments.length - 1].tabCompletes(commandSender, Arrays.copyOf(args, args.length - 1), s); - } - try { - if (arguments[arguments.length - 1].map(commandSender, Arrays.copyOf(args, index), s) == null) { - return null; - } - } catch (Exception e) { - return null; - } - index++; - } - } - return null; - } - } } diff --git a/SpigotCore_Main/src/de/steamwar/command/SubCommand.java b/SpigotCore_Main/src/de/steamwar/command/SubCommand.java new file mode 100644 index 0000000..2a2c267 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/SubCommand.java @@ -0,0 +1,157 @@ +/* + * 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; + +import com.mojang.brigadier.builder.ArgumentBuilder; +import org.bukkit.command.CommandSender; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +import static de.steamwar.command.SWCommandUtils.*; + +class SubCommand { + + ArgumentBuilder argumentNode = null; + boolean normalTabCompleteNeeded = false; + + SWCommand swCommand; + Parameter[] parameters; + Method method; + String[] subCommand; + TypeMapper[] arguments; + Predicate commandSenderPredicate; + Function commandSenderFunction; + Class varArgType = null; + + public SubCommand(SWCommand swCommand, Method method, String[] subCommand, Map> localTypeMapper, Consumer consumer) { + this.swCommand = swCommand; + this.method = method; + + parameters = method.getParameters(); + commandSenderPredicate = sender -> parameters[0].getType().isAssignableFrom(sender.getClass()); + commandSenderFunction = sender -> parameters[0].getType().cast(sender); + this.subCommand = subCommand; + + arguments = new TypeMapper[parameters.length - 1]; + for (int i = 1; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + Class clazz = parameter.getType(); + if (parameter.isVarArgs()) { + clazz = clazz.getComponentType(); + varArgType = clazz; + } + + SWCommand.Mapper mapper = parameter.getAnnotation(SWCommand.Mapper.class); + if (clazz.isEnum() && mapper == null && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) { + Class> enumClass = (Class>) clazz; + List tabCompletes = new ArrayList<>(); + for (Enum enumConstant : enumClass.getEnumConstants()) { + tabCompletes.add(enumConstant.name().toLowerCase()); + } + arguments[i - 1] = SWCommandUtils.createMapper(s -> ENUM_MAPPER.apply(enumClass, s), s -> tabCompletes); + continue; + } + + String name = clazz.getTypeName(); + if (mapper != null) { + name = mapper.value(); + } + arguments[i - 1] = localTypeMapper.containsKey(name) + ? localTypeMapper.get(name) + : MAPPER_FUNCTIONS.getOrDefault(name, ERROR_FUNCTION); + } + + consumer.accept(this); + } + + boolean invoke(CommandSender commandSender, String[] args) { + if (args.length < arguments.length + subCommand.length - (varArgType != null ? 1 : 0)) { + return false; + } + if (varArgType == null && args.length > arguments.length + subCommand.length) { + return false; + } + try { + if (!commandSenderPredicate.test(commandSender)) { + return false; + } + Object[] objects = SWCommandUtils.generateArgumentArray(commandSender, arguments, args, varArgType, subCommand); + objects[0] = commandSenderFunction.apply(commandSender); + method.setAccessible(true); + method.invoke(swCommand, objects); + } catch (IllegalAccessException | RuntimeException | InvocationTargetException e) { + throw new SecurityException(e.getMessage(), e); + } catch (CommandParseException e) { + return false; + } + return true; + } + + List tabComplete(CommandSender commandSender, String[] args) { + if (varArgType == null && args.length > arguments.length + subCommand.length) { + return null; + } + int index = 0; + List argsList = new LinkedList<>(Arrays.asList(args)); + for (String value : subCommand) { + String s = argsList.remove(0); + if (argsList.isEmpty()) return Collections.singletonList(value); + if (!value.equalsIgnoreCase(s)) return null; + index++; + } + for (TypeMapper argument : arguments) { + String s = argsList.remove(0); + if (argsList.isEmpty()) { + return argument.tabCompletes(commandSender, Arrays.copyOf(args, args.length - 1), s); + } + try { + if (argument.map(commandSender, Arrays.copyOf(args, index), s) == null) { + return null; + } + } catch (Exception e) { + return null; + } + index++; + } + if (varArgType != null && !argsList.isEmpty()) { + while (!argsList.isEmpty()) { + String s = argsList.remove(0); + if (argsList.isEmpty()) { + return arguments[arguments.length - 1].tabCompletes(commandSender, Arrays.copyOf(args, args.length - 1), s); + } + try { + if (arguments[arguments.length - 1].map(commandSender, Arrays.copyOf(args, index), s) == null) { + return null; + } + } catch (Exception e) { + return null; + } + index++; + } + } + return null; + } +} -- 2.39.2 From 7e02212680ba66c5ace7030497ad84add2dbc7ac Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 8 Jul 2021 22:38:19 +0200 Subject: [PATCH 04/12] CMDoS because Brigadier --- SpigotCore_Main/pom.xml | 8 -- .../src/de/steamwar/command/SWCommand.java | 5 + .../steamwar/command/SWCommandBrigadier.java | 131 ++++++------------ .../steamwar/command/SWCommandInterface.java | 25 ---- .../de/steamwar/command/SWCommandNormal.java | 93 +------------ .../de/steamwar/command/SWCommandUtils.java | 83 +++++++++++ 6 files changed, 134 insertions(+), 211 deletions(-) delete mode 100644 SpigotCore_Main/src/de/steamwar/command/SWCommandInterface.java 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); + } + }); + } } -- 2.39.2 From 66ae2a837c9d606388a1b2ba871899c8e590c6f1 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 8 Jul 2021 22:46:32 +0200 Subject: [PATCH 05/12] CMDoS because Brigadier --- .../steamwar/command/SWCommandBrigadier.java | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java index 9e31c8b..e928f15 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java @@ -47,6 +47,29 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { protected SWCommandBrigadier(SWCommand swCommand, String command, String... aliases) { this.swCommand = swCommand; + 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()); + } + }; + unregister(); + register(); + SWCommandUtils.createList(swCommand, commandList, commandHelpList, localTypeMapper, this::createSubCommand); Stream.of(commandList, commandHelpList).flatMap(List::stream).forEach(subCommand -> { register(command, subCommand, command, aliases); @@ -82,10 +105,7 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { } argumentBuilder = requiredArgumentBuilder; if (i == current.arguments.length - 1) { - argumentBuilder.executes(commandContext -> { - current.invoke((CommandSender) commandContext.getCommand(), commandContext.getInput().split(" ")); - return 0; - }); + argumentBuilder.executes(commandContext -> 1); } } if (current.varArgType != null) { @@ -117,28 +137,6 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { if (subCommand.argumentNode == null) { return; } - 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); } -- 2.39.2 From da5fca4078a44568f06f0544844f36984043efce Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 8 Jul 2021 23:34:09 +0200 Subject: [PATCH 06/12] CMDoS because Brigadier --- .../steamwar/command/SWCommandBrigadier.java | 42 ++++++++++++------- .../src/de/steamwar/command/SubCommand.java | 2 +- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java index e928f15..5d30862 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java @@ -23,6 +23,7 @@ import com.mojang.brigadier.arguments.*; import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import net.minecraft.server.v1_15_R1.CommandListenerWrapper; import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; @@ -67,16 +68,15 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { .collect(Collectors.toList()); } }; - unregister(); - register(); - 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); + register(this.command.getName(), subCommand); + for (String s : this.command.getAliases()) { + register(s, subCommand); } }); + // unregister(); + // register(); } private SubCommand createSubCommand(Method method, String[] strings) { @@ -96,7 +96,6 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { Parameter parameter = current.parameters[i + 1]; Class parameterType = parameter.getType(); ArgumentType argumentType = getArgumentType(parameter, parameterType, current); - RequiredArgumentBuilder requiredArgumentBuilder = RequiredArgumentBuilder.argument(parameter.getName(), argumentType); if (argumentBuilder != null) { argumentBuilder.then(requiredArgumentBuilder); @@ -104,9 +103,6 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { current.argumentNode = requiredArgumentBuilder; } argumentBuilder = requiredArgumentBuilder; - if (i == current.arguments.length - 1) { - argumentBuilder.executes(commandContext -> 1); - } } if (current.varArgType != null) { // TODO: UNSUPORTED @@ -129,14 +125,29 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { CommandNode commandNode = argumentBuilder.build(); argumentBuilder.redirect(commandNode);*/ } + current.argumentNodeEnd = argumentBuilder; }); } - private void register(String name, SubCommand subCommand, String command, String... aliases) { + private com.mojang.brigadier.Command executes(SubCommand current) { + return commandContext -> { + CommandSender commandSender = ((CommandListenerWrapper) commandContext.getSource()).getBukkitSender(); + List stringList = Arrays.stream(commandContext.getInput().split(" ")).collect(Collectors.toList()); + stringList.remove(0); + String[] args = stringList.toArray(new String[0]); + current.invoke(commandSender, args); + return 0; + }; + } + + private void register(String name, SubCommand subCommand) { LiteralArgumentBuilder literalArgumentBuilder = LiteralArgumentBuilder.literal(name); if (subCommand.argumentNode == null) { + literalArgumentBuilder.executes(executes(subCommand)); + SWCommand.dispatcher.register(literalArgumentBuilder); return; } + subCommand.argumentNodeEnd.executes(executes(subCommand)); literalArgumentBuilder.then(subCommand.argumentNode); SWCommand.dispatcher.register(literalArgumentBuilder); } @@ -174,7 +185,6 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { @Override public void unregister() { - if (command == null) return; SWCommandUtils.knownCommandMap.remove(command.getName()); command.getAliases().forEach(SWCommandUtils.knownCommandMap::remove); command.unregister(SWCommandUtils.commandMap); @@ -182,8 +192,13 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { @Override public void register() { - if (command == null) return; SWCommandUtils.commandMap.register("steamwar", this.command); + Stream.of(commandList, commandHelpList).flatMap(List::stream).forEach(subCommand -> { + register(command.getName(), subCommand); + for (String s : command.getAliases()) { + register(s, subCommand); + } + }); } private ArgumentType getArgumentType(Parameter parameter, Class parameterType, SubCommand subCommand) { @@ -208,7 +223,6 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { else argumentType = DoubleArgumentType.doubleArg(); } else { argumentType = StringArgumentType.string(); - subCommand.normalTabCompleteNeeded = true; } return argumentType; } diff --git a/SpigotCore_Main/src/de/steamwar/command/SubCommand.java b/SpigotCore_Main/src/de/steamwar/command/SubCommand.java index 2a2c267..4119686 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SubCommand.java +++ b/SpigotCore_Main/src/de/steamwar/command/SubCommand.java @@ -35,7 +35,7 @@ import static de.steamwar.command.SWCommandUtils.*; class SubCommand { ArgumentBuilder argumentNode = null; - boolean normalTabCompleteNeeded = false; + ArgumentBuilder argumentNodeEnd = null; SWCommand swCommand; Parameter[] parameters; -- 2.39.2 From 834969959a88cecc5dee4a4824034a744ab53018 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 8 Jul 2021 23:34:41 +0200 Subject: [PATCH 07/12] CMDoS because Brigadier --- .../steamwar/command/SWCommandBrigadier.java | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java index 5d30862..f2f9b5f 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java @@ -152,37 +152,6 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { SWCommand.dispatcher.register(literalArgumentBuilder); } - 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); - } - }); - } - @Override public void unregister() { SWCommandUtils.knownCommandMap.remove(command.getName()); -- 2.39.2 From bc966247a8820a3e41a1b22d131a58d280096e55 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Thu, 8 Jul 2021 23:43:14 +0200 Subject: [PATCH 08/12] CMDoS because Brigadier --- .../steamwar/command/SWCommandBrigadier.java | 49 +++++-------------- 1 file changed, 13 insertions(+), 36 deletions(-) diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java index f2f9b5f..3c4e879 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java @@ -24,54 +24,33 @@ import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import net.minecraft.server.v1_15_R1.CommandListenerWrapper; -import org.bukkit.Bukkit; -import org.bukkit.command.Command; import org.bukkit.command.CommandSender; -import java.lang.annotation.Annotation; import java.lang.reflect.Method; 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; import java.util.stream.Stream; class SWCommandBrigadier implements SWCommand.SWCommandInterface { + private final String command; + private final String[] aliases; + 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; - 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; - } + this.command = command; + this.aliases = aliases; - @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()); - } - }; + this.swCommand = swCommand; SWCommandUtils.createList(swCommand, commandList, commandHelpList, localTypeMapper, this::createSubCommand); Stream.of(commandList, commandHelpList).flatMap(List::stream).forEach(subCommand -> { - register(this.command.getName(), subCommand); - for (String s : this.command.getAliases()) { + register(command, subCommand); + for (String s : aliases) { register(s, subCommand); } }); @@ -105,7 +84,7 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { argumentBuilder = requiredArgumentBuilder; } if (current.varArgType != null) { - // TODO: UNSUPORTED + // TODO: UNSUPPORTED /*Parameter parameter = parameters[parameters.length - 1]; Class parameterType = parameter.getType(); TypeMapper typeMapper = arguments[arguments.length - 1]; @@ -154,17 +133,15 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { @Override public void unregister() { - SWCommandUtils.knownCommandMap.remove(command.getName()); - command.getAliases().forEach(SWCommandUtils.knownCommandMap::remove); - command.unregister(SWCommandUtils.commandMap); + SWCommandUtils.knownCommandMap.remove(command); + Arrays.stream(aliases).forEach(SWCommandUtils.knownCommandMap::remove); } @Override public void register() { - SWCommandUtils.commandMap.register("steamwar", this.command); Stream.of(commandList, commandHelpList).flatMap(List::stream).forEach(subCommand -> { - register(command.getName(), subCommand); - for (String s : command.getAliases()) { + register(command, subCommand); + for (String s : aliases) { register(s, subCommand); } }); -- 2.39.2 From 5240ca3bf45f90b78fa78e07c33941787499ef98 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Fri, 9 Jul 2021 13:38:24 +0200 Subject: [PATCH 09/12] Add CommandNode Start complete rework of CommandSystem --- .../src/de/steamwar/command/CommandNode.java | 93 ++++++++++++++++ .../steamwar/command/SWCommandBrigadier.java | 102 ++++++++++++------ .../src/de/steamwar/command/SubCommand.java | 4 +- 3 files changed, 165 insertions(+), 34 deletions(-) create mode 100644 SpigotCore_Main/src/de/steamwar/command/CommandNode.java diff --git a/SpigotCore_Main/src/de/steamwar/command/CommandNode.java b/SpigotCore_Main/src/de/steamwar/command/CommandNode.java new file mode 100644 index 0000000..36b98c5 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/CommandNode.java @@ -0,0 +1,93 @@ +/* + * 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; + +import org.bukkit.command.CommandSender; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +class CommandNode { + + private final TypeMapper typeMapper; + private boolean varArg = false; + private List commandNodeList = new ArrayList<>(); + private Method executor; + + CommandNode(TypeMapper typeMapper) { + this.typeMapper = typeMapper; + } + + public void addNode(CommandNode commandNode) { + commandNodeList.add(commandNode); + this.varArg = false; + } + + public void setExecutor(Method method) { + this.executor = executor; + } + + public void setVarArg(boolean varArg) { + if (commandNodeList.isEmpty()) { + this.varArg = varArg; + } else { + this.varArg = false; + } + } + + public List tabComplete(CommandSender commandSender, int index, String[] args) { + try { + if (index == args.length - 1) { + return typeMapper.tabCompletes(commandSender, Arrays.copyOf(args, index), args[args.length - 1]); + } + if (typeMapper.map(commandSender, Arrays.copyOf(args, index), args[index]) == null) { + return Collections.emptyList(); + } + if (varArg) { + return tabComplete(commandSender, index + 1, args); + } else { + return commandNodeList.stream() + .map(commandNode -> commandNode.tabComplete(commandSender, index + 1, args)) + .flatMap(List::stream) + .collect(Collectors.toList()); + } + } catch (Exception e) { + return Collections.emptyList(); + } + } + + public boolean execute(CommandSender commandSender, int index, String[] args) { + if (varArg) { + for (int i = index; i < args.length; i++) { + String[] previousArgs = Arrays.copyOf(args, i); + typeMapper.map(commandSender, previousArgs, args[i]); + } + } else { + + } + // if (args.length) + return false; + } + +} diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java index 3c4e879..4f7d386 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandBrigadier.java @@ -23,6 +23,7 @@ import com.mojang.brigadier.arguments.*; import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.tree.CommandNode; import net.minecraft.server.v1_15_R1.CommandListenerWrapper; import org.bukkit.command.CommandSender; @@ -60,51 +61,88 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { private SubCommand createSubCommand(Method method, String[] strings) { return new SubCommand(swCommand, method, strings, localTypeMapper, current -> { - ArgumentBuilder argumentBuilder = null; + List argumentBuilders = new ArrayList<>(); + for (String s : current.subCommand) { LiteralArgumentBuilder literalArgumentBuilder = LiteralArgumentBuilder.literal(s); - if (argumentBuilder != null) { - argumentBuilder.then(literalArgumentBuilder); - } else { - current.argumentNode = literalArgumentBuilder; + List currentBuilders = new ArrayList<>(); + argumentBuilders.forEach(currrentBuilder -> { + currentBuilders.add(currrentBuilder.then(literalArgumentBuilder)); + }); + if (argumentBuilders.isEmpty()) { + currentBuilders.add(literalArgumentBuilder); + if (current.argumentNode.isEmpty()) { + current.argumentNode.add(literalArgumentBuilder); + } } - argumentBuilder = literalArgumentBuilder; + argumentBuilders = currentBuilders; } for (int i = 0; i < current.arguments.length - (current.varArgType != null ? 1 : 0); i++) { Parameter parameter = current.parameters[i + 1]; Class parameterType = parameter.getType(); - ArgumentType argumentType = getArgumentType(parameter, parameterType, current); - RequiredArgumentBuilder requiredArgumentBuilder = RequiredArgumentBuilder.argument(parameter.getName(), argumentType); - if (argumentBuilder != null) { - argumentBuilder.then(requiredArgumentBuilder); + ArgumentType argumentType = getArgumentType(parameter, parameterType); + List newBuilders = new ArrayList<>(); + if (argumentType == null) { + try { + current.arguments[i].tabCompletes(null, null, "").stream() + .map(LiteralArgumentBuilder::literal) + .forEach(newBuilders::add); + } catch (Exception e) { + newBuilders.add(RequiredArgumentBuilder.argument(parameter.getName(), StringArgumentType.string())); + } } else { - current.argumentNode = requiredArgumentBuilder; + newBuilders.add(RequiredArgumentBuilder.argument(parameter.getName(), argumentType)); } - argumentBuilder = requiredArgumentBuilder; + + List currentBuilders = new ArrayList<>(); + argumentBuilders.forEach(currrentBuilder -> { + newBuilders.forEach(argumentBuilder -> { + currentBuilders.add(currrentBuilder.then(argumentBuilder)); + }); + }); + if (argumentBuilders.isEmpty()) { + currentBuilders.addAll(newBuilders); + if (current.argumentNode.isEmpty()) { + current.argumentNode.addAll(newBuilders); + } + } + argumentBuilders = currentBuilders; } if (current.varArgType != null) { - // TODO: UNSUPPORTED - /*Parameter parameter = parameters[parameters.length - 1]; + Parameter parameter = current.parameters[current.parameters.length - 1]; Class parameterType = parameter.getType(); - TypeMapper typeMapper = arguments[arguments.length - 1]; - ArgumentType argumentType = getArgumentType(parameter, parameterType, typeMapper); - - RequiredArgumentBuilder requiredArgumentBuilder = RequiredArgumentBuilder.argument(parameter.getName(), argumentType); - if (argumentBuilder != null) { - argumentBuilder.then(requiredArgumentBuilder); + ArgumentType argumentType = getArgumentType(parameter, parameterType); + List newBuilders = new ArrayList<>(); + if (argumentType == null) { + try { + current.arguments[current.arguments.length - 1].tabCompletes(null, null, "").stream() + .map(LiteralArgumentBuilder::literal) + .forEach(newBuilders::add); + } catch (Exception e) { + newBuilders.add(RequiredArgumentBuilder.argument(parameter.getName(), StringArgumentType.string())); + } } else { - argumentNode = requiredArgumentBuilder; + newBuilders.add(RequiredArgumentBuilder.argument(parameter.getName(), argumentType)); } - argumentBuilder = requiredArgumentBuilder; - argumentBuilder.executes(commandContext -> { - invoke((CommandSender) commandContext.getCommand(), commandContext.getInput().split(" ")); - return 0; + + // TODO: VarArgs + List currentBuilders = new ArrayList<>(); + argumentBuilders.forEach(currrentBuilder -> { + newBuilders.forEach(argumentBuilder -> { + currentBuilders.add(currrentBuilder.then(argumentBuilder)); + }); }); - CommandNode commandNode = argumentBuilder.build(); - argumentBuilder.redirect(commandNode);*/ + if (argumentBuilders.isEmpty()) { + currentBuilders.addAll(newBuilders); + if (current.argumentNode.isEmpty()) { + current.argumentNode.addAll(newBuilders); + } + } + currentBuilders.forEach(argumentBuilder -> argumentBuilder.executes(executes(current))); + argumentBuilders = currentBuilders; } - current.argumentNodeEnd = argumentBuilder; + current.argumentNodeEnd.addAll(argumentBuilders); }); } @@ -126,8 +164,8 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { SWCommand.dispatcher.register(literalArgumentBuilder); return; } - subCommand.argumentNodeEnd.executes(executes(subCommand)); - literalArgumentBuilder.then(subCommand.argumentNode); + subCommand.argumentNodeEnd.forEach(argumentBuilder -> argumentBuilder.executes(executes(subCommand))); + subCommand.argumentNode.forEach(literalArgumentBuilder::then); SWCommand.dispatcher.register(literalArgumentBuilder); } @@ -147,7 +185,7 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { }); } - private ArgumentType getArgumentType(Parameter parameter, Class parameterType, SubCommand subCommand) { + private ArgumentType getArgumentType(Parameter parameter, Class parameterType) { ArgumentType argumentType; if (parameterType == boolean.class || parameterType == Boolean.class) { argumentType = BoolArgumentType.bool(); @@ -168,7 +206,7 @@ class SWCommandBrigadier implements SWCommand.SWCommandInterface { if (doubleRange != null) argumentType = DoubleArgumentType.doubleArg(doubleRange.min(), doubleRange.max()); else argumentType = DoubleArgumentType.doubleArg(); } else { - argumentType = StringArgumentType.string(); + return null; } return argumentType; } diff --git a/SpigotCore_Main/src/de/steamwar/command/SubCommand.java b/SpigotCore_Main/src/de/steamwar/command/SubCommand.java index 4119686..6dffc9b 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SubCommand.java +++ b/SpigotCore_Main/src/de/steamwar/command/SubCommand.java @@ -34,8 +34,8 @@ import static de.steamwar.command.SWCommandUtils.*; class SubCommand { - ArgumentBuilder argumentNode = null; - ArgumentBuilder argumentNodeEnd = null; + List argumentNode = new ArrayList<>(); + List argumentNodeEnd = new ArrayList<>(); SWCommand swCommand; Parameter[] parameters; -- 2.39.2 From deb7f710907f562549d7a3d16a87e8b8b06c004c Mon Sep 17 00:00:00 2001 From: yoyosource Date: Fri, 9 Jul 2021 14:31:24 +0200 Subject: [PATCH 10/12] Finish CommandNode --- .../src/de/steamwar/command/CommandNode.java | 67 ++++++++++++++++--- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/SpigotCore_Main/src/de/steamwar/command/CommandNode.java b/SpigotCore_Main/src/de/steamwar/command/CommandNode.java index 36b98c5..4778bbc 100644 --- a/SpigotCore_Main/src/de/steamwar/command/CommandNode.java +++ b/SpigotCore_Main/src/de/steamwar/command/CommandNode.java @@ -21,21 +21,30 @@ package de.steamwar.command; import org.bukkit.command.CommandSender; +import java.lang.reflect.Array; import java.lang.reflect.Method; +import java.lang.reflect.Parameter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.function.Function; +import java.util.function.Predicate; import java.util.stream.Collectors; class CommandNode { + private final SWCommand swCommand; private final TypeMapper typeMapper; private boolean varArg = false; private List commandNodeList = new ArrayList<>(); private Method executor; - CommandNode(TypeMapper typeMapper) { + private Predicate commandSenderPredicate; + private Function commandSenderObjectFunction; + + CommandNode(SWCommand swCommand, TypeMapper typeMapper) { + this.swCommand = swCommand; this.typeMapper = typeMapper; } @@ -44,8 +53,13 @@ class CommandNode { this.varArg = false; } - public void setExecutor(Method method) { + public void setExecutor(Method executor) { + if (this.executor != null) return; this.executor = executor; + + Parameter parameter = executor.getParameters()[0]; + commandSenderPredicate = parameter.getType()::isInstance; + commandSenderObjectFunction = parameter.getType()::cast; } public void setVarArg(boolean varArg) { @@ -77,17 +91,48 @@ class CommandNode { } } - public boolean execute(CommandSender commandSender, int index, String[] args) { - if (varArg) { - for (int i = index; i < args.length; i++) { - String[] previousArgs = Arrays.copyOf(args, i); - typeMapper.map(commandSender, previousArgs, args[i]); + public boolean execute(CommandSender commandSender, int index, String[] args, List mappedObjects) { + try { + Object o; + if (varArg) { + o = Array.newInstance(Object.class, args.length - index); + for (int i = 0; i < Array.getLength(o); i++) { + Object current = typeMapper.map(commandSender, Arrays.copyOf(args, index + i), args[index + i]); + if (current == null) return false; + Array.set(o, i, current); + } + } else { + o = typeMapper.map(commandSender, Arrays.copyOf(args, index), args[index]); + if (o == null) return false; } - } else { - + mappedObjects.add(o); + } catch (Exception e) { + return false; } - // if (args.length) - return false; + + if (index == args.length - 1) { + if (executor == null) { + return false; + } + if (!commandSenderPredicate.test(commandSender)) { + return false; + } + + List finalMappedObjects = new ArrayList<>(mappedObjects); + finalMappedObjects.add(0, commandSenderObjectFunction.apply(commandSender)); + + Object[] objects = finalMappedObjects.toArray(new Object[0]); + try { + executor.invoke(swCommand, objects); + return true; + } catch (Exception e) { + throw new SecurityException(e.getMessage(), e); + } + } + + return commandNodeList.stream() + .map(commandNode -> commandNode.execute(commandSender, index + 1, args, new ArrayList<>(mappedObjects))) + .findFirst().orElse(false); } } -- 2.39.2 From 9760612d3eb8659fd2e723c777437cc2b157779e Mon Sep 17 00:00:00 2001 From: yoyosource Date: Fri, 9 Jul 2021 17:46:39 +0200 Subject: [PATCH 11/12] Update CommandNode --- SpigotCore_Main/src/de/steamwar/command/CommandNode.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/SpigotCore_Main/src/de/steamwar/command/CommandNode.java b/SpigotCore_Main/src/de/steamwar/command/CommandNode.java index 4778bbc..359da76 100644 --- a/SpigotCore_Main/src/de/steamwar/command/CommandNode.java +++ b/SpigotCore_Main/src/de/steamwar/command/CommandNode.java @@ -135,4 +135,7 @@ class CommandNode { .findFirst().orElse(false); } + public List suggest(CommandSender commandSender, int index, String[] args, List mappedObjects) { + return null; + } } -- 2.39.2 From ba14ccb68c8209ed31aa628041aa92a00de2f897 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Sat, 10 Jul 2021 11:27:50 +0200 Subject: [PATCH 12/12] Complete CommandNode --- .../src/de/steamwar/command/CommandNode.java | 56 ++++++++++++------- .../src/de/steamwar/command/CommandPart.java | 31 ++++++++++ 2 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 SpigotCore_Main/src/de/steamwar/command/CommandPart.java diff --git a/SpigotCore_Main/src/de/steamwar/command/CommandNode.java b/SpigotCore_Main/src/de/steamwar/command/CommandNode.java index 359da76..f8dff55 100644 --- a/SpigotCore_Main/src/de/steamwar/command/CommandNode.java +++ b/SpigotCore_Main/src/de/steamwar/command/CommandNode.java @@ -38,7 +38,9 @@ class CommandNode { private final TypeMapper typeMapper; private boolean varArg = false; private List commandNodeList = new ArrayList<>(); + private Method executor; + private List helpMessages = new ArrayList<>(); private Predicate commandSenderPredicate; private Function commandSenderObjectFunction; @@ -62,6 +64,10 @@ class CommandNode { commandSenderObjectFunction = parameter.getType()::cast; } + public void addHelpMessage(String helpMessage) { + this.helpMessages.add(helpMessage); + } + public void setVarArg(boolean varArg) { if (commandNodeList.isEmpty()) { this.varArg = varArg; @@ -71,24 +77,9 @@ class CommandNode { } public List tabComplete(CommandSender commandSender, int index, String[] args) { - try { - if (index == args.length - 1) { - return typeMapper.tabCompletes(commandSender, Arrays.copyOf(args, index), args[args.length - 1]); - } - if (typeMapper.map(commandSender, Arrays.copyOf(args, index), args[index]) == null) { - return Collections.emptyList(); - } - if (varArg) { - return tabComplete(commandSender, index + 1, args); - } else { - return commandNodeList.stream() - .map(commandNode -> commandNode.tabComplete(commandSender, index + 1, args)) - .flatMap(List::stream) - .collect(Collectors.toList()); - } - } catch (Exception e) { - return Collections.emptyList(); - } + return internalTabCompleteAndSuggest((commandSender1, integer, strings) -> { + return typeMapper.tabCompletes(commandSender1, Arrays.copyOf(strings, integer), strings[strings.length - 1]); + }, commandSender, index, args); } public boolean execute(CommandSender commandSender, int index, String[] args, List mappedObjects) { @@ -135,7 +126,32 @@ class CommandNode { .findFirst().orElse(false); } - public List suggest(CommandSender commandSender, int index, String[] args, List mappedObjects) { - return null; + public List suggest(CommandSender commandSender, int index, String[] args) { + return internalTabCompleteAndSuggest((commandSender1, integer, strings) -> helpMessages, commandSender, index, args); + } + + private List internalTabCompleteAndSuggest(TriFunction> returnFunction, CommandSender commandSender, int index, String[] args) { + try { + if (index == args.length - 1) { + return returnFunction.apply(commandSender, index, args); + } + if (typeMapper.map(commandSender, Arrays.copyOf(args, index), args[index]) == null) { + return Collections.emptyList(); + } + if (varArg) { + return internalTabCompleteAndSuggest(returnFunction, commandSender, index + 1, args); + } else { + return commandNodeList.stream() + .map(commandNode -> commandNode.internalTabCompleteAndSuggest(returnFunction, commandSender, index + 1, args)) + .flatMap(List::stream) + .collect(Collectors.toList()); + } + } catch (Exception e) { + return Collections.emptyList(); + } + } + + private interface TriFunction { + O apply(I1 i1, I2 i2, I3 i3); } } diff --git a/SpigotCore_Main/src/de/steamwar/command/CommandPart.java b/SpigotCore_Main/src/de/steamwar/command/CommandPart.java new file mode 100644 index 0000000..27c5981 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/CommandPart.java @@ -0,0 +1,31 @@ +/* + * 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; + +import java.lang.reflect.Method; +import java.util.Map; + +public class CommandPart { + + public CommandPart(SWCommand swCommand, Method method, String[] subCommand, Map> localTypeMapper) { + + } + +} -- 2.39.2