SteamWar/SpigotCore
Archiviert
13
0

CommandFramework3 #94

Manuell gemergt
Zeanon hat 71 Commits von CommandFramework3 nach master 2021-03-30 21:15:40 +02:00 zusammengeführt
6 geänderte Dateien mit 702 neuen und 0 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,82 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.acommand;
import de.steamwar.command.SWCommand;
import de.steamwar.command.TypeMapper;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class TestCommand extends SWCommand {
public TestCommand() {
// Register this command as 'test'
super("test");
}
// One Help Command, the first Parameter should be some kind of CommandSender
// The second argument can only be a varArgs string of what arguments were tried to map
@Register(help = true)
public void testHelp(Player player, String... args) {
player.sendMessage("This is your help message");
}
// One Command, the first Parameter should be some kind of CommandSender
@Register
public void test(Player player) {
}
// Another Command, subCommands can be implemented with the Register Annotation,
// you can use custom Mappers by putting a Mapper Annotation on a Parameter
@Register({"two"})
public void testTwo(Player player, int i, @Mapper("solidMaterial") Material material) {
Review

Das das ganze mit Strings funktioniert, gefällt mir immer noch nicht (und dir wsl. auch nicht). Da anscheinend keine Interfaces, sondern nur konkrete Instanzen einer Klasse verwendet werden können: Mach doch eine Klasse TypeMapper, die als Parameter eine Funktion String -> Objekt nimmt. Dann ist das eine klare Klasse. Ggf. ist das dann erstmal ein Object (wenn Templating nicht geht) ansonsten spricht aber meines Wissens nach nix dagegen, oder?

Das das ganze mit Strings funktioniert, gefällt mir immer noch nicht (und dir wsl. auch nicht). Da anscheinend keine Interfaces, sondern nur konkrete Instanzen einer Klasse verwendet werden können: Mach doch eine Klasse TypeMapper, die als Parameter eine Funktion String -> Objekt nimmt. Dann ist das eine klare Klasse. Ggf. ist das dann erstmal ein Object (wenn Templating nicht geht) ansonsten spricht aber meines Wissens nach nix dagegen, oder?
Review

Ich verstehe dein Vorschlag nicht ganz. Du willst, dass ich in der Annotation eine Object angebe, welches dann eine Function<String, Object> beinhaltet. Wie soll das das Mapper zeug lösen. Kannst du versuchen das nochmal etwas genauer zu beschreiben?

Ich verstehe dein Vorschlag nicht ganz. Du willst, dass ich in der Annotation eine Object angebe, welches dann eine Function\<String, Object> beinhaltet. Wie soll das das Mapper zeug lösen. Kannst du versuchen das nochmal etwas genauer zu beschreiben?
Review

image

Das hier spricht dagegen (Allowed Types in Annotation):

  • primitive
  • String
  • an Enum
  • another Annotation
  • Class
  • an Array of the above

Multidimensional arrays are forbidden. Arrays of type Class are forbidden.

Hier warum letzteres verboten ist:
image

'Constant Expressions' ist das Stichwort

![image](/devlabs/attachments/3c90a023-f8d5-4ba7-827e-473af354ade4) Das hier spricht dagegen (Allowed Types in Annotation): - primitive - String - an Enum - another Annotation - Class - an Array of the above Multidimensional arrays are forbidden. Arrays of type Class are forbidden. Hier warum letzteres verboten ist: ![image](/devlabs/attachments/2081e223-2da1-4f23-96ca-2cd5b0101bd7) 'Constant Expressions' ist das Stichwort
}
// Add Custom Mapper when this command is registered, all Mapper of you class will be
// created first and than the Commands. Do not use this mapper outside your class, as
// it can only create failures. Use on your own risk. Definition order should be considered.
@Mapper("solidMaterial")
public TypeMapper<Material> materialTypeMapper() {
List<String> tabCompletes = Arrays.stream(Material.values())
.filter(Material::isSolid)
.map(Material::name)
.map(String::toLowerCase)
.collect(Collectors.toList());
return new TypeMapper<Material>() {
@Override
public Material map(String[] previous, String s) {
return Material.valueOf(s.toUpperCase());
}
@Override
public List<String> tabCompletes(CommandSender commandSender, String[] previous, String s) {
return tabCompletes;
}
};
}
}

Datei anzeigen

@ -0,0 +1,42 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
public class CommandParseException extends Exception {
public CommandParseException() {
}
public CommandParseException(String message) {
super(message);
}
public CommandParseException(String message, Throwable cause) {
super(message, cause);
}
public CommandParseException(Throwable cause) {
super(cause);
}
public CommandParseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

Datei anzeigen

@ -0,0 +1,224 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
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;
public abstract class SWCommand {
private final Command command;
private final List<SubCommand> commandSet = new ArrayList<>();
private final List<SubCommand> commandHelpSet = new ArrayList<>();
private final Map<String, TypeMapper<?>> localTypeMapper = new HashMap<>();
protected SWCommand(String command) {
this(command, 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) {
for (SubCommand subCommand : commandSet) {
if (subCommand.invoke(sender, args)) {
return false;
}
}
for (SubCommand subCommand : commandHelpSet) {
if (subCommand.invoke(sender, args)) {
return false;
}
}
return false;
}
@Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
List<String> strings = new ArrayList<>();
for (SubCommand subCommand : commandSet) {
List<String> tabCompletes = subCommand.tabComplete(sender, args);
if (tabCompletes != null) {
strings.addAll(tabCompletes);
}
}
strings = new ArrayList<>(strings);
for (int i = strings.size() - 1; i >= 0; i--) {
if (!strings.get(i).toLowerCase().startsWith(args[args.length - 1].toLowerCase())) {
strings.remove(i);
}
}
return strings;
}
};
register();
for (Method method : getClass().getDeclaredMethods()) {
addMapper(Mapper.class, method, i -> i == 0, false, TypeMapper.class, (anno, typeMapper) -> {
if (anno.local()) {
localTypeMapper.put(anno.value(), typeMapper);
} else {
SWCommandUtils.addMapper(anno.value(), typeMapper);
}
});
addMapper(ClassMapper.class, method, i -> i == 0, false, TypeMapper.class, (anno, typeMapper) -> {
if (anno.local()) {
localTypeMapper.put(anno.value().getTypeName(), typeMapper);
} else {
SWCommandUtils.addMapper(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;
}
commandHelpSet.add(new SubCommand(this, method, anno.value()));
});
}
for (Method method : getClass().getDeclaredMethods()) {
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 = clazz.getTypeName();
if (mapper != null) {
name = mapper.value();
}
if (!SWCommandUtils.MAPPER_FUNCTIONS.containsKey(name)) {
Bukkit.getLogger().log(Level.WARNING, "The parameter '" + parameter.toString() + "' is using an unsupported Mapper of type '" + name + "'");
return;
}
}
commandSet.add(new SubCommand(this, method, anno.value(), localTypeMapper));
});
this.commandSet.sort((o1, o2) -> {
int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length);
if (compare != 0) {
return compare;
} else {
int i1 = o1.varArgType != null ? Integer.MAX_VALUE : o1.arguments.length;
int i2 = o2.varArgType != null ? Integer.MAX_VALUE : o2.arguments.length;
return Integer.compare(i1, i2);
}
});
commandHelpSet.sort(Comparator.comparingInt(o -> -o.subCommand.length));
}
}
private <T extends Annotation> void add(Class<T> annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class<?> returnType, BiConsumer<T, Parameter[]> consumer) {
T anno = SWCommandUtils.getAnnotation(method, annotation);
if (anno == null) {
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;
}
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;
}
consumer.accept(anno, parameters);
}
private <T extends Annotation> void addMapper(Class<T> annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class<?> returnType, BiConsumer<T, TypeMapper<?>> consumer) {
add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> {
try {
method.setAccessible(true);
Object object = method.invoke(this);
consumer.accept(anno, (TypeMapper<?>) object);
} catch (Exception e) {
throw new SecurityException(e.getMessage(), e);
}
});
}
protected void unregister() {
SWCommandUtils.knownCommandMap.remove(command.getName());
for (String alias : command.getAliases()) {
SWCommandUtils.knownCommandMap.remove(alias);
}
command.unregister(SWCommandUtils.commandMap);
}
protected void register() {
SWCommandUtils.commandMap.register("steamwar", this.command);
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
protected @interface Register {
String[] value() default {};
boolean help() default false;
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD})
protected @interface Mapper {
String value();
boolean local() default false;
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
protected @interface ClassMapper {
Class<?> value();
boolean local() default false;
}
}

Datei anzeigen

@ -0,0 +1,185 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.command.Command;
import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.entity.Player;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
public class SWCommandUtils {
private SWCommandUtils() {
throw new IllegalStateException("Utility Class");
}
static final Map<String, TypeMapper<?>> MAPPER_FUNCTIONS = new HashMap<>();
static final TypeMapper<?> ERROR_FUNCTION = createMapper(s -> {
throw new SecurityException();
}, s -> Collections.emptyList());
static final BiFunction<Class<Enum<?>>, String, Enum<?>> ENUM_MAPPER = (enumClass, s) -> {
Enum<?>[] enums = enumClass.getEnumConstants();
for (Enum<?> e : enums) {
if (e.name().equalsIgnoreCase(s)) return e;
}
return null;
};
static {
addMapper(boolean.class, Boolean.class, createMapper(Boolean::parseBoolean, s -> Arrays.asList("true", "false")));
addMapper(float.class, Float.class, createMapper(Float::parseFloat, numberCompleter(Float::parseFloat)));
addMapper(double.class, Double.class, createMapper(Double::parseDouble, numberCompleter(Double::parseDouble)));
addMapper(int.class, Integer.class, createMapper(Integer::parseInt, numberCompleter(Integer::parseInt)));
MAPPER_FUNCTIONS.put(String.class.getTypeName(), createMapper(s -> s, Collections::singletonList));
MAPPER_FUNCTIONS.put(Player.class.getTypeName(), createMapper(Bukkit::getPlayer, s -> Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList())));
MAPPER_FUNCTIONS.put(GameMode.class.getTypeName(), createMapper(s -> {
s = s.toLowerCase();
if (s.equals("s") || s.equals("survival") || s.equals("0")) return GameMode.SURVIVAL;
if (s.equals("c") || s.equals("creative") || s.equals("1")) return GameMode.CREATIVE;
if (s.equals("sp") || s.equals("spectator") || s.equals("3")) return GameMode.SPECTATOR;
if (s.equals("a") || s.equals("adventure") || s.equals("2")) return GameMode.ADVENTURE;
throw new SecurityException();
}, s -> Arrays.asList("s", "survival", "0", "c", "creative", "1", "sp", "specator", "3", "a", "adventure", "2")));
}
private static void addMapper(Class<?> clazz, Class<?> alternativeClazz, TypeMapper<?> mapper) {
MAPPER_FUNCTIONS.put(clazz.getTypeName(), mapper);
MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper);
}
static final CommandMap commandMap;
static final Map<String, Command> knownCommandMap;
static {
try {
final Field commandMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
commandMapField.setAccessible(true);
commandMap = (CommandMap) commandMapField.get(Bukkit.getServer());
} catch (NoSuchFieldException | IllegalAccessException exception) {
Bukkit.shutdown();
throw new SecurityException("Oh shit. Commands cannot be registered.", exception);
}
try {
final Field knownCommandsField = SimpleCommandMap.class.getDeclaredField("knownCommands");
knownCommandsField.setAccessible(true);
knownCommandMap = (Map<String, Command>) knownCommandsField.get(commandMap);
} catch (NoSuchFieldException | IllegalAccessException exception) {
Bukkit.shutdown();
throw new SecurityException("Oh shit. Commands cannot be registered.", exception);
}
}
static Object[] generateArgumentArray(TypeMapper<?>[] parameters, String[] args, Class<?> varArgType, String[] subCommand) throws CommandParseException {
Object[] arguments = new Object[parameters.length + 1];
int index = 0;
while (index < subCommand.length) {
if (!args[index].equalsIgnoreCase(subCommand[index])) {
throw new CommandParseException();
}
index++;
}
if (varArgType != null && index > args.length - 1) {
Object varArgument = Array.newInstance(varArgType, 0);
arguments[arguments.length - 1] = varArgument;
} else {
for (int i = 0; i < parameters.length - (varArgType != null ? 1 : 0); i++) {
arguments[i + 1] = parameters[i].map(Arrays.copyOf(args, index), args[index]);
index++;
if (arguments[i + 1] == null) {
throw new CommandParseException();
}
}
if (varArgType != null) {
int length = args.length - parameters.length - subCommand.length + 1;
Object varArgument = Array.newInstance(varArgType, length);
arguments[arguments.length - 1] = varArgument;
for (int i = 0; i < length; i++) {
Object value = parameters[parameters.length - 1].map(Arrays.copyOf(args, index), args[index]);
if (value == null) {
throw new CommandParseException();
}
Array.set(varArgument, i, value);
index++;
}
}
}
return arguments;
}
public static <T> void addMapper(Class<T> clazz, TypeMapper<T> mapper) {
addMapper(clazz.getTypeName(), mapper);
}
public static void addMapper(String name, TypeMapper<?> mapper) {
if (MAPPER_FUNCTIONS.containsKey(name)) return;
MAPPER_FUNCTIONS.put(name, mapper);
}
public static <T> TypeMapper<T> createMapper(Function<String, T> mapper, Function<String, List<String>> tabCompleter) {
return createMapper(mapper, (commandSender, s) -> tabCompleter.apply(s));
}
public static <T> TypeMapper<T> createMapper(Function<String, T> mapper, BiFunction<CommandSender, String, List<String>> tabCompleter) {
return new TypeMapper<T>() {
@Override
public T map(String[] previous, String s) {
return mapper.apply(s);
}
@Override
public List<String> tabCompletes(CommandSender commandSender, String[] previous, String s) {
return tabCompleter.apply(commandSender, s);
}
};
}
private static Function<String, List<String>> numberCompleter(Function<String, ?> mapper) {
return s -> {
try {
mapper.apply(s);
return Collections.singletonList(s);
} catch (Exception e) {
return Collections.emptyList();
}
};
}
static <T extends Annotation> T getAnnotation(Method method, Class<T> annotation) {
if (method.getAnnotations().length != 1) return null;
return method.getAnnotation(annotation);
}
}

Datei anzeigen

@ -0,0 +1,139 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
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;
class SubCommand {
private SWCommand swCommand;
private Method method;
String[] subCommand;
TypeMapper<?>[] arguments;
private Function<CommandSender, ?> commandSenderFunction;
Class<?> varArgType = null;
public SubCommand(SWCommand swCommand, Method method, String[] subCommand) {
this(swCommand, method, subCommand, new HashMap<>());
}
public SubCommand(SWCommand swCommand, Method method, String[] subCommand, Map<String, TypeMapper<?>> localTypeMapper) {
this.swCommand = swCommand;
this.method = method;
Parameter[] parameters = method.getParameters();
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 && !SWCommandUtils.MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) {
Class<Enum<?>> enumClass = (Class<Enum<?>>) clazz;
List<String> tabCompletes = new ArrayList<>();
for (Enum<?> enumConstant : enumClass.getEnumConstants()) {
tabCompletes.add(enumConstant.name().toLowerCase());
}
arguments[i - 1] = SWCommandUtils.createMapper(s -> SWCommandUtils.ENUM_MAPPER.apply(enumClass, s), s -> tabCompletes);
continue;
}
String name = clazz.getTypeName();
if (mapper != null) {
name = mapper.value();
}
if (localTypeMapper.containsKey(name)) {
arguments[i - 1] = localTypeMapper.getOrDefault(name, SWCommandUtils.ERROR_FUNCTION);
} else {
arguments[i - 1] = SWCommandUtils.MAPPER_FUNCTIONS.getOrDefault(name, SWCommandUtils.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 {
Object[] objects = SWCommandUtils.generateArgumentArray(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<String> tabComplete(CommandSender commandSender, String[] args) {
if (varArgType == null && args.length < arguments.length + subCommand.length - 1) {
return null;
}
List<String> 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;
}
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(Arrays.copyOf(args, argsList.size()), s) == null) {
return null;
}
} catch (Exception e) {
return null;
}
}
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(Arrays.copyOf(args, argsList.size()), s) == null) {
return null;
}
} catch (Exception e) {
return null;
}
}
}
return null;
}
}

Datei anzeigen

@ -0,0 +1,30 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import org.bukkit.command.CommandSender;
import java.util.List;
public interface TypeMapper<T> {
T map(String[] previousArguments, String s);
List<String> tabCompletes(CommandSender commandSender, String[] previousArguments, String s);
}