From d144c3ea8c34927b41f00ff41f972c45ab978bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20S=C3=B6derberg?= Date: Tue, 15 Sep 2020 13:36:13 +0200 Subject: [PATCH] Store component type in the component. Add mappings to native (NMS) Brigadier types. Shorten builder names. Make the Bukkit command manager take in a generic command sender type. --- .../intellectualsites/commands/Command.java | 12 +- .../commands/CommandManager.java | 2 +- .../commands/CommandTree.java | 62 +++---- .../commands/components/CommandComponent.java | 41 ++++- .../commands/components/StaticComponent.java | 2 +- .../components/standard/BooleanComponent.java | 4 +- .../components/standard/ByteComponent.java | 4 +- .../components/standard/CharComponent.java | 4 +- .../components/standard/DoubleComponent.java | 4 +- .../components/standard/EnumComponent.java | 4 +- .../components/standard/FloatComponent.java | 4 +- .../components/standard/IntegerComponent.java | 4 +- .../components/standard/LongComponent.java | 4 +- .../components/standard/ShortComponent.java | 4 +- .../components/standard/StringComponent.java | 4 +- .../commands/context/CommandContext.java | 21 ++- .../commands/CommandPreProcessorTest.java | 6 +- .../commands/CommandTest.java | 4 +- .../commands/CommandTreeTest.java | 14 +- .../commands/jline/JLineCommandManager.java | 22 +-- .../brigadier/CloudBrigadierManager.java | 24 ++- .../commands/BukkitTest.java | 135 +++++++++----- .../commands/BukkitCommand.java | 18 +- .../commands/BukkitCommandManager.java | 12 +- .../commands/BukkitCommandSender.java | 7 +- .../commands/parsers/WorldComponent.java | 174 ++++++++++++++++++ .../commands/parsers/package-info.java | 28 +++ .../commands/PaperBrigadierListener.java | 52 ++++++ .../commands/PaperCommandManager.java | 6 +- 29 files changed, 524 insertions(+), 158 deletions(-) create mode 100644 cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/parsers/WorldComponent.java create mode 100644 cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/parsers/package-info.java diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/Command.java b/cloud-core/src/main/java/com/intellectualsites/commands/Command.java index dec4049d..09e0ae40 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/Command.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/Command.java @@ -244,7 +244,7 @@ public class Command { * @return New builder instance with the command component inserted into the component list */ @Nonnull - public Builder withComponent(@Nonnull final CommandComponent component) { + public Builder component(@Nonnull final CommandComponent component) { final List> commandComponents = new LinkedList<>(this.commandComponents); commandComponents.add(component); return new Builder<>(this.commandMeta, this.senderType, commandComponents, this.commandExecutionHandler, @@ -261,12 +261,12 @@ public class Command { * @return New builder instance with the command component inserted into the component list */ @Nonnull - public Builder withComponent(@Nonnull final Class clazz, - @Nonnull final String name, - @Nonnull final Consumer> builderConsumer) { + public Builder component(@Nonnull final Class clazz, + @Nonnull final String name, + @Nonnull final Consumer> builderConsumer) { final CommandComponent.Builder builder = CommandComponent.ofType(clazz, name); builderConsumer.accept(builder); - return this.withComponent(builder.build()); + return this.component(builder.build()); } /** @@ -276,7 +276,7 @@ public class Command { * @return New builder instance using the command execution handler */ @Nonnull - public Builder withHandler(@Nonnull final CommandExecutionHandler commandExecutionHandler) { + public Builder handler(@Nonnull final CommandExecutionHandler commandExecutionHandler) { return new Builder<>(this.commandMeta, this.senderType, this.commandComponents, commandExecutionHandler, this.commandPermission); } diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/CommandManager.java b/cloud-core/src/main/java/com/intellectualsites/commands/CommandManager.java index 28c3dcdd..6a26f3e4 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/CommandManager.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/CommandManager.java @@ -159,7 +159,7 @@ public abstract class CommandManager registerCommand(@Nonnull final Command command) { + public CommandManager command(@Nonnull final Command command) { this.commandTree.insertCommand(command); return this; } diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/CommandTree.java b/cloud-core/src/main/java/com/intellectualsites/commands/CommandTree.java index 17a2daec..4b49460c 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/CommandTree.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/CommandTree.java @@ -107,12 +107,12 @@ public final class CommandTree { private Optional> parseCommand(@Nonnull final CommandContext commandContext, @Nonnull final Queue commandQueue, @Nonnull final Node> root) { - String permission = this.isPermitted(commandContext.getCommandSender(), root); + String permission = this.isPermitted(commandContext.getSender(), root); if (permission != null) { - throw new NoPermissionException(permission, commandContext.getCommandSender(), this.getChain(root) - .stream() - .map(Node::getValue) - .collect(Collectors.toList())); + throw new NoPermissionException(permission, commandContext.getSender(), this.getChain(root) + .stream() + .map(Node::getValue) + .collect(Collectors.toList())); } final Optional> parsedChild = this.attemptParseUnambiguousChild(commandContext, root, commandQueue); @@ -133,10 +133,10 @@ public final class CommandTree { .apply(root.getValue() .getOwningCommand() .getComponents()), - commandContext.getCommandSender(), this.getChain(root) - .stream() - .map(Node::getValue) - .collect(Collectors.toList())); + commandContext.getSender(), this.getChain(root) + .stream() + .map(Node::getValue) + .collect(Collectors.toList())); } } else { /* Too many arguments. We have a unique path, so we can send the entire context */ @@ -145,10 +145,10 @@ public final class CommandTree { Objects.requireNonNull(root.getValue()) .getOwningCommand()) .getComponents()), - commandContext.getCommandSender(), this.getChain(root) - .stream() - .map(Node::getValue) - .collect(Collectors.toList())); + commandContext.getSender(), this.getChain(root) + .stream() + .map(Node::getValue) + .collect(Collectors.toList())); } } else { final Iterator>> childIterator = root.getChildren().iterator(); @@ -165,7 +165,7 @@ public final class CommandTree { } } /* We could not find a match */ - throw new NoSuchCommandException(commandContext.getCommandSender(), + throw new NoSuchCommandException(commandContext.getSender(), getChain(root).stream().map(Node::getValue).collect(Collectors.toList()), stringOrEmpty(commandQueue.peek())); } @@ -180,12 +180,12 @@ public final class CommandTree { if (children.size() == 1 && !(children.get(0).getValue() instanceof StaticComponent)) { // The value has to be a variable final Node> child = children.get(0); - permission = this.isPermitted(commandContext.getCommandSender(), child); + permission = this.isPermitted(commandContext.getSender(), child); if (permission != null) { - throw new NoPermissionException(permission, commandContext.getCommandSender(), this.getChain(child) - .stream() - .map(Node::getValue) - .collect(Collectors.toList())); + throw new NoPermissionException(permission, commandContext.getSender(), this.getChain(child) + .stream() + .map(Node::getValue) + .collect(Collectors.toList())); } if (child.getValue() != null) { if (commandQueue.isEmpty()) { @@ -199,12 +199,12 @@ public final class CommandTree { .apply(Objects.requireNonNull( child.getValue().getOwningCommand()) .getComponents()), - commandContext.getCommandSender(), this.getChain(root) - .stream() - .map(Node::getValue) - .collect(Collectors.toList())); + commandContext.getSender(), this.getChain(root) + .stream() + .map(Node::getValue) + .collect(Collectors.toList())); } else { - throw new NoSuchCommandException(commandContext.getCommandSender(), + throw new NoSuchCommandException(commandContext.getSender(), this.getChain(root) .stream() .map(Node::getValue) @@ -224,17 +224,17 @@ public final class CommandTree { .apply(Objects.requireNonNull(child.getValue() .getOwningCommand()) .getComponents()), - commandContext.getCommandSender(), this.getChain(root) - .stream() - .map(Node::getValue) - .collect( + commandContext.getSender(), this.getChain(root) + .stream() + .map(Node::getValue) + .collect( Collectors.toList())); } } else { return this.parseCommand(commandContext, commandQueue, child); } } else if (result.getFailure().isPresent()) { - throw new ComponentParseException(result.getFailure().get(), commandContext.getCommandSender(), + throw new ComponentParseException(result.getFailure().get(), commandContext.getSender(), this.getChain(child) .stream() .map(Node::getValue) @@ -264,7 +264,7 @@ public final class CommandTree { @Nonnull final Node> root) { /* If the sender isn't allowed to access the root node, no suggestions are needed */ - if (this.isPermitted(commandContext.getCommandSender(), root) != null) { + if (this.isPermitted(commandContext.getSender(), root) != null) { return Collections.emptyList(); } final List>> children = root.getChildren(); @@ -307,7 +307,7 @@ public final class CommandTree { } final List suggestions = new LinkedList<>(); for (final Node> component : root.getChildren()) { - if (component.getValue() == null || this.isPermitted(commandContext.getCommandSender(), component) != null) { + if (component.getValue() == null || this.isPermitted(commandContext.getSender(), component) != null) { continue; } suggestions.addAll( diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/CommandComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/CommandComponent.java index 3211cbf4..16c75301 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/CommandComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/CommandComponent.java @@ -69,6 +69,10 @@ public class CommandComponent implements Comparable< * Default value, will be empty if none was supplied */ private final String defaultValue; + /** + * The type that is produces by the component's parser + */ + private final Class valueType; private Command owningCommand; @@ -79,9 +83,13 @@ public class CommandComponent implements Comparable< * @param name The component name * @param parser The component parser * @param defaultValue Default value used when no value is provided by the command sender + * @param valueType Type produced by the parser */ - public CommandComponent(final boolean required, @Nonnull final String name, - @Nonnull final ComponentParser parser, @Nonnull final String defaultValue) { + public CommandComponent(final boolean required, + @Nonnull final String name, + @Nonnull final ComponentParser parser, + @Nonnull final String defaultValue, + @Nonnull final Class valueType) { this.required = required; this.name = Objects.requireNonNull(name, "Name may not be null"); if (!NAME_PATTERN.asPredicate().test(name)) { @@ -89,6 +97,7 @@ public class CommandComponent implements Comparable< } this.parser = Objects.requireNonNull(parser, "Parser may not be null"); this.defaultValue = defaultValue; + this.valueType = valueType; } /** @@ -97,10 +106,13 @@ public class CommandComponent implements Comparable< * @param required Whether or not the component is required * @param name The component name * @param parser The component parser + * @param valueType Type produced by the parser */ - public CommandComponent(final boolean required, @Nonnull final String name, - @Nonnull final ComponentParser parser) { - this(required, name, parser, ""); + public CommandComponent(final boolean required, + @Nonnull final String name, + @Nonnull final ComponentParser parser, + @Nonnull final Class valueType) { + this(required, name, parser, "", valueType); } /** @@ -115,7 +127,7 @@ public class CommandComponent implements Comparable< @Nonnull public static CommandComponent.Builder ofType(@Nonnull final Class clazz, @Nonnull final String name) { - return new Builder<>(name); + return new Builder<>(clazz, name); } /** @@ -230,6 +242,15 @@ public class CommandComponent implements Comparable< && !this.getDefaultValue().isEmpty(); } + /** + * Get the type of this component's value + * + * @return Value type + */ + @Nonnull + public Class getValueType() { + return this.valueType; + } /** * Mutable builder for {@link CommandComponent} instances @@ -239,13 +260,17 @@ public class CommandComponent implements Comparable< */ public static class Builder { + private final Class valueType; private final String name; + private boolean required = true; private ComponentParser parser = (c, i) -> ComponentParseResult.failure( new UnsupportedOperationException("No parser was specified")); private String defaultValue = ""; - protected Builder(@Nonnull final String name) { + protected Builder(@Nonnull final Class valueType, + @Nonnull final String name) { + this.valueType = valueType; this.name = name; } @@ -315,7 +340,7 @@ public class CommandComponent implements Comparable< */ @Nonnull public CommandComponent build() { - return new CommandComponent<>(this.required, this.name, this.parser, this.defaultValue); + return new CommandComponent<>(this.required, this.name, this.parser, this.defaultValue, this.valueType); } @Nonnull diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/StaticComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/StaticComponent.java index d0a73bbe..8bf92bfd 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/StaticComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/StaticComponent.java @@ -44,7 +44,7 @@ import java.util.Set; public final class StaticComponent extends CommandComponent { private StaticComponent(final boolean required, @Nonnull final String name, @Nonnull final String... aliases) { - super(required, name, new StaticComponentParser<>(name, aliases)); + super(required, name, new StaticComponentParser<>(name, aliases), String.class); } /** diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/BooleanComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/BooleanComponent.java index 14ad5ee6..e97d0ba2 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/BooleanComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/BooleanComponent.java @@ -40,7 +40,7 @@ public final class BooleanComponent extends CommandComp private BooleanComponent(final boolean required, @Nonnull final String name, final boolean liberal, @Nonnull final String defaultValue) { - super(required, name, new BooleanParser<>(liberal), defaultValue); + super(required, name, new BooleanParser<>(liberal), defaultValue, Boolean.class); this.liberal = liberal; } @@ -100,7 +100,7 @@ public final class BooleanComponent extends CommandComp private boolean liberal = false; protected Builder(@Nonnull final String name) { - super(name); + super(Boolean.class, name); } /** diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ByteComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ByteComponent.java index f01f0674..ffaad36e 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ByteComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ByteComponent.java @@ -41,7 +41,7 @@ public final class ByteComponent extends CommandCompone private ByteComponent(final boolean required, @Nonnull final String name, final byte min, final byte max, final String defaultValue) { - super(required, name, new ByteParser<>(min, max), defaultValue); + super(required, name, new ByteParser<>(min, max), defaultValue, Byte.class); this.min = min; this.max = max; } @@ -102,7 +102,7 @@ public final class ByteComponent extends CommandCompone private byte max = Byte.MAX_VALUE; protected Builder(@Nonnull final String name) { - super(name); + super(Byte.class, name); } /** diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/CharComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/CharComponent.java index 0713a42b..374d2968 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/CharComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/CharComponent.java @@ -37,7 +37,7 @@ public final class CharComponent extends CommandCompone private CharComponent(final boolean required, @Nonnull final String name, @Nonnull final String defaultValue) { - super(required, name, new CharacterParser<>(), defaultValue); + super(required, name, new CharacterParser<>(), defaultValue, Character.class); } /** @@ -94,7 +94,7 @@ public final class CharComponent extends CommandCompone public static final class Builder extends CommandComponent.Builder { protected Builder(@Nonnull final String name) { - super(name); + super(Character.class, name); } /** diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/DoubleComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/DoubleComponent.java index cc474d89..efb6ff70 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/DoubleComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/DoubleComponent.java @@ -44,7 +44,7 @@ public final class DoubleComponent extends CommandCompo final double min, final double max, final String defaultValue) { - super(required, name, new DoubleParser<>(min, max), defaultValue); + super(required, name, new DoubleParser<>(min, max), defaultValue, Double.class); this.min = min; this.max = max; } @@ -106,7 +106,7 @@ public final class DoubleComponent extends CommandCompo private double max = Double.MAX_VALUE; protected Builder(@Nonnull final String name) { - super(name); + super(Double.class, name); } /** diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/EnumComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/EnumComponent.java index 3f131edf..e22930e7 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/EnumComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/EnumComponent.java @@ -48,7 +48,7 @@ public class EnumComponent> extends C final boolean required, @Nonnull final String name, @Nonnull final String defaultValue) { - super(required, name, new EnumParser<>(enumClass), defaultValue); + super(required, name, new EnumParser<>(enumClass), defaultValue, enumClass); } /** @@ -118,7 +118,7 @@ public class EnumComponent> extends C private final Class enumClass; protected Builder(@Nonnull final String name, @Nonnull final Class enumClass) { - super(name); + super(enumClass, name); this.enumClass = enumClass; } diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/FloatComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/FloatComponent.java index 2856a4f2..292b260d 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/FloatComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/FloatComponent.java @@ -44,7 +44,7 @@ public final class FloatComponent extends CommandCompon final float min, final float max, final String defaultValue) { - super(required, name, new FloatParser<>(min, max), defaultValue); + super(required, name, new FloatParser<>(min, max), defaultValue, Float.class); this.min = min; this.max = max; } @@ -106,7 +106,7 @@ public final class FloatComponent extends CommandCompon private float max = Float.MAX_VALUE; protected Builder(@Nonnull final String name) { - super(name); + super(Float.class, name); } /** diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/IntegerComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/IntegerComponent.java index 1c5e31f5..bb40c394 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/IntegerComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/IntegerComponent.java @@ -44,7 +44,7 @@ public final class IntegerComponent extends CommandComp final int min, final int max, final String defaultValue) { - super(required, name, new IntegerParser<>(min, max), defaultValue); + super(required, name, new IntegerParser<>(min, max), defaultValue, Integer.class); this.min = min; this.max = max; } @@ -106,7 +106,7 @@ public final class IntegerComponent extends CommandComp private int max = Integer.MAX_VALUE; protected Builder(@Nonnull final String name) { - super(name); + super(Integer.class, name); } /** diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/LongComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/LongComponent.java index 1fb6aaf1..60036595 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/LongComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/LongComponent.java @@ -44,7 +44,7 @@ public final class LongComponent extends CommandCompone final long min, final long max, final String defaultValue) { - super(required, name, new LongParser<>(min, max), defaultValue); + super(required, name, new LongParser<>(min, max), defaultValue, Long.class); this.min = min; this.max = max; } @@ -106,7 +106,7 @@ public final class LongComponent extends CommandCompone private long max = Long.MAX_VALUE; protected Builder(@Nonnull final String name) { - super(name); + super(Long.class, name); } /** diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ShortComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ShortComponent.java index 1b48aeb2..6ea7c900 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ShortComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/ShortComponent.java @@ -44,7 +44,7 @@ public final class ShortComponent extends CommandCompon final short min, final short max, final String defaultValue) { - super(required, name, new ShortParser<>(min, max), defaultValue); + super(required, name, new ShortParser<>(min, max), defaultValue, Short.class); this.min = min; this.max = max; } @@ -106,7 +106,7 @@ public final class ShortComponent extends CommandCompon private short max = Short.MAX_VALUE; protected Builder(@Nonnull final String name) { - super(name); + super(Short.class, name); } /** diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/StringComponent.java b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/StringComponent.java index 6a8b091f..5f0ff98f 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/StringComponent.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/components/standard/StringComponent.java @@ -46,7 +46,7 @@ public final class StringComponent extends CommandCompo @Nonnull final StringMode stringMode, @Nonnull final String defaultValue, @Nonnull final BiFunction, String, List> suggestionsProvider) { - super(required, name, new StringParser<>(stringMode, suggestionsProvider), defaultValue); + super(required, name, new StringParser<>(stringMode, suggestionsProvider), defaultValue, String.class); this.stringMode = stringMode; } @@ -124,7 +124,7 @@ public final class StringComponent extends CommandCompo private BiFunction, String, List> suggestionsProvider = (v1, v2) -> Collections.emptyList(); protected Builder(@Nonnull final String name) { - super(name); + super(String.class, name); } /** diff --git a/cloud-core/src/main/java/com/intellectualsites/commands/context/CommandContext.java b/cloud-core/src/main/java/com/intellectualsites/commands/context/CommandContext.java index dfe310d3..6657efe4 100644 --- a/cloud-core/src/main/java/com/intellectualsites/commands/context/CommandContext.java +++ b/cloud-core/src/main/java/com/intellectualsites/commands/context/CommandContext.java @@ -55,7 +55,7 @@ public final class CommandContext { * @return Command sender */ @Nonnull - public C getCommandSender() { + public C getSender() { return this.commandSender; } @@ -77,6 +77,7 @@ public final class CommandContext { * @param Value type * @return Value */ + @Nonnull public Optional get(@Nonnull final String key) { final Object value = this.internalStorage.get(key); if (value != null) { @@ -87,4 +88,22 @@ public final class CommandContext { } } + /** + * Get a required argument from the context + * + * @param key Argument key + * @param Argument type + * @return Argument + * @throws NullPointerException If no such argument is stored + */ + @Nonnull + @SuppressWarnings("unchecked") + public T getRequired(@Nonnull final String key) { + final Object value = this.internalStorage.get(key); + if (value == null) { + throw new NullPointerException("No such object stored in the context: " + key); + } + return (T) value; + } + } diff --git a/cloud-core/src/test/java/com/intellectualsites/commands/CommandPreProcessorTest.java b/cloud-core/src/test/java/com/intellectualsites/commands/CommandPreProcessorTest.java index c58dd686..1cefefe8 100644 --- a/cloud-core/src/test/java/com/intellectualsites/commands/CommandPreProcessorTest.java +++ b/cloud-core/src/test/java/com/intellectualsites/commands/CommandPreProcessorTest.java @@ -42,9 +42,9 @@ public class CommandPreProcessorTest { @BeforeAll static void newTree() { manager = new TestCommandManager(); - manager.registerCommand(manager.commandBuilder("test", SimpleCommandMeta.empty()) - .withComponent(EnumComponent.required(SampleEnum.class, "enum")) - .withHandler( + manager.command(manager.commandBuilder("test", SimpleCommandMeta.empty()) + .component(EnumComponent.required(SampleEnum.class, "enum")) + .handler( commandContext -> System.out.printf("enum = %s | integer = %d\n", commandContext.get( "enum").orElse( diff --git a/cloud-core/src/test/java/com/intellectualsites/commands/CommandTest.java b/cloud-core/src/test/java/com/intellectualsites/commands/CommandTest.java index f8d43e8f..b4d42569 100644 --- a/cloud-core/src/test/java/com/intellectualsites/commands/CommandTest.java +++ b/cloud-core/src/test/java/com/intellectualsites/commands/CommandTest.java @@ -38,8 +38,8 @@ class CommandTest { @Test void ensureOrdering() { Assertions.assertThrows(IllegalArgumentException.class, () -> - Command.newBuilder("test", SimpleCommandMeta.empty()).withComponent(StaticComponent.optional("something")) - .withComponent(StaticComponent.required("somethingelse")).build()); + Command.newBuilder("test", SimpleCommandMeta.empty()).component(StaticComponent.optional("something")) + .component(StaticComponent.required("somethingelse")).build()); } } diff --git a/cloud-core/src/test/java/com/intellectualsites/commands/CommandTreeTest.java b/cloud-core/src/test/java/com/intellectualsites/commands/CommandTreeTest.java index 3bf68881..bfe0bef0 100644 --- a/cloud-core/src/test/java/com/intellectualsites/commands/CommandTreeTest.java +++ b/cloud-core/src/test/java/com/intellectualsites/commands/CommandTreeTest.java @@ -46,14 +46,14 @@ class CommandTreeTest { @BeforeAll static void newTree() { commandManager = new TestCommandManager(); - commandManager.registerCommand(commandManager.commandBuilder("test", SimpleCommandMeta.empty()) - .withComponent(StaticComponent.required("one")).build()) - .registerCommand(commandManager.commandBuilder("test", SimpleCommandMeta.empty()) - .withComponent(StaticComponent.required("two")).withPermission("no").build()) - .registerCommand(commandManager.commandBuilder("test", Collections.singleton("other"), + commandManager.command(commandManager.commandBuilder("test", SimpleCommandMeta.empty()) + .component(StaticComponent.required("one")).build()) + .command(commandManager.commandBuilder("test", SimpleCommandMeta.empty()) + .component(StaticComponent.required("two")).withPermission("no").build()) + .command(commandManager.commandBuilder("test", Collections.singleton("other"), SimpleCommandMeta.empty()) - .withComponent(StaticComponent.required("opt", "öpt")) - .withComponent(IntegerComponent + .component(StaticComponent.required("opt", "öpt")) + .component(IntegerComponent .optional("num", EXPECTED_INPUT_NUMBER)) .build()); } diff --git a/cloud-jline/src/main/java/com/intellectualsites/commands/jline/JLineCommandManager.java b/cloud-jline/src/main/java/com/intellectualsites/commands/jline/JLineCommandManager.java index b5d195ef..eb151305 100644 --- a/cloud-jline/src/main/java/com/intellectualsites/commands/jline/JLineCommandManager.java +++ b/cloud-jline/src/main/java/com/intellectualsites/commands/jline/JLineCommandManager.java @@ -75,13 +75,13 @@ public class JLineCommandManager extends CommandManager + .handler(commandContext -> shouldStop[0] = true) .build()) - .registerCommand(jLineCommandManager.commandBuilder("echo", SimpleCommandMeta.empty()) - .withComponent(String.class, "string", builder -> + .command(jLineCommandManager.commandBuilder("echo", SimpleCommandMeta.empty()) + .component(String.class, "string", builder -> builder.asRequired() .withParser(((commandContext, inputQueue) -> { final StringBuilder stringBuilder = @@ -95,17 +95,17 @@ public class JLineCommandManager extends CommandManager commandContext.get("string") + .handler(commandContext -> commandContext.get("string") .ifPresent( System.out::println)) .build()) - .registerCommand(jLineCommandManager.commandBuilder("test", SimpleCommandMeta.empty()) - .withComponent(StaticComponent.required("one")) - .withHandler(commandContext -> System.out.println("Test (1)")) + .command(jLineCommandManager.commandBuilder("test", SimpleCommandMeta.empty()) + .component(StaticComponent.required("one")) + .handler(commandContext -> System.out.println("Test (1)")) .build()) - .registerCommand(jLineCommandManager.commandBuilder("test", SimpleCommandMeta.empty()) - .withComponent(StaticComponent.required("two")) - .withHandler(commandContext -> System.out.println("Test (2)")) + .command(jLineCommandManager.commandBuilder("test", SimpleCommandMeta.empty()) + .component(StaticComponent.required("two")) + .handler(commandContext -> System.out.println("Test (2)")) .build()); System.out.println("Ready..."); while (!shouldStop[0]) { diff --git a/cloud-minecraft/cloud-brigadier/src/main/java/com/intellectualsites/commands/brigadier/CloudBrigadierManager.java b/cloud-minecraft/cloud-brigadier/src/main/java/com/intellectualsites/commands/brigadier/CloudBrigadierManager.java index 92b610b4..10e298b6 100644 --- a/cloud-minecraft/cloud-brigadier/src/main/java/com/intellectualsites/commands/brigadier/CloudBrigadierManager.java +++ b/cloud-minecraft/cloud-brigadier/src/main/java/com/intellectualsites/commands/brigadier/CloudBrigadierManager.java @@ -52,6 +52,7 @@ import javax.annotation.Nonnull; import java.util.Map; import java.util.function.BiPredicate; import java.util.function.Function; +import java.util.function.Supplier; /** * Manager used to map cloud {@link com.intellectualsites.commands.Command} @@ -67,12 +68,14 @@ public final class CloudBrigadierManager { private final Map, Function, ? extends ArgumentType>> mappers; + private final Map, Supplier>> defaultArgumentTypeSuppliers; /** * Create a new cloud brigadier manager */ public CloudBrigadierManager() { this.mappers = Maps.newHashMap(); + this.defaultArgumentTypeSuppliers = Maps.newHashMap(); this.registerInternalMappings(); } @@ -106,9 +109,6 @@ public final class CloudBrigadierManager { }, component -> { final boolean hasMin = component.getMin() != Integer.MIN_VALUE; final boolean hasMax = component.getMax() != Integer.MAX_VALUE; - - System.out.println("Constructing new IntegerArgumentType with min " + hasMin + " | max " + hasMax); - if (hasMin) { return IntegerArgumentType.integer(component.getMin(), component.getMax()); } else if (hasMax) { @@ -150,8 +150,6 @@ public final class CloudBrigadierManager { this.registerMapping(new TypeToken>() { }, component -> { switch (component.getStringMode()) { - case SINGLE: - return StringArgumentType.word(); case QUOTED: return StringArgumentType.string(); case GREEDY: @@ -177,6 +175,17 @@ public final class CloudBrigadierManager { this.mappers.put(componentType.getRawType(), mapper); } + /** + * Register a default mapping to between a class and a Brigadier argument type + * + * @param clazz Type to map + * @param supplier Supplier that supplies the argument type + */ + public void registerDefaultArgumentTypeSupplier(@Nonnull final Class clazz, + @Nonnull final Supplier> supplier) { + this.defaultArgumentTypeSuppliers.put(clazz, supplier); + } + /** * Get a Brigadier {@link ArgumentType} from a cloud {@link CommandComponent} * @@ -199,6 +208,11 @@ public final class CloudBrigadierManager { @Nonnull private > ArgumentType createDefaultMapper(@Nonnull final CommandComponent component) { + final Supplier> argumentTypeSupplier = this.defaultArgumentTypeSuppliers.get(component.getValueType()); + if (argumentTypeSupplier != null) { + return argumentTypeSupplier.get(); + } + System.err.printf("Found not native mapping for '%s'\n", component.getValueType().getCanonicalName()); return StringArgumentType.string(); } diff --git a/cloud-minecraft/cloud-bukkit-test/src/main/java/com/intellectualsites/commands/BukkitTest.java b/cloud-minecraft/cloud-bukkit-test/src/main/java/com/intellectualsites/commands/BukkitTest.java index 1bd07378..bc3aa877 100644 --- a/cloud-minecraft/cloud-bukkit-test/src/main/java/com/intellectualsites/commands/BukkitTest.java +++ b/cloud-minecraft/cloud-bukkit-test/src/main/java/com/intellectualsites/commands/BukkitTest.java @@ -24,18 +24,24 @@ package com.intellectualsites.commands; import com.intellectualsites.commands.components.StaticComponent; +import com.intellectualsites.commands.components.parser.ComponentParseResult; import com.intellectualsites.commands.components.standard.EnumComponent; import com.intellectualsites.commands.components.standard.IntegerComponent; import com.intellectualsites.commands.components.standard.StringComponent; import com.intellectualsites.commands.execution.CommandExecutionCoordinator; +import com.intellectualsites.commands.parsers.WorldComponent; import org.bukkit.Bukkit; import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; public final class BukkitTest extends JavaPlugin { @@ -46,52 +52,89 @@ public final class BukkitTest extends JavaPlugin { @Override public void onEnable() { try { - final PaperCommandManager commandManager = new PaperCommandManager(this, - CommandExecutionCoordinator.simpleCoordinator()); - commandManager.registerBrigadier(); - commandManager.registerCommand(commandManager.commandBuilder("gamemode", - Collections.singleton("gajmöde"), - BukkitCommandMetaBuilder.builder() - .withDescription("Your ugli") - .build()) - .withComponent(EnumComponent.required(GameMode.class, "gamemode")) - .withComponent(StringComponent.newBuilder("player") - .withSuggestionsProvider((v1, v2) -> { - final List suggestions = - new ArrayList<>( - Bukkit.getOnlinePlayers() - .stream() - .map(Player::getName) - .collect(Collectors.toList())); - suggestions.add("dog"); - suggestions.add("cat"); - return suggestions; - }).build()) - .withHandler(c -> c.getCommandSender() - .asPlayer() - .setGameMode(c.get("gamemode") - .orElse(GameMode.SURVIVAL))) - .build()) - .registerCommand(commandManager.commandBuilder("kenny") - .withComponent(StaticComponent.required("sux")) - .withComponent(IntegerComponent - .newBuilder("perc") - .withMin(PERC_MIN).withMax(PERC_MAX).build()) - .withHandler(context -> { - context.getCommandSender().asPlayer().sendMessage(String.format( - "Kenny sux %d%%", - context.get("perc").orElse(PERC_MIN) - )); - }) - .build()) - .registerCommand(commandManager.commandBuilder("test") - .withComponent(StaticComponent.required("one")) - .withHandler(c -> c.getCommandSender().sendMessage("One!")) - .build()) - .registerCommand(commandManager.commandBuilder("test") - .withComponent(StaticComponent.required("two")) - .withHandler(c -> c.getCommandSender().sendMessage("Two!")) - .build()); + final PaperCommandManager mgr = new PaperCommandManager<>(this, + CommandExecutionCoordinator.simpleCoordinator()); + mgr.registerBrigadier(); + mgr.command(mgr.commandBuilder("gamemode", + Collections.singleton("gajmöde"), + BukkitCommandMetaBuilder.builder() + .withDescription("Your ugli") + .build()) + .component(EnumComponent.required(GameMode.class, "gamemode")) + .component(StringComponent.newBuilder("player") + .withSuggestionsProvider((v1, v2) -> { + final List suggestions = + new ArrayList<>( + Bukkit.getOnlinePlayers() + .stream() + .map(Player::getName) + .collect(Collectors.toList())); + suggestions.add("dog"); + suggestions.add("cat"); + return suggestions; + }).build()) + .handler(c -> c.getSender() + .asPlayer() + .setGameMode(c.get("gamemode") + .orElse(GameMode.SURVIVAL))) + .build()) + .command(mgr.commandBuilder("kenny") + .component(StaticComponent.required("sux")) + .component(IntegerComponent + .newBuilder("perc") + .withMin(PERC_MIN).withMax(PERC_MAX).build()) + .handler(context -> { + context.getSender().asPlayer().sendMessage(String.format( + "Kenny sux %d%%", + context.get("perc").orElse(PERC_MIN) + )); + }) + .build()) + .command(mgr.commandBuilder("test") + .component(StaticComponent.required("one")) + .handler(c -> c.getSender().sendMessage("One!")) + .build()) + .command(mgr.commandBuilder("test") + .component(StaticComponent.required("two")) + .handler(c -> c.getSender().sendMessage("Two!")) + .build()) + .command(mgr.commandBuilder("uuidtest") + .component(UUID.class, "uuid", builder -> builder + .asRequired() + .withParser((c, i) -> { + final String string = i.peek(); + try { + final UUID uuid = UUID.fromString(string); + i.remove(); + return ComponentParseResult.success(uuid); + } catch (final Exception e) { + return ComponentParseResult.failure(e); + } + }).build()) + .handler(c -> c.getSender() + .sendMessage(String.format("UUID: %s\n", c.get("uuid").orElse(null)))) + .build()) + .command(mgr.commandBuilder("give") + .component(EnumComponent.required(Material.class, "material")) + .component(IntegerComponent.required("amount")) + .handler(c -> { + final Material material = c.getRequired("material"); + final int amount = c.getRequired("amount"); + final ItemStack itemStack = new ItemStack(material, amount); + c.getSender().asPlayer().getInventory().addItem(itemStack); + c.getSender().sendMessage("You've been given stuff, bro."); + }) + .build()) + .command(mgr.commandBuilder("worldtp", BukkitCommandMetaBuilder.builder() + .withDescription("Teleport to a world") + .build()) + .component(WorldComponent.required("world")) + .handler(c -> { + final World world = c.getRequired("world"); + c.getSender().asPlayer().teleport(world.getSpawnLocation()); + c.getSender().sendMessage("Teleported."); + }) + .build()); } catch (final Exception e) { e.printStackTrace(); } diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/BukkitCommand.java b/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/BukkitCommand.java index fd9ee28d..80305d99 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/BukkitCommand.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/BukkitCommand.java @@ -32,15 +32,15 @@ import org.bukkit.plugin.Plugin; import javax.annotation.Nonnull; import java.util.List; -final class BukkitCommand extends org.bukkit.command.Command implements PluginIdentifiableCommand { +final class BukkitCommand extends org.bukkit.command.Command implements PluginIdentifiableCommand { - private final CommandComponent command; - private final BukkitCommandManager bukkitCommandManager; - private final com.intellectualsites.commands.Command cloudCommand; + private final CommandComponent command; + private final BukkitCommandManager bukkitCommandManager; + private final com.intellectualsites.commands.Command cloudCommand; - BukkitCommand(@Nonnull final com.intellectualsites.commands.Command cloudCommand, - @Nonnull final CommandComponent command, - @Nonnull final BukkitCommandManager bukkitCommandManager) { + BukkitCommand(@Nonnull final com.intellectualsites.commands.Command cloudCommand, + @Nonnull final CommandComponent command, + @Nonnull final BukkitCommandManager bukkitCommandManager) { super(command.getName()); this.command = command; this.bukkitCommandManager = bukkitCommandManager; @@ -54,7 +54,7 @@ final class BukkitCommand extends org.bukkit.command.Command implements PluginId for (final String string : strings) { builder.append(" ").append(string); } - this.bukkitCommandManager.executeCommand(BukkitCommandSender.of(commandSender), builder.toString()) + this.bukkitCommandManager.executeCommand((C) BukkitCommandSender.of(commandSender), builder.toString()) .whenComplete(((commandResult, throwable) -> { if (throwable != null) { commandSender.sendMessage(ChatColor.RED + throwable.getCause().getMessage()); @@ -79,7 +79,7 @@ final class BukkitCommand extends org.bukkit.command.Command implements PluginId for (final String string : args) { builder.append(" ").append(string); } - return this.bukkitCommandManager.suggest(BukkitCommandSender.of(sender), builder.toString()); + return this.bukkitCommandManager.suggest((C) BukkitCommandSender.of(sender), builder.toString()); } @Override diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/BukkitCommandManager.java b/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/BukkitCommandManager.java index 03d9142c..612a048e 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/BukkitCommandManager.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/BukkitCommandManager.java @@ -23,7 +23,10 @@ // package com.intellectualsites.commands; +import com.google.common.reflect.TypeToken; import com.intellectualsites.commands.execution.CommandExecutionCoordinator; +import com.intellectualsites.commands.parsers.WorldComponent; +import org.bukkit.World; import org.bukkit.plugin.Plugin; import javax.annotation.Nonnull; @@ -33,7 +36,7 @@ import java.util.function.Function; * Command manager for the Bukkit platform, using {@link BukkitCommandSender} as the * command sender type */ -public class BukkitCommandManager extends CommandManager { +public class BukkitCommandManager extends CommandManager { private final Plugin owningPlugin; @@ -45,12 +48,15 @@ public class BukkitCommandManager extends CommandManager, - CommandExecutionCoordinator> commandExecutionCoordinator) + @Nonnull final Function, + CommandExecutionCoordinator> commandExecutionCoordinator) throws Exception { super(commandExecutionCoordinator, new BukkitPluginRegistrationHandler()); ((BukkitPluginRegistrationHandler) this.getCommandRegistrationHandler()).initialize(this); this.owningPlugin = owningPlugin; + + /* Register Bukkit parsers */ + this.getParserRegistry().registerParserSupplier(TypeToken.of(World.class), params -> new WorldComponent.WorldParser<>()); } /** diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/BukkitCommandSender.java b/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/BukkitCommandSender.java index 10619bf9..0cf68047 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/BukkitCommandSender.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/BukkitCommandSender.java @@ -37,7 +37,12 @@ public abstract class BukkitCommandSender implements CommandSender { private final org.bukkit.command.CommandSender internalSender; - BukkitCommandSender(@Nonnull final org.bukkit.command.CommandSender internalSender) { + /** + * Create a new command sender from a Bukkit {@link CommandSender} + * + * @param internalSender Bukkit command sender + */ + public BukkitCommandSender(@Nonnull final org.bukkit.command.CommandSender internalSender) { this.internalSender = internalSender; } diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/parsers/WorldComponent.java b/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/parsers/WorldComponent.java new file mode 100644 index 00000000..ca03b190 --- /dev/null +++ b/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/parsers/WorldComponent.java @@ -0,0 +1,174 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +package com.intellectualsites.commands.parsers; + +import com.intellectualsites.commands.BukkitCommandSender; +import com.intellectualsites.commands.components.CommandComponent; +import com.intellectualsites.commands.components.parser.ComponentParseResult; +import com.intellectualsites.commands.components.parser.ComponentParser; +import com.intellectualsites.commands.context.CommandContext; +import org.bukkit.Bukkit; +import org.bukkit.World; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.Queue; +import java.util.stream.Collectors; + +/** + * cloud component type that parses Bukkit {@link org.bukkit.World worlds} + * + * @param Command sender type + */ +public class WorldComponent extends CommandComponent { + + protected WorldComponent(final boolean required, + @Nonnull final String name, + @Nonnull final String defaultValue) { + super(required, name, new WorldParser<>(), defaultValue, World.class); + } + + /** + * Create a new builder + * + * @param name Name of the component + * @param Command sender type + * @return Created builder + */ + @Nonnull + public static CommandComponent.Builder newBuilder(@Nonnull final String name) { + return new WorldComponent.Builder<>(name); + } + + /** + * Create a new required component + * + * @param name Component name + * @param Command sender type + * @return Created component + */ + @Nonnull + public static CommandComponent required(@Nonnull final String name) { + return WorldComponent.newBuilder(name).asRequired().build(); + } + + /** + * Create a new optional component + * + * @param name Component name + * @param Command sender type + * @return Created component + */ + @Nonnull + public static CommandComponent optional(@Nonnull final String name) { + return WorldComponent.newBuilder(name).asOptional().build(); + } + + /** + * Create a new optional component with a default value + * + * @param name Component name + * @param defaultValue Default value + * @param Command sender type + * @return Created component + */ + @Nonnull + public static CommandComponent optional(@Nonnull final String name, + @Nonnull final String defaultValue) { + return WorldComponent.newBuilder(name).asOptionalWithDefault(defaultValue).build(); + } + + + public static final class Builder extends CommandComponent.Builder { + + protected Builder(@Nonnull final String name) { + super(World.class, name); + } + + @Nonnull + @Override + public CommandComponent build() { + return new WorldComponent<>(this.isRequired(), this.getName(), this.getDefaultValue()); + } + } + + + public static final class WorldParser implements ComponentParser { + + @Nonnull + @Override + public ComponentParseResult parse(@Nonnull final CommandContext commandContext, + @Nonnull final Queue inputQueue) { + final String input = inputQueue.peek(); + if (input == null) { + return ComponentParseResult.failure(new NullPointerException("No input was provided")); + } + + final World world = Bukkit.getWorld(input); + if (world == null) { + return ComponentParseResult.failure(new WorldParseException(input)); + } + + inputQueue.remove(); + return ComponentParseResult.success(world); + } + + @Nonnull + @Override + public List suggestions(@Nonnull final CommandContext commandContext, @Nonnull final String input) { + return Bukkit.getWorlds().stream().map(World::getName).collect(Collectors.toList()); + } + + } + + + public static final class WorldParseException extends IllegalArgumentException { + + private final String input; + + /** + * Construct a new WorldParseException + * + * @param input Input + */ + public WorldParseException(@Nonnull final String input) { + this.input = input; + } + + /** + * Get the input provided by the sender + * + * @return Input + */ + public String getInput() { + return this.input; + } + + @Override + public String getMessage() { + return String.format("'%s' is not a valid Minecraft world", this.input); + } + } + +} diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/parsers/package-info.java b/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/parsers/package-info.java new file mode 100644 index 00000000..5c5f7f58 --- /dev/null +++ b/cloud-minecraft/cloud-bukkit/src/main/java/com/intellectualsites/commands/parsers/package-info.java @@ -0,0 +1,28 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +/** + * Bukkit specific command components + */ +package com.intellectualsites.commands.parsers; diff --git a/cloud-minecraft/cloud-paper/src/main/java/com/intellectualsites/commands/PaperBrigadierListener.java b/cloud-minecraft/cloud-paper/src/main/java/com/intellectualsites/commands/PaperBrigadierListener.java index dd195908..734ae367 100644 --- a/cloud-minecraft/cloud-paper/src/main/java/com/intellectualsites/commands/PaperBrigadierListener.java +++ b/cloud-minecraft/cloud-paper/src/main/java/com/intellectualsites/commands/PaperBrigadierListener.java @@ -27,10 +27,19 @@ import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; import com.destroystokyo.paper.event.brigadier.CommandRegisteredEvent; import com.intellectualsites.commands.brigadier.CloudBrigadierManager; import com.intellectualsites.commands.components.CommandComponent; +import com.mojang.brigadier.arguments.ArgumentType; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import javax.annotation.Nonnull; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.UUID; class PaperBrigadierListener implements Listener { @@ -40,6 +49,49 @@ class PaperBrigadierListener implements Listener { PaperBrigadierListener(@Nonnull final PaperCommandManager paperCommandManager) throws Exception { this.paperCommandManager = paperCommandManager; this.brigadierManager = new CloudBrigadierManager<>(); + /* Register default mappings */ + final String version = Bukkit.getServer().getClass().getPackage().getName(); + final String nms = version.substring(version.lastIndexOf(".") + 1); + try { + /* Map UUID */ + this.mapSimpleNMS(UUID.class, this.getNMSArgument("UUID", nms).getConstructor()); + /* Map World */ + this.mapSimpleNMS(World.class, this.getNMSArgument("Dimension", nms).getConstructor()); + /* Map Enchantment */ + this.mapSimpleNMS(Enchantment.class, this.getNMSArgument("Enchantment", nms).getConstructor()); + /* Map EntityType */ + this.mapSimpleNMS(EntityType.class, this.getNMSArgument("EntitySummon", nms).getConstructor()); + /* Map Material */ + this.mapSimpleNMS(Material.class, this.getNMSArgument("ItemStack", nms).getConstructor()); + } catch (final Exception e) { + this.paperCommandManager.getOwningPlugin() + .getLogger() + .warning("Failed to map Bukkit types to NMS argument types"); + } + } + + @Nonnull + private Class getNMSArgument(@Nonnull final String argument, @Nonnull final String nms) throws Exception { + return Class.forName(String.format("net.minecraft.server.%s.Argument%s", nms, argument)); + } + + private void mapSimpleNMS(@Nonnull final Class type, + @Nonnull final Constructor constructor) { + try { + this.brigadierManager.registerDefaultArgumentTypeSupplier(type, () -> { + try { + return (ArgumentType) constructor.newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + return null; + }); + } catch (final Exception e) { + this.paperCommandManager.getOwningPlugin() + .getLogger() + .warning(String.format("Failed to map '%s' to a Mojang serializable argument type", + type.getCanonicalName())); + } } @EventHandler diff --git a/cloud-minecraft/cloud-paper/src/main/java/com/intellectualsites/commands/PaperCommandManager.java b/cloud-minecraft/cloud-paper/src/main/java/com/intellectualsites/commands/PaperCommandManager.java index ae85825f..b2a0e01a 100644 --- a/cloud-minecraft/cloud-paper/src/main/java/com/intellectualsites/commands/PaperCommandManager.java +++ b/cloud-minecraft/cloud-paper/src/main/java/com/intellectualsites/commands/PaperCommandManager.java @@ -33,7 +33,7 @@ import java.util.function.Function; /** * Paper command manager that extends {@link BukkitCommandManager} */ -public class PaperCommandManager extends BukkitCommandManager { +public class PaperCommandManager extends BukkitCommandManager { /** * Construct a new Paper command manager @@ -43,8 +43,8 @@ public class PaperCommandManager extends BukkitCommandManager { * @throws Exception If the construction of the manager fails */ public PaperCommandManager(@Nonnull final Plugin owningPlugin, - @Nonnull final Function, - CommandExecutionCoordinator> commandExecutionCoordinator) throws + @Nonnull final Function, + CommandExecutionCoordinator> commandExecutionCoordinator) throws Exception { super(owningPlugin, commandExecutionCoordinator); }