[SOLVED] How to correctly use CommandSpec or implement CommandCallable

Good Afternoon All,

I’m currently working on creating my first sponge plugin and I’m having difficulty getting commands via CommandSpec implemented correctly.

Essentialy it boils down to me having two separate issues, so I’m hoping someone could advise on both.

Issue 1: Unable to use Player as an argument because it crashes out when a Java.lang.NullPointerException when attempting to call the command.

When executing the command, the following error is observed in the Console:

[14:35:41] [Server thread/ERROR] [Sponge]: Error occurred while executing command 'permissionmanager grant plugin.test foleyl2012' for source EntityPlayerMP['foleyl2012'/426, l='world', x=89.48, y=68.00, z=243.70]: null
java.lang.NullPointerException 
    at org.spongepowered.api.util.command.args.GenericArguments$PlayerCommandElement.getChoices(GenericArguments.java:920) ~[GenericArguments$PlayerCommandElement.class:1.8-1499-2.1DEV-621+spongeproject-ci-b621.git-9c30171977fff8a08188f76c1489a0d8d15bbc47]
    at org.spongepowered.api.util.command.args.PatternMatchingCommandElement.parseValue(PatternMatchingCommandElement.java:57) ~[PatternMatchingCommandElement.class:1.8-1499-2.1DEV-621+spongeproject-ci-b621.git-9c30171977fff8a08188f76c1489a0d8d15bbc47]

The commandspecs are implemented as follows:

CommandSpec permissionManagerGrantSpec = CommandSpec.builder()
        .description(Texts.of("Grant a permission to a user"))
        .permission("permissionmanager.control.grant")
        .arguments(
                GenericArguments.string(Texts.of("message")),
                GenericArguments.onlyOne(GenericArguments.player(Texts.of("player"), this.game)))
        .executor(new permissionManagerGrant())
        .build();

CommandSpec permissionManagerSpec = CommandSpec.builder()
        .description(Texts.of("Permission Manager control interface"))
        .permission("permissionmanager.control")
        .child(permissionManagerGrantSpec, "grant")
        .build();

The registration of the commands are done as below:

this.game.getCommandDispatcher().register(this, permissionManagerSpec, "permissionmanager", "permman", "pm");

Could anyone advise me why this may be happening? (I’ve tried running on a Linux and Windows build, running the latest build of SpongeCoremod.

As for the second issue that I have, it’s actually to do with the usage output of the commands.

In the Chat Window if I run the following

/permissionmanager

I can output saying “Not enough arguments. Usage: /permissionmanager grant|revoke” which is what I would expect to happen because there’s no commandexecutor assigned to the main command

If however I run the following in the Chat Windows

/permissionmanager grant

I receive output saying “Not enough arguments. grant. Usage /permissionmanager grant|revoke”, however I would expect to see a usage statement similar to the following:

Usage: /permissionmanager grant <message> <player>

I’ve tried countless ways of trying to get the usage displayed correctly but I just cannot figure this one out. I’ve even tried creating commands using the Low-Level API by implementing CommandCallable and adjusting the output from getUsage() but all I seem to do with this approach is to get usage displayed twice (once without any parameters and one with custom output by throwing ArgumentParseException)

Can someone please help?

Many Thanks,
Luke

2 Likes

On the first one: Please make sure this.game is not null. It appears to be, which then causes the exception.

Thanks for coming back to me. My original thought was that this may be the case, however I set this before registering the commands like below from within the GameServerStartedEvent.

this.game = event.getGame ();

Just to double check, I also run the following after setting this.game

log.info ((this.game == null).toString ())

This outputs false to the console, so all is looking okay with this variable.

I’m confused at why you are getting a NullPointerException, I suspect it’s a problem in sponge that should be fixed, it should probably be continuing to process and provide you will an Optional.absent instead.

I suspect the cause of the error however, is that GenericArguments.string is eagerly matching the rest of the command, so you are getting /(permissionManager)(grant)(plugin.test foleyl2012)(null) (command)(subcommand)(string)(player)

If the documentation is correct https://docs.spongepowered.org/en/plugin/commands/arguments.html?highlight=command I would have only expected this behaviour from remainingJoinedStrings and instead expected
Sponge to handle this somewhat more gracefully and either understand that you want the last argument to be a player, or at the very least have a better exception message.

On a different note the formatting in your code is off-putting and conflicts with most Java style guidelines, FirstLetterCapitals should be reserved for class names, and variable identifiers generally use secondLetterCapitals instead. The only reason I bring this up is it can make understanding your code difficult to people used to the other patterns.

As a trial I have removed the GenericArguments.string from the Arguments list and have instead solely gone with one Argument of GenericArguments.onlyOne(Generic Arguments.Player) and the same issue occurs.

With regards to the coding style, apologies for any confusion. I come from a C# background and have always used the convention as I did above, but I will try and use correct capitalisation going forward.

Evening All,

Now I’m finished work for the day, I’ve had a chance to really read through the comments made in your replys and analyse the code I’ve written. @Saladoc, you were indeed correct, it was this.game that was null.

I’d made a mistake in my code, and although checking this.game was not null before registering my Commands, I’d create the CommandSpecs inside the class itself which must have been initialized before the GameServerStartedEvent was fired. I’ve now moved these CommandSpecs into the method where this.game is assigned and all is now working, so that’s issue 1 complete.

With regards to my second issue, the Usage statements not displaying correctly for child commands, can anyone help?

2 Likes

Just as a helpful tip, you can always inject Game, so you won’t have to ever deal with those kinds of issues. You can read more here.

1 Like

Thanks very much. Using dependency injection looks to be an easier approach :smile: