diff --git a/SpigotCore_Main/src/de/steamwar/acommand/TestCommand.java b/SpigotCore_Main/src/de/steamwar/acommand/TestCommand.java new file mode 100644 index 0000000..8444aba --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/acommand/TestCommand.java @@ -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 . + */ + +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) { + + } + + // 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 materialTypeMapper() { + List tabCompletes = Arrays.stream(Material.values()) + .filter(Material::isSolid) + .map(Material::name) + .map(String::toLowerCase) + .collect(Collectors.toList()); + return new TypeMapper() { + @Override + public Material map(String[] previous, String s) { + return Material.valueOf(s.toUpperCase()); + } + + @Override + public List tabCompletes(CommandSender commandSender, String[] previous, String s) { + return tabCompletes; + } + }; + } + +} diff --git a/SpigotCore_Main/src/de/steamwar/command/CommandParseException.java b/SpigotCore_Main/src/de/steamwar/command/CommandParseException.java new file mode 100644 index 0000000..b47cace --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/CommandParseException.java @@ -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 . + */ + +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); + } +} diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommand.java b/SpigotCore_Main/src/de/steamwar/command/SWCommand.java new file mode 100644 index 0000000..240766b --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommand.java @@ -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 . + */ + +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 commandSet = new ArrayList<>(); + private final List commandHelpSet = new ArrayList<>(); + private final Map> 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 tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { + List strings = new ArrayList<>(); + for (SubCommand subCommand : commandSet) { + List 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 void add(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer 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 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); + 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; + } +} diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java new file mode 100644 index 0000000..1536d51 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java @@ -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 . + */ + +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> MAPPER_FUNCTIONS = new HashMap<>(); + + static final TypeMapper ERROR_FUNCTION = createMapper(s -> { + throw new SecurityException(); + }, s -> Collections.emptyList()); + + static final BiFunction>, 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 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) 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 void addMapper(Class clazz, TypeMapper 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 TypeMapper createMapper(Function mapper, Function> tabCompleter) { + return createMapper(mapper, (commandSender, s) -> tabCompleter.apply(s)); + } + + public static TypeMapper createMapper(Function mapper, BiFunction> tabCompleter) { + return new TypeMapper() { + @Override + public T map(String[] previous, String s) { + return mapper.apply(s); + } + + @Override + public List tabCompletes(CommandSender commandSender, String[] previous, String s) { + return tabCompleter.apply(commandSender, s); + } + }; + } + + private static Function> numberCompleter(Function mapper) { + return s -> { + try { + mapper.apply(s); + return Collections.singletonList(s); + } catch (Exception e) { + return Collections.emptyList(); + } + }; + } + + static T getAnnotation(Method method, Class annotation) { + if (method.getAnnotations().length != 1) return null; + return method.getAnnotation(annotation); + } +} 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..1ce4c11 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/SubCommand.java @@ -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 . + */ + +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 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> 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> enumClass = (Class>) clazz; + List 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 tabComplete(CommandSender commandSender, String[] args) { + if (varArgType == null && args.length < arguments.length + subCommand.length - 1) { + return null; + } + 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; + } + 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; + } +} diff --git a/SpigotCore_Main/src/de/steamwar/command/TypeMapper.java b/SpigotCore_Main/src/de/steamwar/command/TypeMapper.java new file mode 100644 index 0000000..27b04f7 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/TypeMapper.java @@ -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 . + */ + +package de.steamwar.command; + +import org.bukkit.command.CommandSender; + +import java.util.List; + +public interface TypeMapper { + T map(String[] previousArguments, String s); + + List tabCompletes(CommandSender commandSender, String[] previousArguments, String s); +}