From ffac750f0d8735e19ec47eceb1f063e966390c85 Mon Sep 17 00:00:00 2001 From: Jake Potrebic <15055071+Machine-Maker@users.noreply.github.com> Date: Tue, 31 Aug 2021 13:08:24 -0700 Subject: [PATCH] paper: Add KeyedWorldArgument (#293) --- CHANGELOG.md | 1 + build-logic/src/main/kotlin/Versions.kt | 2 +- .../paper/PaperBrigadierListener.java | 2 +- .../paper/PaperBrigadierMapper.java | 56 ++++++ .../paper/argument/KeyedWorldArgument.java | 174 ++++++++++++++++++ .../paper/argument/package-info.java | 27 +++ 6 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/PaperBrigadierMapper.java create mode 100644 cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/argument/KeyedWorldArgument.java create mode 100644 cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/argument/package-info.java diff --git a/CHANGELOG.md b/CHANGELOG.md index e1494265..6052f122 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Kotlin: Support for suspending command functions using `AnnotationParser.installCoroutineSupport()` - Flags can be bound to a permission +- Paper: Implement KeyedWorldArgument for matching worlds by their namespaced key ### Changed - Added `executeFuture` to `CommandExecutionHandler` which is now used internally. By default, this delegates to the old diff --git a/build-logic/src/main/kotlin/Versions.kt b/build-logic/src/main/kotlin/Versions.kt index c770407c..47817643 100644 --- a/build-logic/src/main/kotlin/Versions.kt +++ b/build-logic/src/main/kotlin/Versions.kt @@ -18,7 +18,7 @@ object Versions { const val cloudburst = "1.0.0-SNAPSHOT" const val adventureApi = "4.8.1" const val adventurePlatform = "4.0.0-SNAPSHOT" - const val paperApi = "1.15.2-R0.1-SNAPSHOT" + const val paperApi = "1.16.5-R0.1-SNAPSHOT" const val velocityApi = "1.1.0" const val spongeApi7 = "7.3.0" const val jetbrainsAnnotations = "20.1.0" diff --git a/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/PaperBrigadierListener.java b/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/PaperBrigadierListener.java index e064c80f..f2d7e94a 100644 --- a/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/PaperBrigadierListener.java +++ b/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/PaperBrigadierListener.java @@ -55,7 +55,7 @@ class PaperBrigadierListener implements Listener { this.brigadierManager.brigadierSenderMapper(sender -> this.paperCommandManager.getCommandSenderMapper().apply(sender.getBukkitSender())); - new BukkitBrigadierMapper<>(this.paperCommandManager, this.brigadierManager); + new PaperBrigadierMapper<>(new BukkitBrigadierMapper<>(this.paperCommandManager, this.brigadierManager)); this.brigadierManager .backwardsBrigadierSenderMapper(new BukkitBackwardsBrigadierSenderMapper<>(this.paperCommandManager)); diff --git a/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/PaperBrigadierMapper.java b/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/PaperBrigadierMapper.java new file mode 100644 index 00000000..2e45a55e --- /dev/null +++ b/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/PaperBrigadierMapper.java @@ -0,0 +1,56 @@ +// +// MIT License +// +// Copyright (c) 2021 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.paper; + +import cloud.commandframework.bukkit.BukkitBrigadierMapper; +import cloud.commandframework.bukkit.internal.CraftBukkitReflection; +import cloud.commandframework.paper.argument.KeyedWorldArgument; +import io.leangen.geantyref.TypeToken; +import org.bukkit.World; +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * Brigadier mappings for Paper native Brigadier. This is currently only used when the PaperBrigadierListener is in use, + * not when the CloudCommodoreManager is in use on Paper. This is because all argument types registered here require + * Paper 1.15+ anyways. + * + * @param sender type + */ +final class PaperBrigadierMapper { + + PaperBrigadierMapper( + final @NonNull BukkitBrigadierMapper mapper + ) { + this.registerMappings(mapper); + } + + private void registerMappings(final @NonNull BukkitBrigadierMapper mapper) { + final Class keyed = CraftBukkitReflection.findClass("org.bukkit.Keyed"); + if (keyed != null && keyed.isAssignableFrom(World.class)) { + mapper.mapSimpleNMS(new TypeToken>() { + }, "resource_location"); + } + } + +} diff --git a/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/argument/KeyedWorldArgument.java b/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/argument/KeyedWorldArgument.java new file mode 100644 index 00000000..87ecab51 --- /dev/null +++ b/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/argument/KeyedWorldArgument.java @@ -0,0 +1,174 @@ +// +// MIT License +// +// Copyright (c) 2021 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.paper.argument; + +import cloud.commandframework.ArgumentDescription; +import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.arguments.parser.ArgumentParseResult; +import cloud.commandframework.arguments.parser.ArgumentParser; +import cloud.commandframework.bukkit.parsers.WorldArgument; +import cloud.commandframework.context.CommandContext; +import cloud.commandframework.exceptions.parsing.NoInputProvidedException; +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; +import java.util.function.BiFunction; + +/** + * Argument type that parses Bukkit {@link World worlds} from a {@link NamespacedKey}. + * + * @param Command sender type + */ +public class KeyedWorldArgument extends CommandArgument { + + protected KeyedWorldArgument( + final boolean required, + final @NonNull String name, + final @NonNull String defaultValue, + final @Nullable BiFunction, String, List> suggestionsProvider, + final @NonNull ArgumentDescription defaultDescription + ) { + super(required, name, new Parser<>(), defaultValue, World.class, suggestionsProvider, defaultDescription); + } + + /** + * Create a new {@link Builder}. + * + * @param name Name of the argument + * @param Command sender type + * @return Created builder + */ + public static KeyedWorldArgument.@NonNull Builder builder(final @NonNull String name) { + return new KeyedWorldArgument.Builder<>(name); + } + + /** + * Create a new required {@link KeyedWorldArgument}. + * + * @param name Argument name + * @param Command sender type + * @return Created argument + */ + public static @NonNull KeyedWorldArgument of(final @NonNull String name) { + return KeyedWorldArgument.builder(name).asRequired().build(); + } + + /** + * Create a new optional {@link KeyedWorldArgument}. + * + * @param name Argument name + * @param Command sender type + * @return Created argument + */ + public static @NonNull KeyedWorldArgument optional(final @NonNull String name) { + return KeyedWorldArgument.builder(name).asOptional().build(); + } + + /** + * Create a new {@link KeyedWorldArgument} with the specified default value. + * + * @param name Argument name + * @param defaultValue Default value + * @param Command sender type + * @return Created argument + */ + public static @NonNull KeyedWorldArgument optional( + final @NonNull String name, + final @NonNull String defaultValue + ) { + return KeyedWorldArgument.builder(name).asOptionalWithDefault(defaultValue).build(); + } + + public static final class Builder extends CommandArgument.TypedBuilder> { + + private Builder(final @NonNull String name) { + super(World.class, name); + } + + @Override + public @NonNull KeyedWorldArgument build() { + return new KeyedWorldArgument<>( + this.isRequired(), + this.getName(), + this.getDefaultValue(), + this.getSuggestionsProvider(), + this.getDefaultDescription() + ); + } + + } + + public static final class Parser implements ArgumentParser { + + @Override + public @NonNull ArgumentParseResult<@NonNull World> 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( + Parser.class, + commandContext + )); + } + + final NamespacedKey key = NamespacedKey.fromString(input); + if (key == null) { + return ArgumentParseResult.failure(new WorldArgument.WorldParseException(input, commandContext)); + } + + final World world = Bukkit.getWorld(key); + if (world == null) { + return ArgumentParseResult.failure(new WorldArgument.WorldParseException(input, commandContext)); + } + + inputQueue.remove(); + return ArgumentParseResult.success(world); + } + + @Override + public @NonNull List<@NonNull String> suggestions( + final @NonNull CommandContext commandContext, + final @NonNull String input + ) { + final List completions = new ArrayList<>(); + for (final World world : Bukkit.getWorlds()) { + if (world.getKey().getNamespace().equals(NamespacedKey.MINECRAFT)) { + completions.add(world.getKey().getKey()); + } else { + completions.add(world.getKey().toString()); + } + } + return completions; + } + + } +} diff --git a/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/argument/package-info.java b/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/argument/package-info.java new file mode 100644 index 00000000..6a5870c8 --- /dev/null +++ b/cloud-minecraft/cloud-paper/src/main/java/cloud/commandframework/paper/argument/package-info.java @@ -0,0 +1,27 @@ +// +// MIT License +// +// Copyright (c) 2021 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. +// +/** + * Paper specific command arguments + */ +package cloud.commandframework.paper.argument;