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

Redirected command needs to have arguments #46

Open
I-Al-Istannen opened this issue Nov 10, 2018 · 8 comments
Open

Redirected command needs to have arguments #46

I-Al-Istannen opened this issue Nov 10, 2018 · 8 comments

Comments

@I-Al-Istannen
Copy link

I-Al-Istannen commented Nov 10, 2018

Problem

I wanted to make an alias for a command, so I registered a node that just redirected to my target, which worked nicely if the target command had arguments.

MWE

CommandDispatcher<Object> subject = new CommandDispatcher<>();
Object source = new Object();

LiteralCommandNode<Object> node = subject.register(
    literal("Hey")
        .executes((it) -> {
            System.out.println("Hey!");
            return 0;
        })
);
subject.register(
    literal("redirected")
        .redirect(node)
);
System.out.println(subject.execute("redirected", source));

Expected result

Hey!
0

Actual result

com.mojang.brigadier.exceptions.CommandSyntaxException: Unknown command at position 10: redirected<--[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:176)
	at com.mojang.brigadier.CommandDispatcher.execute(CommandDispatcher.java:141)

Small analysis

It looks like this might be by design, if so documenting it would be nice (or a RTFM link :)

If am not mistaken the reason reason for this is as follows:

context.withCommand(child.getCommand());

// (1)
if (reader.canRead(child.getRedirect() == null ? 2 : 1)) {
    reader.skip();
    if (child.getRedirect() != null) {
        final CommandContextBuilder<S> childContext = new CommandContextBuilder<>(this, source, child.getRedirect(), reader.getCursor());

        // (2)
        final ParseResults<S> parse = parseNodes(child.getRedirect(), reader, childContext);
        context.withChild(parse.getContext());
        return new ParseResults<>(context, parse.getReader(), parse.getExceptions());
    } else {
        final ParseResults<S> parse = parseNodes(child, reader, context);
        if (potentials == null) {
            potentials = new ArrayList<>(1);
        }
        potentials.add(parse);
    }

Line (1) prevents the command from being parsed correctly, as the input ends after the literal of the redirected command. This is also the line that makes me think it might be deliberate.

You'd need to change that 1 to 0 in the first if and special case the case when the reader can not read anymore. Additionally you need to take care that the child context is correctly set, so that redirect modifiers work.

Further remarks

Line (1) could also hint at somebody just wanting to require an argument separator behind a redirected command and allow it to have no arguments, but that has a few flaws:

  • It doesn't work if there just is no space at the end, which is likely the norm
  • It doesn't work. It will recursively try to parse the rest and find nothing (as the reader is now at the end thanks to the skip call), which means that the recursive parseNodes call (2) fails. This leads to the current context having the wrong node, and the child context having no nodes.
    This context is then passed on to execute, which does the following:
    1. Finds that contexts is not empty and enters the loop
    2. Reads the first (and only) context and finds that the child is not null
    3. Enters the if(child != null)
    4. Checks if(child.hasNodes()), which will be false, as outlined above - it didn't match anything in the recursive call
    5. This leads to the whole portion reassigning next to be skipped, commandFound is not set and the outer while loop exists, as contexts is set to next which means to null.
    6. The last if realizes no command was found and throws an exception.

Closing remarks

I think that wall of text can be summed up with a few lines:

  1. Is redirecting to commands without any arguments deliberately forbidden?
  2. Why does the if check if the command is followed by only one character if it has a modifier, when that does not seem to change the outcome?
  3. Some stuff about why it happens and a way to likely make it work.

Thank you for reading up until here :)

@Gamebuster19901
Copy link

Gamebuster19901 commented Jun 16, 2020

I had the same issue. I was able to get around it by doing the following:

public static void register(CommandDispatcher<MessageContext> dispatcher) {
	LiteralArgumentBuilder<MessageContext> builder = Commands.literal("!online").executes((command) -> {
		return sendResponse(command.getSource());
	});
	
	dispatcher.register(builder);
	dispatcher.register(Commands.literal("!o").executes(builder.getCommand()));
}

https://github.com/Gamebuster19901/ExciteBot/compare/e83499560e289a4fab6b965546225359ba562285..1656342766c2566a26ffcd5adb8b0cbea26a847c

@hugmanrique
Copy link

hugmanrique commented Jul 21, 2020

See https://github.com/VelocityPowered/Velocity/blob/8abc9c80a69158ebae0121fda78b55c865c0abad/proxy/src/main/java/com/velocitypowered/proxy/util/BrigadierUtils.java#L38 for a brute-force solution. Gamebuster's solution is incomplete and only works for nodes with no children. Note that this is not a proper fix.

@5HT2
Copy link

5HT2 commented Sep 18, 2020

Still no official solution on this? I'd love it if I could just register multiple literals for LiteralArgumentBuilder

jpenilla added a commit to Incendo/cloud that referenced this issue Feb 7, 2021
hugmanrique added a commit to hugmanrique/brigadier that referenced this issue Mar 4, 2021
Makes parseNodes consider the Command of a redirect target when no
contents can be read. For example, given a "foo" literal with a non-null
Command, and a "bar" literal forwarding to the "foo" node, the parse
results' context contains the command of the "foo" node, and the parsed node
is the "bar" node.

This follows the same logic as a regular redirect.
CommandDispatcherTest#testExecuteRedirectedMultipleTimes highlights the
parse tree contains the redirected node (`redirectNode`), while the
executed Command is that of the target node (`concreteNode`).

Fixes Mojang#46
hugmanrique added a commit to hugmanrique/brigadier that referenced this issue Mar 4, 2021
Makes parseNodes consider the Command of a redirect target when no
contents can be read. For example, given a "foo" literal with a non-null
Command, and a "bar" literal forwarding to the "foo" node, the parse
results' context contains the command of the "foo" node, and the parsed node
is the "bar" node.

This follows the same logic as a regular redirect.
`CommandDispatcherTest#testExecuteRedirectedMultipleTimes` highlights
the parse tree contains the redirected node (`redirectNode`), while
the executed Command is that of the target node (`concreteNode`).

Fixes Mojang#46
jpenilla added a commit to Incendo/cloud that referenced this issue Mar 20, 2021
broccolai pushed a commit to broccolai/cloud that referenced this issue Mar 29, 2021
kashike pushed a commit to PaperMC/velocity-brigadier that referenced this issue May 13, 2021
Makes parseNodes consider the Command of a redirect target when no
contents can be read. For example, given a "foo" literal with a non-null
Command, and a "bar" literal forwarding to the "foo" node, the parse
results' context contains the command of the "foo" node, and the parsed node
is the "bar" node.

This follows the same logic as a regular redirect.
`CommandDispatcherTest#testExecuteRedirectedMultipleTimes` highlights
the parse tree contains the redirected node (`redirectNode`), while
the executed Command is that of the target node (`concreteNode`).

Fixes Mojang#46
jpenilla added a commit to Incendo/cloud that referenced this issue Jun 27, 2021
jpenilla added a commit to Incendo/cloud that referenced this issue Jul 5, 2021
jakobkmar added a commit to SilkMC/silk that referenced this issue Jun 24, 2022
BomBardyGamer added a commit to KryptonMC/Krypton that referenced this issue Feb 7, 2023
@xpple
Copy link

xpple commented Mar 15, 2023

Could this be fixed?

nea89o added a commit to nea89o/NotEnoughUpdates that referenced this issue Apr 14, 2023
@polvallverdu
Copy link

still happening. bump

@xknat
Copy link

xknat commented Jun 14, 2023

mattco98 added a commit to mattco98/ctjs that referenced this issue Jul 16, 2023
Consider the following situation:

    val node = dispatcher.register(literal("foo")
    	.then(argument("arg", StringArgumentType.string())
    		.executes {
    			ChatLib.chat("foo with arg")
    			1
    		})
    	.executes {
    		ChatLib.chat("foo without arg")
    		1
    	})

    dispatcher.register(literal("bar").redirect(node))

With this setup, "/bar test" will work as expected, however "/bar" will
fail! Redirect nodes currently do not consider the command of the node
they redirect to, only their children.

As the comment is this Mixin indicates, this is more of a band-aid fix
than a proper one since any redirect modifiers used in the redirect()
call will be ignored if there are no arguments following the
redirection. This isn't so important for the purposes of the CT-provided
API, but may affect commands from other mods. We'll have to keep an eye
on this and address it if it becomes a problem.

Relevant issue: Mojang/brigadier#46
jpenilla added a commit to Incendo/cloud-minecraft-modded that referenced this issue Jan 14, 2024
jpenilla added a commit to Incendo/cloud-minecraft that referenced this issue Jan 14, 2024
@xNoobyyy
Copy link

xNoobyyy commented Apr 7, 2024

would be nice to have that fixed or have someone comment if this is intentional behaviour

@sakurawald
Copy link

Maybe try this solution: #137

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants