Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Brigadier commands registered from Commands do not allow to get previous argument in a SuggestionProvider #11384

Open
DevSolaris opened this issue Sep 9, 2024 · 2 comments
Labels
status: needs triage type: bug Something doesn't work as it was intended to. version: 1.21.1

Comments

@DevSolaris
Copy link

Expected behavior

I expect this code to work and to give the "config" argument

public final class BrigadierTest extends JavaPlugin {

    @Override
    public void onEnable() {
        // Plugin startup logic
        LifecycleEventManager<Plugin> manager = this.getLifecycleManager();
        manager.registerEventHandler(LifecycleEvents.COMMANDS, event -> {
            final Commands commands = event.registrar();

            SuggestionProvider<CommandSourceStack> configSuggestionProvider = (context, builder) -> {
                builder.suggest("config1");
                builder.suggest("config2");

                return builder.buildFuture();
            };

            SuggestionProvider<CommandSourceStack> optionSuggestionProvider = (context, builder) -> {
                var configName = context.getArgument("config", String.class);

                builder.suggest("option");

                return builder.buildFuture();
            };

            commands.register(
                    literal("testcommand")
                            .requires(source -> source.getSender().hasPermission("lumenconfig.manage"))
                            .then(literal("copy")
                                    .then(argument("config", StringArgumentType.string())
                                            .suggests(configSuggestionProvider)
                                            .then(argument("from", StringArgumentType.string())
                                                    .suggests(optionSuggestionProvider)
                                                    .then(argument("to", StringArgumentType.string())
                                                            .executes(context -> {
                                                                context.getSource().getSender().sendMessage("Config: " + context.getArgument("config", String.class));
                                                                context.getSource().getSender().sendMessage("From: " + context.getArgument("from", String.class));
                                                                context.getSource().getSender().sendMessage("To: " + context.getArgument("to", String.class));
                                                                return 1;
                                                            })
                                                    )
                                            )
                                    )
                            )
                            .build(),
                    "testcommand",
                    List.of("testalias")
            );
        });
    }

    @Override
    public void onDisable() {
        // Plugin shutdown logic
    }
}

Observed/Actual behavior

[22:12:49 WARN]: Unhandled exception when tab completing
java.util.concurrent.ExecutionException: org.bukkit.command.CommandException: Unhandled exception executing tab-completer for 'testcommand copy config1 ' in io.papermc.paper.command.brigadier.PluginVanillaCommandWrapper(testcommand)
        at org.bukkit.craftbukkit.util.Waitable.get(Waitable.java:41) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at org.bukkit.craftbukkit.command.ConsoleCommandCompleter.complete(ConsoleCommandCompleter.java:103) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at org.jline.reader.impl.LineReaderImpl.doComplete(LineReaderImpl.java:4381) ~[jline-reader-3.20.0.jar:?]
        at org.jline.reader.impl.LineReaderImpl.doComplete(LineReaderImpl.java:4347) ~[jline-reader-3.20.0.jar:?]
        at org.jline.reader.impl.LineReaderImpl.expandOrComplete(LineReaderImpl.java:4286) ~[jline-reader-3.20.0.jar:?]
        at org.jline.reader.impl.LineReaderImpl$1.apply(LineReaderImpl.java:3778) ~[jline-reader-3.20.0.jar:?]
        at org.jline.reader.impl.LineReaderImpl.readLine(LineReaderImpl.java:679) ~[jline-reader-3.20.0.jar:?]
        at org.jline.reader.impl.LineReaderImpl.readLine(LineReaderImpl.java:468) ~[jline-reader-3.20.0.jar:?]
        at net.minecrell.terminalconsole.SimpleTerminalConsole.readCommands(SimpleTerminalConsole.java:158) ~[terminalconsoleappender-1.3.0.jar:?]
        at net.minecrell.terminalconsole.SimpleTerminalConsole.start(SimpleTerminalConsole.java:141) ~[terminalconsoleappender-1.3.0.jar:?]
        at net.minecraft.server.dedicated.DedicatedServer$1.run(DedicatedServer.java:117) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
Caused by: org.bukkit.command.CommandException: Unhandled exception executing tab-completer for 'testcommand copy config1 ' in io.papermc.paper.command.brigadier.PluginVanillaCommandWrapper(testcommand)
        at org.bukkit.command.SimpleCommandMap.tabComplete(SimpleCommandMap.java:256) ~[paper-mojangapi-1.21.1-R0.1-SNAPSHOT.jar:?]
        at org.bukkit.command.SimpleCommandMap.tabComplete(SimpleCommandMap.java:201) ~[paper-mojangapi-1.21.1-R0.1-SNAPSHOT.jar:?]
        at org.bukkit.craftbukkit.command.ConsoleCommandCompleter$2.evaluate(ConsoleCommandCompleter.java:93) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at org.bukkit.craftbukkit.command.ConsoleCommandCompleter$2.evaluate(ConsoleCommandCompleter.java:90) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at org.bukkit.craftbukkit.util.Waitable.run(Waitable.java:23) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at net.minecraft.server.MinecraftServer.tickChildren(MinecraftServer.java:1754) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at net.minecraft.server.dedicated.DedicatedServer.tickChildren(DedicatedServer.java:473) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at net.minecraft.server.MinecraftServer.tickServer(MinecraftServer.java:1598) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1304) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:330) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at java.base/java.lang.Thread.run(Thread.java:1583) ~[?:?]
Caused by: java.lang.IllegalArgumentException: No such argument 'config' exists on this command
        at com.mojang.brigadier.context.CommandContext.getArgument(CommandContext.java:102) ~[brigadier-1.3.10.jar:?]
        at BrigadierTest-1.0.0-SNAPSHOT.jar/com.test.brigadiertest.BrigadierTest.lambda$onEnable$1(BrigadierTest.java:34) ~[BrigadierTest-1.0.0-SNAPSHOT.jar:?]
        at com.mojang.brigadier.tree.ArgumentCommandNode.listSuggestions(ArgumentCommandNode.java:71) ~[brigadier-1.3.10.jar:1.21.1-74-971a7a5]
        at com.mojang.brigadier.CommandDispatcher.getCompletionSuggestions(CommandDispatcher.java:551) ~[paper-1.21.1.jar:?]
        at com.mojang.brigadier.CommandDispatcher.getCompletionSuggestions(CommandDispatcher.java:531) ~[paper-1.21.1.jar:?]
        at org.bukkit.craftbukkit.command.VanillaCommandWrapper.tabComplete(VanillaCommandWrapper.java:69) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at org.bukkit.command.SimpleCommandMap.tabComplete(SimpleCommandMap.java:250) ~[paper-mojangapi-1.21.1-R0.1-SNAPSHOT.jar:?]
        at org.bukkit.command.SimpleCommandMap.tabComplete(SimpleCommandMap.java:201) ~[paper-mojangapi-1.21.1-R0.1-SNAPSHOT.jar:?]
        at org.bukkit.craftbukkit.command.ConsoleCommandCompleter$2.evaluate(ConsoleCommandCompleter.java:93) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at org.bukkit.craftbukkit.command.ConsoleCommandCompleter$2.evaluate(ConsoleCommandCompleter.java:90) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at org.bukkit.craftbukkit.util.Waitable.run(Waitable.java:23) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at net.minecraft.server.MinecraftServer.tickChildren(MinecraftServer.java:1754) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at net.minecraft.server.dedicated.DedicatedServer.tickChildren(DedicatedServer.java:473) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at net.minecraft.server.MinecraftServer.tickServer(MinecraftServer.java:1598) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1304) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:330) ~[paper-1.21.1.jar:1.21.1-74-971a7a5]
        at java.base/java.lang.Thread.run(Thread.java:1583) ~[?:?]

Steps/models to reproduce

Type testcommand copy config1 and press tab

Plugin and Datapack List

Just this test plugin, no datapacks

Paper version

[22:13:27 INFO]: Checking version, please wait...
[22:13:27 INFO]: This server is running Paper version 1.21.1-74-master@971a7a5 (2024-09-08T20:24:20Z) (Implementing API version 1.21.1-R0.1-SNAPSHOT)
You are running the latest version
Previous version: 1.21.1-69-925c3b9 (MC: 1.21.1)

Other

When registering the same command using the dispatcher, code below, it does work fine and the tab complete works

public final class BrigadierTest extends JavaPlugin {

    @Override
    public void onEnable() {
        // Plugin startup logic
        LifecycleEventManager<Plugin> manager = this.getLifecycleManager();
        manager.registerEventHandler(LifecycleEvents.COMMANDS, event -> {
            final Commands commands = event.registrar();

            SuggestionProvider<CommandSourceStack> configSuggestionProvider = (context, builder) -> {
                builder.suggest("config1");
                builder.suggest("config2");

                return builder.buildFuture();
            };

            SuggestionProvider<CommandSourceStack> optionSuggestionProvider = (context, builder) -> {
                var configName = context.getArgument("config", String.class);

                builder.suggest("option");

                return builder.buildFuture();
            };

            commands.getDispatcher().register(
                    literal("testcommand")
                        .requires(source -> source.getSender().hasPermission("lumenconfig.manage"))
                        .then(literal("copy")
                                .then(argument("config", StringArgumentType.string())
                                        .suggests(configSuggestionProvider)
                                        .then(argument("from", StringArgumentType.string())
                                                .suggests(optionSuggestionProvider)
                                                .then(argument("to", StringArgumentType.string())
                                                        .executes(context -> {
                                                            context.getSource().getSender().sendMessage("Config: " + context.getArgument("config", String.class));
                                                            context.getSource().getSender().sendMessage("From: " + context.getArgument("from", String.class));
                                                            context.getSource().getSender().sendMessage("To: " + context.getArgument("to", String.class));
                                                            return 1;
                                                        })
                                                )
                                        )
                                )
                        )
            );
        });
    }

    @Override
    public void onDisable() {
        // Plugin shutdown logic
    }
}
@DevSolaris DevSolaris added status: needs triage type: bug Something doesn't work as it was intended to. labels Sep 9, 2024
@Machine-Maker
Copy link
Member

Contexts are linked. Use getLastChild() on the context in the suggestion provider to get the correct context to get the parsed argument.

@willkroboth
Copy link
Contributor

Yeah, this is a combination of how Brigadier behaves (Mojang/brigadier#142) and how Paper registers commands.

When you register a command through Paper's Commands#register, the main label ("testcommand" here) gets registered as a redirect to the namespaced node ("brigadiertest:testcommand" for example). When a command redirects, Brigadier puts future arguments into a child CommandContext. So, to access the previous arguments in the SuggestionProvider, you need to call CommandContext.getLastChild()#getArgument.

On the other hand, using Commands.getDispatcher()#register does not create a redirected node, which is why the command you gave under Other works. Note that this command can still generate suggestions in a redirected situation using /execute run testcommand..., so it can help to call CommandContext#getLastChild anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs triage type: bug Something doesn't work as it was intended to. version: 1.21.1
Projects
Status: 🕑 Needs Triage
Development

No branches or pull requests

3 participants