From 57bd89c06dc5fb8fcb18f8ba9dd1d64d89a55ecf Mon Sep 17 00:00:00 2001 From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> Date: Mon, 1 Aug 2022 22:52:29 -0400 Subject: [PATCH 01/27] Brig Update Rebased Rebase mark COMMANDS lifecycle event as blocking reloads re-fire event for plugins after bukkit reload move command reloading up Update and rebase rebase clean up examples in TestPlugin switch to lifecycle events rebased + javadoc + style changes fix compile issues refactor to not rely on MinecraftServer.getServer() update brig cleanup fix plugin command overrides add range arguments some refactoring Unwrap custom argument types Make BasicCommand even more basic Remove use of brig types for BasicCommand fix issue with redirected sub commands not running requires checks Add color and heightmap arg types Add requirement for redirects fix issue with redirect root command not being applied rework command registration Rebuild Fix Add some utilitites + new api Remove deprecations Fixes Rework api a bit Resolve conflicts Fix removal logic Update command builder Cleanup Some resolves Rename methods, add validation to constructor Revert "Pull #7387" This reverts commit b2fc9b47fdb7b6ed87bac423e5abdfbb481ebb5f. Pull #7387 Remove class usage in resolve argument utility Fix alias, smart usage, and overriding issues Add resolver utility Fix splitting getting rid of trailing spaces Wtf is this? Fix tests Fix arguments + unknown command event logic Support dispatcher reloading with bukkit commands Assume true if unexpected command source is used to check conditions. Properly remove legacy command aliases/namespaced names Don't recreate collections, prefer mirroring + support mutability Diff cleanup Improve example command + update docs Fix tests + cleanup Cleanup + fix test Documentation + redirect sending fixes Signing support Use only 1 dispatcher + shadow node one dispatcher Rebase + bukkit test Work More work Initial work --- .../brigadier/BukkitBrigadierCommand.java | 2 + .../BukkitBrigadierCommandSource.java | 4 + .../AsyncPlayerSendCommandsEvent.java | 2 + .../brigadier/CommandRegisteredEvent.java | 2 + .../paper/brigadier/PaperBrigadier.java | 7 +- .../brigadier/PaperBrigadierProvider.java | 30 - .../0474-Brigadier-based-command-API.patch | 1197 ++++++++ ...d-missing-default-perms-for-commands.patch | 6 +- .../1060-Brigadier-based-command-API.patch | 2506 +++++++++++++++++ .../io/papermc/testplugin/TestPlugin.java | 110 + .../testplugin/TestPluginBootstrap.java | 47 + .../ComponentCommandExceptionType.java | 25 + .../example/ExampleAdminCommand.java | 153 + .../testplugin/example/IceCreamType.java | 9 + .../example/IceCreamTypeArgument.java | 48 + .../example/MaterialArgumentType.java | 88 + 16 files changed, 4203 insertions(+), 33 deletions(-) delete mode 100644 Paper-MojangAPI/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProvider.java create mode 100644 patches/api/0474-Brigadier-based-command-API.patch create mode 100644 patches/server/1060-Brigadier-based-command-API.patch create mode 100644 test-plugin/src/main/java/io/papermc/testplugin/example/ComponentCommandExceptionType.java create mode 100644 test-plugin/src/main/java/io/papermc/testplugin/example/ExampleAdminCommand.java create mode 100644 test-plugin/src/main/java/io/papermc/testplugin/example/IceCreamType.java create mode 100644 test-plugin/src/main/java/io/papermc/testplugin/example/IceCreamTypeArgument.java create mode 100644 test-plugin/src/main/java/io/papermc/testplugin/example/MaterialArgumentType.java diff --git a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommand.java b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommand.java index 0b1af3a8d45d..515595d589ed 100644 --- a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommand.java +++ b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommand.java @@ -9,6 +9,8 @@ * Brigadier {@link Command}, {@link SuggestionProvider}, and permission checker for Bukkit {@link Command}s. * * @param command source type + * @deprecated For removal, use the new brigadier api. */ +@Deprecated(forRemoval = true) // Paper public interface BukkitBrigadierCommand extends Command, Predicate, SuggestionProvider { } diff --git a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommandSource.java b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommandSource.java index 7a0e81658cc2..904c56acd3e2 100644 --- a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommandSource.java +++ b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommandSource.java @@ -6,6 +6,10 @@ import org.bukkit.entity.Entity; import org.jetbrains.annotations.Nullable; +/** + * @deprecated For removal, use the new brigadier api. + */ +@Deprecated(forRemoval = true) // Paper public interface BukkitBrigadierCommandSource { @Nullable diff --git a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.java b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.java index 495b0f0d2f29..3513ec81a4ea 100644 --- a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.java +++ b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.java @@ -28,8 +28,10 @@ *

If your logic is not safe to run asynchronously, only react to the synchronous version.

* *

This is a draft/experimental API and is subject to change.

+ * @deprecated For removal, use the new brigadier api. */ @ApiStatus.Experimental +@Deprecated public class AsyncPlayerSendCommandsEvent extends PlayerEvent { private static final HandlerList handlers = new HandlerList(); diff --git a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/CommandRegisteredEvent.java b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/CommandRegisteredEvent.java index eb0409d81683..e754439cf3e3 100644 --- a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/CommandRegisteredEvent.java +++ b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/CommandRegisteredEvent.java @@ -20,8 +20,10 @@ * run at a later point in the server lifetime due to plugins, a server reload, etc.

* *

This is a draft/experimental API and is subject to change.

+ * @deprecated For removal, use the new brigadier api. */ @ApiStatus.Experimental +@Deprecated public class CommandRegisteredEvent extends ServerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); diff --git a/Paper-MojangAPI/src/main/java/io/papermc/paper/brigadier/PaperBrigadier.java b/Paper-MojangAPI/src/main/java/io/papermc/paper/brigadier/PaperBrigadier.java index 1ed5a6d271b7..f66abe214922 100644 --- a/Paper-MojangAPI/src/main/java/io/papermc/paper/brigadier/PaperBrigadier.java +++ b/Paper-MojangAPI/src/main/java/io/papermc/paper/brigadier/PaperBrigadier.java @@ -1,6 +1,7 @@ package io.papermc.paper.brigadier; import com.mojang.brigadier.Message; +import io.papermc.paper.command.brigadier.MessageComponentSerializer; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.TextComponent; @@ -8,7 +9,9 @@ /** * Helper methods to bridge the gaps between Brigadier and Paper-MojangAPI. + * @deprecated For removal, use the new brigadier api. */ +@Deprecated(forRemoval = true) // Paper public final class PaperBrigadier { private PaperBrigadier() { throw new RuntimeException("PaperBrigadier is not to be instantiated!"); @@ -23,7 +26,7 @@ private PaperBrigadier() { * @return A new Brigadier {@link Message} */ public static @NonNull Message message(final @NonNull ComponentLike componentLike) { - return PaperBrigadierProvider.instance().message(componentLike); + return MessageComponentSerializer.message().serialize(componentLike.asComponent()); } /** @@ -37,6 +40,6 @@ private PaperBrigadier() { * @return The created {@link Component} */ public static @NonNull Component componentFromMessage(final @NonNull Message message) { - return PaperBrigadierProvider.instance().componentFromMessage(message); + return MessageComponentSerializer.message().deserialize(message); } } diff --git a/Paper-MojangAPI/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProvider.java b/Paper-MojangAPI/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProvider.java deleted file mode 100644 index 7f248063843c..000000000000 --- a/Paper-MojangAPI/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProvider.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.papermc.paper.brigadier; - -import com.mojang.brigadier.Message; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.ComponentLike; -import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -import org.checkerframework.checker.nullness.qual.NonNull; - -import static java.util.Objects.requireNonNull; - -interface PaperBrigadierProvider { - final class Holder { - private static @MonotonicNonNull PaperBrigadierProvider INSTANCE; - } - - static @NonNull PaperBrigadierProvider instance() { - return requireNonNull(Holder.INSTANCE, "PaperBrigadierProvider has not yet been initialized!"); - } - - static void initialize(final @NonNull PaperBrigadierProvider instance) { - if (Holder.INSTANCE != null) { - throw new IllegalStateException("PaperBrigadierProvider has already been initialized!"); - } - Holder.INSTANCE = instance; - } - - @NonNull Message message(@NonNull ComponentLike componentLike); - - @NonNull Component componentFromMessage(@NonNull Message message); -} diff --git a/patches/api/0474-Brigadier-based-command-API.patch b/patches/api/0474-Brigadier-based-command-API.patch new file mode 100644 index 000000000000..9515fdf10f86 --- /dev/null +++ b/patches/api/0474-Brigadier-based-command-API.patch @@ -0,0 +1,1197 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> +Date: Mon, 1 Aug 2022 22:50:29 -0400 +Subject: [PATCH] Brigadier based command API + + +diff --git a/build.gradle.kts b/build.gradle.kts +index 04853c43b99951bf0d4c96ef73724625bdaf018f..72e8ee7b2df322a667a787a1d18bc91a7c9d247d 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -27,6 +27,7 @@ configurations.api { + } + + dependencies { ++ api("com.mojang:brigadier:1.1.8") // Paper, expose! + // api dependencies are listed transitively to API consumers + api("com.google.guava:guava:32.1.2-jre") + api("com.google.code.gson:gson:2.10.1") +diff --git a/src/main/java/io/papermc/paper/command/brigadier/BasicCommand.java b/src/main/java/io/papermc/paper/command/brigadier/BasicCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0eb33bdf8d142b5f066b75b081b343000a352067 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/BasicCommand.java +@@ -0,0 +1,37 @@ ++package io.papermc.paper.command.brigadier; ++ ++import java.util.Collection; ++import java.util.Collections; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * This represents a simple command implementation that is wrapped ++ * around brigadier. ++ */ ++@ApiStatus.Experimental ++@FunctionalInterface ++public interface BasicCommand { ++ ++ /** ++ * Executes the command with the given {@link CommandSourceStack} and arguments. ++ * ++ * @param commandSourceStack the commandSourceStack of the command. ++ * @param args the arguments of the command. ++ * @return the result of the command execution. Typically return -1 for fail, and 1 for success. ++ */ ++ @ApiStatus.OverrideOnly ++ int execute(@NotNull CommandSourceStack commandSourceStack, @NotNull String[] args); ++ ++ /** ++ * Suggests possible completions for the given command {@link CommandSourceStack} and arguments. ++ * ++ * @param commandSourceStack the commandSourceStack of the command. ++ * @param args the arguments of the command. ++ * @return a collection of suggestions ++ */ ++ @ApiStatus.OverrideOnly ++ default @NotNull Collection suggest(final @NotNull CommandSourceStack commandSourceStack, final @NotNull String[] args) { ++ return Collections.emptyList(); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/CommandSourceStack.java b/src/main/java/io/papermc/paper/command/brigadier/CommandSourceStack.java +new file mode 100644 +index 0000000000000000000000000000000000000000..66960d317b608e1266d4de2a873a37913d901f2a +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/CommandSourceStack.java +@@ -0,0 +1,35 @@ ++package io.papermc.paper.command.brigadier; ++ ++import org.bukkit.Location; ++import org.bukkit.command.CommandSender; ++import org.bukkit.entity.Entity; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++/** ++ * Represents a vanilla command source stack which is used ++ * for command execution. ++ */ ++@ApiStatus.NonExtendable ++@ApiStatus.Experimental ++public interface CommandSourceStack { ++ ++ /** ++ * Gets the location that this command is being executed at. ++ * @return location ++ */ ++ @NotNull Location getLocation(); ++ ++ /** ++ * Gets the sender that executed this command. ++ * @return source type ++ */ ++ @NotNull CommandSender getSender(); ++ ++ /** ++ * Gets the entity that triggered the execution of this command. ++ * May not always be the command source. ++ */ ++ @Nullable Entity getExecutor(); ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/Commands.java b/src/main/java/io/papermc/paper/command/brigadier/Commands.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5747ea03aef2f2a464bd6b30aba3a536292ea14d +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/Commands.java +@@ -0,0 +1,209 @@ ++package io.papermc.paper.command.brigadier; ++ ++import com.mojang.brigadier.CommandDispatcher; ++import com.mojang.brigadier.arguments.ArgumentType; ++import com.mojang.brigadier.builder.LiteralArgumentBuilder; ++import com.mojang.brigadier.builder.RequiredArgumentBuilder; ++import com.mojang.brigadier.tree.LiteralCommandNode; ++import io.papermc.paper.plugin.configuration.PluginMeta; ++import io.papermc.paper.plugin.lifecycle.event.registrar.Registrar; ++import java.util.Collections; ++import java.util.List; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++/** ++ * The registrar for custom commands. ++ */ ++@ApiStatus.Experimental ++@ApiStatus.NonExtendable ++public interface Commands extends Registrar { ++ ++ /** ++ * Utility to create a literal command node builder with the correct generic. ++ * ++ * @param literal literal name ++ * @return builder ++ */ ++ static @NotNull LiteralArgumentBuilder literal(final @NotNull String literal) { ++ return LiteralArgumentBuilder.literal(literal); ++ } ++ ++ /** ++ * Utility to create a required argument builder with the correct generic. ++ * ++ * @param name name ++ * @param argumentType type ++ * @return value ++ * @param argument type ++ */ ++ static @NotNull RequiredArgumentBuilder argument(final @NotNull String name, final @NotNull ArgumentType argumentType) { ++ return RequiredArgumentBuilder.argument(name, argumentType); ++ } ++ ++ /** ++ * Gets the underlying {@link CommandDispatcher}. ++ * ++ *

Note: This is a delicate API that must be used with care to ensure a consistent user experience.

++ * ++ *

When registering commands, it should be preferred to use {@link #register(PluginMeta, LiteralCommandNode, String, List) register methods} ++ * over directly registering to the dispatcher wherever possible. {@link #register(PluginMeta, LiteralCommandNode, String, List) Register methods} ++ * automatically handle command namespacing, command help, plugin association with commands, and more.

++ * ++ *

Example use cases for this method may include: ++ *

    ++ *
  • Implementing integration between an external command framework and Paper (although {@link #register(PluginMeta, LiteralCommandNode, String, List) register methods} should still be preferred where possible)
  • ++ *
  • Registering new child nodes to an existing plugin command (for example an "addon" plugin to another plugin may want to do this)
  • ++ *
  • Retrieving existing command nodes to build redirects
  • ++ *
++ * @return dispatcher ++ */ ++ @ApiStatus.Experimental ++ @NotNull CommandDispatcher getDispatcher(); ++ ++ /** ++ * Registers a command for the current plugin context. ++ * ++ *

Commands have certain the overriding behavior of: ++ *

    ++ *
  • Aliases will not override already existing commands (excluding namespaced ones)
  • ++ *
  • The main command/namespaced label will override already existing commands
  • ++ *
++ * ++ * @param node the built literal command node ++ * @return true if the command was registered successfully ++ */ ++ default boolean register(final @NotNull LiteralCommandNode node) { ++ return this.register(node, null, Collections.emptyList()); ++ } ++ ++ /** ++ * Registers a command for the current plugin context. ++ * ++ *

Commands have certain the overriding behavior of: ++ *

    ++ *
  • Aliases will not override already existing commands (excluding namespaced ones)
  • ++ *
  • The main command/namespaced label will override already existing commands
  • ++ *
++ * ++ * @param node the built literal command node ++ * @param description the help description for the root literal node ++ * @return true if the command was registered successfully ++ */ ++ default boolean register(final @NotNull LiteralCommandNode node, final @Nullable String description) { ++ return this.register(node, description, Collections.emptyList()); ++ } ++ ++ /** ++ * Registers a command for the current plugin context. ++ * ++ *

Commands have certain the overriding behavior of: ++ *

    ++ *
  • Aliases will not override already existing commands (excluding namespaced ones)
  • ++ *
  • The main command/namespaced label will override already existing commands
  • ++ *
++ * ++ * @param node the built literal command node ++ * @param aliases a collection of aliases to register the literal node's command to ++ * @return true if the literal node or at least one alias was registered successfully ++ */ ++ default boolean register(final @NotNull LiteralCommandNode node, final @NotNull List aliases) { ++ return this.register(node, null, aliases); ++ } ++ ++ /** ++ * Registers a command for the current plugin context. ++ * ++ *

Commands have certain the overriding behavior of: ++ *

    ++ *
  • Aliases will not override already existing commands (excluding namespaced ones)
  • ++ *
  • The main command/namespaced label will override already existing commands
  • ++ *
++ * ++ * @param node the built literal command node ++ * @param description the help description for the root literal node ++ * @param aliases a collection of aliases to register the literal node's command to ++ * @return true if the literal node or at least one alias was registered successfully ++ */ ++ boolean register(@NotNull LiteralCommandNode node, @Nullable String description, @NotNull List aliases); ++ ++ /** ++ * Registers a command for a plugin. ++ * ++ *

Commands have certain the overriding behavior of: ++ *

    ++ *
  • Aliases will not override already existing commands (excluding namespaced ones)
  • ++ *
  • The main command/namespaced label will override already existing commands
  • ++ *
++ * ++ * @param pluginMeta the owning plugin's meta ++ * @param node the built literal command node ++ * @param description the help description for the root literal node ++ * @param aliases a collection of aliases to register the literal node's command to ++ * @return true if the literal node or at least one alias was registered successfully ++ */ ++ boolean register(@NotNull PluginMeta pluginMeta, @NotNull LiteralCommandNode node, @Nullable String description, @NotNull List aliases); ++ ++ /** ++ * Registers a command under the same logic as {@link Commands#register(LiteralCommandNode, String, List)}. ++ * ++ * @param label command label ++ * @param basicCommand command implementation ++ * @return true if the command was registered ++ */ ++ default boolean register(final @NotNull String label, final @NotNull BasicCommand basicCommand) { ++ return this.register(label, null, Collections.emptyList(), basicCommand); ++ } ++ ++ /** ++ * Registers a command under the same logic as {@link Commands#register(LiteralCommandNode, String, List)}. ++ * ++ * @param label command label ++ * @param description the help description for the root literal node ++ * @param basicCommand command implementation ++ * @return true if the command was registered ++ */ ++ default boolean register(final @NotNull String label, final @Nullable String description, final @NotNull BasicCommand basicCommand) { ++ return this.register(label, description, Collections.emptyList(), basicCommand); ++ } ++ ++ /** ++ * Registers a command under the same logic as {@link Commands#register(LiteralCommandNode, String, List)}. ++ * ++ * @param label command label ++ * @param aliases a collection of aliases ++ * @param basicCommand command implementation ++ * @return true if at least one label was properly registered ++ * into the dispatcher ++ */ ++ default boolean register(final @NotNull String label, final @NotNull List aliases, final @NotNull BasicCommand basicCommand) { ++ return this.register(label, null, aliases, basicCommand); ++ } ++ ++ /** ++ * Registers a command under the same logic as {@link Commands#register(LiteralCommandNode, String, List)}. ++ * ++ * @param label command label ++ * @param description the help description for the root literal node ++ * @param aliases a collection of aliases ++ * @param basicCommand command implementation ++ * @return true if at least one label was properly registered ++ * into the dispatcher ++ */ ++ boolean register(@NotNull String label, @Nullable String description, @NotNull List aliases, @NotNull BasicCommand basicCommand); ++ ++ /** ++ * Registers a command under the same logic as {@link Commands#register(PluginMeta, LiteralCommandNode, String, List)}. ++ * ++ * @param pluginMeta the owning plugin's meta ++ * @param label command label ++ * @param description the help description for the root literal node ++ * @param aliases a collection of aliases ++ * @param basicCommand command implementation ++ * @return true if at least one label was properly registered ++ * into the dispatcher ++ */ ++ boolean register(@NotNull PluginMeta pluginMeta, @NotNull String label, @Nullable String description, @NotNull List aliases, @NotNull BasicCommand basicCommand); ++ ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializer.java b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializer.java +new file mode 100644 +index 0000000000000000000000000000000000000000..10de213a52edec495e0dc3202193a3c74aa1dd92 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializer.java +@@ -0,0 +1,24 @@ ++package io.papermc.paper.command.brigadier; ++ ++import com.mojang.brigadier.Message; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.serializer.ComponentSerializer; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * A component serializer that converts it to a brigadier compatible type. ++ */ ++@ApiStatus.Experimental ++@ApiStatus.NonExtendable ++public interface MessageComponentSerializer extends ComponentSerializer { ++ ++ /** ++ * A component serializer that converts it to a brigadier compatible type. ++ * ++ * @return serializer instance ++ */ ++ static @NotNull MessageComponentSerializer message() { ++ return MessageComponentSerializerHolder.PROVIDER; ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerHolder.java b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerHolder.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c30a6e6924c0d515f6ba1eef635113676f7efff9 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerHolder.java +@@ -0,0 +1,12 @@ ++package io.papermc.paper.command.brigadier; ++ ++import java.util.ServiceLoader; ++import org.jetbrains.annotations.ApiStatus; ++ ++@ApiStatus.Internal ++final class MessageComponentSerializerHolder { ++ ++ public static final MessageComponentSerializer PROVIDER = ServiceLoader.load(MessageComponentSerializer.class) ++ .findFirst() ++ .orElseThrow(); ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/ArgumentTypes.java b/src/main/java/io/papermc/paper/command/brigadier/argument/ArgumentTypes.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e9059ca88cbb1a82356ccb5eae6360a73cfce318 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/ArgumentTypes.java +@@ -0,0 +1,303 @@ ++package io.papermc.paper.command.brigadier.argument; ++ ++import com.mojang.brigadier.arguments.ArgumentType; ++import io.papermc.paper.command.brigadier.argument.predicate.ItemStackPredicate; ++import io.papermc.paper.command.brigadier.argument.range.DoubleRangeProvider; ++import io.papermc.paper.command.brigadier.argument.range.IntegerRangeProvider; ++import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver; ++import io.papermc.paper.command.brigadier.argument.resolvers.PlayerProfileListResolver; ++import io.papermc.paper.command.brigadier.argument.resolvers.selector.EntitySelectorArgumentResolver; ++import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver; ++import io.papermc.paper.entity.LookAnchor; ++import java.util.ServiceLoader; ++import java.util.UUID; ++import net.kyori.adventure.key.Key; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.format.NamedTextColor; ++import org.bukkit.GameMode; ++import org.bukkit.HeightMap; ++import org.bukkit.NamespacedKey; ++import org.bukkit.Rotation; ++import org.bukkit.World; ++import org.bukkit.block.BlockState; ++import org.bukkit.block.structure.Mirror; ++import org.bukkit.inventory.ItemStack; ++import org.bukkit.scoreboard.Criteria; ++import org.bukkit.scoreboard.DisplaySlot; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * Vanilla argument registry ++ *

++ * These provide rich argument parsing on the client and ++ * may also provide additional signing context. ++ */ ++@ApiStatus.Experimental ++public final class ArgumentTypes { ++ ++ static final @NotNull VanillaArgumentProvider PROVIDER = ServiceLoader.load(VanillaArgumentProvider.class) ++ .findFirst() ++ .orElseThrow(); ++ ++ @ApiStatus.Internal ++ ArgumentTypes() { ++ } ++ ++ /** ++ * Represents a selector that can capture any ++ * entity. ++ * ++ * @return argument that takes one entity ++ */ ++ public static @NotNull ArgumentType entity() { ++ return PROVIDER.entity(); ++ } ++ ++ /** ++ * Represents a selector that can capture multiple ++ * entities. ++ * ++ * @return argument that takes multiple entities ++ */ ++ public static @NotNull ArgumentType entities() { ++ return PROVIDER.entities(); ++ } ++ ++ /** ++ * Represents a selector that can capture a ++ * player entity. ++ * ++ * @return argument that takes one player ++ */ ++ public static @NotNull ArgumentType player() { ++ return PROVIDER.player(); ++ } ++ ++ /** ++ * Represents a selector that can capture multiple ++ * player entities. ++ * ++ * @return argument that takes multiple players ++ */ ++ public static @NotNull ArgumentType players() { ++ return PROVIDER.players(); ++ } ++ ++ /** ++ * Represents a selector that provides list ++ * of player profiles ++ * ++ * @return player profile arguments ++ */ ++ public static @NotNull ArgumentType playerProfiles() { ++ return PROVIDER.playerProfiles(); ++ } ++ ++ /** ++ * A block position argument. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType blockPos() { ++ return PROVIDER.blockPos(); ++ } ++ ++ /** ++ * A blockstate argument which will provide rich parsing for specifying ++ * the specific block variant and then the block entity NBT if applicable. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType blockState() { ++ return PROVIDER.blockState(); ++ } ++ ++ /** ++ * An ItemStack argument which provides rich parsing for ++ * specifying item material and item NBT information. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType itemStack() { ++ return PROVIDER.itemStack(); ++ } ++ ++ /** ++ * An item predicate argument. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType itemPredicate() { ++ return PROVIDER.itemStackPredicate(); ++ } ++ ++ /** ++ * A NamedTextColor argument which provides a color. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType namedColor() { ++ return PROVIDER.namedColor(); ++ } ++ ++ /** ++ * A component argument. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType component() { ++ return PROVIDER.component(); ++ } ++ ++ /** ++ * A signed message argument. ++ * This argument can be resolved to retrieve the underlying ++ * signed message. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType signedMessage() { ++ return PROVIDER.signedMessage(); ++ } ++ ++ /** ++ * A scoreboard display slot argument. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType scoreboardDisplaySlot() { ++ return PROVIDER.scoreboardDisplaySlot(); ++ } ++ ++ /** ++ * A namespaced key argument. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType namespacedKey() { ++ return PROVIDER.namespacedKey(); ++ } ++ ++ /** ++ * A key argument. ++ * ++ * @return argument ++ */ ++ // include both key types as we are slowly moving to use adventure's key ++ public static @NotNull ArgumentType key() { ++ return PROVIDER.key(); ++ } ++ ++ /** ++ * An inclusive range of integers that may be unbounded on either end ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType integerRange() { ++ return PROVIDER.integerRange(); ++ } ++ ++ /** ++ * An inclusive range of doubles that may be unbounded on either end ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType doubleRange() { ++ return PROVIDER.doubleRange(); ++ } ++ ++ /** ++ * A world argument. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType world() { ++ return PROVIDER.world(); ++ } ++ ++ /** ++ * A game mode argument. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType gameMode() { ++ return PROVIDER.gameMode(); ++ } ++ ++ /** ++ * A argument for getting a heightmap type. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType heightMap() { ++ return PROVIDER.heightMap(); ++ } ++ ++ /** ++ * A uuid argument. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType uuid() { ++ return PROVIDER.uuid(); ++ } ++ ++ /** ++ * An objective criteria argument ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType objectiveCriteria() { ++ return PROVIDER.objectiveCriteria(); ++ } ++ ++ /** ++ * An entity anchor argument ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType entityAnchor() { ++ return PROVIDER.entityAnchor(); ++ } ++ ++ /** ++ * A time argument, returning the number of ticks. ++ * This parses things like "1d" ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType time() { ++ return time(0); ++ } ++ ++ /** ++ * A time argument, returning the number of ticks. ++ * This parses things like "1d" ++ * ++ * @param mintime The minimum time required for this argument. ++ * @return argument ++ */ ++ public static @NotNull ArgumentType time(int mintime) { ++ return PROVIDER.time(mintime); ++ } ++ ++ /** ++ * A template mirror argument ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType templateMirror() { ++ return PROVIDER.templateMirror(); ++ } ++ ++ /** ++ * A template mirror argument ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType templateRotation() { ++ return PROVIDER.templateRotation(); ++ } ++ ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/CustomArgumentType.java b/src/main/java/io/papermc/paper/command/brigadier/argument/CustomArgumentType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5d33e1e04b1f279d6ad6371d0cfd9f129fcd8370 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/CustomArgumentType.java +@@ -0,0 +1,107 @@ ++package io.papermc.paper.command.brigadier.argument; ++ ++import com.mojang.brigadier.StringReader; ++import com.mojang.brigadier.arguments.ArgumentType; ++import com.mojang.brigadier.context.CommandContext; ++import com.mojang.brigadier.exceptions.CommandSyntaxException; ++import com.mojang.brigadier.suggestion.Suggestions; ++import com.mojang.brigadier.suggestion.SuggestionsBuilder; ++import java.util.Collection; ++import java.util.concurrent.CompletableFuture; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * An argument type that wraps around a native-to-vanilla argument type. ++ * This argument type is special in that the underlying native argument type will ++ * be sent to the client. ++ *

++ * When extending this class, you have to implement your own parsing logic. If ++ * you want to have the command value parsed into the native type and then ++ * converted to your custom type, use {@link Converted}. ++ * ++ * @param custom type ++ * @param type with an argument native to minecraft ++ */ ++@ApiStatus.Experimental ++public interface CustomArgumentType extends ArgumentType { ++ ++ /** ++ * Parses the argument using the native argument type. Keep in mind ++ * that this parsing will be done on the server. This means that if ++ * you throw a {@link CommandSyntaxException} during parsing, this ++ * will only show on the server after the user has executed the command ++ * not while they are still typing it in. ++ * ++ * @param reader string reader ++ * @return value ++ * @throws CommandSyntaxException if an error occurs while parsing ++ */ ++ @Override ++ @NotNull T parse(final @NotNull StringReader reader) throws CommandSyntaxException; ++ ++ /** ++ * Gets the native type that this argument uses, ++ * or the type that is sent to the client. ++ * ++ * @return native argument type ++ */ ++ @NotNull ArgumentType getNativeType(); ++ ++ /** ++ * Cannot be controlled by the server. ++ * Returned in cases where there are multiple arguments in the same node. ++ * This helps differentiate and tell the player what the possible inputs are. ++ * ++ * @return client set examples ++ */ ++ @Override ++ @ApiStatus.NonExtendable ++ default @NotNull Collection getExamples() { ++ return this.getNativeType().getExamples(); ++ } ++ ++ /** ++ * Provides a list of suggestions to show to the client. ++ * ++ * @param context command context ++ * @param builder suggestion builder ++ * @return suggestions ++ * @param context type ++ */ ++ @Override ++ default @NotNull CompletableFuture listSuggestions(final @NotNull CommandContext context, final @NotNull SuggestionsBuilder builder) { ++ return ArgumentType.super.listSuggestions(context, builder); ++ } ++ ++ /** ++ * An argument type that wraps around a native-to-vanilla argument type. ++ * This argument is special in that the underlying native argument type ++ * will be sent to the client. ++ *

++ * The parsed native type will be converted via the implementation of ++ * {@link #convert(Object)}. Use {@link CustomArgumentType} if you want ++ * to handle parsing the type yourself. ++ * ++ * @param custom type ++ * @param native type (has an argument native to vanilla Minecraft). ++ */ ++ @ApiStatus.Experimental ++ interface Converted extends CustomArgumentType { ++ ++ @ApiStatus.NonExtendable ++ @Override ++ default @NotNull T parse(final @NotNull StringReader reader) throws CommandSyntaxException { ++ return this.convert(this.getNativeType().parse(reader)); ++ } ++ ++ /** ++ * Converts the value from the native type to this argument type. ++ * ++ * @param nativeType native argument provided value ++ * @return converted value ++ * @throws CommandSyntaxException if an exception occurs while parsing ++ */ ++ @NotNull T convert(@NotNull N nativeType) throws CommandSyntaxException; ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentResponse.java b/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentResponse.java +new file mode 100644 +index 0000000000000000000000000000000000000000..07051125a3b4963fb5c7b45bd1d5e93f432de6e5 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentResponse.java +@@ -0,0 +1,39 @@ ++package io.papermc.paper.command.brigadier.argument; ++ ++import com.mojang.brigadier.context.CommandContext; ++import com.mojang.brigadier.exceptions.CommandSyntaxException; ++import io.papermc.paper.command.brigadier.CommandSourceStack; ++import java.util.concurrent.CompletableFuture; ++import net.kyori.adventure.chat.SignedMessage; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * Argument type returned from the Message argument. ++ */ ++@ApiStatus.Experimental ++@ApiStatus.NonExtendable ++public interface MessageArgumentResponse { ++ ++ /** ++ * Content of the message ++ * ++ * @return player made message ++ */ ++ @NotNull String content(); ++ ++ /** ++ * Resolves a signed message from this response. ++ * This will use context stored in the command source stack ++ * and signed arguments sent from the client. ++ *

++ * In the case that signed message information isn't provided, a "system" ++ * signed message will be provided instead. ++ * @param argumentName argument name ++ * @param context context ++ * @return signed message ++ * @throws CommandSyntaxException syntax exception ++ */ ++ @NotNull CompletableFuture resolveSignedMessage(@NotNull String argumentName, @NotNull CommandContext context) throws CommandSyntaxException; ++ ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProvider.java b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProvider.java +new file mode 100644 +index 0000000000000000000000000000000000000000..45e0e76d276b4ac87eb1ae3b01a10467a71edb11 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProvider.java +@@ -0,0 +1,86 @@ ++package io.papermc.paper.command.brigadier.argument; ++ ++import com.mojang.brigadier.arguments.ArgumentType; ++import io.papermc.paper.command.brigadier.argument.predicate.ItemStackPredicate; ++import io.papermc.paper.command.brigadier.argument.range.DoubleRangeProvider; ++import io.papermc.paper.command.brigadier.argument.range.IntegerRangeProvider; ++import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver; ++import io.papermc.paper.command.brigadier.argument.resolvers.PlayerProfileListResolver; ++import io.papermc.paper.command.brigadier.argument.resolvers.selector.EntitySelectorArgumentResolver; ++import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver; ++import io.papermc.paper.entity.LookAnchor; ++import java.util.UUID; ++import net.kyori.adventure.key.Key; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.format.NamedTextColor; ++import org.bukkit.GameMode; ++import org.bukkit.HeightMap; ++import org.bukkit.NamespacedKey; ++import org.bukkit.Rotation; ++import org.bukkit.World; ++import org.bukkit.block.BlockState; ++import org.bukkit.block.structure.Mirror; ++import org.bukkit.inventory.ItemStack; ++import org.bukkit.scoreboard.Criteria; ++import org.bukkit.scoreboard.DisplaySlot; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.framework.qual.DefaultQualifier; ++import org.jetbrains.annotations.ApiStatus; ++ ++@ApiStatus.Internal ++@DefaultQualifier(NonNull.class) ++interface VanillaArgumentProvider { ++ ++ ArgumentType entity(); ++ ++ ArgumentType player(); ++ ++ ArgumentType entities(); ++ ++ ArgumentType players(); ++ ++ ArgumentType playerProfiles(); ++ ++ ArgumentType blockPos(); ++ ++ ArgumentType blockState(); ++ ++ ArgumentType itemStack(); ++ ++ ArgumentType itemStackPredicate(); ++ ++ ArgumentType namedColor(); ++ ++ ArgumentType component(); ++ ++ ArgumentType signedMessage(); ++ ++ ArgumentType scoreboardDisplaySlot(); ++ ++ ArgumentType namespacedKey(); ++ ++ // include both key types as we are slowly moving to use adventure's key ++ ArgumentType key(); ++ ++ ArgumentType integerRange(); ++ ++ ArgumentType doubleRange(); ++ ++ ArgumentType world(); ++ ++ ArgumentType gameMode(); ++ ++ ArgumentType heightMap(); ++ ++ ArgumentType uuid(); ++ ++ ArgumentType objectiveCriteria(); ++ ++ ArgumentType entityAnchor(); ++ ++ ArgumentType time(int minTicks); ++ ++ ArgumentType templateMirror(); ++ ++ ArgumentType templateRotation(); ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/predicate/ItemStackPredicate.java b/src/main/java/io/papermc/paper/command/brigadier/argument/predicate/ItemStackPredicate.java +new file mode 100644 +index 0000000000000000000000000000000000000000..53430bda084c73068e84207478ed5d431162f0fa +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/predicate/ItemStackPredicate.java +@@ -0,0 +1,9 @@ ++package io.papermc.paper.command.brigadier.argument.predicate; ++ ++import java.util.function.Predicate; ++import org.bukkit.inventory.ItemStack; ++import org.jetbrains.annotations.ApiStatus; ++ ++@ApiStatus.Experimental ++public interface ItemStackPredicate extends Predicate { ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/range/DoubleRangeProvider.java b/src/main/java/io/papermc/paper/command/brigadier/argument/range/DoubleRangeProvider.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cab501212f790e8b9c7a74ff32b3218e1bd17656 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/range/DoubleRangeProvider.java +@@ -0,0 +1,9 @@ ++package io.papermc.paper.command.brigadier.argument.range; ++ ++import org.jetbrains.annotations.ApiStatus; ++ ++@ApiStatus.Experimental ++@ApiStatus.NonExtendable ++public non-sealed interface DoubleRangeProvider extends RangeProvider { ++ ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/range/IntegerRangeProvider.java b/src/main/java/io/papermc/paper/command/brigadier/argument/range/IntegerRangeProvider.java +new file mode 100644 +index 0000000000000000000000000000000000000000..116f0d01ad2d9598b630ca62ab11268b775c40c2 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/range/IntegerRangeProvider.java +@@ -0,0 +1,9 @@ ++package io.papermc.paper.command.brigadier.argument.range; ++ ++import org.jetbrains.annotations.ApiStatus; ++ ++@ApiStatus.Experimental ++@ApiStatus.NonExtendable ++public non-sealed interface IntegerRangeProvider extends RangeProvider { ++ ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/range/RangeProvider.java b/src/main/java/io/papermc/paper/command/brigadier/argument/range/RangeProvider.java +new file mode 100644 +index 0000000000000000000000000000000000000000..42e390d8df44c669bd2a1ed1566e06ec37a31fd2 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/range/RangeProvider.java +@@ -0,0 +1,14 @@ ++package io.papermc.paper.command.brigadier.argument.range; ++ ++import com.google.common.collect.Range; ++import org.jetbrains.annotations.NotNull; ++ ++public sealed interface RangeProvider> permits DoubleRangeProvider, IntegerRangeProvider { ++ ++ /** ++ * Provides the given range. ++ * @return range ++ */ ++ @NotNull ++ Range range(); ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/ArgumentResolver.java b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/ArgumentResolver.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f6397c41cb13a6e80c782f6bfe15f25787a7af1f +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/ArgumentResolver.java +@@ -0,0 +1,24 @@ ++package io.papermc.paper.command.brigadier.argument.resolvers; ++ ++import com.mojang.brigadier.exceptions.CommandSyntaxException; ++import io.papermc.paper.command.brigadier.CommandSourceStack; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.NotNull; ++ ++/** ++ * Represents an argument which requires ++ * the context of a command source stack to be fully resolved. ++ * @param type ++ */ ++@ApiStatus.Experimental ++@ApiStatus.NonExtendable ++public interface ArgumentResolver { ++ ++ /** ++ * Resolves the argument with the given ++ * command source stack. ++ * @param sourceStack source stack ++ * @return resolved ++ */ ++ @NotNull T resolve(@NotNull CommandSourceStack sourceStack) throws CommandSyntaxException; ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/BlockPositionResolver.java b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/BlockPositionResolver.java +new file mode 100644 +index 0000000000000000000000000000000000000000..14b8ab2809bc1ca75e6e05d1fe6fc42836f401fc +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/BlockPositionResolver.java +@@ -0,0 +1,9 @@ ++package io.papermc.paper.command.brigadier.argument.resolvers; ++ ++import io.papermc.paper.math.BlockPosition; ++import org.jetbrains.annotations.ApiStatus; ++ ++@ApiStatus.Experimental ++@ApiStatus.NonExtendable ++public interface BlockPositionResolver extends ArgumentResolver { ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/PlayerProfileListResolver.java b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/PlayerProfileListResolver.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1d95315154c67fcc1e29c927335fb4007f5ae7d8 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/PlayerProfileListResolver.java +@@ -0,0 +1,10 @@ ++package io.papermc.paper.command.brigadier.argument.resolvers; ++ ++import com.destroystokyo.paper.profile.PlayerProfile; ++import java.util.Collection; ++import org.jetbrains.annotations.ApiStatus; ++ ++@ApiStatus.Experimental ++@ApiStatus.NonExtendable ++public interface PlayerProfileListResolver extends ArgumentResolver> { ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/selector/EntitySelectorArgumentResolver.java b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/selector/EntitySelectorArgumentResolver.java +new file mode 100644 +index 0000000000000000000000000000000000000000..66141519e26ecabf20abd4d1e73057f30b203dd9 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/selector/EntitySelectorArgumentResolver.java +@@ -0,0 +1,10 @@ ++package io.papermc.paper.command.brigadier.argument.resolvers.selector; ++ ++import java.util.List; ++import org.bukkit.entity.Entity; ++import org.jetbrains.annotations.ApiStatus; ++ ++@ApiStatus.Experimental ++@ApiStatus.NonExtendable ++public interface EntitySelectorArgumentResolver extends SelectorArgumentResolver> { ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/selector/PlayerSelectorArgumentResolver.java b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/selector/PlayerSelectorArgumentResolver.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ecd7d56d80d64e1fef61dc39072bffb516b724a3 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/selector/PlayerSelectorArgumentResolver.java +@@ -0,0 +1,10 @@ ++package io.papermc.paper.command.brigadier.argument.resolvers.selector; ++ ++import java.util.List; ++import org.bukkit.entity.Player; ++import org.jetbrains.annotations.ApiStatus; ++ ++@ApiStatus.Experimental ++@ApiStatus.NonExtendable ++public interface PlayerSelectorArgumentResolver extends SelectorArgumentResolver> { ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/selector/SelectorArgumentResolver.java b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/selector/SelectorArgumentResolver.java +new file mode 100644 +index 0000000000000000000000000000000000000000..20fd1eca4775c4b56b717f78af4986ba68e06f76 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/resolvers/selector/SelectorArgumentResolver.java +@@ -0,0 +1,14 @@ ++package io.papermc.paper.command.brigadier.argument.resolvers.selector; ++ ++import io.papermc.paper.command.brigadier.argument.resolvers.ArgumentResolver; ++import org.jetbrains.annotations.ApiStatus; ++ ++/** ++ * Represents a selector argument which requires ++ * the context of a command source stack to be fully resolved. ++ * @param type ++ */ ++@ApiStatus.Experimental ++@ApiStatus.NonExtendable ++public interface SelectorArgumentResolver extends ArgumentResolver { ++} +diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java +index 304f978e40e1759bb19704cc5cec399500905195..ac4a57295abeff97479fee2fb768666efd3d3cd0 100644 +--- a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java ++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEvents.java +@@ -1,9 +1,11 @@ + package io.papermc.paper.plugin.lifecycle.event.types; + ++import io.papermc.paper.command.brigadier.Commands; + import io.papermc.paper.plugin.bootstrap.BootstrapContext; + import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent; + import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager; + import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner; ++import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent; + import org.bukkit.plugin.Plugin; + import org.jetbrains.annotations.ApiStatus; + +@@ -15,6 +17,8 @@ import org.jetbrains.annotations.ApiStatus; + @ApiStatus.Experimental + public final class LifecycleEvents { + ++ public static final LifecycleEventType.Prioritizable> COMMANDS = prioritized("commands", LifecycleEventOwner.class); ++ + // + @ApiStatus.Internal + private static LifecycleEventType.Monitorable plugin(final String name) { +diff --git a/src/main/java/org/bukkit/command/Command.java b/src/main/java/org/bukkit/command/Command.java +index b791358f90fe92bc2264d9a26492245763813af3..d816d1677dface8cb3035d5adb9919aa75bd8e44 100644 +--- a/src/main/java/org/bukkit/command/Command.java ++++ b/src/main/java/org/bukkit/command/Command.java +@@ -488,4 +488,9 @@ public abstract class Command { + public String toString() { + return getClass().getName() + '(' + name + ')'; + } ++ ++ // Paper start ++ @org.jetbrains.annotations.ApiStatus.Internal ++ public boolean canBeOverriden() { return false; } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/command/FormattedCommandAlias.java b/src/main/java/org/bukkit/command/FormattedCommandAlias.java +index 9d4f553c04784cca63901a56a7aea62a5cae1d72..abe256e1e45ce28036da4aa1586715bc8a1a3414 100644 +--- a/src/main/java/org/bukkit/command/FormattedCommandAlias.java ++++ b/src/main/java/org/bukkit/command/FormattedCommandAlias.java +@@ -117,7 +117,7 @@ public class FormattedCommandAlias extends Command { + index = formatString.indexOf('$', index); + } + +- return formatString; ++ return formatString.trim(); // Paper - Causes an extra space at the end, breaks with brig commands + } + + @NotNull +diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java +index ac9a28922f8a556944a4c3649d74c32c622f0cb0..c3a9cf65db73ed534bf20996c7f05b5eb0aaebe1 100644 +--- a/src/main/java/org/bukkit/command/SimpleCommandMap.java ++++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java +@@ -22,10 +22,14 @@ import org.jetbrains.annotations.NotNull; + import org.jetbrains.annotations.Nullable; + + public class SimpleCommandMap implements CommandMap { +- protected final Map knownCommands = new HashMap(); ++ protected final Map knownCommands; // Paper + private final Server server; + +- public SimpleCommandMap(@NotNull final Server server) { ++ // Paper start ++ @org.jetbrains.annotations.ApiStatus.Internal ++ public SimpleCommandMap(@NotNull final Server server, Map backing) { ++ this.knownCommands = backing; ++ // Paper end + this.server = server; + setDefaultCommands(); + } +@@ -102,7 +106,10 @@ public class SimpleCommandMap implements CommandMap { + */ + private synchronized boolean register(@NotNull String label, @NotNull Command command, boolean isAlias, @NotNull String fallbackPrefix) { + knownCommands.put(fallbackPrefix + ":" + label, command); +- if ((command instanceof BukkitCommand || isAlias) && knownCommands.containsKey(label)) { ++ // Paper start ++ Command known = knownCommands.get(label); ++ if ((command instanceof BukkitCommand || isAlias) && (known != null && !known.canBeOverriden())) { ++ // Paper end + // Request is for an alias/fallback command and it conflicts with + // a existing command or previous alias ignore it + // Note: This will mean it gets removed from the commands list of active aliases +@@ -114,7 +121,9 @@ public class SimpleCommandMap implements CommandMap { + // If the command exists but is an alias we overwrite it, otherwise we return + Command conflict = knownCommands.get(label); + if (conflict != null && conflict.getLabel().equals(label)) { ++ if (!conflict.canBeOverriden()) { // Paper + return false; ++ } // Paper + } + + if (!isAlias) { diff --git a/patches/server/0453-Add-missing-default-perms-for-commands.patch b/patches/server/0453-Add-missing-default-perms-for-commands.patch index d49628ee3257..ca9019118dc9 100644 --- a/patches/server/0453-Add-missing-default-perms-for-commands.patch +++ b/patches/server/0453-Add-missing-default-perms-for-commands.patch @@ -91,7 +91,7 @@ new file mode 100644 index 0000000000000000000000000000000000000000..ca71c688b37ce2c8b712a4f9216cf872c8edf78e --- /dev/null +++ b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java -@@ -0,0 +1,82 @@ +@@ -0,0 +1,86 @@ +package io.papermc.paper.permissions; + +import com.mojang.brigadier.tree.CommandNode; @@ -138,6 +138,10 @@ index 0000000000000000000000000000000000000000..ca71c688b37ce2c8b712a4f9216cf872 + Set missing = new LinkedHashSet<>(); + Set foundPerms = new HashSet<>(); + for (CommandNode child : root.getChildren()) { ++ // Ignore aliases ++ if (child.getRedirect() != null) { ++ child = child.getRedirect(); ++ } + final String vanillaPerm = VanillaCommandWrapper.getPermission(child); + if (!perms.contains(vanillaPerm)) { + missing.add("Missing permission for " + child.getName() + " (" + vanillaPerm + ") command"); diff --git a/patches/server/1060-Brigadier-based-command-API.patch b/patches/server/1060-Brigadier-based-command-API.patch new file mode 100644 index 000000000000..b7888e209d83 --- /dev/null +++ b/patches/server/1060-Brigadier-based-command-API.patch @@ -0,0 +1,2506 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> +Date: Mon, 1 Aug 2022 22:50:34 -0400 +Subject: [PATCH] Brigadier based command API + +== AT == +public net.minecraft.commands.arguments.blocks.BlockInput tag +public net.minecraft.commands.arguments.DimensionArgument ERROR_INVALID_VALUE +public net.minecraft.server.ReloadableServerResources commandBuildContext + +diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java +index 4b4f812eb13d5f03bcf3f8724d8aa8dbbc724e8b..a4d5d7017e0be79844b996de85a63cad5f8488bc 100644 +--- a/src/main/java/com/mojang/brigadier/CommandDispatcher.java ++++ b/src/main/java/com/mojang/brigadier/CommandDispatcher.java +@@ -459,7 +459,7 @@ public class CommandDispatcher { + } + + private String getSmartUsage(final CommandNode node, final S source, final boolean optional, final boolean deep) { +- if (!node.canUse(source)) { ++ if (source != null && !node.canUse(source)) { // Paper + return null; + } + +@@ -473,7 +473,7 @@ public class CommandDispatcher { + final String redirect = node.getRedirect() == this.root ? "..." : "-> " + node.getRedirect().getUsageText(); + return self + CommandDispatcher.ARGUMENT_SEPARATOR + redirect; + } else { +- final Collection> children = node.getChildren().stream().filter(c -> c.canUse(source)).collect(Collectors.toList()); ++ final Collection> children = node.getChildren().stream().filter(c -> source == null || c.canUse(source)).collect(Collectors.toList()); // Paper + if (children.size() == 1) { + final String usage = this.getSmartUsage(children.iterator().next(), source, childOptional, childOptional); + if (usage != null) { +diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java +index b02fb15c98ab873fa78635d7a23706ddff8cc94d..7438c31ac70cf7394146cbc7e7a0d37282a8896a 100644 +--- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java ++++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java +@@ -35,6 +35,8 @@ public abstract class CommandNode implements Comparable> { + private final boolean forks; + private Command command; + public LiteralCommandNode clientNode; // Paper - Brigadier API ++ public CommandNode unwrappedCached = null; // Paper - Brigadier Command API ++ public CommandNode wrappedCached = null; // Paper - Brigadier Command API + // CraftBukkit start + public void removeCommand(String name) { + this.children.remove(name); +@@ -203,4 +205,11 @@ public abstract class CommandNode implements Comparable> { + } + + public abstract Collection getExamples(); ++ // Paper start - Brigadier Command API ++ public void clearAll() { ++ this.children.clear(); ++ this.literals.clear(); ++ this.arguments.clear(); ++ } ++ // Paper end - Brigadier Command API + } +diff --git a/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProviderImpl.java b/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProviderImpl.java +deleted file mode 100644 +index dd6012b6a097575b2d1471be5069eccee4537c0a..0000000000000000000000000000000000000000 +--- a/src/main/java/io/papermc/paper/brigadier/PaperBrigadierProviderImpl.java ++++ /dev/null +@@ -1,30 +0,0 @@ +-package io.papermc.paper.brigadier; +- +-import com.mojang.brigadier.Message; +-import io.papermc.paper.adventure.PaperAdventure; +-import net.kyori.adventure.text.Component; +-import net.kyori.adventure.text.ComponentLike; +-import net.minecraft.network.chat.ComponentUtils; +-import org.checkerframework.checker.nullness.qual.NonNull; +- +-import static java.util.Objects.requireNonNull; +- +-public enum PaperBrigadierProviderImpl implements PaperBrigadierProvider { +- INSTANCE; +- +- PaperBrigadierProviderImpl() { +- PaperBrigadierProvider.initialize(this); +- } +- +- @Override +- public @NonNull Message message(final @NonNull ComponentLike componentLike) { +- requireNonNull(componentLike, "componentLike"); +- return PaperAdventure.asVanilla(componentLike.asComponent()); +- } +- +- @Override +- public @NonNull Component componentFromMessage(final @NonNull Message message) { +- requireNonNull(message, "message"); +- return PaperAdventure.asAdventure(ComponentUtils.fromMessage(message)); +- } +-} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/ApiMirrorRootNode.java b/src/main/java/io/papermc/paper/command/brigadier/ApiMirrorRootNode.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7c7a281ec145c9ffdc8a16739579435f3899f33a +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/ApiMirrorRootNode.java +@@ -0,0 +1,251 @@ ++package io.papermc.paper.command.brigadier; ++ ++import com.google.common.collect.Collections2; ++import com.mojang.brigadier.CommandDispatcher; ++import com.mojang.brigadier.arguments.ArgumentType; ++import com.mojang.brigadier.arguments.BoolArgumentType; ++import com.mojang.brigadier.arguments.DoubleArgumentType; ++import com.mojang.brigadier.arguments.FloatArgumentType; ++import com.mojang.brigadier.arguments.IntegerArgumentType; ++import com.mojang.brigadier.arguments.LongArgumentType; ++import com.mojang.brigadier.arguments.StringArgumentType; ++import com.mojang.brigadier.context.CommandContext; ++import com.mojang.brigadier.suggestion.SuggestionProvider; ++import com.mojang.brigadier.suggestion.SuggestionsBuilder; ++import com.mojang.brigadier.tree.ArgumentCommandNode; ++import com.mojang.brigadier.tree.CommandNode; ++import com.mojang.brigadier.tree.LiteralCommandNode; ++import com.mojang.brigadier.tree.RootCommandNode; ++import io.papermc.paper.command.brigadier.argument.CustomArgumentType; ++import io.papermc.paper.command.brigadier.argument.VanillaArgumentProviderImpl; ++import io.papermc.paper.command.brigadier.argument.WrappedArgumentCommandNode; ++import java.lang.reflect.Method; ++import java.util.Collection; ++import java.util.Set; ++import net.minecraft.commands.synchronization.ArgumentTypeInfos; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++/** ++ * This root command node is responsible for wrapping around vanilla's dispatcher. ++ *

++ * The reason for this is conversion is we do NOT want there to be NMS types ++ * in the api. This allows us to reconstruct the nodes to be more api friendly, while ++ * we can then unwrap it when needed and convert them to NMS types. ++ *

++ * Command nodes such as vanilla (those without a proper "api node") ++ * will be assigned a {@link ShadowBrigNode}. ++ * This prevents certain parts of it (children) from being accessed by the api. ++ */ ++public abstract class ApiMirrorRootNode extends RootCommandNode { ++ ++ /** ++ * Represents argument types that are allowed to exist in the api. ++ * These typically represent primitives that don't need to be wrapped ++ * by NMS. ++ */ ++ private static final Set>> ARGUMENT_WHITELIST = Set.of( ++ BoolArgumentType.class, ++ DoubleArgumentType.class, ++ FloatArgumentType.class, ++ IntegerArgumentType.class, ++ LongArgumentType.class, ++ StringArgumentType.class ++ ); ++ ++ public static void validatePrimitiveType(ArgumentType type) { ++ if (ARGUMENT_WHITELIST.contains(type.getClass())) { ++ if (!ArgumentTypeInfos.isClassRecognized(type.getClass())) { ++ throw new IllegalArgumentException("This whitelisted primitive argument type is not recognized by the server!"); ++ } ++ } else if (!(type instanceof VanillaArgumentProviderImpl.NativeWrapperArgumentType nativeWrapperArgumentType) || !ArgumentTypeInfos.isClassRecognized(nativeWrapperArgumentType.nativeNmsArgumentType().getClass())) { ++ throw new IllegalArgumentException("Custom argument type was passed, this was not a recognized type to send to the client! You must only pass vanilla arguments or primitive brig args in the wrapper!"); ++ } ++ } ++ ++ public abstract CommandDispatcher getDispatcher(); ++ ++ /** ++ * This logic is responsible for unwrapping an API node to be supported by NMS. ++ * See the method implementation for detailed steps. ++ * ++ * @param wrapped api provided node / node to be "wrapped" ++ * @return wrapped node ++ */ ++ @SuppressWarnings({"rawtypes", "unchecked"}) ++ private @NotNull CommandNode unwrapNode(CommandNode wrapped) { ++ /* ++ If the type is a shadow node we can assume that the type that it represents is an already supported NMS node. ++ This is because these are typically minecraft command nodes. ++ */ ++ if (wrapped instanceof ShadowBrigNode shadowBrigNode) { ++ return (CommandNode) shadowBrigNode.getHandle(); ++ } ++ ++ /* ++ This node already has had an unwrapped node created, so we can assume that it's safe to reuse that cached copy. ++ */ ++ if (wrapped.unwrappedCached != null) { ++ return wrapped.unwrappedCached; ++ } ++ ++ /* ++ Logic for wrapping each node. ++ */ ++ CommandNode unwrapped; ++ if (wrapped instanceof LiteralCommandNode node) { ++ /* ++ Remap the literal node, we only have to account ++ for the redirect in this case. ++ */ ++ unwrapped = this.simpleUnwrap(node); ++ } else if (wrapped instanceof ArgumentCommandNode original) { ++ ArgumentType unwrappedArgType = original.getType(); ++ /* ++ Check to see if this argument type is a wrapped type, if so we know that ++ we can unwrap the node to get an NMS type. ++ */ ++ if (unwrappedArgType instanceof CustomArgumentType customArgumentType) { ++ final SuggestionProvider suggestionProvider; ++ try { ++ final Method listSuggestions = customArgumentType.getClass().getMethod("listSuggestions", CommandContext.class, SuggestionsBuilder.class); ++ if (listSuggestions.getDeclaringClass() != CustomArgumentType.class) { ++ suggestionProvider = customArgumentType::listSuggestions; ++ } else { ++ suggestionProvider = null; ++ } ++ } catch (final NoSuchMethodException ex) { ++ throw new IllegalStateException("Could not determine if the custom argument type " + customArgumentType + " overrides listSuggestions", ex); ++ } ++ ++ unwrapped = this.unwrapArgumentWrapper(original, customArgumentType, customArgumentType.getNativeType(), suggestionProvider); ++ } else if (unwrappedArgType instanceof VanillaArgumentProviderImpl.NativeWrapperArgumentType nativeWrapperArgumentType) { ++ unwrapped = this.unwrapArgumentWrapper(original, nativeWrapperArgumentType, nativeWrapperArgumentType, null); // "null" for suggestion provider so it uses the argument type's suggestion provider ++ ++ /* ++ If it's not a wrapped type, it either has to be a primitive or an already ++ defined NMS type. ++ This method allows us to check if this is recognized by vanilla. ++ */ ++ } else if (ArgumentTypeInfos.isClassRecognized(unwrappedArgType.getClass())) { ++ if (ARGUMENT_WHITELIST.contains(unwrappedArgType.getClass())) { ++ // If this argument is whitelisted simply unwrap it and ignore the argument type. ++ unwrapped = this.simpleUnwrap(original); ++ } else { ++ // If this was an NMS type but not a primitive ++ throw new IllegalArgumentException("NMS argument type was passed (%s), should be wrapped inside an CustomArgumentType. Don't add NMS args here!".formatted(unwrappedArgType)); ++ } ++ } else { ++ // Unknown argument type was passed ++ throw new IllegalArgumentException("Custom unknown argument type was passed, should be wrapped inside an CustomArgumentType."); ++ } ++ } else { ++ throw new IllegalArgumentException("Unknown command node passed. Don't know how to unwrap this."); ++ } ++ ++ /* ++ Add the children to the node, unwrapping each child in the process. ++ */ ++ for (CommandNode child : wrapped.getChildren()) { ++ unwrapped.addChild(this.unwrapNode(child)); ++ } ++ ++ unwrapped.wrappedCached = wrapped; ++ wrapped.unwrappedCached = unwrapped; ++ ++ return unwrapped; ++ } ++ ++ /** ++ * This logic is responsible for rewrapping a node. ++ * If a node was unwrapped in the past, it should have a wrapped type ++ * stored in its cache. ++ *

++ * However, if it doesn't seem to have a wrapped version we will return ++ * a {@link ShadowBrigNode} instead. This supports being unwrapped/wrapped while ++ * preventing the API from accessing it unsafely. ++ * ++ * @param unwrapped argument node ++ * @return wrapped node ++ */ ++ private @Nullable CommandNode wrapNode(@Nullable CommandNode unwrapped) { ++ if (unwrapped == null) { ++ return null; ++ } ++ ++ /* ++ This was most likely created by API and has a wrapped variant, ++ so we can return this safely. ++ */ ++ if (unwrapped.wrappedCached != null) { ++ return unwrapped.wrappedCached; ++ } ++ ++ /* ++ We don't know the type of this, or where this came from. ++ Return a shadow, where we will allow the api to handle this but have ++ restrictive access. ++ */ ++ CommandNode shadow = new ShadowBrigNode(unwrapped); ++ unwrapped.wrappedCached = shadow; ++ return shadow; ++ } ++ ++ /** ++ * Nodes added to this dispatcher must be unwrapped ++ * in order to be added to the NMS dispatcher. ++ * ++ * @param node node to add ++ */ ++ @SuppressWarnings({"rawtypes", "unchecked"}) ++ @Override ++ public void addChild(CommandNode node) { ++ CommandNode convertedNode = this.unwrapNode(node); ++ this.getDispatcher().getRoot().addChild(convertedNode); ++ } ++ ++ /** ++ * Gets the children for the vanilla dispatcher, ++ * ensuring that all are wrapped. ++ * ++ * @return wrapped children ++ */ ++ @Override ++ public Collection> getChildren() { ++ return Collections2.transform(this.getDispatcher().getRoot().getChildren(), this::wrapNode); ++ } ++ ++ @Override ++ public CommandNode getChild(String name) { ++ return this.wrapNode(this.getDispatcher().getRoot().getChild(name)); ++ } ++ ++ // These are needed for bukkit... we should NOT allow this ++ @Override ++ public void removeCommand(String name) { ++ this.getDispatcher().getRoot().removeCommand(name); ++ } ++ ++ @Override ++ public void clearAll() { ++ this.getDispatcher().getRoot().clearAll(); ++ } ++ ++ @SuppressWarnings({"rawtypes", "unchecked"}) ++ private CommandNode unwrapArgumentWrapper(final ArgumentCommandNode node, final ArgumentType wrappedArgumentType, final ArgumentType possiblyWrappedNativeArgumentType, @Nullable SuggestionProvider argumentTypeSuggestionProvider) { ++ validatePrimitiveType(possiblyWrappedNativeArgumentType); ++ final CommandNode redirectNode = node.getRedirect() == null ? null : this.unwrapNode(node.getRedirect()); ++ // If there is already a custom suggestion provider, ignore the suggestion provider from the argument type ++ final SuggestionProvider suggestionProvider = node.getCustomSuggestions() != null ? node.getCustomSuggestions() : argumentTypeSuggestionProvider; ++ ++ ArgumentType nativeArgumentType = possiblyWrappedNativeArgumentType instanceof VanillaArgumentProviderImpl.NativeWrapperArgumentType nativeWrapperArgumentType ? nativeWrapperArgumentType.nativeNmsArgumentType() : possiblyWrappedNativeArgumentType; ++ return new WrappedArgumentCommandNode<>(node.getName(), wrappedArgumentType, nativeArgumentType, node.getCommand(), node.getRequirement(), redirectNode, node.getRedirectModifier(), node.isFork(), suggestionProvider); ++ } ++ ++ private CommandNode simpleUnwrap(CommandNode node) { ++ return node.createBuilder() ++ .redirect(node.getRedirect() == null ? null : this.unwrapNode(node.getRedirect())) ++ .build(); ++ } ++ ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerImpl.java b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerImpl.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0b33c6cf2366568641e6f2fd7f74fb74f6ea0145 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerImpl.java +@@ -0,0 +1,20 @@ ++package io.papermc.paper.command.brigadier; ++ ++import com.mojang.brigadier.Message; ++import io.papermc.paper.adventure.PaperAdventure; ++import net.kyori.adventure.text.Component; ++import net.minecraft.network.chat.ComponentUtils; ++import org.jetbrains.annotations.NotNull; ++ ++public final class MessageComponentSerializerImpl implements MessageComponentSerializer { ++ ++ @Override ++ public @NotNull Component deserialize(@NotNull Message input) { ++ return PaperAdventure.asAdventure(ComponentUtils.fromMessage(input)); ++ } ++ ++ @Override ++ public @NotNull Message serialize(@NotNull Component component) { ++ return PaperAdventure.asVanilla(component); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/PaperBrigadier.java b/src/main/java/io/papermc/paper/command/brigadier/PaperBrigadier.java +new file mode 100644 +index 0000000000000000000000000000000000000000..82a57ffc048454fbc4c705adbac83d16c44c5054 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/PaperBrigadier.java +@@ -0,0 +1,56 @@ ++package io.papermc.paper.command.brigadier; ++ ++import com.mojang.brigadier.CommandDispatcher; ++import com.mojang.brigadier.tree.CommandNode; ++import com.mojang.brigadier.tree.LiteralCommandNode; ++import io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap; ++import io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode; ++import net.minecraft.commands.Commands; ++import net.minecraft.server.MinecraftServer; ++import org.bukkit.Bukkit; ++import org.bukkit.Server; ++import org.bukkit.command.Command; ++import org.bukkit.command.CommandMap; ++import org.bukkit.craftbukkit.command.VanillaCommandWrapper; ++ ++import java.util.Map; ++ ++public class PaperBrigadier { ++ ++ @SuppressWarnings({"unchecked", "rawtypes"}) ++ public static Command wrapNode(CommandNode node) { ++ if (!(node instanceof LiteralCommandNode)) { ++ throw new IllegalArgumentException("Unsure how to wrap a " + node); ++ } ++ ++ if (!(node instanceof PluginCommandNode pluginCommandNode)) { ++ return new VanillaCommandWrapper(null, node); ++ } ++ CommandNode argumentCommandNode = node; ++ if (argumentCommandNode.getRedirect() != null) { ++ argumentCommandNode = argumentCommandNode.getRedirect(); ++ } ++ ++ Map, String> map = PaperCommands.INSTANCE.getDispatcherInternal().getSmartUsage(argumentCommandNode, null); ++ String usage = map.isEmpty() ? pluginCommandNode.getUsageText() : pluginCommandNode.getUsageText() + " " + String.join("\n" + pluginCommandNode.getUsageText() + " ", map.values()); ++ return new PluginVanillaCommandWrapper(pluginCommandNode.getName(), pluginCommandNode.getDescription(), usage, pluginCommandNode.getAliases(), node, pluginCommandNode.getPlugin()); ++ } ++ ++ /* ++ Previously, Bukkit used one command dispatcher and ignored minecraft's reloading logic. ++ ++ In order to allow for legacy commands to be properly added, we will iterate through previous bukkit commands ++ in the old dispatcher and re-register them. ++ */ ++ @SuppressWarnings({"unchecked", "rawtypes"}) ++ public static void moveBukkitCommands(Commands before, Commands after) { ++ CommandDispatcher erasedDispatcher = before.getDispatcher(); ++ ++ for (Object node : erasedDispatcher.getRoot().getChildren()) { ++ if (node instanceof CommandNode commandNode && commandNode.getCommand() instanceof BukkitCommandNode.BukkitBrigCommand) { ++ after.getDispatcher().getRoot().removeCommand(((CommandNode) node).getName()); // Remove already existing commands ++ after.getDispatcher().getRoot().addChild((CommandNode) node); ++ } ++ } ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java b/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9e293468778a8e1b8d489e239936ffbc03ba81e2 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java +@@ -0,0 +1,63 @@ ++package io.papermc.paper.command.brigadier; ++ ++import com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource; ++import net.minecraft.world.level.Level; ++import net.minecraft.world.phys.Vec2; ++import net.minecraft.world.phys.Vec3; ++import org.bukkit.Location; ++import org.bukkit.command.CommandSender; ++import org.bukkit.entity.Entity; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++public interface PaperCommandSourceStack extends CommandSourceStack, BukkitBrigadierCommandSource { ++ ++ net.minecraft.commands.CommandSourceStack getHandle(); ++ ++ @Override ++ default @NotNull Location getLocation() { ++ Vec2 rot = this.getHandle().getRotation(); ++ Vec3 pos = this.getHandle().getPosition(); ++ Level level = this.getHandle().getLevel(); ++ ++ return new org.bukkit.Location(level.getWorld(), pos.x, pos.y, pos.z, rot.x, rot.y); ++ } ++ ++ @Override ++ @NotNull ++ default CommandSender getSender() { ++ return this.getHandle().getBukkitSender(); ++ } ++ ++ @Override ++ @Nullable ++ default Entity getExecutor() { ++ net.minecraft.world.entity.Entity nmsEntity = this.getHandle().getEntity(); ++ if (nmsEntity == null) { ++ return null; ++ } ++ ++ return nmsEntity.getBukkitEntity(); ++ } ++ ++ // OLD METHODS ++ @Override ++ default org.bukkit.entity.Entity getBukkitEntity() { ++ return this.getExecutor(); ++ } ++ ++ @Override ++ default org.bukkit.World getBukkitWorld() { ++ return this.getLocation().getWorld(); ++ } ++ ++ @Override ++ default org.bukkit.Location getBukkitLocation() { ++ return this.getLocation(); ++ } ++ ++ @Override ++ default CommandSender getBukkitSender() { ++ return this.getSender(); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java b/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java +new file mode 100644 +index 0000000000000000000000000000000000000000..2c17d57a95aaf48ff7fee6818d3616d5c6da0ece +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java +@@ -0,0 +1,158 @@ ++package io.papermc.paper.command.brigadier; ++ ++import com.google.common.base.Preconditions; ++import com.mojang.brigadier.CommandDispatcher; ++import com.mojang.brigadier.arguments.StringArgumentType; ++import com.mojang.brigadier.builder.LiteralArgumentBuilder; ++import com.mojang.brigadier.builder.RequiredArgumentBuilder; ++import com.mojang.brigadier.suggestion.SuggestionsBuilder; ++import com.mojang.brigadier.tree.LiteralCommandNode; ++import io.papermc.paper.plugin.configuration.PluginMeta; ++import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner; ++import io.papermc.paper.plugin.lifecycle.event.registrar.PaperRegistrar; ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.List; ++import java.util.Locale; ++import net.minecraft.commands.CommandBuildContext; ++import net.minecraft.server.ReloadableServerResources; ++import org.apache.commons.lang3.StringUtils; ++import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++import static java.util.Objects.requireNonNull; ++ ++@DefaultQualifier(NonNull.class) ++public class PaperCommands implements Commands, PaperRegistrar { ++ ++ public static final PaperCommands INSTANCE = new PaperCommands(); ++ ++ private @Nullable LifecycleEventOwner currentContext; ++ private @MonotonicNonNull CommandDispatcher dispatcher; ++ private CommandBuildContext.@MonotonicNonNull Configurable buildContext; ++ private boolean invalid = false; ++ ++ @Override ++ public void setCurrentContext(final @Nullable LifecycleEventOwner context) { ++ this.currentContext = context; ++ } ++ ++ public void setResources(final ReloadableServerResources resources) { ++ this.invalid = false; ++ this.dispatcher = new CommandDispatcher<>(new ApiMirrorRootNode() { ++ @Override ++ public CommandDispatcher getDispatcher() { ++ return resources.getCommands().getDispatcher(); ++ } ++ }); ++ this.buildContext = resources.commandBuildContext; ++ } ++ ++ @Override ++ public void invalidate() { ++ this.invalid = true; ++ } ++ ++ // use this method internally as it bypasses the valid check ++ public CommandDispatcher getDispatcherInternal() { ++ Preconditions.checkState(this.dispatcher != null, "the dispatcher hasn't been set yet"); ++ return this.dispatcher; ++ } ++ ++ public CommandBuildContext.Configurable getBuildContext() { ++ Preconditions.checkState(this.buildContext != null, "the build context hasn't been set yet"); ++ return this.buildContext; ++ } ++ ++ @Override ++ public CommandDispatcher getDispatcher() { ++ Preconditions.checkState(!this.invalid && this.dispatcher != null, "cannot access the dispatcher in this context"); ++ return this.dispatcher; ++ } ++ ++ @Override ++ public boolean register(final LiteralCommandNode node, final @Nullable String description, final List aliases) { ++ return this.register(requireNonNull(this.currentContext, "No lifecycle owner context is set").getPluginMeta(), node, description, aliases); ++ } ++ ++ @Override ++ public boolean register(final PluginMeta pluginMeta, final LiteralCommandNode node, final @Nullable String description, final List aliases) { ++ String identifier = pluginMeta.getName().toLowerCase(Locale.ROOT); ++ final String literal = node.getLiteral(); ++ boolean registeredNode = false; ++ PluginCommandNode pluginLiteral = new PluginCommandNode(identifier + ":" + literal, pluginMeta, node, description); // Treat the keyed version of the command as the root ++ ++ registeredNode |= this.registerIntoDispatcher(pluginLiteral, true); ++ registeredNode |= this.registerRedirect(literal, pluginMeta, pluginLiteral, description, true); // Plugin commands should override vanilla commands ++ ++ // Add aliases ++ List registeredAliases = new ArrayList<>(); ++ for (String alias : aliases) { ++ if (registeredNode |= this.registerRedirect(alias, pluginMeta, pluginLiteral, description, false)) { ++ registeredAliases.add(alias); ++ } ++ if (registeredNode |= this.registerRedirect(identifier + ":" + alias, pluginMeta, pluginLiteral, description, false)) { ++ registeredAliases.add(identifier + ":" + alias); ++ } ++ } ++ ++ if (!registeredAliases.isEmpty()) { ++ pluginLiteral.setAliases(registeredAliases); ++ } ++ ++ return registeredNode; ++ } ++ ++ private boolean registerRedirect(final String aliasLiteral, final PluginMeta plugin, final PluginCommandNode redirectTo, final @Nullable String description, final boolean override) { ++ LiteralCommandNode redirect = Commands.literal(aliasLiteral) ++ .redirect(redirectTo) ++ .executes(redirectTo.getCommand()) ++ .requires(redirectTo.getRequirement()) ++ .build(); ++ return this.registerIntoDispatcher(new PluginCommandNode(aliasLiteral, plugin, redirect, description), override); ++ } ++ ++ private boolean registerIntoDispatcher(final PluginCommandNode node, final boolean override) { ++ boolean hasChild = this.getDispatcher().getRoot().getChild(node.getLiteral()) != null; ++ if (!hasChild || override) { // Avoid merging behavior. Maybe something to look into in the future ++ if (override) { ++ this.getDispatcher().getRoot().removeCommand(node.getLiteral()); ++ } ++ this.getDispatcher().getRoot().addChild(node); ++ return true; ++ } ++ ++ return false; ++ } ++ ++ @Override ++ public boolean register(final String label, final @Nullable String description, final List aliases, final BasicCommand basicCommand) { ++ return this.register(requireNonNull(this.currentContext, "No lifecycle owner context is set").getPluginMeta(), label, description, aliases, basicCommand); ++ } ++ ++ @Override ++ public boolean register(final PluginMeta pluginMeta, final String label, final @Nullable String description, final List aliases, final BasicCommand basicCommand) { ++ LiteralArgumentBuilder builder = Commands.literal(label) ++ .then( ++ RequiredArgumentBuilder.argument("args", StringArgumentType.greedyString()) ++ .suggests((context, suggestionsBuilder) -> { ++ String[] args = suggestionsBuilder.getRemaining().split(" ", -1); // We need the command included -- Set limit to -1, allow for trailing spaces ++ SuggestionsBuilder offsetSuggestionsBuilder = suggestionsBuilder.createOffset(suggestionsBuilder.getInput().lastIndexOf(' ') + 1);; ++ ++ final Collection suggestions = basicCommand.suggest(context.getSource(), args); ++ suggestions.forEach(offsetSuggestionsBuilder::suggest); ++ return offsetSuggestionsBuilder.buildFuture(); ++ }) ++ .executes((stack) -> { ++ return basicCommand.execute(stack.getSource(), StringUtils.split(stack.getArgument("args", String.class), ' ')); ++ }) ++ ) ++ .executes((stack) -> { ++ return basicCommand.execute(stack.getSource(), new String[0]); ++ }); ++ ++ return this.register(pluginMeta, builder.build(), description, aliases); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/PluginCommandNode.java b/src/main/java/io/papermc/paper/command/brigadier/PluginCommandNode.java +new file mode 100644 +index 0000000000000000000000000000000000000000..3a9f58873b83f10ba354ae4968c4ab0632662439 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/PluginCommandNode.java +@@ -0,0 +1,50 @@ ++package io.papermc.paper.command.brigadier; ++ ++import com.mojang.brigadier.tree.CommandNode; ++import com.mojang.brigadier.tree.LiteralCommandNode; ++import java.util.Collections; ++import java.util.List; ++import java.util.Objects; ++import io.papermc.paper.plugin.configuration.PluginMeta; ++import org.bukkit.Bukkit; ++import org.bukkit.plugin.Plugin; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++public class PluginCommandNode extends LiteralCommandNode { ++ ++ private final PluginMeta plugin; ++ private final String description; ++ private List aliases = Collections.emptyList(); ++ ++ public PluginCommandNode(final @NotNull String literal, final @NotNull PluginMeta plugin, final @NotNull LiteralCommandNode rootLiteral, final @Nullable String description) { ++ super( ++ literal, rootLiteral.getCommand(), rootLiteral.getRequirement(), ++ rootLiteral.getRedirect(), rootLiteral.getRedirectModifier(), rootLiteral.isFork() ++ ); ++ this.plugin = plugin; ++ this.description = description; ++ ++ for (CommandNode argument : rootLiteral.getChildren()) { ++ this.addChild(argument); ++ } ++ } ++ ++ @NotNull ++ public Plugin getPlugin() { ++ return Objects.requireNonNull(Bukkit.getPluginManager().getPlugin(this.plugin.getName())); ++ } ++ ++ @NotNull ++ public String getDescription() { ++ return this.description; ++ } ++ ++ public void setAliases(List aliases) { ++ this.aliases = aliases; ++ } ++ ++ public List getAliases() { ++ return this.aliases; ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/PluginVanillaCommandWrapper.java b/src/main/java/io/papermc/paper/command/brigadier/PluginVanillaCommandWrapper.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cf8359af60601d7917e77fd06a00b64992a85953 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/PluginVanillaCommandWrapper.java +@@ -0,0 +1,46 @@ ++package io.papermc.paper.command.brigadier; ++ ++import com.mojang.brigadier.tree.CommandNode; ++import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.commands.Commands; ++import org.bukkit.command.Command; ++import org.bukkit.command.PluginIdentifiableCommand; ++import org.bukkit.craftbukkit.command.VanillaCommandWrapper; ++import org.bukkit.plugin.Plugin; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.List; ++ ++// Exists to that /help can show the plugin ++public class PluginVanillaCommandWrapper extends VanillaCommandWrapper implements PluginIdentifiableCommand { ++ ++ private final Plugin plugin; ++ private final List alises; ++ ++ public PluginVanillaCommandWrapper(String name, String description, String usageMessage, List aliases, CommandNode vanillaCommand, Plugin plugin) { ++ super(name, description, usageMessage, aliases, vanillaCommand); ++ this.plugin = plugin; ++ this.alises = aliases; ++ } ++ ++ @Override ++ public @NotNull List getAliases() { ++ return this.alises; ++ } ++ ++ @Override ++ public @NotNull Command setAliases(@NotNull List aliases) { ++ return this; ++ } ++ ++ @Override ++ public @NotNull Plugin getPlugin() { ++ return this.plugin; ++ } ++ ++ // Show in help menu! ++ @Override ++ public boolean isRegistered() { ++ return true; ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/ShadowBrigNode.java b/src/main/java/io/papermc/paper/command/brigadier/ShadowBrigNode.java +new file mode 100644 +index 0000000000000000000000000000000000000000..895addef908e09d527e4bc9210599e8827c53807 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/ShadowBrigNode.java +@@ -0,0 +1,35 @@ ++package io.papermc.paper.command.brigadier; ++ ++import com.mojang.brigadier.tree.CommandNode; ++import com.mojang.brigadier.tree.LiteralCommandNode; ++ ++import java.util.Collection; ++ ++public class ShadowBrigNode extends LiteralCommandNode { ++ ++ private final CommandNode handle; ++ ++ public ShadowBrigNode(CommandNode node) { ++ super(node.getName(), context -> 0, (s) -> false, node.getRedirect() == null ? null : new ShadowBrigNode(node.getRedirect()), null, node.isFork()); ++ this.handle = node; ++ } ++ ++ @Override ++ public Collection> getChildren() { ++ throw new UnsupportedOperationException("Cannot retrieve children from this node."); ++ } ++ ++ @Override ++ public CommandNode getChild(String name) { ++ throw new UnsupportedOperationException("Cannot retrieve children from this node."); ++ } ++ ++ @Override ++ public void addChild(CommandNode node) { ++ throw new UnsupportedOperationException("Cannot modify children for this node."); ++ } ++ ++ public CommandNode getHandle() { ++ return this.handle; ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentImpl.java b/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentImpl.java +new file mode 100644 +index 0000000000000000000000000000000000000000..adc0df7c3a762d99e7ad458576d4be68e94cc351 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentImpl.java +@@ -0,0 +1,29 @@ ++package io.papermc.paper.command.brigadier.argument; ++ ++import com.mojang.brigadier.context.CommandContext; ++import com.mojang.brigadier.exceptions.CommandSyntaxException; ++import net.kyori.adventure.chat.SignedMessage; ++import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.commands.arguments.MessageArgument; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.concurrent.CompletableFuture; ++ ++public record MessageArgumentImpl(MessageArgument.Message message) implements MessageArgumentResponse { ++ @Override ++ public String content() { ++ return this.message.getText(); ++ } ++ ++ @Override ++ public @NotNull CompletableFuture resolveSignedMessage(String argumentName, CommandContext erased) throws CommandSyntaxException { ++ CommandContext type = erased; ++ CompletableFuture future = new CompletableFuture<>(); ++ ++ MessageArgument.Message response = type.getArgument(argumentName, MessageArgumentImpl.class).message; ++ MessageArgument.resolveChatMessage(response, type, argumentName, (message) -> { ++ future.complete(message.adventureView()); ++ }); ++ return future; ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0e59060730269b9c97376e518e906bc82b602a0e +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java +@@ -0,0 +1,311 @@ ++package io.papermc.paper.command.brigadier.argument; ++ ++import com.destroystokyo.paper.profile.CraftPlayerProfile; ++import com.google.common.collect.Collections2; ++import com.google.common.collect.Lists; ++import com.google.common.collect.Range; ++import com.mojang.brigadier.StringReader; ++import com.mojang.brigadier.arguments.ArgumentType; ++import com.mojang.brigadier.context.CommandContext; ++import com.mojang.brigadier.exceptions.CommandSyntaxException; ++import com.mojang.brigadier.suggestion.Suggestions; ++import com.mojang.brigadier.suggestion.SuggestionsBuilder; ++import io.papermc.paper.adventure.PaperAdventure; ++import io.papermc.paper.command.brigadier.ApiMirrorRootNode; ++import io.papermc.paper.command.brigadier.PaperCommands; ++import io.papermc.paper.command.brigadier.argument.predicate.ItemStackPredicate; ++import io.papermc.paper.command.brigadier.argument.range.DoubleRangeProvider; ++import io.papermc.paper.command.brigadier.argument.range.IntegerRangeProvider; ++import io.papermc.paper.command.brigadier.argument.range.RangeProvider; ++import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver; ++import io.papermc.paper.command.brigadier.argument.resolvers.PlayerProfileListResolver; ++import io.papermc.paper.command.brigadier.argument.resolvers.selector.*; ++import io.papermc.paper.entity.LookAnchor; ++import io.papermc.paper.math.Position; ++import java.util.Collection; ++import java.util.Collections; ++import java.util.List; ++import java.util.UUID; ++import java.util.concurrent.CompletableFuture; ++import java.util.function.Function; ++import io.papermc.paper.util.Tick; ++import net.kyori.adventure.key.Key; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.format.NamedTextColor; ++import net.minecraft.advancements.critereon.MinMaxBounds; ++import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.commands.arguments.ColorArgument; ++import net.minecraft.commands.arguments.ComponentArgument; ++import net.minecraft.commands.arguments.DimensionArgument; ++import net.minecraft.commands.arguments.EntityAnchorArgument; ++import net.minecraft.commands.arguments.EntityArgument; ++import net.minecraft.commands.arguments.GameModeArgument; ++import net.minecraft.commands.arguments.GameProfileArgument; ++import net.minecraft.commands.arguments.HeightmapTypeArgument; ++import net.minecraft.commands.arguments.MessageArgument; ++import net.minecraft.commands.arguments.ObjectiveCriteriaArgument; ++import net.minecraft.commands.arguments.RangeArgument; ++import net.minecraft.commands.arguments.ResourceLocationArgument; ++import net.minecraft.commands.arguments.ScoreboardSlotArgument; ++import net.minecraft.commands.arguments.TemplateMirrorArgument; ++import net.minecraft.commands.arguments.TemplateRotationArgument; ++import net.minecraft.commands.arguments.TimeArgument; ++import net.minecraft.commands.arguments.UuidArgument; ++import net.minecraft.commands.arguments.blocks.BlockStateArgument; ++import net.minecraft.commands.arguments.coordinates.BlockPosArgument; ++import net.minecraft.commands.arguments.coordinates.RotationArgument; ++import net.minecraft.commands.arguments.item.ItemArgument; ++import net.minecraft.commands.arguments.item.ItemPredicateArgument; ++import net.minecraft.core.BlockPos; ++import net.minecraft.core.registries.Registries; ++import net.minecraft.resources.ResourceKey; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.level.ServerLevel; ++import net.minecraft.server.level.ServerPlayer; ++import net.minecraft.world.level.Level; ++import org.bukkit.GameMode; ++import org.bukkit.HeightMap; ++import org.bukkit.NamespacedKey; ++import org.bukkit.Rotation; ++import org.bukkit.World; ++import org.bukkit.block.BlockState; ++import org.bukkit.block.structure.Mirror; ++import org.bukkit.craftbukkit.CraftHeightMap; ++import org.bukkit.craftbukkit.block.CraftBlockStates; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.scoreboard.CraftCriteria; ++import org.bukkit.craftbukkit.scoreboard.CraftScoreboardTranslations; ++import org.bukkit.craftbukkit.util.CraftNamespacedKey; ++import org.bukkit.inventory.ItemStack; ++import org.bukkit.scoreboard.Criteria; ++import org.bukkit.scoreboard.DisplaySlot; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++import static java.util.Objects.requireNonNull; ++ ++@DefaultQualifier(NonNull.class) ++public class VanillaArgumentProviderImpl implements VanillaArgumentProvider { ++ ++ @Override ++ public ArgumentType entity() { ++ return this.of(EntityArgument.entity(), (result) -> sourceStack -> { ++ return List.of(result.findSingleEntity((CommandSourceStack) sourceStack).getBukkitEntity()); ++ }); ++ } ++ ++ @Override ++ public ArgumentType entities() { ++ return this.of(EntityArgument.entities(), (result) -> sourceStack -> { ++ return Lists.transform(result.findEntities((CommandSourceStack) sourceStack), net.minecraft.world.entity.Entity::getBukkitEntity); ++ }); ++ } ++ ++ @Override ++ public ArgumentType player() { ++ return this.of(EntityArgument.player(), (result) -> sourceStack -> { ++ return List.of(result.findSinglePlayer((CommandSourceStack) sourceStack).getBukkitEntity()); ++ }); ++ } ++ ++ @Override ++ public ArgumentType players() { ++ return this.of(EntityArgument.players(), (result) -> sourceStack -> { ++ return Lists.transform(result.findPlayers((CommandSourceStack) sourceStack), ServerPlayer::getBukkitEntity); ++ }); ++ } ++ ++ @Override ++ public ArgumentType playerProfiles() { ++ return this.of(GameProfileArgument.gameProfile(), result -> { ++ if (result instanceof GameProfileArgument.SelectorResult) { ++ return sourceStack -> Collections.unmodifiableCollection(Collections2.transform(result.getNames((CommandSourceStack) sourceStack), CraftPlayerProfile::new)); ++ } else { ++ return sourceStack -> Collections.unmodifiableCollection(Collections2.transform(result.getNames((CommandSourceStack) sourceStack), CraftPlayerProfile::new)); ++ } ++ }); ++ } ++ ++ @Override ++ public ArgumentType blockPos() { ++ return this.of(BlockPosArgument.blockPos(), (result) -> sourceStack -> { ++ BlockPos pos = result.getBlockPos((CommandSourceStack) sourceStack); ++ ++ return Position.block(pos.getX(), pos.getY(), pos.getZ()); ++ }); ++ } ++ ++ @Override ++ public ArgumentType blockState() { ++ return this.of(BlockStateArgument.block(PaperCommands.INSTANCE.getBuildContext()), (result) -> { ++ return CraftBlockStates.getBlockState(result.getState(), result.tag); ++ }); ++ } ++ ++ @Override ++ public ArgumentType itemStack() { ++ return this.of(ItemArgument.item(PaperCommands.INSTANCE.getBuildContext()), (result) -> { ++ return CraftItemStack.asBukkitCopy(result.createItemStack(1, true)); ++ }); ++ } ++ ++ @Override ++ public ArgumentType itemStackPredicate() { ++ return this.of(ItemPredicateArgument.itemPredicate(PaperCommands.INSTANCE.getBuildContext()), type -> itemStack -> type.test(CraftItemStack.asNMSCopy(itemStack))); ++ } ++ ++ @Override ++ public ArgumentType namedColor() { ++ return this.of(ColorArgument.color(), result -> ++ requireNonNull( ++ NamedTextColor.namedColor( ++ requireNonNull(result.getColor(), () -> result + " didn't have a color") ++ ), ++ () -> result.getColor() + " didn't map to an adventure named color" ++ ) ++ ); ++ } ++ ++ @Override ++ public ArgumentType component() { ++ return this.of(ComponentArgument.textComponent(), PaperAdventure::asAdventure); ++ } ++ ++ @Override ++ public ArgumentType signedMessage() { ++ return this.of(MessageArgument.message(), MessageArgumentImpl::new); ++ } ++ ++ @Override ++ public ArgumentType scoreboardDisplaySlot() { ++ return this.of(ScoreboardSlotArgument.displaySlot(), CraftScoreboardTranslations::toBukkitSlot); ++ } ++ ++ @Override ++ public ArgumentType namespacedKey() { ++ return this.of(ResourceLocationArgument.id(), CraftNamespacedKey::fromMinecraft); ++ } ++ ++ @Override ++ public ArgumentType key() { ++ return this.of(ResourceLocationArgument.id(), CraftNamespacedKey::fromMinecraft); ++ } ++ ++ @Override ++ public ArgumentType integerRange() { ++ return this.of(RangeArgument.intRange(), type -> VanillaArgumentProviderImpl.convertToRange(type, integerRange -> () -> integerRange)); ++ } ++ ++ @Override ++ public ArgumentType doubleRange() { ++ return this.of(RangeArgument.floatRange(), type -> VanillaArgumentProviderImpl.convertToRange(type, doubleRange -> () -> doubleRange)); ++ } ++ ++ private static , T extends RangeProvider> T convertToRange(MinMaxBounds bounds, Function, T> converter) { ++ if (bounds.isAny()) { ++ return converter.apply(Range.all()); ++ } else if (bounds.min().isPresent() && bounds.max().isPresent()) { ++ return converter.apply(Range.closed(bounds.min().get(), bounds.max().get())); ++ } else if (bounds.max().isPresent()) { ++ return converter.apply(Range.atMost(bounds.max().get())); ++ } else if (bounds.min().isPresent()) { ++ return converter.apply(Range.atLeast(bounds.min().get())); ++ } ++ throw new IllegalStateException("This is a bug: " + bounds); ++ } ++ ++ @Override ++ public ArgumentType world() { ++ return this.of(DimensionArgument.dimension(), dimensionLocation -> { ++ // based on DimensionArgument#getDimension ++ ResourceKey resourceKey = ResourceKey.create(Registries.DIMENSION, dimensionLocation); ++ @Nullable ServerLevel serverLevel = MinecraftServer.getServer().getLevel(resourceKey); ++ if (serverLevel == null) { ++ throw DimensionArgument.ERROR_INVALID_VALUE.create(dimensionLocation); ++ } else { ++ return serverLevel.getWorld(); ++ } ++ }); ++ } ++ ++ @Override ++ public ArgumentType gameMode() { ++ return this.of(GameModeArgument.gameMode(), type -> requireNonNull(GameMode.getByValue(type.getId()))); ++ } ++ ++ @Override ++ public ArgumentType heightMap() { ++ return this.of(HeightmapTypeArgument.heightmap(), CraftHeightMap::fromNMS); ++ } ++ ++ @Override ++ public ArgumentType uuid() { ++ return this.of(UuidArgument.uuid(), (identity) -> identity); ++ } ++ ++ @Override ++ public ArgumentType objectiveCriteria() { ++ return this.of(ObjectiveCriteriaArgument.criteria(), CraftCriteria::getFromNMS); ++ } ++ ++ @Override ++ public ArgumentType entityAnchor() { ++ return this.of(EntityAnchorArgument.anchor(), type -> LookAnchor.valueOf(type.name())); ++ } ++ ++ @Override ++ public ArgumentType time(final int minTicks) { ++ return this.of(TimeArgument.time(minTicks), identity -> identity); ++ } ++ ++ @Override ++ public ArgumentType templateMirror() { ++ return this.of(TemplateMirrorArgument.templateMirror(), mirror -> Mirror.valueOf(mirror.name())); ++ } ++ ++ @Override ++ public ArgumentType templateRotation() { ++ return this.of(TemplateRotationArgument.templateRotation(), mirror -> Rotation.valueOf(mirror.name())); ++ } ++ ++ private ArgumentType of(ArgumentType base, ResultConverter converter) { ++ return new NativeWrapperArgumentType<>(base, converter); ++ } ++ ++ @FunctionalInterface ++ interface ResultConverter { ++ ++ R convert(T type) throws CommandSyntaxException; ++ } ++ ++ public static final class NativeWrapperArgumentType implements ArgumentType

{ ++ ++ private final ArgumentType nmsBase; ++ private final ResultConverter converter; ++ ++ private NativeWrapperArgumentType(final ArgumentType nmsBase, final ResultConverter converter) { ++ this.nmsBase = nmsBase; ++ this.converter = converter; ++ } ++ ++ public ArgumentType nativeNmsArgumentType() { ++ return this.nmsBase; ++ } ++ ++ @Override ++ public P parse(final StringReader reader) throws CommandSyntaxException { ++ return this.converter.convert(this.nmsBase.parse(reader)); ++ } ++ ++ @Override ++ public CompletableFuture listSuggestions(final CommandContext context, final SuggestionsBuilder builder) { ++ return this.nmsBase.listSuggestions(context, builder); ++ } ++ ++ @Override ++ public Collection getExamples() { ++ return this.nmsBase.getExamples(); ++ } ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/WrappedArgumentCommandNode.java b/src/main/java/io/papermc/paper/command/brigadier/argument/WrappedArgumentCommandNode.java +new file mode 100644 +index 0000000000000000000000000000000000000000..7a838bab16b33025b9a2eae8325f353ad9689e1b +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/argument/WrappedArgumentCommandNode.java +@@ -0,0 +1,45 @@ ++package io.papermc.paper.command.brigadier.argument; ++ ++import com.mojang.brigadier.Command; ++import com.mojang.brigadier.RedirectModifier; ++import com.mojang.brigadier.StringReader; ++import com.mojang.brigadier.arguments.ArgumentType; ++import com.mojang.brigadier.context.CommandContextBuilder; ++import com.mojang.brigadier.context.ParsedArgument; ++import com.mojang.brigadier.exceptions.CommandSyntaxException; ++import com.mojang.brigadier.suggestion.SuggestionProvider; ++import com.mojang.brigadier.tree.ArgumentCommandNode; ++import com.mojang.brigadier.tree.CommandNode; ++import io.papermc.paper.command.brigadier.CommandSourceStack; ++import net.minecraft.commands.synchronization.ArgumentTypeInfos; ++ ++import java.util.function.Predicate; ++ ++/* ++Basically this converts the argument to a different type when parsing. ++ */ ++public class WrappedArgumentCommandNode extends ArgumentCommandNode { ++ ++ private final ArgumentType argument; ++ ++ public WrappedArgumentCommandNode(String name, ArgumentType argument, ArgumentType nms, Command command, Predicate requirement, CommandNode redirect, RedirectModifier modifier, boolean forks, SuggestionProvider customSuggestions) { ++ super(name, nms, command, requirement, redirect, modifier, forks, customSuggestions); ++ if (!ArgumentTypeInfos.isClassRecognized(nms.getClass())) { ++ // Is this argument an NMS argument? ++ throw new IllegalArgumentException("Unexpected argument type was passed. This should be an NMS type!"); ++ } ++ ++ this.argument = argument; ++ } ++ ++ // See ArgumentCommandNode#parse ++ @Override ++ public void parse(StringReader reader, CommandContextBuilder contextBuilder) throws CommandSyntaxException { ++ final int start = reader.getCursor(); ++ API result = this.argument.parse(reader); // Use the api argument parser ++ final ParsedArgument parsed = new ParsedArgument<>(start, reader.getCursor(), result); // Return an API parsed argument instead. ++ ++ contextBuilder.withArgument(this.getName(), parsed); ++ contextBuilder.withNode(this, parsed.getRange()); ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitBrigForwardingMap.java b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitBrigForwardingMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..685b3da3f294d8c6ab0fe74d1485a81b03f3215a +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitBrigForwardingMap.java +@@ -0,0 +1,332 @@ ++package io.papermc.paper.command.brigadier.bukkit; ++ ++import com.google.common.collect.Iterators; ++import com.mojang.brigadier.CommandDispatcher; ++import com.mojang.brigadier.tree.CommandNode; ++import com.mojang.brigadier.tree.LiteralCommandNode; ++import io.papermc.paper.command.brigadier.CommandSourceStack; ++import io.papermc.paper.command.brigadier.PaperBrigadier; ++import io.papermc.paper.command.brigadier.PaperCommands; ++import java.util.AbstractCollection; ++import java.util.AbstractSet; ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.HashMap; ++import java.util.Iterator; ++import java.util.Map; ++import java.util.Objects; ++import java.util.Set; ++import java.util.Spliterator; ++import java.util.function.Consumer; ++import java.util.stream.Stream; ++import org.bukkit.command.Command; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++/* ++This map is supposed to act as a legacy bridge for the command map and the command dispatcher. ++ */ ++public class BukkitBrigForwardingMap extends HashMap { ++ ++ public static BukkitBrigForwardingMap INSTANCE = new BukkitBrigForwardingMap(); ++ ++ private final EntrySet entrySet = new EntrySet(); ++ private final KeySet keySet = new KeySet(); ++ private final Values values = new Values(); ++ ++ // Previous dispatcher used to get commands to migrate to another dispatcher ++ ++ public CommandDispatcher getDispatcher() { ++ return PaperCommands.INSTANCE.getDispatcherInternal(); ++ } ++ ++ @Override ++ public int size() { ++ return this.getDispatcher().getRoot().getChildren().size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return this.size() != 0; ++ } ++ ++ @Override ++ public boolean containsKey(Object key) { ++ if (!(key instanceof String stringKey)) { ++ return false; ++ } ++ ++ // Do any children match? ++ return this.getDispatcher().getRoot().getChild(stringKey) != null; ++ } ++ ++ @Override ++ public boolean containsValue(Object value) { ++ if (value == null) { ++ return false; ++ } ++ ++ for (CommandNode child : this.getDispatcher().getRoot().getChildren()) { ++ // If child is a bukkit command node, we can convert it! ++ if (child instanceof BukkitCommandNode bukkitCommandNode) { ++ return bukkitCommandNode.getBukkitCommand().equals(value); ++ } ++ } ++ ++ return false; ++ } ++ ++ @Override ++ public Command get(Object key) { ++ CommandNode node = this.getDispatcher().getRoot().getChild((String) key); ++ if (node == null) { ++ return null; ++ } ++ ++ if (node instanceof BukkitCommandNode bukkitCommandNode) { ++ return bukkitCommandNode.getBukkitCommand(); ++ } ++ ++ return PaperBrigadier.wrapNode(node); ++ } ++ ++ @Nullable ++ @Override ++ public Command put(String key, Command value) { ++ Command old = this.get(key); ++ this.getDispatcher().getRoot().removeCommand(key); // Override previous command ++ this.getDispatcher().getRoot().addChild(BukkitCommandNode.of(key, value)); ++ return old; ++ } ++ ++ @Override ++ public Command remove(Object key) { ++ if (!(key instanceof String string)) { ++ return null; ++ } ++ ++ Command old = this.get(key); ++ if (old != null) { ++ this.getDispatcher().getRoot().removeCommand(string); ++ } ++ ++ return old; ++ } ++ ++ @Override ++ public boolean remove(Object key, Object value) { ++ Command old = this.get(key); ++ if (Objects.equals(old, value)) { ++ this.remove(key); ++ return true; ++ } ++ ++ return false; ++ } ++ ++ @Override ++ public void putAll(@NotNull Map m) { ++ for (Entry entry : m.entrySet()) { ++ this.put(entry.getKey(), entry.getValue()); ++ } ++ } ++ ++ @Override ++ public void clear() { ++ this.getDispatcher().getRoot().clearAll(); ++ } ++ ++ @NotNull ++ @Override ++ public Set keySet() { ++ return this.keySet; ++ } ++ ++ @NotNull ++ @Override ++ public Collection values() { ++ return this.values; ++ } ++ ++ @NotNull ++ @Override ++ public Set> entrySet() { ++ return this.entrySet; ++ } ++ ++ final class Values extends AbstractCollection { ++ ++ @Override ++ public Iterator iterator() { ++ // AVOID CME since commands can modify multiple commands now through alises, which means it may appear in the iterator even if removed. ++ // Oh well! ++ Iterator> iterator = new ArrayList<>(BukkitBrigForwardingMap.this.getDispatcher().getRoot().getChildren()).iterator(); ++ ++ return new Iterator<>() { ++ ++ private CommandNode lastFetched; ++ ++ @Override ++ public void remove() { ++ if (this.lastFetched == null) { ++ throw new IllegalStateException("next not yet called"); ++ } ++ ++ BukkitBrigForwardingMap.this.remove(this.lastFetched.getName()); ++ iterator.remove(); ++ } ++ ++ @Override ++ public boolean hasNext() { ++ return iterator.hasNext(); ++ } ++ ++ @Override ++ public Command next() { ++ CommandNode next = iterator.next(); ++ this.lastFetched = next; ++ if (next instanceof BukkitCommandNode bukkitCommandNode) { ++ return bukkitCommandNode.getBukkitCommand(); ++ } else { ++ return PaperBrigadier.wrapNode(next); ++ } ++ } ++ }; ++ } ++ ++ @Override ++ public int size() { ++ return BukkitBrigForwardingMap.this.getDispatcher().getRoot().getChildren().size(); ++ } ++ ++ @Override ++ public void clear() { ++ BukkitBrigForwardingMap.this.clear(); ++ } ++ } ++ ++ ++ final class KeySet extends AbstractSet { ++ ++ @Override ++ public int size() { ++ return BukkitBrigForwardingMap.this.size(); ++ } ++ ++ @Override ++ public void clear() { ++ BukkitBrigForwardingMap.this.clear(); ++ } ++ ++ @Override ++ public Iterator iterator() { ++ return Iterators.transform(BukkitBrigForwardingMap.this.values.iterator(), Command::getName); // Wrap around the values iterator for consistancy ++ } ++ ++ @Override ++ public boolean contains(Object o) { ++ return BukkitBrigForwardingMap.this.containsKey(o); ++ } ++ ++ @Override ++ public boolean remove(Object o) { ++ return BukkitBrigForwardingMap.this.remove(o) != null; ++ } ++ ++ @Override ++ public Spliterator spliterator() { ++ return this.entryStream().spliterator(); ++ } ++ ++ @Override ++ public void forEach(Consumer action) { ++ this.entryStream().forEach(action); ++ } ++ ++ private Stream entryStream() { ++ return BukkitBrigForwardingMap.this.getDispatcher().getRoot().getChildren().stream().map(CommandNode::getName); ++ } ++ } ++ ++ final class EntrySet extends AbstractSet> { ++ @Override ++ public int size() { ++ return BukkitBrigForwardingMap.this.size(); ++ } ++ ++ ++ @Override ++ public void clear() { ++ BukkitBrigForwardingMap.this.clear(); ++ } ++ ++ @Override ++ public Iterator> iterator() { ++ return this.entryStream().iterator(); ++ } ++ ++ @Override ++ public boolean contains(Object o) { ++ if (!(o instanceof Map.Entry entry)) { ++ return false; ++ } ++ ++ Object key = entry.getKey(); ++ Command candidate = get(key); ++ return candidate != null && candidate.equals(entry.getValue()); ++ } ++ ++ @Override ++ public boolean remove(Object o) { ++ if (o instanceof Map.Entry e) { ++ Object key = e.getKey(); ++ Object value = e.getValue(); ++ return BukkitBrigForwardingMap.this.remove(key, value); ++ } ++ return false; ++ } ++ ++ @Override ++ public Spliterator> spliterator() { ++ return this.entryStream().spliterator(); ++ } ++ ++ @Override ++ public void forEach(Consumer> action) { ++ this.entryStream().forEach(action); ++ } ++ ++ private Stream> entryStream() { ++ return BukkitBrigForwardingMap.this.getDispatcher().getRoot().getChildren().stream().map(BukkitBrigForwardingMap.this::nodeToEntry); ++ } ++ } ++ ++ private Map.Entry nodeToEntry(CommandNode node) { ++ if (node instanceof BukkitCommandNode bukkitCommandNode) { ++ return this.mutableEntry(bukkitCommandNode.getName(), bukkitCommandNode.getBukkitCommand()); ++ } else { ++ Command wrapped = PaperBrigadier.wrapNode(node); ++ return this.mutableEntry(node.getName(), wrapped); ++ } ++ } ++ ++ private Map.Entry mutableEntry(String key, Command command) { ++ return new Entry<>() { ++ @Override ++ public String getKey() { ++ return key; ++ } ++ ++ @Override ++ public Command getValue() { ++ return command; ++ } ++ ++ @Override ++ public Command setValue(Command value) { ++ return BukkitBrigForwardingMap.this.put(key, value); ++ } ++ }; ++ } ++ ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java +new file mode 100644 +index 0000000000000000000000000000000000000000..10a113b057b0a4d27cce3bae975e1108aaa7b517 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java +@@ -0,0 +1,135 @@ ++package io.papermc.paper.command.brigadier.bukkit; ++ ++import co.aikar.timings.Timing; ++import com.mojang.brigadier.arguments.StringArgumentType; ++import com.mojang.brigadier.builder.RequiredArgumentBuilder; ++import com.mojang.brigadier.context.CommandContext; ++import com.mojang.brigadier.exceptions.CommandSyntaxException; ++import com.mojang.brigadier.suggestion.SuggestionProvider; ++import com.mojang.brigadier.suggestion.Suggestions; ++import com.mojang.brigadier.suggestion.SuggestionsBuilder; ++import com.mojang.brigadier.tree.LiteralCommandNode; ++import io.papermc.paper.command.brigadier.CommandSourceStack; ++import net.minecraft.commands.CommandSource; ++import org.bukkit.Bukkit; ++import org.bukkit.ChatColor; ++import org.bukkit.Location; ++import org.bukkit.command.Command; ++import org.bukkit.command.CommandException; ++import org.bukkit.command.CommandSender; ++ ++import java.util.Arrays; ++import java.util.List; ++import java.util.concurrent.CompletableFuture; ++import java.util.logging.Level; ++ ++public class BukkitCommandNode extends LiteralCommandNode { ++ ++ private final Command command; ++ ++ private BukkitCommandNode(String literal, Command command, BukkitBrigCommand bukkitBrigCommand) { ++ super( ++ literal, bukkitBrigCommand, source -> { ++ // If the source is null, assume it's true. ++ // As bukkit doesn't really map the command sender well in all cases ++ if (source instanceof net.minecraft.commands.CommandSourceStack commandSourceStack && commandSourceStack.source == CommandSource.NULL) { ++ return true; ++ } else { ++ return command.testPermissionSilent(source.getSender()); ++ } ++ }, ++ null, null, false ++ ); ++ this.command = command; ++ } ++ ++ public static BukkitCommandNode of(String name, Command command) { ++ BukkitBrigCommand bukkitBrigCommand = new BukkitBrigCommand(command, name); ++ BukkitCommandNode commandNode = new BukkitCommandNode(name, command, bukkitBrigCommand); ++ commandNode.addChild( ++ RequiredArgumentBuilder.argument("args", StringArgumentType.greedyString()) ++ .suggests(new BukkitBrigSuggestionProvider(command, name)) ++ .executes(bukkitBrigCommand).build() ++ ); ++ ++ return commandNode; ++ } ++ ++ public Command getBukkitCommand() { ++ return this.command; ++ } ++ ++ public static class BukkitBrigCommand implements com.mojang.brigadier.Command { ++ ++ private final org.bukkit.command.Command command; ++ private final String literal; ++ ++ BukkitBrigCommand(org.bukkit.command.Command command, String literal) { ++ this.command = command; ++ this.literal = literal; ++ } ++ ++ @Override ++ public int run(CommandContext context) throws CommandSyntaxException { ++ CommandSender sender = context.getSource().getSender(); ++ ++ // Plugins do weird things to workaround normal registration ++ if (this.command.timings == null) { ++ this.command.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, this.command); ++ } ++ ++ String content = context.getRange().get(context.getInput()); ++ String[] args = org.apache.commons.lang3.StringUtils.split(content, ' '); // fix adjacent spaces (from console/plugins) causing empty array elements ++ ++ try (Timing ignored = this.command.timings.startTiming()) { ++ // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false) ++ this.command.execute(sender, this.literal, Arrays.copyOfRange(args, 1, args.length)); ++ } ++ ++ // return true as command was handled ++ return 1; ++ } ++ } ++ ++ static class BukkitBrigSuggestionProvider implements SuggestionProvider { ++ ++ private final org.bukkit.command.Command command; ++ private final String literal; ++ ++ BukkitBrigSuggestionProvider(org.bukkit.command.Command command, String literal) { ++ this.command = command; ++ this.literal = literal; ++ } ++ ++ @Override ++ public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException { ++ // Paper start ++ org.bukkit.command.CommandSender sender = context.getSource().getSender(); ++ String[] args = builder.getRemaining().split(" ", -1); // We need the command included -- Set limit to -1, allow for trailing spaces ++ ++ List results = null; ++ Location pos = context.getSource().getLocation(); ++ try { ++ results = this.command.tabComplete(sender, this.literal, args, pos.clone()); ++ } catch (CommandException ex) { ++ sender.sendMessage(ChatColor.RED + "An internal error occurred while attempting to tab-complete this command"); ++ Bukkit.getServer().getLogger().log(Level.SEVERE, "Exception when " + sender.getName() + " attempted to tab complete " + builder.getRemaining(), ex); ++ } ++ ++ // Paper end ++ if (results == null) { ++ return builder.buildFuture(); ++ } ++ ++ // Defaults to sub nodes, but we have just one giant args node, so offset accordingly ++ builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1); ++ ++ for (String s : results) { ++ builder.suggest(s); ++ } ++ ++ return builder.buildFuture(); ++ } ++ } ++ ++} +diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java +index f84c9c80e701231e5c33ac3c5573f1093e80f38b..6c072e44a8144de6658b4eb818c996f0eac5805b 100644 +--- a/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java ++++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/LifecycleEventRunner.java +@@ -9,6 +9,7 @@ import io.papermc.paper.plugin.lifecycle.event.registrar.RegistrarEventImpl; + import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent; + import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType; + import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType; ++import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; + import io.papermc.paper.plugin.lifecycle.event.types.OwnerAwareLifecycleEvent; + import java.util.ArrayList; + import java.util.List; +@@ -26,6 +27,7 @@ public class LifecycleEventRunner { + + private static final Logger LOGGER = LogUtils.getClassLogger(); + private static final Supplier>> BLOCKS_RELOADING = Suppliers.memoize(() -> Set.of( // lazy due to cyclic initialization ++ LifecycleEvents.COMMANDS + )); + public static final LifecycleEventRunner INSTANCE = new LifecycleEventRunner(); + +diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java +index f341813e9713e39bfe142ca34b751de3d8efd25b..5110f8c23d199157404655e94a75588805144c3e 100644 +--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java ++++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java +@@ -45,8 +45,7 @@ import net.minecraft.world.phys.Vec2; + import net.minecraft.world.phys.Vec3; + import com.mojang.brigadier.tree.CommandNode; // CraftBukkit + +-public class CommandSourceStack implements ExecutionCommandSource, SharedSuggestionProvider, com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource { // Paper - Brigadier API +- ++public class CommandSourceStack implements ExecutionCommandSource, SharedSuggestionProvider, io.papermc.paper.command.brigadier.PaperCommandSourceStack { // Paper - Brigadier API + public static final SimpleCommandExceptionType ERROR_NOT_PLAYER = new SimpleCommandExceptionType(Component.translatable("permissions.requires.player")); + public static final SimpleCommandExceptionType ERROR_NOT_ENTITY = new SimpleCommandExceptionType(Component.translatable("permissions.requires.entity")); + public final CommandSource source; +@@ -170,26 +169,6 @@ public class CommandSourceStack implements ExecutionCommandSource dispatcher = new com.mojang.brigadier.CommandDispatcher(); +- public final java.util.List> vanillaCommandNodes = new java.util.ArrayList<>(); // Paper - Add UnknownCommandEvent + + public Commands(Commands.CommandSelection environment, CommandBuildContext commandRegistryAccess) { +- this(); // CraftBukkit + AdvancementCommands.register(this.dispatcher); + AttributeCommand.register(this.dispatcher, commandRegistryAccess); + ExecuteCommand.register(this.dispatcher, commandRegistryAccess); +@@ -255,7 +253,6 @@ public class Commands { + if (environment.includeIntegrated) { + PublishCommand.register(this.dispatcher); + } +- this.vanillaCommandNodes.addAll(this.dispatcher.getRoot().getChildren()); // Paper - Add UnknownCommandEvent + + // Paper start - Vanilla command permission fixes + for (final CommandNode node : this.dispatcher.getRoot().getChildren()) { +@@ -264,11 +261,17 @@ public class Commands { + } + } + // Paper end - Vanilla command permission fixes +- // CraftBukkit start +- } +- +- public Commands() { +- // CraftBukkkit end ++ // Paper start - Brigadier Command API ++ // Create legacy minecraft namespace commands ++ for (final CommandNode node : new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren())) { ++ this.dispatcher.register( ++ com.mojang.brigadier.builder.LiteralArgumentBuilder.literal("minecraft:" + node.getName()) ++ .executes(node.getCommand()) ++ .redirect(node) ++ ); ++ } ++ // Paper end - Brigadier Command API ++ // Paper - remove public constructor, no longer needed + this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer()); + } + +@@ -324,6 +327,11 @@ public class Commands { + } + + public void performCommand(ParseResults parseresults, String s, String label) { // CraftBukkit ++ // Paper start ++ this.performCommand(parseresults, s, label, false); ++ } ++ public void performCommand(ParseResults parseresults, String s, String label, boolean throwCommandError) { ++ // Paper end + CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseresults.getContext().getSource(); + + commandlistenerwrapper.getServer().getProfiler().push(() -> { +@@ -338,6 +346,7 @@ public class Commands { + }); + } + } catch (Exception exception) { ++ if (throwCommandError) throw exception; + MutableComponent ichatmutablecomponent = Component.literal(exception.getMessage() == null ? exception.getClass().getName() : exception.getMessage()); + + if (commandlistenerwrapper.getServer().isDebugging() || Commands.LOGGER.isDebugEnabled()) { // Paper - Debugging +@@ -372,7 +381,7 @@ public class Commands { + } catch (CommandSyntaxException commandsyntaxexception) { + // Paper start - Add UnknownCommandEvent + final net.kyori.adventure.text.TextComponent.Builder builder = net.kyori.adventure.text.Component.text(); +- if ((parseresults.getContext().getNodes().isEmpty() || !this.vanillaCommandNodes.contains(parseresults.getContext().getNodes().get(0).getNode()))) { ++ if (parseresults.getContext().getNodes().isEmpty() || ((CommandNode) parseresults.getContext().getNodes().get(0).getNode()) instanceof io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode) { + if (!org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty()) { + builder.append(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.unknownCommandMessage)); + } +@@ -482,7 +491,7 @@ public class Commands { + Map, CommandNode> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues + RootCommandNode vanillaRoot = new RootCommandNode(); + +- RootCommandNode vanilla = player.server.vanillaCommandDispatcher.getDispatcher().getRoot(); ++ RootCommandNode vanilla = player.server.getCommands().getDispatcher().getRoot(); // Paper + map.put(vanilla, vanillaRoot); + this.fillUsableCommands(vanilla, vanillaRoot, player.createCommandSourceStack(), (Map) map); + +@@ -520,6 +529,7 @@ public class Commands { + } + + private void fillUsableCommands(CommandNode tree, CommandNode result, CommandSourceStack source, Map, CommandNode> resultNodes) { ++ resultNodes.keySet().removeIf((node) -> !org.spigotmc.SpigotConfig.sendNamespaced && node.getName().contains( ":" )); // Paper - Remove namedspaced from result nodes to prevent redirect trimming ~ see comment below + Iterator iterator = tree.getChildren().iterator(); + + boolean registeredAskServerSuggestionsForTree = false; // Paper - tell clients to ask server for suggestions for EntityArguments +@@ -534,6 +544,42 @@ public class Commands { + + if (commandnode2.canUse(source)) { + ArgumentBuilder argumentbuilder = commandnode2.createBuilder(); // CraftBukkit - decompile error ++ // Paper start ++ /* ++ Because of how commands can be yeeted right left and center due to bad bukkit practices ++ we need to be able to ensure that ALL commands are registered (even redirects). ++ ++ What this will do is IF the redirect seems to be "dead" it will create a builder and essentially populate (flatten) ++ all the children from the dead redirect to the node. ++ ++ So, if minecraft:msg redirects to msg but the original msg node has been overriden minecraft:msg will now act as msg and will explicilty inherit its children. ++ ++ The only way to fix this is to either: ++ - Send EVERYTHING flattened, don't use redirects ++ - Don't allow command nodes to be deleted ++ - Do this :) ++ */ ++ ++ // Is there an invalid command redirect? ++ if (argumentbuilder.getRedirect() != null && (CommandNode) resultNodes.get(argumentbuilder.getRedirect()) == null) { ++ // Create the argument builder with the same values as the specified node, but with a different literal and populated children ++ ++ CommandNode redirect = argumentbuilder.getRedirect(); ++ // Diff copied from LiteralCommand#createBuilder ++ final com.mojang.brigadier.builder.LiteralArgumentBuilder builder = com.mojang.brigadier.builder.LiteralArgumentBuilder.literal(commandnode2.getName()); ++ builder.requires(redirect.getRequirement()); ++ // builder.forward(redirect.getRedirect(), redirect.getRedirectModifier(), redirect.isFork()); We don't want to migrate the forward, since it's invalid. ++ if (redirect.getCommand() != null) { ++ builder.executes(redirect.getCommand()); ++ } ++ // Diff copied from LiteralCommand#createBuilder ++ for (CommandNode child : redirect.getChildren()) { ++ builder.then(child); ++ } ++ ++ argumentbuilder = builder; ++ } ++ // Paper end + + argumentbuilder.requires((icompletionprovider) -> { + return true; +diff --git a/src/main/java/net/minecraft/commands/arguments/MessageArgument.java b/src/main/java/net/minecraft/commands/arguments/MessageArgument.java +index 23f6b833e71002c648e20744ed0beb344efbe8b9..3cfb8314d623de47c3f19bee17d39f2393e9b767 100644 +--- a/src/main/java/net/minecraft/commands/arguments/MessageArgument.java ++++ b/src/main/java/net/minecraft/commands/arguments/MessageArgument.java +@@ -36,6 +36,11 @@ public class MessageArgument implements SignedArgument + + public static void resolveChatMessage(CommandContext context, String name, Consumer callback) throws CommandSyntaxException { + MessageArgument.Message message = context.getArgument(name, MessageArgument.Message.class); ++ // Paper start ++ resolveChatMessage(message, context, name, callback); ++ } ++ public static void resolveChatMessage(MessageArgument.Message message, CommandContext context, String name, Consumer callback) throws CommandSyntaxException { ++ // Paper end + CommandSourceStack commandSourceStack = context.getSource(); + Component component = message.resolveComponent(commandSourceStack); + CommandSigningContext commandSigningContext = commandSourceStack.getSigningContext(); +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 2dc07e5ef249636e85ad9c78e3729e9e066a8fe8..231f90ce796ff270da79362fc1ab54c2c3c10aaf 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -295,7 +295,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); + public int autosavePeriod; +- public Commands vanillaCommandDispatcher; ++ // Paper - don't store the vanilla dispatcher + public boolean forceTicks; // Paper + // CraftBukkit end + // Spigot start +@@ -383,7 +383,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { ++ io.papermc.paper.command.brigadier.PaperBrigadier.moveBukkitCommands(this.resources.managers().getCommands(), minecraftserver_reloadableresources.managers().commands); // Paper + this.resources.close(); + this.resources = minecraftserver_reloadableresources; +- this.server.syncCommands(); // SPIGOT-5884: Lost on reload + this.packRepository.setSelected(dataPacks); + WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(MinecraftServer.getSelectedPacks(this.packRepository), this.worldData.enabledFeatures()); + +@@ -2263,8 +2265,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop getDispatcher() { +- return this.server.vanillaCommandDispatcher.getDispatcher(); // CraftBukkit ++ return this.server.getCommands().getDispatcher(); // CraftBukkit // Paper - Don't override command dispatcher + } + + public void tick() { +diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +index 68d268b6fff126e8645b6deec3fb549ea2286b77..80009f2835c2b220d79464dba09f8a0623e0af6c 100644 +--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +@@ -220,7 +220,6 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command + com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics + com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now +- io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // Paper - init PaperBrigadierProvider + + this.setPvpAllowed(dedicatedserverproperties.pvp); + this.setFlightAllowed(dedicatedserverproperties.allowFlight); +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index fe2ef36ab5dc4b933abf24dbfd0e811c53239cf0..20d88d5949f6acfe63d4d0f120e19ae97182c875 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -2392,33 +2392,20 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl + } + } + ++ @Deprecated // Paper + public void handleCommand(String s) { // Paper - private -> public +- org.spigotmc.AsyncCatcher.catchOp("Command Dispatched Async: " + s); // Paper - Add async catcher +- co.aikar.timings.MinecraftTimings.playerCommandTimer.startTiming(); // Paper +- if ( org.spigotmc.SpigotConfig.logCommands ) // Spigot +- this.LOGGER.info(this.player.getScoreboardName() + " issued server command: " + s); +- +- CraftPlayer player = this.getCraftPlayer(); +- +- PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(player, s, new LazyPlayerSet(this.server)); +- this.cserver.getPluginManager().callEvent(event); +- +- if (event.isCancelled()) { +- co.aikar.timings.MinecraftTimings.playerCommandTimer.stopTiming(); // Paper +- return; +- } +- +- try { +- if (this.cserver.dispatchCommand(event.getPlayer(), event.getMessage().substring(1))) { +- return; +- } +- } catch (org.bukkit.command.CommandException ex) { +- player.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command"); +- java.util.logging.Logger.getLogger(ServerGamePacketListenerImpl.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); +- return; +- } finally { +- co.aikar.timings.MinecraftTimings.playerCommandTimer.stopTiming(); // Paper ++ // Paper start - Remove all this old duplicated logic ++ if (s.startsWith("/")) { ++ s = s.substring(1); + } ++ /* ++ It should be noted that this represents the "legacy" command execution path. ++ Api can call commands even if there is no additional context provided. ++ This method should ONLY be used if you need to execute a command WITHOUT ++ an actual player's input. ++ */ ++ this.performChatCommand(new ServerboundChatCommandPacket(s, java.time.Instant.now(), 0, net.minecraft.commands.arguments.ArgumentSignatures.EMPTY, null), null); ++ // Paper end + } + // CraftBukkit end + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index c490a29bcf7410bc54959ee71375605964379ed5..562fc91973df3cbddaac4e9abedf08557ef4e7f6 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -272,11 +272,11 @@ public final class CraftServer implements Server { + private final Logger logger = Logger.getLogger("Minecraft"); + private final ServicesManager servicesManager = new SimpleServicesManager(); + private final CraftScheduler scheduler = new CraftScheduler(); +- private final CraftCommandMap commandMap = new CraftCommandMap(this); ++ private final CraftCommandMap commandMap; // Paper - Move down + private final SimpleHelpMap helpMap = new SimpleHelpMap(this); + private final StandardMessenger messenger = new StandardMessenger(); +- private final SimplePluginManager pluginManager = new SimplePluginManager(this, commandMap); +- public final io.papermc.paper.plugin.manager.PaperPluginManagerImpl paperPluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(this, this.commandMap, pluginManager); {this.pluginManager.paperPluginManager = this.paperPluginManager;} // Paper ++ private final SimplePluginManager pluginManager; // Paper - Move down ++ public final io.papermc.paper.plugin.manager.PaperPluginManagerImpl paperPluginManager; // Paper + private final StructureManager structureManager; + protected final DedicatedServer console; + protected final DedicatedPlayerList playerList; +@@ -400,6 +400,13 @@ public final class CraftServer implements Server { + this.serverTickManager = new CraftServerTickManager(console.tickRateManager()); + + Bukkit.setServer(this); ++ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setResources(console.resources.managers()); // Paper - reset invalid state ++ // Paper start ++ this.commandMap = new CraftCommandMap(this); ++ this.pluginManager = new SimplePluginManager(this, commandMap); ++ this.paperPluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(this, this.commandMap, pluginManager); ++ this.pluginManager.paperPluginManager = this.paperPluginManager; ++ // Paper end + + CraftRegistry.setMinecraftRegistry(console.registryAccess()); + +@@ -571,49 +578,16 @@ public final class CraftServer implements Server { + } + + private void setVanillaCommands(boolean first) { // Spigot +- Commands dispatcher = this.console.vanillaCommandDispatcher; +- +- // Build a list of all Vanilla commands and create wrappers +- for (CommandNode cmd : dispatcher.getDispatcher().getRoot().getChildren()) { +- // Spigot start +- VanillaCommandWrapper wrapper = new VanillaCommandWrapper(dispatcher, cmd); +- if (org.spigotmc.SpigotConfig.replaceCommands.contains( wrapper.getName() ) ) { +- if (first) { +- this.commandMap.register("minecraft", wrapper); +- } +- } else if (!first) { +- this.commandMap.register("minecraft", wrapper); +- } +- // Spigot end +- } ++ // Paper - Replace implementation ++ } ++ // Paper start ++ public void initializeHelpMapCommands() { ++ this.helpMap.initializeCommands(); + } ++ // Paper end + + public void syncCommands() { +- // Clear existing commands +- Commands dispatcher = this.console.resources.managers().commands = new Commands(); +- +- // Register all commands, vanilla ones will be using the old dispatcher references +- for (Map.Entry entry : this.commandMap.getKnownCommands().entrySet()) { +- String label = entry.getKey(); +- Command command = entry.getValue(); +- +- if (command instanceof VanillaCommandWrapper) { +- LiteralCommandNode node = (LiteralCommandNode) ((VanillaCommandWrapper) command).vanillaCommand; +- if (!node.getLiteral().equals(label)) { +- LiteralCommandNode clone = new LiteralCommandNode(label, node.getCommand(), node.getRequirement(), node.getRedirect(), node.getRedirectModifier(), node.isFork()); +- +- for (CommandNode child : node.getChildren()) { +- clone.addChild(child); +- } +- node = clone; +- } +- dispatcher.vanillaCommandNodes.add(node); // Paper +- +- dispatcher.getDispatcher().getRoot().addChild(node); +- } else { +- new BukkitCommandWrapper(this, entry.getValue()).register(dispatcher.getDispatcher(), label); +- } +- } ++ Commands dispatcher = this.getHandle().getServer().getCommands(); // Paper - We now register directly to the dispatcher. + + // Refresh commands + for (ServerPlayer player : this.getHandle().players) { +@@ -1000,17 +974,31 @@ public final class CraftServer implements Server { + return true; + } + +- // Spigot start +- if (!org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty()) { +- // Paper start +- org.bukkit.event.command.UnknownCommandEvent event = new org.bukkit.event.command.UnknownCommandEvent(sender, commandLine, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.unknownCommandMessage)); +- this.getPluginManager().callEvent(event); +- if (event.message() != null) { +- sender.sendMessage(event.message()); +- } +- // Paper end ++ return this.dispatchCommand(VanillaCommandWrapper.getListener(sender), commandLine); ++ } ++ ++ public boolean dispatchCommand(CommandSourceStack sourceStack, String commandLine) { ++ net.minecraft.commands.Commands commands = this.getHandle().getServer().getCommands(); ++ com.mojang.brigadier.CommandDispatcher dispatcher = commands.getDispatcher(); ++ com.mojang.brigadier.ParseResults results = dispatcher.parse(commandLine, sourceStack); ++ ++ CommandSender sender = sourceStack.getBukkitSender(); ++ String[] args = org.apache.commons.lang3.StringUtils.split(commandLine, ' '); // Paper - fix adjacent spaces (from console/plugins) causing empty array elements ++ Command target = this.commandMap.getCommand(args[0].toLowerCase(java.util.Locale.ENGLISH)); ++ ++ try { ++ commands.performCommand(results, commandLine, commandLine, true); ++ } catch (CommandException ex) { ++ this.pluginManager.callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper ++ //target.timings.stopTiming(); // Spigot // Paper ++ throw ex; ++ } catch (Throwable ex) { ++ //target.timings.stopTiming(); // Spigot // Paper ++ String msg = "Unhandled exception executing '" + commandLine + "' in " + target; ++ this.pluginManager.callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper ++ throw new CommandException(msg, ex); + } +- // Spigot end ++ // Paper end + + return false; + } +@@ -1072,8 +1060,9 @@ public final class CraftServer implements Server { + } + + Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper ++ this.commandMap.clearCommands(); // Paper - Move command reloading up + this.pluginManager.clearPlugins(); +- this.commandMap.clearCommands(); ++ // Paper - move up + // Paper start + for (Plugin plugin : pluginClone) { + entityMetadata.removeAll(plugin); +@@ -1113,6 +1102,9 @@ public final class CraftServer implements Server { + this.enablePlugins(PluginLoadOrder.STARTUP); + this.enablePlugins(PluginLoadOrder.POSTWORLD); + this.getPluginManager().callEvent(new ServerLoadEvent(ServerLoadEvent.LoadType.RELOAD)); ++ io.papermc.paper.command.brigadier.PaperCommands.INSTANCE.setResources(this.console.resources.managers()); // Paper - to clear invalid state ++ io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents.COMMANDS, io.papermc.paper.command.brigadier.PaperCommands.INSTANCE, org.bukkit.plugin.Plugin.class, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // Paper - call commands event for regular plugins ++ this.syncCommands(); // Paper - Refresh commands after event + org.spigotmc.WatchdogThread.hasStarted = true; // Paper - Disable watchdog early timeout on reload + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java +index 21b6f90cf5bd7087d1a0f512289d971f2c3e1afa..a3c02200da9e793de79a74fe7e0cd72634150f64 100644 +--- a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java ++++ b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java +@@ -20,6 +20,7 @@ import org.bukkit.command.CommandException; + import org.bukkit.command.CommandSender; + import org.bukkit.craftbukkit.CraftServer; + ++@Deprecated(forRemoval = true) // Paper - Don't use + public class BukkitCommandWrapper implements com.mojang.brigadier.Command, Predicate, SuggestionProvider, com.destroystokyo.paper.brigadier.BukkitBrigadierCommand { // Paper + + private final CraftServer server; +diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftCommandMap.java b/src/main/java/org/bukkit/craftbukkit/command/CraftCommandMap.java +index 4b1ac1fe7ea07f419ae2818251900e7ba434ee16..90ed57a7fbcd0625b64084347460e9864216f610 100644 +--- a/src/main/java/org/bukkit/craftbukkit/command/CraftCommandMap.java ++++ b/src/main/java/org/bukkit/craftbukkit/command/CraftCommandMap.java +@@ -8,7 +8,7 @@ import org.bukkit.command.SimpleCommandMap; + public class CraftCommandMap extends SimpleCommandMap { + + public CraftCommandMap(Server server) { +- super(server); ++ super(server, io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap.INSTANCE); + } + + public Map getKnownCommands() { +diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +index 61115db85b81e627d11a0de21691a2ca69aafe2c..8c49ed07737f841717833d944e15df8353650925 100644 +--- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java ++++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +@@ -23,14 +23,26 @@ import org.bukkit.craftbukkit.entity.CraftMinecartCommand; + import org.bukkit.entity.Entity; + import org.bukkit.entity.minecart.CommandMinecart; + +-public final class VanillaCommandWrapper extends BukkitCommand { ++public class VanillaCommandWrapper extends BukkitCommand { // Paper + +- private final Commands dispatcher; ++ //private final Commands dispatcher; // Paper + public final CommandNode vanillaCommand; + ++ // Paper start ++ public VanillaCommandWrapper(String name, String description, String usageMessage, List aliases, CommandNode vanillaCommand) { ++ super(name, description, usageMessage, aliases); ++ //this.dispatcher = dispatcher; // Paper ++ this.vanillaCommand = vanillaCommand; ++ } ++ ++ Commands commands() { ++ return net.minecraft.server.MinecraftServer.getServer().getCommands(); ++ } ++ ++ // Paper end + public VanillaCommandWrapper(Commands dispatcher, CommandNode vanillaCommand) { + super(vanillaCommand.getName(), "A Mojang provided command.", vanillaCommand.getUsageText(), Collections.EMPTY_LIST); +- this.dispatcher = dispatcher; ++ // this.dispatcher = dispatcher; // Paper + this.vanillaCommand = vanillaCommand; + this.setPermission(VanillaCommandWrapper.getPermission(vanillaCommand)); + } +@@ -40,7 +52,7 @@ public final class VanillaCommandWrapper extends BukkitCommand { + if (!this.testPermission(sender)) return true; + + CommandSourceStack icommandlistener = VanillaCommandWrapper.getListener(sender); +- this.dispatcher.performPrefixedCommand(icommandlistener, this.toDispatcher(args, this.getName()), this.toDispatcher(args, commandLabel)); ++ this.commands().performPrefixedCommand(icommandlistener, this.toDispatcher(args, this.getName()), this.toDispatcher(args, commandLabel)); // Paper + return true; + } + +@@ -51,10 +63,10 @@ public final class VanillaCommandWrapper extends BukkitCommand { + Preconditions.checkArgument(alias != null, "Alias cannot be null"); + + CommandSourceStack icommandlistener = VanillaCommandWrapper.getListener(sender); +- ParseResults parsed = this.dispatcher.getDispatcher().parse(this.toDispatcher(args, this.getName()), icommandlistener); ++ ParseResults parsed = this.commands().getDispatcher().parse(this.toDispatcher(args, this.getName()), icommandlistener); // Paper + + List results = new ArrayList<>(); +- this.dispatcher.getDispatcher().getCompletionSuggestions(parsed).thenAccept((suggestions) -> { ++ this.commands().getDispatcher().getCompletionSuggestions(parsed).thenAccept((suggestions) -> { // Paper + suggestions.getList().forEach((s) -> results.add(s.getText())); + }); + +@@ -113,4 +125,15 @@ public final class VanillaCommandWrapper extends BukkitCommand { + private String toDispatcher(String[] args, String name) { + return name + ((args.length > 0) ? " " + Joiner.on(' ').join(args) : ""); + } ++ // Paper start ++ @Override ++ public boolean canBeOverriden() { ++ return true; ++ } ++ ++ @Override ++ public boolean isRegistered() { ++ return true; ++ } ++ // Paper end + } +diff --git a/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java b/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java +index 05d3aecd4abaab6a94effcb1ab35c1b82410865f..97141968f36b3ef88bd6e520c2ccc37c97e4adb1 100644 +--- a/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java ++++ b/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java +@@ -200,15 +200,18 @@ public class SimpleHelpMap implements HelpMap { + } + + private String getCommandPluginName(Command command) { ++ // Paper start - Move up ++ if (command instanceof PluginIdentifiableCommand) { ++ return ((PluginIdentifiableCommand) command).getPlugin().getName(); ++ } ++ // Paper end + if (command instanceof VanillaCommandWrapper) { + return "Minecraft"; + } + if (command instanceof BukkitCommand) { + return "Bukkit"; + } +- if (command instanceof PluginIdentifiableCommand) { +- return ((PluginIdentifiableCommand) command).getPlugin().getName(); +- } ++ // Paper - Move PluginIdentifiableCommand instanceof check to allow brig commands + return null; + } + +diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java +index 8464531a4ee400834d25c23b1eb723f49be8689e..4a0b1587180381123eb843819cd10630e49c7a02 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java ++++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java +@@ -53,7 +53,13 @@ public final class CraftCriteria implements Criteria { + return RenderType.values()[this.criteria.getDefaultRenderType().ordinal()]; + } + +- static CraftCriteria getFromNMS(Objective objective) { ++ // Paper start ++ public static CraftCriteria getFromNMS(ObjectiveCriteria criteria) { ++ return java.util.Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(criteria.getName()), () -> new CraftCriteria(criteria)); ++ } ++ // Paper end ++ ++ public static CraftCriteria getFromNMS(Objective objective) { + return java.util.Objects.requireNonNullElseGet(CraftCriteria.DEFAULTS.get(objective.getCriteria().getName()), () -> new CraftCriteria(objective.getCriteria())); // Paper + } + +diff --git a/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.CommandBuilderProvider b/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.CommandBuilderProvider +new file mode 100644 +index 0000000000000000000000000000000000000000..2f0b1f0ed9ca3605cd24a75466973e1a0a745ee5 +--- /dev/null ++++ b/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.CommandBuilderProvider +@@ -0,0 +1 @@ ++io.papermc.paper.command.brigadier.CommandBuilderImpl$ProviderImpl +diff --git a/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.MessageComponentSerializer b/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.MessageComponentSerializer +new file mode 100644 +index 0000000000000000000000000000000000000000..2428b577b9bf0eac6947f5d919cbb51f7aca3d50 +--- /dev/null ++++ b/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.MessageComponentSerializer +@@ -0,0 +1 @@ ++io.papermc.paper.command.brigadier.MessageComponentSerializerImpl +diff --git a/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.argument.VanillaArgumentProvider b/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.argument.VanillaArgumentProvider +new file mode 100644 +index 0000000000000000000000000000000000000000..b2fdb8351c2abb55283850a929d2a87aa6ecb80f +--- /dev/null ++++ b/src/main/resources/META-INF/services/io.papermc.paper.command.brigadier.argument.VanillaArgumentProvider +@@ -0,0 +1 @@ ++io.papermc.paper.command.brigadier.argument.VanillaArgumentProviderImpl +diff --git a/src/test/java/io/papermc/paper/command/brigadier/BukkitCommandConversionTest.java b/src/test/java/io/papermc/paper/command/brigadier/BukkitCommandConversionTest.java +new file mode 100644 +index 0000000000000000000000000000000000000000..815c7901492b666cd1713d5c9438325a796df0f3 +--- /dev/null ++++ b/src/test/java/io/papermc/paper/command/brigadier/BukkitCommandConversionTest.java +@@ -0,0 +1,113 @@ ++package io.papermc.paper.command.brigadier; ++ ++import com.mojang.brigadier.CommandDispatcher; ++import com.mojang.brigadier.ResultConsumer; ++import com.mojang.brigadier.context.CommandContext; ++import com.mojang.brigadier.exceptions.CommandSyntaxException; ++import com.mojang.brigadier.suggestion.Suggestions; ++import com.mojang.brigadier.tree.CommandNode; ++import io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap; ++import net.minecraft.server.MinecraftServer; ++import org.apache.logging.log4j.core.util.Assert; ++import org.bukkit.Bukkit; ++import org.bukkit.Location; ++import org.bukkit.World; ++import org.bukkit.command.Command; ++import org.bukkit.command.CommandMap; ++import org.bukkit.command.CommandSender; ++import org.bukkit.command.SimpleCommandMap; ++import org.bukkit.craftbukkit.command.CraftCommandMap; ++import org.bukkit.craftbukkit.command.VanillaCommandWrapper; ++import org.bukkit.entity.Entity; ++import org.bukkit.plugin.PluginManager; ++import org.bukkit.support.AbstractTestingBase; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++import org.junit.jupiter.api.Assertions; ++import org.junit.jupiter.api.Test; ++import org.mockito.Mockito; ++ ++import java.util.List; ++import java.util.Map; ++import java.util.logging.Logger; ++ ++public class BukkitCommandConversionTest extends AbstractTestingBase { ++ ++ private CommandSender getSender() { ++ return Mockito.mock(CommandSender.class); ++ } ++ ++ @Test ++ public void test() throws CommandSyntaxException { ++ CommandSender sender = this.getSender(); ++ CommandSourceStack object = Mockito.mock(CommandSourceStack.class); ++ Mockito.when(object.getLocation()).thenReturn(new Location(null, 0, 0, 0)); ++ ++ CommandDispatcher dispatcher = DATA_PACK.commands.getDispatcher(); ++ PaperCommands.INSTANCE.setResources(DATA_PACK); ++ dispatcher.setConsumer((context, success, result) -> {}); ++ CommandMap commandMap = new SimpleCommandMap(Bukkit.getServer(), new BukkitBrigForwardingMap()); ++ Map stringCommandMap = commandMap.getKnownCommands(); ++ // All commands should be mirrored -- or equal ++ int commandMapSize = stringCommandMap.values().size(); ++ ExampleCommand exampleCommand = new ExampleCommand(); ++ ++ Assertions.assertEquals(commandMapSize, dispatcher.getRoot().getChildren().size()); ++ ++ // Register a new command ++ commandMap.register("test", exampleCommand); ++ Assertions.assertEquals(commandMapSize + (3 * 2), stringCommandMap.values().size()); // Make sure commands are accounted for, including those with namespaced keys ++ ++ // Test Registration ++ for (String alias : exampleCommand.getAliases()) { ++ Assertions.assertEquals(stringCommandMap.get(alias), exampleCommand); ++ Assertions.assertEquals(stringCommandMap.get("test:" + alias), exampleCommand); ++ } ++ // Test command instance equality ++ Assertions.assertEquals(stringCommandMap.get(exampleCommand.getName()), exampleCommand); ++ Assertions.assertEquals(stringCommandMap.get("test:" + exampleCommand.getName()), exampleCommand); ++ ++ // Test command map execution ++ commandMap.dispatch(sender, "main-example example"); ++ Assertions.assertEquals(exampleCommand.invocations, 1); ++ Assertions.assertEquals(commandMap.tabComplete(sender, "main-example 1 2"), List.of("complete")); ++ ++ // Test dispatcher execution ++ dispatcher.execute("main-example example", object); ++ Assertions.assertEquals(exampleCommand.invocations, 2); ++ ++ dispatcher.execute("test:example2 example", object); ++ Assertions.assertEquals(exampleCommand.invocations, 3); ++ ++ Suggestions suggestions = (Suggestions) dispatcher.getCompletionSuggestions(dispatcher.parse("main-example 1 2", object)).join(); ++ Assertions.assertEquals(suggestions.getList().get(0).getText(), "complete"); ++ ++ ++ // Test command map removal ++ commandMap.getKnownCommands().remove("test"); ++ Assertions.assertNull(commandMap.getCommand("test")); ++ Assertions.assertNull(dispatcher.getRoot().getChild("test")); ++ } ++ ++ private static class ExampleCommand extends Command { ++ ++ int invocations; ++ ++ protected ExampleCommand() { ++ super("main-example", "This is an example.", "", List.of("example", "example2")); ++ } ++ ++ @Override ++ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { ++ Assertions.assertEquals(args[0], "example"); ++ this.invocations++; ++ return true; ++ } ++ ++ @Override ++ public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException { ++ Assertions.assertEquals(args.length, 2); ++ return List.of("complete"); ++ } ++ } ++} +diff --git a/src/test/java/org/bukkit/support/DummyServer.java b/src/test/java/org/bukkit/support/DummyServer.java +index 3b3e44c5ed24f653f7dc1e5d3d4f0ff76084f277..48463a50963f50b1fc8fc0ae7cb79037756463fa 100644 +--- a/src/test/java/org/bukkit/support/DummyServer.java ++++ b/src/test/java/org/bukkit/support/DummyServer.java +@@ -52,7 +52,7 @@ public final class DummyServer { + final Thread currentThread = Thread.currentThread(); + when(instance.isPrimaryThread()).thenAnswer(ignored -> Thread.currentThread().equals(currentThread)); + +- final org.bukkit.plugin.PluginManager pluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(instance, new org.bukkit.command.SimpleCommandMap(instance), null); ++ final org.bukkit.plugin.PluginManager pluginManager = new io.papermc.paper.plugin.manager.PaperPluginManagerImpl(instance, new org.bukkit.command.SimpleCommandMap(instance, new java.util.HashMap<>()), null); // Paper + when(instance.getPluginManager()).thenReturn(pluginManager); + when(instance.getTag(anyString(), any(org.bukkit.NamespacedKey.class), any())).thenAnswer(ignored -> new io.papermc.paper.util.EmptyTag()); + // paper end - testing additions diff --git a/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java b/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java index 4e68423bb759..25ea688fe69b 100644 --- a/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java +++ b/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java @@ -1,12 +1,122 @@ package io.papermc.testplugin; +import com.mojang.brigadier.Command; +import io.papermc.paper.command.brigadier.BasicCommand; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.Commands; +import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager; +import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; +import io.papermc.testplugin.example.ExampleAdminCommand; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.bukkit.command.CommandSender; +import org.bukkit.command.defaults.BukkitCommand; import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; public final class TestPlugin extends JavaPlugin implements Listener { @Override public void onEnable() { this.getServer().getPluginManager().registerEvents(this, this); + + // legacy registration via CommandMap + this.registerLegacyCommands(); + + // registration via lifecycle event system + this.registerViaLifecycleEvents(); + } + + private void registerViaLifecycleEvents() { + final LifecycleEventManager lifecycleManager = this.getLifecycleManager(); + lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS, event -> { + final Commands commands = event.registrar(); + // ensure plugin commands override + commands.register(Commands.literal("tag") + .executes(ctx -> { + ctx.getSource().getSender().sendPlainMessage("overriden command"); + return Command.SINGLE_SUCCESS; + }) + .build(), + null, + Collections.emptyList() + ); + }); + + lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS.newHandler(event -> { + final Commands commands = event.registrar(); + commands.register(this.getPluginMeta(), Commands.literal("root_command") + .then(Commands.literal("sub_command") + .requires(source -> source.getSender().hasPermission("testplugin.test")) + .executes(ctx -> { + ctx.getSource().getSender().sendPlainMessage("root_command sub_command"); + return Command.SINGLE_SUCCESS; + })).build(), + null, + Collections.emptyList() + ); + + commands.register(this.getPluginMeta(), "example", "test", Collections.emptyList(), new BasicCommand() { + @Override + public int execute(@NotNull final CommandSourceStack commandSourceStack, final @NotNull String[] args) { + System.out.println(Arrays.toString(args)); + return Command.SINGLE_SUCCESS; + } + + @Override + public @NotNull Collection suggest(final @NotNull CommandSourceStack commandSourceStack, final @NotNull String[] args) { + System.out.println(Arrays.toString(args)); + return List.of("apple", "banana"); + } + }); + + + commands.register(this.getPluginMeta(), Commands.literal("water") + .requires(source -> { + System.out.println("isInWater check"); + return source.getExecutor().isInWater(); + }) + .executes(ctx -> { + ctx.getSource().getExecutor().sendMessage("You are in water!"); + return Command.SINGLE_SUCCESS; + }).then(Commands.literal("lava") + .requires(source -> { + System.out.println("isInLava check"); + return source.getExecutor().isInLava(); + }) + .executes(ctx -> { + ctx.getSource().getExecutor().sendMessage("You are in lava!"); + return Command.SINGLE_SUCCESS; + })).build(), + null, + Collections.emptyList()); + + + ExampleAdminCommand.register(this, commands); + }).priority(10)); + } + + private void registerLegacyCommands() { + this.getServer().getCommandMap().register("fallback", new BukkitCommand("hi", "cool hi command", "<>", List.of("hialias")) { + @Override + public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { + sender.sendMessage("hi"); + return true; + } + }); + this.getServer().getCommandMap().register("fallback", new BukkitCommand("cooler-command", "cool hi command", "<>", List.of("cooler-command-alias")) { + @Override + public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { + sender.sendMessage("hi"); + return true; + } + }); + this.getServer().getCommandMap().getKnownCommands().values().removeIf((command) -> { + return command.getName().equals("hi"); + }); } } diff --git a/test-plugin/src/main/java/io/papermc/testplugin/TestPluginBootstrap.java b/test-plugin/src/main/java/io/papermc/testplugin/TestPluginBootstrap.java index e978b15f97f7..bdf8bf3b2673 100644 --- a/test-plugin/src/main/java/io/papermc/testplugin/TestPluginBootstrap.java +++ b/test-plugin/src/main/java/io/papermc/testplugin/TestPluginBootstrap.java @@ -1,13 +1,60 @@ package io.papermc.testplugin; +import com.mojang.brigadier.Command; +import io.papermc.paper.command.brigadier.Commands; +import io.papermc.paper.command.brigadier.argument.ArgumentTypes; +import io.papermc.paper.command.brigadier.argument.range.DoubleRangeProvider; import io.papermc.paper.plugin.bootstrap.BootstrapContext; import io.papermc.paper.plugin.bootstrap.PluginBootstrap; +import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager; +import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; +import io.papermc.testplugin.example.MaterialArgumentType; +import java.util.Collections; +import org.bukkit.Material; import org.jetbrains.annotations.NotNull; public class TestPluginBootstrap implements PluginBootstrap { @Override public void bootstrap(@NotNull BootstrapContext context) { + final LifecycleEventManager lifecycleManager = context.getLifecycleManager(); + lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS, event -> { + final Commands commands = event.registrar(); + commands.register(Commands.literal("material") + .then(Commands.literal("item") + .then(Commands.argument("mat", MaterialArgumentType.item()) + .executes(ctx -> { + ctx.getSource().getSender().sendPlainMessage(ctx.getArgument("mat", Material.class).name()); + return Command.SINGLE_SUCCESS; + }) + ) + ).then(Commands.literal("block") + .then(Commands.argument("mat", MaterialArgumentType.block()) + .executes(ctx -> { + ctx.getSource().getSender().sendPlainMessage(ctx.getArgument("mat", Material.class).name()); + return Command.SINGLE_SUCCESS; + }) + ) + ) + .build(), + null, + Collections.emptyList() + ); + }); + + lifecycleManager.registerEventHandler(LifecycleEvents.COMMANDS.newHandler(event -> { + final Commands commands = event.registrar(); + commands.register(Commands.literal("heya") + .then(Commands.argument("range", ArgumentTypes.doubleRange()) + .executes((ct) -> { + ct.getSource().getSender().sendPlainMessage(ct.getArgument("range", DoubleRangeProvider.class).range().toString()); + return 1; + }) + ).build(), + null, + Collections.emptyList() + ); + }).priority(10)); } } diff --git a/test-plugin/src/main/java/io/papermc/testplugin/example/ComponentCommandExceptionType.java b/test-plugin/src/main/java/io/papermc/testplugin/example/ComponentCommandExceptionType.java new file mode 100644 index 000000000000..0cdce7b5e849 --- /dev/null +++ b/test-plugin/src/main/java/io/papermc/testplugin/example/ComponentCommandExceptionType.java @@ -0,0 +1,25 @@ +package io.papermc.testplugin.example; + +import com.mojang.brigadier.ImmutableStringReader; +import com.mojang.brigadier.Message; +import com.mojang.brigadier.exceptions.CommandExceptionType; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import io.papermc.paper.command.brigadier.MessageComponentSerializer; +import net.kyori.adventure.text.Component; + +public class ComponentCommandExceptionType implements CommandExceptionType { + + private final Message message; + + public ComponentCommandExceptionType(final Component message) { + this.message = MessageComponentSerializer.message().serialize(message); + } + + public CommandSyntaxException create() { + return new CommandSyntaxException(this, this.message); + } + + public CommandSyntaxException createWithContext(final ImmutableStringReader reader) { + return new CommandSyntaxException(this, this.message, reader.getString(), reader.getCursor()); + } +} diff --git a/test-plugin/src/main/java/io/papermc/testplugin/example/ExampleAdminCommand.java b/test-plugin/src/main/java/io/papermc/testplugin/example/ExampleAdminCommand.java new file mode 100644 index 000000000000..65a2137ef031 --- /dev/null +++ b/test-plugin/src/main/java/io/papermc/testplugin/example/ExampleAdminCommand.java @@ -0,0 +1,153 @@ +package io.papermc.testplugin.example; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.Commands; +import io.papermc.paper.command.brigadier.argument.MessageArgumentResponse; +import io.papermc.paper.command.brigadier.argument.ArgumentTypes; +import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver; +import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver; +import io.papermc.paper.math.BlockPosition; +import io.papermc.testplugin.TestPlugin; +import net.kyori.adventure.chat.ChatType; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class ExampleAdminCommand { + + public static void register(TestPlugin plugin, Commands commands) { + final LiteralArgumentBuilder adminBuilder = Commands.literal("admin") + .executes((ct) -> { + ct.getSource().getSender().sendPlainMessage("root admin"); + return 1; + }) + .then( + Commands.literal("tp") + .then( + Commands.argument("player", ArgumentTypes.player()).executes((source) -> { + CommandSourceStack sourceStack = source.getSource(); + Player resolved = source.getArgument("player", PlayerSelectorArgumentResolver.class).resolve(sourceStack).get(0); + + if (resolved == source.getSource().getExecutor()) { + source.getSource().getExecutor().sendMessage(Component.text("Can't teleport to self!")); + return 0; + } + Entity entity = source.getSource().getExecutor(); + if (entity != null) { + entity.teleport(resolved); + } + + return 1; + }) + ) + ) + .then( + Commands.literal("tp-self") + .executes((cmd) -> { + if (cmd.getSource().getSender() instanceof Player player) { + player.teleport(cmd.getSource().getLocation()); + } + + return com.mojang.brigadier.Command.SINGLE_SUCCESS; + }) + ) + .then( + Commands.literal("broadcast") + .then( + Commands.argument("message", ArgumentTypes.component()).executes((source) -> { + Component message = source.getArgument("message", Component.class); + Bukkit.broadcast(message); + return 1; + }) + ) + ) + .then( + Commands.literal("ice_cream").then( + Commands.argument("type", new IceCreamTypeArgument()).executes((context) -> { + IceCreamType argumentResponse = context.getArgument("type", IceCreamType.class); // Gets the raw argument + context.getSource().getSender().sendMessage(Component.text("You like: " + argumentResponse)); + return 1; + }) + ) + ) + .then( + Commands.literal("execute") + .redirect(commands.getDispatcher().getRoot().getChild("execute")) + ) + .then( + Commands.literal("signed_message").then( + Commands.argument("msg", ArgumentTypes.signedMessage()).executes((context) -> { + MessageArgumentResponse argumentResponse = context.getArgument("msg", MessageArgumentResponse.class); // Gets the raw argument + + // This is a better way of getting signed messages, includes the concept of "disguised" messages. + argumentResponse.resolveSignedMessage("msg", context) + .thenAccept((signedMsg) -> { + context.getSource().getSender().sendMessage(signedMsg, ChatType.SAY_COMMAND.bind(Component.text("STATIC"))); + }); + + return 1; + }) + ) + ) + .then( + Commands.literal("setblock").then( + Commands.argument("block", ArgumentTypes.blockState()) + .then(Commands.argument("pos", ArgumentTypes.blockPos()) + .executes((context) -> { + CommandSourceStack sourceStack = context.getSource(); + BlockPosition position = context.getArgument("pos", BlockPositionResolver.class).resolve(sourceStack); + BlockState state = context.getArgument("block", BlockState.class); + + // TODO: better block state api here? :thinking: + Block block = context.getSource().getLocation().getWorld().getBlockAt(position.blockX(), position.blockY(), position.blockZ()); + block.setType(state.getType()); + block.setBlockData(state.getBlockData()); + + return 1; + }) + ) + ) + ); + commands.register(plugin.getPluginMeta(), adminBuilder.build(), "Cool command showcasing what you can do!", List.of("alias_for_admin_that_you_shouldnt_use", "a")); + + + Bukkit.getCommandMap().register( + "legacy", + new Command("legacy_command") { + @Override + public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { + throw new UnsupportedOperationException(); + } + + @Override + public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException { + return List.of(String.join(" ", args)); + } + } + ); + + Bukkit.getCommandMap().register( + "legacy", + new Command("legacy_fail") { + @Override + public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { + return false; + } + + @Override + public @NotNull List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) throws IllegalArgumentException { + return List.of(String.join(" ", args)); + } + } + ); + } +} diff --git a/test-plugin/src/main/java/io/papermc/testplugin/example/IceCreamType.java b/test-plugin/src/main/java/io/papermc/testplugin/example/IceCreamType.java new file mode 100644 index 000000000000..64f77c3dc3ae --- /dev/null +++ b/test-plugin/src/main/java/io/papermc/testplugin/example/IceCreamType.java @@ -0,0 +1,9 @@ +package io.papermc.testplugin.example; + +public enum IceCreamType { + VANILLA, + CHOCOLATE, + BLUE_MOON, + STRAWBERRY, + WHOLE_MILK +} diff --git a/test-plugin/src/main/java/io/papermc/testplugin/example/IceCreamTypeArgument.java b/test-plugin/src/main/java/io/papermc/testplugin/example/IceCreamTypeArgument.java new file mode 100644 index 000000000000..17f74dcadd01 --- /dev/null +++ b/test-plugin/src/main/java/io/papermc/testplugin/example/IceCreamTypeArgument.java @@ -0,0 +1,48 @@ +package io.papermc.testplugin.example; + +import com.mojang.brigadier.Message; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import io.papermc.paper.command.brigadier.MessageComponentSerializer; +import io.papermc.paper.command.brigadier.argument.CustomArgumentType; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.CompletableFuture; + +public class IceCreamTypeArgument implements CustomArgumentType.Converted { + + + @Override + public @NotNull IceCreamType convert(String nativeType) throws CommandSyntaxException { + try { + return IceCreamType.valueOf(nativeType.toUpperCase()); + } catch (Exception e) { + Message message = MessageComponentSerializer.message().serialize(Component.text("Invalid species %s!".formatted(nativeType), NamedTextColor.RED)); + + throw new CommandSyntaxException(new SimpleCommandExceptionType(message), message); + } + } + + @Override + public @NotNull ArgumentType getNativeType() { + return StringArgumentType.word(); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + for (IceCreamType species : IceCreamType.values()) { + builder.suggest(species.name(), MessageComponentSerializer.message().serialize(Component.text("COOL! TOOLTIP!", NamedTextColor.GREEN))); + } + + return CompletableFuture.completedFuture( + builder.build() + ); + } +} diff --git a/test-plugin/src/main/java/io/papermc/testplugin/example/MaterialArgumentType.java b/test-plugin/src/main/java/io/papermc/testplugin/example/MaterialArgumentType.java new file mode 100644 index 000000000000..109cfa8e46ec --- /dev/null +++ b/test-plugin/src/main/java/io/papermc/testplugin/example/MaterialArgumentType.java @@ -0,0 +1,88 @@ +package io.papermc.testplugin.example; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import io.papermc.paper.command.brigadier.argument.CustomArgumentType; +import io.papermc.paper.command.brigadier.argument.ArgumentTypes; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import org.bukkit.Keyed; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.jetbrains.annotations.NotNull; + +import static net.kyori.adventure.text.Component.translatable; + +public class MaterialArgumentType implements CustomArgumentType.Converted { + + private static final ComponentCommandExceptionType ERROR_INVALID = new ComponentCommandExceptionType(translatable("argument.id.invalid")); + + private final Predicate check; + + private MaterialArgumentType(Predicate check) { + this.check = check; + } + + public static MaterialArgumentType item() { + return new MaterialArgumentType(Material::isItem); + } + + public static MaterialArgumentType block() { + return new MaterialArgumentType(Material::isBlock); + } + + @Override + public @NotNull Material convert(final @NotNull NamespacedKey nativeType) throws CommandSyntaxException { + final Material material = Registry.MATERIAL.get(nativeType); + if (material == null) { + throw ERROR_INVALID.create(); + } + if (!this.check.test(material)) { + throw ERROR_INVALID.create(); + } + return material; + } + + static boolean matchesSubStr(String remaining, String candidate) { + for(int i = 0; !candidate.startsWith(remaining, i); ++i) { + i = candidate.indexOf('_', i); + if (i < 0) { + return false; + } + } + + return true; + } + + @Override + public @NotNull ArgumentType getNativeType() { + return ArgumentTypes.namespacedKey(); + } + + @Override + public @NotNull CompletableFuture listSuggestions(final @NotNull CommandContext context, final @NotNull SuggestionsBuilder builder) { + final Stream stream = StreamSupport.stream(Registry.MATERIAL.spliterator(), false); + final String remaining = builder.getRemaining(); + boolean containsColon = remaining.indexOf(':') > -1; + stream.filter(this.check) + .map(Keyed::key) + .forEach(key -> { + final String keyAsString = key.asString(); + if (containsColon) { + if (matchesSubStr(remaining, keyAsString)) { + builder.suggest(keyAsString); + } + } else if (matchesSubStr(remaining, key.namespace()) || "minecraft".equals(key.namespace()) && matchesSubStr(remaining, key.value())) { + builder.suggest(keyAsString); + } + }); + return builder.buildFuture(); + } + +} From 9d741471182d772bc0d7e1a7511378d0f8f5293e Mon Sep 17 00:00:00 2001 From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:39:12 -0400 Subject: [PATCH 02/27] update --- ...=> 0475-Brigadier-based-command-API.patch} | 6 +- ...d-experimental-improved-give-command.patch | 8 +- ...=> 1045-Brigadier-based-command-API.patch} | 118 ++++++++---------- 3 files changed, 57 insertions(+), 75 deletions(-) rename patches/api/{0474-Brigadier-based-command-API.patch => 0475-Brigadier-based-command-API.patch} (99%) rename patches/server/{1060-Brigadier-based-command-API.patch => 1045-Brigadier-based-command-API.patch} (95%) diff --git a/patches/api/0474-Brigadier-based-command-API.patch b/patches/api/0475-Brigadier-based-command-API.patch similarity index 99% rename from patches/api/0474-Brigadier-based-command-API.patch rename to patches/api/0475-Brigadier-based-command-API.patch index 9515fdf10f86..7fb0ef4df550 100644 --- a/patches/api/0474-Brigadier-based-command-API.patch +++ b/patches/api/0475-Brigadier-based-command-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Brigadier based command API diff --git a/build.gradle.kts b/build.gradle.kts -index 04853c43b99951bf0d4c96ef73724625bdaf018f..72e8ee7b2df322a667a787a1d18bc91a7c9d247d 100644 +index 65e67b8726f1e19a6bcb1fe2f448e4ab68df11d1..475487c7d5adb3bebbd26cd571848ed9c2e3b395 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,6 +27,7 @@ configurations.api { @@ -1126,10 +1126,10 @@ index 304f978e40e1759bb19704cc5cec399500905195..ac4a57295abeff97479fee2fb768666e @ApiStatus.Internal private static LifecycleEventType.Monitorable plugin(final String name) { diff --git a/src/main/java/org/bukkit/command/Command.java b/src/main/java/org/bukkit/command/Command.java -index b791358f90fe92bc2264d9a26492245763813af3..d816d1677dface8cb3035d5adb9919aa75bd8e44 100644 +index b3a2c274f05156fd603bcc7a68ab41265f2eaf44..c7cdc2ad8a2c43e8c0fcaa1761d3b81726c5ebcb 100644 --- a/src/main/java/org/bukkit/command/Command.java +++ b/src/main/java/org/bukkit/command/Command.java -@@ -488,4 +488,9 @@ public abstract class Command { +@@ -512,4 +512,9 @@ public abstract class Command { public String toString() { return getClass().getName() + '(' + name + ')'; } diff --git a/patches/server/1038-Add-experimental-improved-give-command.patch b/patches/server/1038-Add-experimental-improved-give-command.patch index 4ff1984b0574..2f1e2459db0b 100644 --- a/patches/server/1038-Add-experimental-improved-give-command.patch +++ b/patches/server/1038-Add-experimental-improved-give-command.patch @@ -240,12 +240,12 @@ index 61115db85b81e627d11a0de21691a2ca69aafe2c..ba2a2ca0c36e61cb3cc00fafc7a5dd9f } diff --git a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java -index ca71c688b37ce2c8b712a4f9216cf872c8edf78e..2f3ff50bf3f70b6b404d02d5ffcc079162a63bc1 100644 +index 23faad674e1761e31febc398cdc0ddc632a6dd28..8d85caa1ab5ff53d445e5677a8b5fb9cc09ef9de 100644 --- a/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java +++ b/src/test/java/io/papermc/paper/permissions/MinecraftCommandPermissionsTest.java -@@ -45,6 +45,9 @@ public class MinecraftCommandPermissionsTest extends AbstractTestingBase { - Set foundPerms = new HashSet<>(); - for (CommandNode child : root.getChildren()) { +@@ -49,6 +49,9 @@ public class MinecraftCommandPermissionsTest extends AbstractTestingBase { + child = child.getRedirect(); + } final String vanillaPerm = VanillaCommandWrapper.getPermission(child); + if ("bukkit.command.paper.pgive".equals(vanillaPerm)) { // skip our custom give command + continue; diff --git a/patches/server/1060-Brigadier-based-command-API.patch b/patches/server/1045-Brigadier-based-command-API.patch similarity index 95% rename from patches/server/1060-Brigadier-based-command-API.patch rename to patches/server/1045-Brigadier-based-command-API.patch index b7888e209d83..4a1a4ff96aba 100644 --- a/patches/server/1060-Brigadier-based-command-API.patch +++ b/patches/server/1045-Brigadier-based-command-API.patch @@ -6,7 +6,8 @@ Subject: [PATCH] Brigadier based command API == AT == public net.minecraft.commands.arguments.blocks.BlockInput tag public net.minecraft.commands.arguments.DimensionArgument ERROR_INVALID_VALUE -public net.minecraft.server.ReloadableServerResources commandBuildContext +public net.minecraft.server.ReloadableServerResources registryLookup +public net.minecraft.server.ReloadableServerResources diff --git a/src/main/java/com/mojang/brigadier/CommandDispatcher.java b/src/main/java/com/mojang/brigadier/CommandDispatcher.java index 4b4f812eb13d5f03bcf3f8724d8aa8dbbc724e8b..a4d5d7017e0be79844b996de85a63cad5f8488bc 100644 @@ -31,13 +32,13 @@ index 4b4f812eb13d5f03bcf3f8724d8aa8dbbc724e8b..a4d5d7017e0be79844b996de85a63cad final String usage = this.getSmartUsage(children.iterator().next(), source, childOptional, childOptional); if (usage != null) { diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java -index b02fb15c98ab873fa78635d7a23706ddff8cc94d..7438c31ac70cf7394146cbc7e7a0d37282a8896a 100644 +index 1f4963bf4681a771130abc1da179819626ecfc1f..03ce8a2abb6dceaa922dcce7f3adbc228bbde4bc 100644 --- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java @@ -35,6 +35,8 @@ public abstract class CommandNode implements Comparable> { private final boolean forks; private Command command; - public LiteralCommandNode clientNode; // Paper - Brigadier API + public CommandNode clientNode; // Paper - Brigadier API + public CommandNode unwrappedCached = null; // Paper - Brigadier Command API + public CommandNode wrappedCached = null; // Paper - Brigadier Command API // CraftBukkit start @@ -507,10 +508,10 @@ index 0000000000000000000000000000000000000000..9e293468778a8e1b8d489e239936ffbc +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java b/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java new file mode 100644 -index 0000000000000000000000000000000000000000..2c17d57a95aaf48ff7fee6818d3616d5c6da0ece +index 0000000000000000000000000000000000000000..13571c20cd837fad8d4275bd2a31002c51fe5b5a --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java -@@ -0,0 +1,158 @@ +@@ -0,0 +1,159 @@ +package io.papermc.paper.command.brigadier; + +import com.google.common.base.Preconditions; @@ -528,6 +529,7 @@ index 0000000000000000000000000000000000000000..2c17d57a95aaf48ff7fee6818d3616d5 +import java.util.List; +import java.util.Locale; +import net.minecraft.commands.CommandBuildContext; ++import net.minecraft.server.MinecraftServer; +import net.minecraft.server.ReloadableServerResources; +import org.apache.commons.lang3.StringUtils; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -544,7 +546,7 @@ index 0000000000000000000000000000000000000000..2c17d57a95aaf48ff7fee6818d3616d5 + + private @Nullable LifecycleEventOwner currentContext; + private @MonotonicNonNull CommandDispatcher dispatcher; -+ private CommandBuildContext.@MonotonicNonNull Configurable buildContext; ++ private @MonotonicNonNull CommandBuildContext buildContext; + private boolean invalid = false; + + @Override @@ -560,7 +562,7 @@ index 0000000000000000000000000000000000000000..2c17d57a95aaf48ff7fee6818d3616d5 + return resources.getCommands().getDispatcher(); + } + }); -+ this.buildContext = resources.commandBuildContext; ++ this.buildContext = CommandBuildContext.simple(resources.registryLookup, MinecraftServer.getServer().getWorldData().enabledFeatures()); + } + + @Override @@ -574,7 +576,7 @@ index 0000000000000000000000000000000000000000..2c17d57a95aaf48ff7fee6818d3616d5 + return this.dispatcher; + } + -+ public CommandBuildContext.Configurable getBuildContext() { ++ public CommandBuildContext getBuildContext() { + Preconditions.checkState(this.buildContext != null, "the build context hasn't been set yet"); + return this.buildContext; + } @@ -855,7 +857,7 @@ index 0000000000000000000000000000000000000000..adc0df7c3a762d99e7ad458576d4be68 +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java new file mode 100644 -index 0000000000000000000000000000000000000000..0e59060730269b9c97376e518e906bc82b602a0e +index 0000000000000000000000000000000000000000..5a7e587f5772a8b314cf93f50486c4df78c998e3 --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java @@ -0,0 +1,311 @@ @@ -1030,7 +1032,7 @@ index 0000000000000000000000000000000000000000..0e59060730269b9c97376e518e906bc8 + + @Override + public ArgumentType component() { -+ return this.of(ComponentArgument.textComponent(), PaperAdventure::asAdventure); ++ return this.of(ComponentArgument.textComponent(PaperCommands.INSTANCE.getBuildContext()), PaperAdventure::asAdventure); + } + + @Override @@ -1721,7 +1723,7 @@ index f84c9c80e701231e5c33ac3c5573f1093e80f38b..6c072e44a8144de6658b4eb818c996f0 public static final LifecycleEventRunner INSTANCE = new LifecycleEventRunner(); diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java -index f341813e9713e39bfe142ca34b751de3d8efd25b..5110f8c23d199157404655e94a75588805144c3e 100644 +index e6c7f62ed379a78645933670299e4fcda8540ed1..59d7e8a3d83d3ab7aa28606401bb129ccaeff240 100644 --- a/src/main/java/net/minecraft/commands/CommandSourceStack.java +++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java @@ -45,8 +45,7 @@ import net.minecraft.world.phys.Vec2; @@ -1761,7 +1763,7 @@ index f341813e9713e39bfe142ca34b751de3d8efd25b..5110f8c23d199157404655e94a755888 @Override public boolean hasPermission(int level) { // CraftBukkit start -@@ -461,6 +440,12 @@ public class CommandSourceStack implements ExecutionCommandSource dispatcher = new com.mojang.brigadier.CommandDispatcher(); -- public final java.util.List> vanillaCommandNodes = new java.util.ArrayList<>(); // Paper - Add UnknownCommandEvent public Commands(Commands.CommandSelection environment, CommandBuildContext commandRegistryAccess) { - this(); // CraftBukkit ++ // Paper AdvancementCommands.register(this.dispatcher); AttributeCommand.register(this.dispatcher, commandRegistryAccess); ExecuteCommand.register(this.dispatcher, commandRegistryAccess); -@@ -255,7 +253,6 @@ public class Commands { - if (environment.includeIntegrated) { - PublishCommand.register(this.dispatcher); - } -- this.vanillaCommandNodes.addAll(this.dispatcher.getRoot().getChildren()); // Paper - Add UnknownCommandEvent - - // Paper start - Vanilla command permission fixes - for (final CommandNode node : this.dispatcher.getRoot().getChildren()) { -@@ -264,11 +261,17 @@ public class Commands { +@@ -265,11 +265,17 @@ public class Commands { } } // Paper end - Vanilla command permission fixes @@ -1820,7 +1812,7 @@ index b7f338e982d0dcab99137ab6dc200b82ac6b7cba..feab1133e4ec9da0d687dbca48ca6c5d this.dispatcher.setConsumer(ExecutionCommandSource.resultConsumer()); } -@@ -324,6 +327,11 @@ public class Commands { +@@ -325,6 +331,11 @@ public class Commands { } public void performCommand(ParseResults parseresults, String s, String label) { // CraftBukkit @@ -1832,7 +1824,7 @@ index b7f338e982d0dcab99137ab6dc200b82ac6b7cba..feab1133e4ec9da0d687dbca48ca6c5d CommandSourceStack commandlistenerwrapper = (CommandSourceStack) parseresults.getContext().getSource(); commandlistenerwrapper.getServer().getProfiler().push(() -> { -@@ -338,6 +346,7 @@ public class Commands { +@@ -339,6 +350,7 @@ public class Commands { }); } } catch (Exception exception) { @@ -1840,16 +1832,7 @@ index b7f338e982d0dcab99137ab6dc200b82ac6b7cba..feab1133e4ec9da0d687dbca48ca6c5d MutableComponent ichatmutablecomponent = Component.literal(exception.getMessage() == null ? exception.getClass().getName() : exception.getMessage()); if (commandlistenerwrapper.getServer().isDebugging() || Commands.LOGGER.isDebugEnabled()) { // Paper - Debugging -@@ -372,7 +381,7 @@ public class Commands { - } catch (CommandSyntaxException commandsyntaxexception) { - // Paper start - Add UnknownCommandEvent - final net.kyori.adventure.text.TextComponent.Builder builder = net.kyori.adventure.text.Component.text(); -- if ((parseresults.getContext().getNodes().isEmpty() || !this.vanillaCommandNodes.contains(parseresults.getContext().getNodes().get(0).getNode()))) { -+ if (parseresults.getContext().getNodes().isEmpty() || ((CommandNode) parseresults.getContext().getNodes().get(0).getNode()) instanceof io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode) { - if (!org.spigotmc.SpigotConfig.unknownCommandMessage.isEmpty()) { - builder.append(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(org.spigotmc.SpigotConfig.unknownCommandMessage)); - } -@@ -482,7 +491,7 @@ public class Commands { +@@ -477,7 +489,7 @@ public class Commands { Map, CommandNode> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues RootCommandNode vanillaRoot = new RootCommandNode(); @@ -1858,7 +1841,7 @@ index b7f338e982d0dcab99137ab6dc200b82ac6b7cba..feab1133e4ec9da0d687dbca48ca6c5d map.put(vanilla, vanillaRoot); this.fillUsableCommands(vanilla, vanillaRoot, player.createCommandSourceStack(), (Map) map); -@@ -520,6 +529,7 @@ public class Commands { +@@ -515,6 +527,7 @@ public class Commands { } private void fillUsableCommands(CommandNode tree, CommandNode result, CommandSourceStack source, Map, CommandNode> resultNodes) { @@ -1866,7 +1849,7 @@ index b7f338e982d0dcab99137ab6dc200b82ac6b7cba..feab1133e4ec9da0d687dbca48ca6c5d Iterator iterator = tree.getChildren().iterator(); boolean registeredAskServerSuggestionsForTree = false; // Paper - tell clients to ask server for suggestions for EntityArguments -@@ -534,6 +544,42 @@ public class Commands { +@@ -529,6 +542,42 @@ public class Commands { if (commandnode2.canUse(source)) { ArgumentBuilder argumentbuilder = commandnode2.createBuilder(); // CraftBukkit - decompile error @@ -1910,10 +1893,10 @@ index b7f338e982d0dcab99137ab6dc200b82ac6b7cba..feab1133e4ec9da0d687dbca48ca6c5d argumentbuilder.requires((icompletionprovider) -> { return true; diff --git a/src/main/java/net/minecraft/commands/arguments/MessageArgument.java b/src/main/java/net/minecraft/commands/arguments/MessageArgument.java -index 23f6b833e71002c648e20744ed0beb344efbe8b9..3cfb8314d623de47c3f19bee17d39f2393e9b767 100644 +index 982b2bab27e3d55d0ba07060862c0c3183ad91b0..5fa8a3343ffc11e82c20b78a73205fd8a42d3c5d 100644 --- a/src/main/java/net/minecraft/commands/arguments/MessageArgument.java +++ b/src/main/java/net/minecraft/commands/arguments/MessageArgument.java -@@ -36,6 +36,11 @@ public class MessageArgument implements SignedArgument +@@ -39,6 +39,11 @@ public class MessageArgument implements SignedArgument public static void resolveChatMessage(CommandContext context, String name, Consumer callback) throws CommandSyntaxException { MessageArgument.Message message = context.getArgument(name, MessageArgument.Message.class); @@ -1926,10 +1909,10 @@ index 23f6b833e71002c648e20744ed0beb344efbe8b9..3cfb8314d623de47c3f19bee17d39f23 Component component = message.resolveComponent(commandSourceStack); CommandSigningContext commandSigningContext = commandSourceStack.getSigningContext(); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 2dc07e5ef249636e85ad9c78e3729e9e066a8fe8..231f90ce796ff270da79362fc1ab54c2c3c10aaf 100644 +index 8dc2f9df367c849ca333bf1a1fd92ff91617b548..d3e031441e0bd519236c64669fc6f5cc68abb1f6 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -295,7 +295,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); public int autosavePeriod; @@ -1938,7 +1921,7 @@ index 2dc07e5ef249636e85ad9c78e3729e9e066a8fe8..231f90ce796ff270da79362fc1ab54c2 public boolean forceTicks; // Paper // CraftBukkit end // Spigot start -@@ -383,7 +383,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { @@ -1965,9 +1947,9 @@ index 2dc07e5ef249636e85ad9c78e3729e9e066a8fe8..231f90ce796ff270da79362fc1ab54c2 this.resources = minecraftserver_reloadableresources; - this.server.syncCommands(); // SPIGOT-5884: Lost on reload this.packRepository.setSelected(dataPacks); - WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(MinecraftServer.getSelectedPacks(this.packRepository), this.worldData.enabledFeatures()); + WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(MinecraftServer.getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures()); -@@ -2263,8 +2265,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop getKnownCommands() { diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java -index 61115db85b81e627d11a0de21691a2ca69aafe2c..8c49ed07737f841717833d944e15df8353650925 100644 +index ba2a2ca0c36e61cb3cc00fafc7a5dd9f7050388f..006561010dac5f34cb82a0905c5cbe100c2669a5 100644 --- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java @@ -23,14 +23,26 @@ import org.bukkit.craftbukkit.entity.CraftMinecartCommand; @@ -2290,7 +2272,7 @@ index 61115db85b81e627d11a0de21691a2ca69aafe2c..8c49ed07737f841717833d944e15df83 suggestions.getList().forEach((s) -> results.add(s.getText())); }); -@@ -113,4 +125,15 @@ public final class VanillaCommandWrapper extends BukkitCommand { +@@ -116,4 +128,15 @@ public final class VanillaCommandWrapper extends BukkitCommand { private String toDispatcher(String[] args, String name) { return name + ((args.length > 0) ? " " + Joiner.on(' ').join(args) : ""); } @@ -2492,10 +2474,10 @@ index 0000000000000000000000000000000000000000..815c7901492b666cd1713d5c9438325a + } +} diff --git a/src/test/java/org/bukkit/support/DummyServer.java b/src/test/java/org/bukkit/support/DummyServer.java -index 3b3e44c5ed24f653f7dc1e5d3d4f0ff76084f277..48463a50963f50b1fc8fc0ae7cb79037756463fa 100644 +index 7a4681155f740a98ecafa0b992eae1fb5524551f..efc03dcbf07ba680aae531d570981410f3d9da91 100644 --- a/src/test/java/org/bukkit/support/DummyServer.java +++ b/src/test/java/org/bukkit/support/DummyServer.java -@@ -52,7 +52,7 @@ public final class DummyServer { +@@ -54,7 +54,7 @@ public final class DummyServer { final Thread currentThread = Thread.currentThread(); when(instance.isPrimaryThread()).thenAnswer(ignored -> Thread.currentThread().equals(currentThread)); From 6986bae3bff44af0a8a1c1982cd96d971a66c9e6 Mon Sep 17 00:00:00 2001 From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:44:45 -0400 Subject: [PATCH 03/27] update --- patches/server/1045-Brigadier-based-command-API.patch | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/patches/server/1045-Brigadier-based-command-API.patch b/patches/server/1045-Brigadier-based-command-API.patch index 4a1a4ff96aba..3eb5e883ec4a 100644 --- a/patches/server/1045-Brigadier-based-command-API.patch +++ b/patches/server/1045-Brigadier-based-command-API.patch @@ -822,7 +822,7 @@ index 0000000000000000000000000000000000000000..895addef908e09d527e4bc9210599e88 +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentImpl.java b/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentImpl.java new file mode 100644 -index 0000000000000000000000000000000000000000..adc0df7c3a762d99e7ad458576d4be68e94cc351 +index 0000000000000000000000000000000000000000..5c380b62738244c4045c6678e22f167c36217bc9 --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentImpl.java @@ -0,0 +1,29 @@ @@ -840,7 +840,7 @@ index 0000000000000000000000000000000000000000..adc0df7c3a762d99e7ad458576d4be68 +public record MessageArgumentImpl(MessageArgument.Message message) implements MessageArgumentResponse { + @Override + public String content() { -+ return this.message.getText(); ++ return this.message.text(); + } + + @Override @@ -1989,7 +1989,7 @@ index eb4fc900164d1fb3a78653ae8bc42ea30323f5b7..2eb9c584cc77237f1c82d880a51a3f8b this.setPvpAllowed(dedicatedserverproperties.pvp); this.setFlightAllowed(dedicatedserverproperties.allowFlight); diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 8e67853a7a93fa736c147e8b2df537746dc8e94f..9e313e7bd35cd512be4a8d4b5e667fcd6e034129 100644 +index 8e67853a7a93fa736c147e8b2df537746dc8e94f..40ca86931b399afcb437eccaf55dd767872b47bb 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -2462,33 +2462,20 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl @@ -2033,7 +2033,7 @@ index 8e67853a7a93fa736c147e8b2df537746dc8e94f..9e313e7bd35cd512be4a8d4b5e667fcd + This method should ONLY be used if you need to execute a command WITHOUT + an actual player's input. + */ -+ this.performChatCommand(new ServerboundChatCommandPacket(s, java.time.Instant.now(), 0, net.minecraft.commands.arguments.ArgumentSignatures.EMPTY, null), null); ++ this.performUnsignedChatCommand(s); + // Paper end } // CraftBukkit end From 946036adef79c8aa601e16d54d9a16a2d6699edc Mon Sep 17 00:00:00 2001 From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> Date: Tue, 30 Apr 2024 16:53:49 -0400 Subject: [PATCH 04/27] update --- .../1045-Brigadier-based-command-API.patch | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/patches/server/1045-Brigadier-based-command-API.patch b/patches/server/1045-Brigadier-based-command-API.patch index 3eb5e883ec4a..a171c61287dc 100644 --- a/patches/server/1045-Brigadier-based-command-API.patch +++ b/patches/server/1045-Brigadier-based-command-API.patch @@ -508,10 +508,10 @@ index 0000000000000000000000000000000000000000..9e293468778a8e1b8d489e239936ffbc +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java b/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java new file mode 100644 -index 0000000000000000000000000000000000000000..13571c20cd837fad8d4275bd2a31002c51fe5b5a +index 0000000000000000000000000000000000000000..b429f5d2034614c41a22a50b7e942f0089397a20 --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java -@@ -0,0 +1,159 @@ +@@ -0,0 +1,167 @@ +package io.papermc.paper.command.brigadier; + +import com.google.common.base.Preconditions; @@ -531,11 +531,13 @@ index 0000000000000000000000000000000000000000..13571c20cd837fad8d4275bd2a31002c +import net.minecraft.commands.CommandBuildContext; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.ReloadableServerResources; ++import net.minecraft.world.flag.FeatureFlagSet; +import org.apache.commons.lang3.StringUtils; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; ++import org.jetbrains.annotations.TestOnly; + +import static java.util.Objects.requireNonNull; + @@ -554,7 +556,13 @@ index 0000000000000000000000000000000000000000..13571c20cd837fad8d4275bd2a31002c + this.currentContext = context; + } + ++ + public void setResources(final ReloadableServerResources resources) { ++ setResources(resources, MinecraftServer.getServer().getWorldData().enabledFeatures()); ++ } ++ ++ @TestOnly ++ public void setResources(final ReloadableServerResources resources, final FeatureFlagSet featureFlagSet) { + this.invalid = false; + this.dispatcher = new CommandDispatcher<>(new ApiMirrorRootNode() { + @Override @@ -562,7 +570,7 @@ index 0000000000000000000000000000000000000000..13571c20cd837fad8d4275bd2a31002c + return resources.getCommands().getDispatcher(); + } + }); -+ this.buildContext = CommandBuildContext.simple(resources.registryLookup, MinecraftServer.getServer().getWorldData().enabledFeatures()); ++ this.buildContext = CommandBuildContext.simple(resources.registryLookup, featureFlagSet); + } + + @Override @@ -2356,10 +2364,10 @@ index 0000000000000000000000000000000000000000..b2fdb8351c2abb55283850a929d2a87a +io.papermc.paper.command.brigadier.argument.VanillaArgumentProviderImpl diff --git a/src/test/java/io/papermc/paper/command/brigadier/BukkitCommandConversionTest.java b/src/test/java/io/papermc/paper/command/brigadier/BukkitCommandConversionTest.java new file mode 100644 -index 0000000000000000000000000000000000000000..815c7901492b666cd1713d5c9438325a796df0f3 +index 0000000000000000000000000000000000000000..83074a70b1008d4149c886f90c3f99038bc49b67 --- /dev/null +++ b/src/test/java/io/papermc/paper/command/brigadier/BukkitCommandConversionTest.java -@@ -0,0 +1,113 @@ +@@ -0,0 +1,114 @@ +package io.papermc.paper.command.brigadier; + +import com.mojang.brigadier.CommandDispatcher; @@ -2370,6 +2378,7 @@ index 0000000000000000000000000000000000000000..815c7901492b666cd1713d5c9438325a +import com.mojang.brigadier.tree.CommandNode; +import io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap; +import net.minecraft.server.MinecraftServer; ++import net.minecraft.world.flag.FeatureFlags; +import org.apache.logging.log4j.core.util.Assert; +import org.bukkit.Bukkit; +import org.bukkit.Location; @@ -2403,10 +2412,10 @@ index 0000000000000000000000000000000000000000..815c7901492b666cd1713d5c9438325a + public void test() throws CommandSyntaxException { + CommandSender sender = this.getSender(); + CommandSourceStack object = Mockito.mock(CommandSourceStack.class); -+ Mockito.when(object.getLocation()).thenReturn(new Location(null, 0, 0, 0)); ++ Mockito.when(object.getLocation()).thenReturn(new Location(null, 0, 0, 0));; + + CommandDispatcher dispatcher = DATA_PACK.commands.getDispatcher(); -+ PaperCommands.INSTANCE.setResources(DATA_PACK); ++ PaperCommands.INSTANCE.setResources(DATA_PACK, FeatureFlags.REGISTRY.allFlags()); + dispatcher.setConsumer((context, success, result) -> {}); + CommandMap commandMap = new SimpleCommandMap(Bukkit.getServer(), new BukkitBrigForwardingMap()); + Map stringCommandMap = commandMap.getKnownCommands(); From 35ce2b1608aebdfc881b94d47d516c60616341b2 Mon Sep 17 00:00:00 2001 From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> Date: Sat, 4 May 2024 14:27:02 -0400 Subject: [PATCH 05/27] update --- ...and-API.patch => 0477-Brigadier-based-command-API.patch} | 0 ...and-API.patch => 1046-Brigadier-based-command-API.patch} | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename patches/api/{0475-Brigadier-based-command-API.patch => 0477-Brigadier-based-command-API.patch} (100%) rename patches/server/{1045-Brigadier-based-command-API.patch => 1046-Brigadier-based-command-API.patch} (99%) diff --git a/patches/api/0475-Brigadier-based-command-API.patch b/patches/api/0477-Brigadier-based-command-API.patch similarity index 100% rename from patches/api/0475-Brigadier-based-command-API.patch rename to patches/api/0477-Brigadier-based-command-API.patch diff --git a/patches/server/1045-Brigadier-based-command-API.patch b/patches/server/1046-Brigadier-based-command-API.patch similarity index 99% rename from patches/server/1045-Brigadier-based-command-API.patch rename to patches/server/1046-Brigadier-based-command-API.patch index a171c61287dc..6c1e44fcea6d 100644 --- a/patches/server/1045-Brigadier-based-command-API.patch +++ b/patches/server/1046-Brigadier-based-command-API.patch @@ -2047,7 +2047,7 @@ index 8e67853a7a93fa736c147e8b2df537746dc8e94f..40ca86931b399afcb437eccaf55dd767 // CraftBukkit end diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 05e304f9fc8d0291fa779da589bd060ef4165b49..f0e38a1484ed46d3f5468282ec10fe97686e3cd9 100644 +index 57db399bc1d3b6b015740b059987bc8d9bcc3101..631d1256c74c7e9d304f20f45f842d257cb5787b 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -274,11 +274,11 @@ public final class CraftServer implements Server { @@ -2483,10 +2483,10 @@ index 0000000000000000000000000000000000000000..83074a70b1008d4149c886f90c3f9903 + } +} diff --git a/src/test/java/org/bukkit/support/DummyServer.java b/src/test/java/org/bukkit/support/DummyServer.java -index 7a4681155f740a98ecafa0b992eae1fb5524551f..efc03dcbf07ba680aae531d570981410f3d9da91 100644 +index bd56792fc674c4e3606a3179ebf5a84ef0a4e35c..aded6d8b36008d87a1039e88333fa4b86077b56a 100644 --- a/src/test/java/org/bukkit/support/DummyServer.java +++ b/src/test/java/org/bukkit/support/DummyServer.java -@@ -54,7 +54,7 @@ public final class DummyServer { +@@ -51,7 +51,7 @@ public final class DummyServer { final Thread currentThread = Thread.currentThread(); when(instance.isPrimaryThread()).thenAnswer(ignored -> Thread.currentThread().equals(currentThread)); From 0e18effa233a6e02e96136efd56e9b29030565c3 Mon Sep 17 00:00:00 2001 From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> Date: Sat, 4 May 2024 14:32:50 -0400 Subject: [PATCH 06/27] update --- .../brigadier/BukkitBrigadierCommand.java | 2 +- .../BukkitBrigadierCommandSource.java | 2 +- .../AsyncPlayerSendCommandsEvent.java | 4 ++-- .../paper/brigadier/PaperBrigadier.java | 2 +- .../api/0477-Brigadier-based-command-API.patch | 18 ++++++++++-------- .../1046-Brigadier-based-command-API.patch | 9 +++++---- .../example/IceCreamTypeArgument.java | 1 - 7 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommand.java b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommand.java index 515595d589ed..be167b009168 100644 --- a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommand.java +++ b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommand.java @@ -9,7 +9,7 @@ * Brigadier {@link Command}, {@link SuggestionProvider}, and permission checker for Bukkit {@link Command}s. * * @param command source type - * @deprecated For removal, use the new brigadier api. + * @deprecated For removal, see {@link io.papermc.paper.command.brigadier.Commands} on how to use the new Brigadier API. */ @Deprecated(forRemoval = true) // Paper public interface BukkitBrigadierCommand extends Command, Predicate, SuggestionProvider { diff --git a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommandSource.java b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommandSource.java index 904c56acd3e2..a595ff3e14ff 100644 --- a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommandSource.java +++ b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/brigadier/BukkitBrigadierCommandSource.java @@ -7,7 +7,7 @@ import org.jetbrains.annotations.Nullable; /** - * @deprecated For removal, use the new brigadier api. + * @deprecated For removal, see {@link io.papermc.paper.command.brigadier.Commands} on how to use the new Brigadier API. */ @Deprecated(forRemoval = true) // Paper public interface BukkitBrigadierCommandSource { diff --git a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.java b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.java index 3513ec81a4ea..6639be5dba9b 100644 --- a/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.java +++ b/Paper-MojangAPI/src/main/java/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.java @@ -28,10 +28,10 @@ *

If your logic is not safe to run asynchronously, only react to the synchronous version.

* *

This is a draft/experimental API and is subject to change.

- * @deprecated For removal, use the new brigadier api. + * @deprecated For removal, see {@link io.papermc.paper.command.brigadier.Commands} on how to use the new Brigadier API. */ @ApiStatus.Experimental -@Deprecated +@Deprecated(forRemoval = true) public class AsyncPlayerSendCommandsEvent extends PlayerEvent { private static final HandlerList handlers = new HandlerList(); diff --git a/Paper-MojangAPI/src/main/java/io/papermc/paper/brigadier/PaperBrigadier.java b/Paper-MojangAPI/src/main/java/io/papermc/paper/brigadier/PaperBrigadier.java index f66abe214922..f7471af8a569 100644 --- a/Paper-MojangAPI/src/main/java/io/papermc/paper/brigadier/PaperBrigadier.java +++ b/Paper-MojangAPI/src/main/java/io/papermc/paper/brigadier/PaperBrigadier.java @@ -9,7 +9,7 @@ /** * Helper methods to bridge the gaps between Brigadier and Paper-MojangAPI. - * @deprecated For removal, use the new brigadier api. + * @deprecated For removal, see {@link io.papermc.paper.command.brigadier.Commands} on how to use the new Brigadier API. */ @Deprecated(forRemoval = true) // Paper public final class PaperBrigadier { diff --git a/patches/api/0477-Brigadier-based-command-API.patch b/patches/api/0477-Brigadier-based-command-API.patch index 7fb0ef4df550..9155da6fdae0 100644 --- a/patches/api/0477-Brigadier-based-command-API.patch +++ b/patches/api/0477-Brigadier-based-command-API.patch @@ -5,14 +5,14 @@ Subject: [PATCH] Brigadier based command API diff --git a/build.gradle.kts b/build.gradle.kts -index 65e67b8726f1e19a6bcb1fe2f448e4ab68df11d1..475487c7d5adb3bebbd26cd571848ed9c2e3b395 100644 +index 65e67b8726f1e19a6bcb1fe2f448e4ab68df11d1..c252ce446cab6ff7ce530a591e40bf1168d9bc7c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,6 +27,7 @@ configurations.api { } dependencies { -+ api("com.mojang:brigadier:1.1.8") // Paper, expose! ++ api("com.mojang:brigadier:1.1.8") // Paper - Brigadier command api // api dependencies are listed transitively to API consumers api("com.google.guava:guava:32.1.2-jre") api("com.google.code.gson:gson:2.10.1") @@ -365,10 +365,10 @@ index 0000000000000000000000000000000000000000..c30a6e6924c0d515f6ba1eef63511367 +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/ArgumentTypes.java b/src/main/java/io/papermc/paper/command/brigadier/argument/ArgumentTypes.java new file mode 100644 -index 0000000000000000000000000000000000000000..e9059ca88cbb1a82356ccb5eae6360a73cfce318 +index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9df8d23c0 --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/argument/ArgumentTypes.java -@@ -0,0 +1,303 @@ +@@ -0,0 +1,304 @@ +package io.papermc.paper.command.brigadier.argument; + +import com.mojang.brigadier.arguments.ArgumentType; @@ -392,6 +392,7 @@ index 0000000000000000000000000000000000000000..e9059ca88cbb1a82356ccb5eae6360a7 +import org.bukkit.World; +import org.bukkit.block.BlockState; +import org.bukkit.block.structure.Mirror; ++import org.bukkit.block.structure.StructureRotation; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scoreboard.Criteria; +import org.bukkit.scoreboard.DisplaySlot; @@ -667,7 +668,7 @@ index 0000000000000000000000000000000000000000..e9059ca88cbb1a82356ccb5eae6360a7 + * + * @return argument + */ -+ public static @NotNull ArgumentType templateRotation() { ++ public static @NotNull ArgumentType templateRotation() { + return PROVIDER.templateRotation(); + } + @@ -832,10 +833,10 @@ index 0000000000000000000000000000000000000000..07051125a3b4963fb5c7b45bd1d5e93f +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProvider.java b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProvider.java new file mode 100644 -index 0000000000000000000000000000000000000000..45e0e76d276b4ac87eb1ae3b01a10467a71edb11 +index 0000000000000000000000000000000000000000..a9bf62827d46c795f0b3b12de5bcdd825f4f546a --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProvider.java -@@ -0,0 +1,86 @@ +@@ -0,0 +1,87 @@ +package io.papermc.paper.command.brigadier.argument; + +import com.mojang.brigadier.arguments.ArgumentType; @@ -858,6 +859,7 @@ index 0000000000000000000000000000000000000000..45e0e76d276b4ac87eb1ae3b01a10467 +import org.bukkit.World; +import org.bukkit.block.BlockState; +import org.bukkit.block.structure.Mirror; ++import org.bukkit.block.structure.StructureRotation; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scoreboard.Criteria; +import org.bukkit.scoreboard.DisplaySlot; @@ -920,7 +922,7 @@ index 0000000000000000000000000000000000000000..45e0e76d276b4ac87eb1ae3b01a10467 + + ArgumentType templateMirror(); + -+ ArgumentType templateRotation(); ++ ArgumentType templateRotation(); +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/predicate/ItemStackPredicate.java b/src/main/java/io/papermc/paper/command/brigadier/argument/predicate/ItemStackPredicate.java new file mode 100644 diff --git a/patches/server/1046-Brigadier-based-command-API.patch b/patches/server/1046-Brigadier-based-command-API.patch index 6c1e44fcea6d..5c11f4fb9a74 100644 --- a/patches/server/1046-Brigadier-based-command-API.patch +++ b/patches/server/1046-Brigadier-based-command-API.patch @@ -865,10 +865,10 @@ index 0000000000000000000000000000000000000000..5c380b62738244c4045c6678e22f167c +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java new file mode 100644 -index 0000000000000000000000000000000000000000..5a7e587f5772a8b314cf93f50486c4df78c998e3 +index 0000000000000000000000000000000000000000..a913f75269d57a51c2e231de8c18a0d4757a7415 --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/argument/VanillaArgumentProviderImpl.java -@@ -0,0 +1,311 @@ +@@ -0,0 +1,312 @@ +package io.papermc.paper.command.brigadier.argument; + +import com.destroystokyo.paper.profile.CraftPlayerProfile; @@ -941,6 +941,7 @@ index 0000000000000000000000000000000000000000..5a7e587f5772a8b314cf93f50486c4df +import org.bukkit.World; +import org.bukkit.block.BlockState; +import org.bukkit.block.structure.Mirror; ++import org.bukkit.block.structure.StructureRotation; +import org.bukkit.craftbukkit.CraftHeightMap; +import org.bukkit.craftbukkit.block.CraftBlockStates; +import org.bukkit.craftbukkit.inventory.CraftItemStack; @@ -1136,8 +1137,8 @@ index 0000000000000000000000000000000000000000..5a7e587f5772a8b314cf93f50486c4df + } + + @Override -+ public ArgumentType templateRotation() { -+ return this.of(TemplateRotationArgument.templateRotation(), mirror -> Rotation.valueOf(mirror.name())); ++ public ArgumentType templateRotation() { ++ return this.of(TemplateRotationArgument.templateRotation(), mirror -> StructureRotation.valueOf(mirror.name())); + } + + private ArgumentType of(ArgumentType base, ResultConverter converter) { diff --git a/test-plugin/src/main/java/io/papermc/testplugin/example/IceCreamTypeArgument.java b/test-plugin/src/main/java/io/papermc/testplugin/example/IceCreamTypeArgument.java index 17f74dcadd01..d1467337de9a 100644 --- a/test-plugin/src/main/java/io/papermc/testplugin/example/IceCreamTypeArgument.java +++ b/test-plugin/src/main/java/io/papermc/testplugin/example/IceCreamTypeArgument.java @@ -18,7 +18,6 @@ public class IceCreamTypeArgument implements CustomArgumentType.Converted { - @Override public @NotNull IceCreamType convert(String nativeType) throws CommandSyntaxException { try { From 525b6857ebba48c3b410f96754ca049ade9a9b56 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sat, 4 May 2024 12:08:16 -0700 Subject: [PATCH 07/27] add back removed hunks (again) --- .../0477-Brigadier-based-command-API.patch | 32 ++++++ .../1046-Brigadier-based-command-API.patch | 104 ++++++++++++------ 2 files changed, 100 insertions(+), 36 deletions(-) diff --git a/patches/api/0477-Brigadier-based-command-API.patch b/patches/api/0477-Brigadier-based-command-API.patch index 9155da6fdae0..3e5790df941b 100644 --- a/patches/api/0477-Brigadier-based-command-API.patch +++ b/patches/api/0477-Brigadier-based-command-API.patch @@ -1197,3 +1197,35 @@ index ac9a28922f8a556944a4c3649d74c32c622f0cb0..c3a9cf65db73ed534bf20996c7f05b5e } if (!isAlias) { +diff --git a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java +index 3ec32b46264cfff857b50129b5e0fa5584943ec6..bdfe68b386b5ca2878475e548d3c9a3808fce848 100644 +--- a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java ++++ b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java +@@ -18,6 +18,9 @@ public class ReloadCommand extends BukkitCommand { + this.setAliases(Arrays.asList("rl")); + } + ++ @org.jetbrains.annotations.ApiStatus.Internal // Paper ++ public static final String RELOADING_DISABLED_MESSAGE = "A lifecycle event handler has been registered which makes reloading plugins not possible"; // Paper ++ + @Override + public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) { // Paper + if (!testPermission(sender)) return true; +@@ -51,7 +54,16 @@ public class ReloadCommand extends BukkitCommand { + + Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues when using some plugins."); + Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server."); +- Bukkit.reload(); ++ // Paper start - lifecycle events ++ try { ++ Bukkit.reload(); ++ } catch (final IllegalStateException ex) { ++ if (ex.getMessage().equals(RELOADING_DISABLED_MESSAGE)) { ++ Command.broadcastCommandMessage(sender, ChatColor.RED + RELOADING_DISABLED_MESSAGE); ++ return true; ++ } ++ } ++ // Paper end - lifecycle events + Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Reload complete."); + + return true; diff --git a/patches/server/1046-Brigadier-based-command-API.patch b/patches/server/1046-Brigadier-based-command-API.patch index 5c11f4fb9a74..8933d48f4b43 100644 --- a/patches/server/1046-Brigadier-based-command-API.patch +++ b/patches/server/1046-Brigadier-based-command-API.patch @@ -439,7 +439,7 @@ index 0000000000000000000000000000000000000000..82a57ffc048454fbc4c705adbac83d16 +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java b/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java new file mode 100644 -index 0000000000000000000000000000000000000000..9e293468778a8e1b8d489e239936ffbc03ba81e2 +index 0000000000000000000000000000000000000000..1b1642f306771f029e6214a2e2ebebb6ae6abc3e --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/PaperCommandSourceStack.java @@ -0,0 +1,63 @@ @@ -465,7 +465,7 @@ index 0000000000000000000000000000000000000000..9e293468778a8e1b8d489e239936ffbc + Vec3 pos = this.getHandle().getPosition(); + Level level = this.getHandle().getLevel(); + -+ return new org.bukkit.Location(level.getWorld(), pos.x, pos.y, pos.z, rot.x, rot.y); ++ return new Location(level.getWorld(), pos.x, pos.y, pos.z, rot.y, rot.x); + } + + @Override @@ -508,10 +508,10 @@ index 0000000000000000000000000000000000000000..9e293468778a8e1b8d489e239936ffbc +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java b/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java new file mode 100644 -index 0000000000000000000000000000000000000000..b429f5d2034614c41a22a50b7e942f0089397a20 +index 0000000000000000000000000000000000000000..b7a0b33a66d4cea9dc5d8f1816b67151948605ea --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/PaperCommands.java -@@ -0,0 +1,167 @@ +@@ -0,0 +1,165 @@ +package io.papermc.paper.command.brigadier; + +import com.google.common.base.Preconditions; @@ -556,21 +556,19 @@ index 0000000000000000000000000000000000000000..b429f5d2034614c41a22a50b7e942f00 + this.currentContext = context; + } + -+ -+ public void setResources(final ReloadableServerResources resources) { -+ setResources(resources, MinecraftServer.getServer().getWorldData().enabledFeatures()); -+ } -+ -+ @TestOnly -+ public void setResources(final ReloadableServerResources resources, final FeatureFlagSet featureFlagSet) { ++ public void setDispatcher(final net.minecraft.commands.Commands commands, final CommandBuildContext commandBuildContext) { + this.invalid = false; + this.dispatcher = new CommandDispatcher<>(new ApiMirrorRootNode() { + @Override + public CommandDispatcher getDispatcher() { -+ return resources.getCommands().getDispatcher(); ++ return commands.getDispatcher(); + } + }); -+ this.buildContext = CommandBuildContext.simple(resources.registryLookup, featureFlagSet); ++ this.buildContext = commandBuildContext; ++ } ++ ++ public void setValid() { ++ this.invalid = false; + } + + @Override @@ -830,7 +828,7 @@ index 0000000000000000000000000000000000000000..895addef908e09d527e4bc9210599e88 +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentImpl.java b/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentImpl.java new file mode 100644 -index 0000000000000000000000000000000000000000..5c380b62738244c4045c6678e22f167c36217bc9 +index 0000000000000000000000000000000000000000..1f3c8bb21bb7f6e3cfbb6a0fd87a1a17e9b69ab8 --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentImpl.java @@ -0,0 +1,29 @@ @@ -852,11 +850,11 @@ index 0000000000000000000000000000000000000000..5c380b62738244c4045c6678e22f167c + } + + @Override -+ public @NotNull CompletableFuture resolveSignedMessage(String argumentName, CommandContext erased) throws CommandSyntaxException { -+ CommandContext type = erased; -+ CompletableFuture future = new CompletableFuture<>(); ++ public @NotNull CompletableFuture resolveSignedMessage(final String argumentName, final CommandContext erased) throws CommandSyntaxException { ++ final CommandContext type = erased; ++ final CompletableFuture future = new CompletableFuture<>(); + -+ MessageArgument.Message response = type.getArgument(argumentName, MessageArgumentImpl.class).message; ++ final MessageArgument.Message response = type.getArgument(argumentName, MessageArgumentImpl.class).message; + MessageArgument.resolveChatMessage(response, type, argumentName, (message) -> { + future.complete(message.adventureView()); + }); @@ -1918,7 +1916,7 @@ index 982b2bab27e3d55d0ba07060862c0c3183ad91b0..5fa8a3343ffc11e82c20b78a73205fd8 Component component = message.resolveComponent(commandSourceStack); CommandSigningContext commandSigningContext = commandSourceStack.getSigningContext(); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 8dc2f9df367c849ca333bf1a1fd92ff91617b548..d3e031441e0bd519236c64669fc6f5cc68abb1f6 100644 +index 8dc2f9df367c849ca333bf1a1fd92ff91617b548..678000a7fa4f1c03ac5900bdd8fc8ff3b79a4c59 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -302,7 +302,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { @@ -1958,7 +1957,7 @@ index 8dc2f9df367c849ca333bf1a1fd92ff91617b548..d3e031441e0bd519236c64669fc6f5cc this.packRepository.setSelected(dataPacks); WorldDataConfiguration worlddataconfiguration = new WorldDataConfiguration(MinecraftServer.getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures()); -@@ -2323,8 +2324,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop {}); + CommandMap commandMap = new SimpleCommandMap(Bukkit.getServer(), new BukkitBrigForwardingMap()); + Map stringCommandMap = commandMap.getKnownCommands(); From 57d035f74ee0a7c11456af045883237e051427f2 Mon Sep 17 00:00:00 2001 From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> Date: Sat, 4 May 2024 15:41:57 -0400 Subject: [PATCH 08/27] update --- .../0477-Brigadier-based-command-API.patch | 94 +++++++++++-------- .../1046-Brigadier-based-command-API.patch | 11 ++- 2 files changed, 64 insertions(+), 41 deletions(-) diff --git a/patches/api/0477-Brigadier-based-command-API.patch b/patches/api/0477-Brigadier-based-command-API.patch index 3e5790df941b..c05011e4aca9 100644 --- a/patches/api/0477-Brigadier-based-command-API.patch +++ b/patches/api/0477-Brigadier-based-command-API.patch @@ -317,7 +317,7 @@ index 0000000000000000000000000000000000000000..5747ea03aef2f2a464bd6b30aba3a536 +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializer.java b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializer.java new file mode 100644 -index 0000000000000000000000000000000000000000..10de213a52edec495e0dc3202193a3c74aa1dd92 +index 0000000000000000000000000000000000000000..40bdc1e4da3234f4ba7ec3476fc18c8811c0fe54 --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializer.java @@ -0,0 +1,24 @@ @@ -342,33 +342,33 @@ index 0000000000000000000000000000000000000000..10de213a52edec495e0dc3202193a3c7 + * @return serializer instance + */ + static @NotNull MessageComponentSerializer message() { -+ return MessageComponentSerializerHolder.PROVIDER; ++ return MessageComponentSerializerHolder.PROVIDER.orElseThrow(); + } +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerHolder.java b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerHolder.java new file mode 100644 -index 0000000000000000000000000000000000000000..c30a6e6924c0d515f6ba1eef635113676f7efff9 +index 0000000000000000000000000000000000000000..2db12952461c92a64505d6646f6f49f824e83050 --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerHolder.java @@ -0,0 +1,12 @@ +package io.papermc.paper.command.brigadier; + ++import java.util.Optional; +import java.util.ServiceLoader; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Internal +final class MessageComponentSerializerHolder { + -+ public static final MessageComponentSerializer PROVIDER = ServiceLoader.load(MessageComponentSerializer.class) -+ .findFirst() -+ .orElseThrow(); ++ static final Optional PROVIDER = ServiceLoader.load(MessageComponentSerializer.class) ++ .findFirst(); +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/ArgumentTypes.java b/src/main/java/io/papermc/paper/command/brigadier/argument/ArgumentTypes.java new file mode 100644 -index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9df8d23c0 +index 0000000000000000000000000000000000000000..37ba7db7514e466460a8c923b0fa69fb43ee88e6 --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/argument/ArgumentTypes.java -@@ -0,0 +1,304 @@ +@@ -0,0 +1,317 @@ +package io.papermc.paper.command.brigadier.argument; + +import com.mojang.brigadier.arguments.ArgumentType; @@ -380,11 +380,13 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 +import io.papermc.paper.command.brigadier.argument.resolvers.selector.EntitySelectorArgumentResolver; +import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver; +import io.papermc.paper.entity.LookAnchor; ++import java.util.Optional; +import java.util.ServiceLoader; +import java.util.UUID; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; ++import net.kyori.adventure.text.format.Style; +import org.bukkit.GameMode; +import org.bukkit.HeightMap; +import org.bukkit.NamespacedKey; @@ -408,9 +410,8 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 +@ApiStatus.Experimental +public final class ArgumentTypes { + -+ static final @NotNull VanillaArgumentProvider PROVIDER = ServiceLoader.load(VanillaArgumentProvider.class) -+ .findFirst() -+ .orElseThrow(); ++ static final Optional PROVIDER = ServiceLoader.load(VanillaArgumentProvider.class) ++ .findFirst(); + + @ApiStatus.Internal + ArgumentTypes() { @@ -423,7 +424,7 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 + * @return argument that takes one entity + */ + public static @NotNull ArgumentType entity() { -+ return PROVIDER.entity(); ++ return provider().entity(); + } + + /** @@ -433,7 +434,7 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 + * @return argument that takes multiple entities + */ + public static @NotNull ArgumentType entities() { -+ return PROVIDER.entities(); ++ return provider().entities(); + } + + /** @@ -443,7 +444,7 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 + * @return argument that takes one player + */ + public static @NotNull ArgumentType player() { -+ return PROVIDER.player(); ++ return provider().player(); + } + + /** @@ -453,7 +454,7 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 + * @return argument that takes multiple players + */ + public static @NotNull ArgumentType players() { -+ return PROVIDER.players(); ++ return provider().players(); + } + + /** @@ -463,7 +464,7 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 + * @return player profile arguments + */ + public static @NotNull ArgumentType playerProfiles() { -+ return PROVIDER.playerProfiles(); ++ return provider().playerProfiles(); + } + + /** @@ -472,7 +473,7 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 + * @return argument + */ + public static @NotNull ArgumentType blockPos() { -+ return PROVIDER.blockPos(); ++ return provider().blockPos(); + } + + /** @@ -482,7 +483,7 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 + * @return argument + */ + public static @NotNull ArgumentType blockState() { -+ return PROVIDER.blockState(); ++ return provider().blockState(); + } + + /** @@ -492,7 +493,7 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 + * @return argument + */ + public static @NotNull ArgumentType itemStack() { -+ return PROVIDER.itemStack(); ++ return provider().itemStack(); + } + + /** @@ -501,7 +502,7 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 + * @return argument + */ + public static @NotNull ArgumentType itemPredicate() { -+ return PROVIDER.itemStackPredicate(); ++ return provider().itemStackPredicate(); + } + + /** @@ -510,7 +511,7 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 + * @return argument + */ + public static @NotNull ArgumentType namedColor() { -+ return PROVIDER.namedColor(); ++ return provider().namedColor(); + } + + /** @@ -519,7 +520,16 @@ index 0000000000000000000000000000000000000000..7aaa70648f26c3aa5ad5b4b9942b78c9 + * @return argument + */ + public static @NotNull ArgumentType component() { -+ return PROVIDER.component(); ++ return provider().component(); ++ } ++ ++ /** ++ * A style argument. ++ * ++ * @return argument ++ */ ++ public static @NotNull ArgumentType