Child Commands Not Passing All Arguments?

Hello all,

I’m really banging my head against the wall on this one. I’ve followed the tutorials on the Sponge docs and I believe I have this set up correctly. For the record when I execute the command

/helpme view 3

It works as expected and the single argument is passed. But when I execute:

/helpme message 3 Adding to the message

I always get the following error:

Error occurred while executing command: No value present

Below is my command registration and the executor function associated with the child command in question. According to the stack trace in console, the error happens in the first line of the executor function trying to get the ticketnum. Any help is appreciated!

CommandSpec helpMeCommandAddMessage = CommandSpec.builder()
        .description(Text.of("HelpMe Advanced. Add another message to the ticket"))
        .permission("helpme.command.message")
        .arguments(GenericArguments.onlyOne(GenericArguments.string(Text.of("ticketnum"))))
        .arguments(GenericArguments.remainingJoinedStrings(Text.of("message")))
        .executor(helpMeCommand.AddMessageCommand)
        .build();

CommandSpec helpMeBaseCommand = CommandSpec.builder()
        .description(Text.of("HelpMe Advanced Command"))
        .permission("helpme.command.use")
        .child(helpMeAdminViewCommand, "view")
        .child(helpMeCommandAddMessage, "message")
        .arguments(GenericArguments.remainingJoinedStrings(Text.of("message")))
        .executor(helpMeCommand.BaseCommand)
        .build();

        Sponge.getCommandManager().register(this, helpMeBaseCommand, "helpme");

Executor:

public CommandExecutor AddMessageCommand = new CommandExecutor(){
    
        @Override
        public CommandResult execute(CommandSource src, CommandContext args) throws CommandException {
            String ticketNumber = args.<String>getOne("ticketnum").get();
            String message = args.<String>getOne("message").get();
            _dbHelper.createMessage(Integer.parseInt(ticketNumber), message);
            Text returnMessage = Text.builder("===Ticket:" + ticketNumber + " Updated!===").color(TextColors.YELLOW).build();
            src.sendMessage(returnMessage);
            return CommandResult.success();
        }
    };

You’re making two calls to .arguments(), the latter of which is replacing the first. This causes there to be no argument named ticketnum, and thus you get a NoSuchElementException from calling Optional.get().

Now, let’s take a step back here and talk about a main piece of the Command API. Sponge is one of the few platforms that have a proper argument parsing system built in, and I highly recommend you make use of it. If you have an argument that you expect to be a number, you should use GenericArguments#integer instead - this will require the argument to be a valid integer parse it into an Integer object for you.

There are a couple other ways you should be looking to improve your code, but I’ll note two of them. First, make sure you’re accounting for possible exceptions in your code - if someone enters an argument for ticketnum that isn’t an integer, Integer#parseInt is going to throw a NumberFormatException. If you use GenericArguments#integer, this is handled by Sponge for you.

Second, I don’t see anything that identifies whether a ticket exists or not. You may have cut this out for simplicities sake, but just a friendly reminder that you should always verify user input (both in object type and validity) and provide the appropriate responses. If a ticket with id -1768 doesn’t exist, the user shouldn’t receive a message telling them it was updated.

As a final note on messages, you don’t have to use Text#builder all the time - Text.of(TextColors.YELLOW, "===Ticked:", ticketNumber, " Updated!==="); will do the same (you can look into the TextAPI more if you’d like to understand why).

Good luck, and welcome to Sponge!

Wow thank you for writing such a detailed response. What you’re saying makes sense and I understand it. I’m really enjoying Sponge so far. Could you explain a bit more about how I would add multiple arguments properly then if subsequent calls to .arguments() cause overwriting. Or did I just misunderstand the plugin documentation?

.arguments() accepts a varargs (basically an array) of CommandElements. Instead of calling it multiple times, separate each element with a ,. You can see the example for that here.

As a bit of an extra note, this is because multiple arguments are combined into a SequenceElement by Sponge automatically, which is just a wrapper around multiple chained elements.

1 Like