diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fa39a2e..5efa104f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added CloudInjectionModule to cloud-velocity - Added PlayerArgument to cloud-velocity - Added TextColorArgument to minecraft-extras + - Added ServerArgument to cloud-velocity ## [1.0.2] - 2020-10-18 diff --git a/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityCaptionKeys.java b/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityCaptionKeys.java index 3fa8ce35..d8f9f7a9 100644 --- a/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityCaptionKeys.java +++ b/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityCaptionKeys.java @@ -42,6 +42,11 @@ public final class VelocityCaptionKeys { */ public static final Caption ARGUMENT_PARSE_FAILURE_PLAYER = of("argument.parse.failure.player"); + /** + * Variables: {input} + */ + public static final Caption ARGUMENT_PARSE_FAILURE_SERVER = of("argument.parse.failure.server"); + private VelocityCaptionKeys() { } diff --git a/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityCommandManager.java b/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityCommandManager.java index d186504b..83292c7b 100644 --- a/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityCommandManager.java +++ b/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityCommandManager.java @@ -54,6 +54,11 @@ public class VelocityCommandManager extends CommandManager { */ public static final String ARGUMENT_PARSE_FAILURE_PLAYER = "'{input}' is not a valid player"; + /** + * Default caption for {@link VelocityCaptionKeys#ARGUMENT_PARSE_FAILURE_SERVER} + */ + public static final String ARGUMENT_PARSE_FAILURE_SERVER = "'{input}' is not a valid server"; + private final ProxyServer proxyServer; private final Function commandSenderMapper; private final Function backwardsCommandSenderMapper; @@ -82,8 +87,14 @@ public class VelocityCommandManager extends CommandManager { if (this.getCaptionRegistry() instanceof FactoryDelegatingCaptionRegistry) { final FactoryDelegatingCaptionRegistry factoryDelegatingCaptionRegistry = (FactoryDelegatingCaptionRegistry) this.getCaptionRegistry(); - factoryDelegatingCaptionRegistry.registerMessageFactory(VelocityCaptionKeys.ARGUMENT_PARSE_FAILURE_PLAYER, - (context, key) -> ARGUMENT_PARSE_FAILURE_PLAYER); + factoryDelegatingCaptionRegistry.registerMessageFactory( + VelocityCaptionKeys.ARGUMENT_PARSE_FAILURE_PLAYER, + (context, key) -> ARGUMENT_PARSE_FAILURE_PLAYER + ); + factoryDelegatingCaptionRegistry.registerMessageFactory( + VelocityCaptionKeys.ARGUMENT_PARSE_FAILURE_SERVER, + (context, key) -> ARGUMENT_PARSE_FAILURE_SERVER + ); } } diff --git a/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/arguments/ServerArgument.java b/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/arguments/ServerArgument.java new file mode 100644 index 00000000..f5ff4ddc --- /dev/null +++ b/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/arguments/ServerArgument.java @@ -0,0 +1,216 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// 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 cloud.commandframework.velocity.arguments; + +import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.arguments.parser.ArgumentParseResult; +import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.captions.CaptionVariable; +import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.NoInputProvidedException; +import cloud.commandframework.exceptions.parsing.ParserException; +import cloud.commandframework.velocity.VelocityCaptionKeys; +import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.server.RegisteredServer; +import io.leangen.geantyref.TypeToken; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.function.BiFunction; +import java.util.stream.Collectors; + +/** + * Argument parser for {@link RegisteredServer servers} + * + * @param Command sender type + */ +public final class ServerArgument extends CommandArgument { + + private ServerArgument( + final @NonNull ProxyServer proxyServer, + final boolean required, + final @NonNull String name, + final @Nullable BiFunction, String, List> suggestionsProvider, + final @NonNull Collection<@NonNull BiFunction<@NonNull CommandContext, @NonNull Queue<@NonNull String>, + @NonNull ArgumentParseResult>> argumentPreprocessors + ) { + super( + required, + name, + new ServerParser<>(proxyServer), + "", + TypeToken.get(RegisteredServer.class), + suggestionsProvider, + argumentPreprocessors + ); + } + + /** + * Create a new argument builder + * + * @param name Argument name + * @param proxyServer Proxy server instance + * @param Command sender type + * @return Constructed builder + */ + public static CommandArgument.@NonNull Builder newBuilder( + final @NonNull String name, + final @NonNull ProxyServer proxyServer + ) { + return new Builder( + name, + proxyServer + ).withParser( + new ServerParser<>( + proxyServer + ) + ); + } + + /** + * Create a new required player argument + * + * @param name Argument name + * @param proxyServer Proxy server instance + * @param Command sender type + * @return Created argument + */ + public static @NonNull CommandArgument of( + final @NonNull String name, + final @NonNull ProxyServer proxyServer + ) { + return ServerArgument.newBuilder(name, proxyServer).asRequired().build(); + } + + /** + * Create a new optional server argument + * + * @param name Argument name + * @param proxyServer Proxy server instance + * @param Command sender type + * @return Created argument + */ + public static @NonNull CommandArgument optional( + final @NonNull String name, + final @NonNull ProxyServer proxyServer + ) { + return ServerArgument.newBuilder(name, proxyServer).asOptional().build(); + } + + public static final class Builder extends CommandArgument.Builder { + + private final ProxyServer proxyServer; + + private Builder( + final @NonNull String name, + final @NonNull ProxyServer proxyServer + ) { + super(TypeToken.get(RegisteredServer.class), name); + this.proxyServer = proxyServer; + } + + @Override + public @NonNull CommandArgument<@NonNull C, @NonNull RegisteredServer> build() { + return new ServerArgument<>( + this.proxyServer, + this.isRequired(), + this.getName(), + this.getSuggestionsProvider(), + new LinkedList<>() + ); + } + + } + + public static final class ServerParser implements ArgumentParser { + + private final ProxyServer proxyServer; + + /** + * Create a new server parser + * + * @param proxyServer Proxy server instance + */ + public ServerParser( + @NonNull final ProxyServer proxyServer + ) { + this.proxyServer = proxyServer; + } + + @Override + public @NonNull ArgumentParseResult<@NonNull RegisteredServer> parse( + @NonNull final CommandContext<@NonNull C> commandContext, + @NonNull final Queue<@NonNull String> inputQueue + ) { + final String input = inputQueue.peek(); + if (input == null) { + return ArgumentParseResult.failure(new NoInputProvidedException( + ServerParser.class, + commandContext + )); + } + final RegisteredServer server = this.proxyServer.getServer(input).orElse(null); + if (server == null) { + return ArgumentParseResult.failure( + new ServerParseException( + input, + commandContext + ) + ); + } + inputQueue.remove(); + return ArgumentParseResult.success(server); + } + + @Override + public @NonNull List<@NonNull String> suggestions( + final @NonNull CommandContext commandContext, + final @NonNull String input + ) { + return this.proxyServer.getAllServers().stream().map(s -> s.getServerInfo().getName()).collect(Collectors.toList()); + } + + } + + public static final class ServerParseException extends ParserException { + + private ServerParseException( + final @NonNull String input, + final @NonNull CommandContext context + ) { + super( + ServerParser.class, + context, + VelocityCaptionKeys.ARGUMENT_PARSE_FAILURE_SERVER, + CaptionVariable.of("input", input) + ); + } + + } + +} diff --git a/examples/example-velocity/src/main/java/cloud/commandframework/examples/velocity/ExampleVelocityPlugin.java b/examples/example-velocity/src/main/java/cloud/commandframework/examples/velocity/ExampleVelocityPlugin.java index c5c5f889..9ae17693 100644 --- a/examples/example-velocity/src/main/java/cloud/commandframework/examples/velocity/ExampleVelocityPlugin.java +++ b/examples/example-velocity/src/main/java/cloud/commandframework/examples/velocity/ExampleVelocityPlugin.java @@ -27,6 +27,7 @@ import cloud.commandframework.execution.CommandExecutionCoordinator; import cloud.commandframework.velocity.CloudInjectionModule; import cloud.commandframework.velocity.VelocityCommandManager; import cloud.commandframework.velocity.arguments.PlayerArgument; +import cloud.commandframework.velocity.arguments.ServerArgument; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Key; @@ -37,6 +38,7 @@ import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; +import com.velocitypowered.api.proxy.server.RegisteredServer; import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -94,6 +96,21 @@ public final class ExampleVelocityPlugin { } ) ); + commandManager.command( + commandManager.commandBuilder("example-server") + .argument(ServerArgument.of("server", this.server)) + .handler(context -> { + final RegisteredServer server = context.get("server"); + context.getSender().sendMessage( + Identity.nil(), + Component.text().append( + Component.text("Selected ", NamedTextColor.GOLD) + ).append( + Component.text(server.getServerInfo().getName(), NamedTextColor.AQUA) + ).build() + ); + }) + ); } }