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 extends Object> 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
+
+
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 extends Object> 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;
- }
-}