Suggestion for Argument Parsing

So i’ve found Sponge’s way of handling arguments a lot different then Bukkit’s way which im used to. So i have my Plugin MultiWorld. Main Plugin command /mw without arguments just returns Version and other miscellaneous info. But otherwise is structured like this

/mw <create|delete|modify|tp|join>

now each sub command has its own arguments also. I wish there was a way on the main command spec to disable sponge argument handling like

CommandSpec commandSpec = CommandSpec.builder()
    .permission(...)
    .description(...)
    .disableSpongeHandling(true)
    .executor(...)
    .build()

even though it has its own implemented way of handling arguments its a little out of the ordinary and developers should be able to choose. But i digress from what my real question is. Im trying which way to handle the arguments for my plugin. The command structure is laid out as such…

<> are required arguments
[] are optional arguments

/mw create <name> [type] [seed]
/mw delete <name>
/mw modify <type> <value>
/mw tp <world> <x> <y> <z>
/mw join <world>

Im currently using this option

// /mw create <name> [type] [seed] Command
CommandSpec create = CommandSpec.builder()
        .description(Texts.of("Create a world"))
        .executor(new Command())
        .arguments(GenericArguments.seq(
                GenericArguments.string(Texts.of("name")),
                GenericArguments.dimension(Texts.of("type"), game),
                GenericArguments.integer(Texts.of("seed"))
        ))
        .build();
// /mw delete <name> command
CommandSpec delete = CommandSpec.builder()
        .description(Texts.of("Delete a world"))
        .executor(new Command())
        .arguments(GenericArguments.string(Texts.of("name")))
        .build();
// /mw help command
CommandSpec help = CommandSpec.builder()
        .description(Texts.of("MultiWorld help Command"))
        .executor(new Command())
        .build();
// /mw modify <type> <value> command
Map<String, Boolean> choices = new HashMap<String, Boolean>();
CommandSpec modify = CommandSpec.builder()
        .description(Texts.of("Modify your current world"))
        .executor(new Command())
        .arguments(GenericArguments.seq(GenericArguments.string(Texts.of("type")), GenericArguments.choices(Texts.of("value"), choices)))
        .build();
// /mw version command
CommandSpec version = CommandSpec.builder()
        .description(Texts.of("MultiWorld version"))
        .executor(new Command())
        .build();
// /tp <world> <x> <y> <z> command.
CommandSpec tp = CommandSpec.builder()
        .description(Texts.of("Teleport to a world at an exact location"))
        .executor(new Command())
        .arguments(GenericArguments.seq(GenericArguments.world(Texts.of("world"), game), GenericArguments.integer(Texts.of("x")), GenericArguments.integer(Texts.of("y")), GenericArguments.integer(Texts.of("z"))))
        .build();
// /join <world> command
CommandSpec join = CommandSpec.builder()
        .description(Texts.of("Join a world at its spawn."))
        .executor(new Command())
        .arguments(GenericArguments.world(Texts.of("world"), game))
        .build();

CommandSpec mwCommandSpec = CommandSpec.builder()
        .description(Texts.of("MultiWorld Command"))
        .child(create, "create")
        .child(delete, "delete")
        .child(modify, "modify")
        .child(version, "version")
        .child(tp, "tp")
        .child(join, "join")
        .child(help, "help")
        .executor(new Command())
        .build();

But the way its handling it now is not how i want it to and it doesnt handle well. In the executor i have set it to handle the arguments i just need a way to make the command ignore Sponge’s handling.

Disable the handling? What do you mean by that though? If you want to handle arguments the way Bukkit did, you may do so as far as I know.

No rule says you need to parse it all in the definition…
You could still do separate code for each primary argument, and just stop right there in these definitions, then do your own handling for the full set of arguments in each executor - just think of the executor being the "if arg[0]==“create” block of code , only the /mw create entries would go into it. Then do the argument handling however you wish still there. Doesn’t have to be all done up-front in the comspec setup (but you will have to parse it yourself in the executor)

Honestly I would like to know more about the issues you have with the current GenericArguments / command definition builder style, it seems a LOT more readable then the args[1] crap that you used to have to do with bukkit.

The only thing that I wish SpongeAPI had was exposing some of it’s abstract CommandElements to make it easier to implement your own.

@intronate67

If you want to process commands like Bukkit, use the Low-Level Command API

1 Like

I think I worked out what you were trying to ask, was your problem with the optional values not being optional? You need to wrap them in GenericArguments.optional() and you can optionally provide a default value.

import static org.spongepowered.api.text.Texts.of;
import static org.spongepowered.api.util.command.args.GenericArguments.optional;
import static org.spongepowered.api.util.command.args.GenericArguments.dimension;
import static org.spongepowered.api.util.command.args.GenericArguments.string;
        //..
        // Snip
        //..
        CommandSpec.builder()
                .description(
                        of("Create a World")
                )
                .arguments(
                        string(of("worldname")),
                        optional(dimension(of("type", game)), DimensionTypes.OVERWORLD),
                        optional(string(of("Seed")))
                )
                .executor(
                        new CommandExecutor() {/** snip **/}
                )
                .permission("testPermission")
                .build();

All the static imports xD

It’s exactly what they are useful for, and it’s better to import only the ones you use.

1 Like

Right there. But I see people getting mad at those :stuck_out_tongue: . Although imo extremely generic names such as SomethinClass#of(Blah blah) don’t do well when statically imported because they’re just extremely generic. Anyway, wasn’t planning on derailing this :smile:

I absolutely agree, but if your classes are short and have single responsibility then it should be fairly easy to reason where Text.of is coming from. The genericArgument imports just make sense in the given context though.

I’d also argue that anyone familiar with Sponge Commands would recognize it fairly quickly if they were familiar with the static import feature.

@Flibio & @ryantheleach Both of your ides are basically what im going for, thanks for the help :smile:

1 Like