brigadier icon indicating copy to clipboard operation
brigadier copied to clipboard

Unexpected argument collision in single-argument branches

Open ZeroMemes opened this issue 7 years ago • 3 comments

Consider the following command tree:

dispatcher.register(literal("goal")
    .then(argument("y", integer())
        .executes(c -> {
            setGoal(new GoalYLevel(
                c.getArgument("y", Integer.class)
            ));
            return 0;
        })
    )
    .then(argument("x", integer())
        .then(argument("z", integer())
            .executes(c -> {
                setGoal(new GoalXZ(
                    c.getArgument("x", Integer.class),
                    c.getArgument("z", Integer.class)
                ));
                return 0;
            })
        )
    )
    .then(argument("x", integer())
        .then(argument("y", integer())
            .then(argument("z", integer())
                .executes(c -> {
                    setGoal(new GoalBlock(
                        c.getArgument("x", Integer.class),
                        c.getArgument("y", Integer.class),
                        c.getArgument("z", Integer.class)
                    ));
                    return 0;
                })
            )
        )
    )
);

This should have 3 possible usages

goal <y>
goal <x> <z>
goal <x> <y> <z>

However, when I try to execute the command with a single integer argument (via goal 1) an exception is thrown!

com.mojang.brigadier.exceptions.CommandSyntaxException: Unknown command at position 6: goal 1<--[HERE]
  at com.mojang.brigadier.exceptions.SimpleCommandExceptionType.createWithContext(SimpleCommandExceptionType.java:21)
  at com.mojang.brigadier.CommandDispatcher.execute(CommandDispatcher.java:283)
  at com.mojang.brigadier.CommandDispatcher.execute(CommandDispatcher.java:178)
  at com.mojang.brigadier.CommandDispatcher.execute(CommandDispatcher.java:143)
  ...

Both the 2-arg and 3-arg usages work as intended: Chat Output

I have found that the only solution to this is to rename the argument in the single argument branch from y. I believe this issue relates to the comment made by Dinnerbone Here.

ZeroMemes avatar Oct 22 '18 04:10 ZeroMemes

It appears that one of the x nodes is choosen (see also @NeunEinser's comment). This could possibly be solved (unless I missed something) by adding a check to the sorting to prefer nodes which do not have children in case the reader reached the end.

Marcono1234 avatar Oct 23 '18 18:10 Marcono1234

Yes, changing the sorting would be the easy fix and was my first thought as well (Not nodes without children, though, but nodes that have an command assigned)

However, I still think it would be a bit neater if the parser already threw an exception when the reader reached the end and the parsed command is not executable.

There are two reasons why I'd prefer that soution (1) In my understanding it just makes more sense if execute only fails when the parser collected an exception or there is a runtime reason. Might be a bit more efficient, too. (2) It should be very easily possible to provide a better error message in the parsing method (like "Missing argument. Usage: [getSmartUsage]"). I really dislike the current "Unkown command"

NeunEinser avatar Oct 23 '18 22:10 NeunEinser

There are 2 xes on the same level. Try this:

dispatcher.register(literal("goal")
    .then(argument("y", integer())
        .executes(c -> {
            setGoal(new GoalYLevel(
                c.getArgument("y", Integer.class)
            ));
            return 0;
        })

    .then(argument("x", integer())
        .then(argument("z", integer())
            .executes(c -> {
                setGoal(new GoalXZ(
                    c.getArgument("x", Integer.class),
                    c.getArgument("z", Integer.class)
                ));
                return 0;
            })
        )
        .then(argument("y", integer())
            .then(argument("z", integer())
                .executes(c -> {
                    setGoal(new GoalBlock(
                        c.getArgument("x", Integer.class),
                        c.getArgument("y", Integer.class),
                        c.getArgument("z", Integer.class)
                    ));
                    return 0;
                })
            )
        )
    )
);

@ZeroMemes

GiantNuker avatar Feb 07 '20 00:02 GiantNuker