Proper Way to Create sub-commands and their permissions?

Looking at Sponge and all of its structure, I can see that you guys are writing really robust software here (as well as forcing me to learn Java 8). Because of this, I am wondering if sponge has built in support for subcommands. Say I have a command that is ‘/example’. If I want to be able to do things like ‘/example version’ and ‘/example help’ and ‘/example test’. Is there a way to register different commands as subcommands as others or do I need to make my own handler and just have the subcommands be arguments? That seems do-able. The only problem is… how do I handle the permissions for such subcommands? Using the last example, how could I make it so that ‘/example version’ uses permission ‘example.version’ and ‘/example help’ uses ‘example.help’ and ‘/example test’ uses ‘example.test’. Also, can this be modular even further (‘example.test.foo’, ‘example.test.bar’)?

One more unrelated thing, I’m kinda new to sponge and I noticed that the SpongeVanilla and SpongeForge downloads are both supposedly on SpongeAPI 6.0.0 but there is no download for API 6. What’s up with that?

CommandSpec version = CommandSpec.builder()
    .executor(this::version)
    .permission("example.version")
    .build();
CommandSpec help = CommandSpec.builder()
    .executor(this::help)
    .permission("example.help")
    .build();
CommandSpec example = CommandSpec.builder()
    .executor(this::example)
    .child(version, "version")
    .child(help, "help")
    .build();

Note that for parent commands, you don’t even need an executor() if you don’t ever intend for it to be run by itself.

As for the API, if you’re using a build system you shouldn’t need an API download. The current stable release, however, is API 5, which most plugins are for.

Yeah, I’m using gradle, I was just looking at the downloads page. Also, do I only register the parent command or do I register the children separately as well?

Also what is the usage of the whole this::method ?
When did java become C++

Yes, only register the parent command. The CommandManager is effectively a command itself, so you’re registering your main command to its “parent”.

this::method is, in Java 6 terms, an anonymous class Function/Supplier/Consumer whose sole purpose is to call the function you specify. So it’s (roughly) equivalent to:

CommandSpec.builder().executor(new Function<CommandSource, CommandContext, CommandResult>() {
    @Overrride
    public CommandResult apply(CommandSource source, CommandContext context) {
        this.example(source, context);
    }
})

When the CommandSpec wants to execute the command, instead of calling a function you’ve overriden in an extended class, it calls that function with the provided values instead. It’s a lot cleaner IMO than have a class for each command, as you could have one method per command instead.

Since you’re new to java 8, check this out
https://leanpub.com/whatsnewinjava8/read

More information can be found on the SpongeDocs.

So I’ve done a bit of research on java and I understand that using this::help (for example) would reference a methd “help” that I have defined in the current class. I just don’t understand how this would work practically. Doesn’t the thing specified in executor() have to be a CommandExecutor? if so, wouldn’t the method “help” have to be a constructor for a CommandExecutor object? Also, you said that the “this::help” was roughly equivalent to that other bit of code, but they don’t seem at all the same to me. From what I know about java, the best way I can think to implement this is to have a separate class for each CommandExecutor, but clearly there is something i’m missing here. Can anyone help me figure this out?

I oversimplified a bit.

CommandExecutor is a class with only one abstract method. This, by magic of the Java compiler, effectively makes it an implementation of Function.

So when you compile the code, the Java compiler knows by the :: syntax that what you actually want to do is call your function as if it were CommandResult execute(CommandSource, CommandArgs) on a CommandExecutor object.

One big advantage with this is that the function still has access to the private/protected fields that it’s parent class has. So you can “create” multiple executors while all sharing private-level access to the same objects.

Ah, okay that makes a bit more sense. I know this isn’t really related to sponge itself, but you’re pretty good at explaining things, so one more question: This isn’t a lambda expression, right? Those are the ones that have () -> {} stuff, if I’m correct. I’m just curious, whats the difference between using a lambda expression and say an anonymous method?

Lambdas are to methods what anonymous classes are to concrete classes. Rather than defining a method on a class and passing that, you define just the function and the compiler does the rest to create a wrapper object for it.

The :: by comparison references an already existing function, so is similar but not the same.

And the difference between a lambda and an anonymous class (I assume you meant that) is… nothing. They will do the same thing semantically, it’s just that (foo) -> foo.bar().baz() is a lot more visually appealing. It’s a matter of style - if you absolutely hated Java 8 you could keep using anonymous functions to your hearts content.

1 Like

The point of it is the very definition of functional interfaces. Though the @FunctionalInterface annotation just compile-checks that it’s an interface with one abstract method, that’s not quite true. For example: Take the Sponge interface Subject. An instance of Subject exists for the instance; you’re checking that it has this additional functionality, that permissions are applicable to it. However, the functional interface CommandExecutor doesn’t exist for its instance; it only exists for the single function. The class / interface exists solely to hold this function; it can be completely stateless (and should). So when you’re making a new anonymous class, you’re saying that you’re concerned with the class, its instance, and its state. When you’re doing a lambda or a method reference, you’re only defining the method of the class, emphasizing that it’s all about the method.

Method references and I believe lambdas also use a special bytecode for their methods that is faster than an anonymous class. Definitely method references in particular; they’re super lightweight. So for instance if you’re going over a huge number of Optionals, and you’re using the orElse method which does something expensive like database lookup or memory allocating like constructing, it’s much better to use a reference or lambda in order to make it only do the operation when it has to.

1 Like

Using this example i registering my subcommands and perms, but i have a question about subcommands with two or more argumments.

As example, i need to register a command "/chat ignore (player)

I registered with this commandSpec:

CommandSpec ignorePlayer = CommandSpec.builder()
				.arguments(GenericArguments.string(Text.of("ignore")), GenericArguments.player(Text.of("player")))
				.description(Text.of("Ignore a player."))
				.permission("uchat.ignore.player")
				.executor((src,args) -> {{
					if (src instanceof Player){
						Player p = (Player) src;
						//uchat ignore player
						if (args.getOne("ignore").isPresent() && args.<String>getOne("ignore").get().equals("ignore") && args.<Player>getOne("player").isPresent()){
							Player play = args.<Player>getOne("player").get();
			    			if (UCMessages.isIgnoringPlayers(p.getName(), play.getName())){
								UCMessages.unIgnorePlayer(p.getName(), play.getName());
								p.sendMessage(UCUtil.toText(UCLang.get("player.unignoring").replace("{player}", play.getName())));
							} else {
								UCMessages.ignorePlayer(p.getName(), play.getName());
								p.sendMessage(UCUtil.toText(UCLang.get("player.ignoring").replace("{player}", play.getName())));
							}
						}
					}					
			    	return CommandResult.success();
				}}).build();

How i register the alias here?

CommandSpec uchat = CommandSpec.builder()
    .executor(this::example)
    .child(ignorePlayer , "ignore") //I use only "ignore" on alias?
    .child(ignoreChannel , "ignore") //I need to register other ignore, its the same way, same alias?
    .build();

*Note: I porting a plugin from bukkit and cant test now because is not ready.

You might want to opt for making a single child command, ignore, that handles ignorePlayer and ignoreChannel together. I believe registering the second “ignore” overrides the first, which is not what you want. You could also opt to use a child with it’s own children: for example, register an ignore child with alias “ignore”, then register ignorePlayer and ignoreChannel with “player” and “channel” aliases, respectively. That way, you could type /chat ignore player [player] or /chat ignore channel [channel] and it would work. It’s not as nice as having /chat ignore [player|channel], but it gets the job done.

As well as that, I noticed that you have “ignore” as an alias and “ignore” as an argument; the argument is unnecessary, as it would make the command look like this: /chat ignore [ignore] [player], where [ignore] can be anything and [player] is a player. Instead, you should just have the player argument.

Thanks, this is a good way ^^ After is done i will test.

.child(CommandSpec.builder()
			    		.child(ignorePlayer, "player")
			    		.child(ignoreChannel, "channel")
			    		.build(), "ignore")

Hi, i back xD

I added the child command like you said and the argumment only work if the player has the permission like spected and the plugin is working, but the command only work if i repeat the argumment:

Command reload as example:

/uchat reload reload 

I use like this:

        CommandSpec reload = CommandSpec.builder()
				.arguments(GenericArguments.string(Text.of("reload")))
				.description(Text.of("Command to reload uchat."))
				.permission("uchat.cmd.reload")
				.executor((src,args) -> {{
					//uchat reload
			    	if (args.<String>getOne("reload").get().equals("reload")){
						 try {
							UChat.get().reload();
							src.sendMessage(UCLang.getText("plugin.reloaded"));
						} catch (Exception e) {
							e.printStackTrace();
						}						
			    	}
			    	return CommandResult.success();
				}}).build();

        //uchat <args...>
		CommandSpec uchat = CommandSpec.builder()
			    .description(Text.of("Main command for uchat."))
			    .executor((src, args) -> { {	    	
			    	//no args
			    	src.sendMessage(UCUtil.toText("&b---------------- " + UChat.plugin.getName() + " ----------------"));
			    	src.sendMessage(UCUtil.toText("&bDeveloped by &6" + UChat.plugin.getAuthors().get(0) + "."));
			    	src.sendMessage(UCUtil.toText("&bFor more information about the commands, type [&6/uchat ? &b]."));
			    	src.sendMessage(UCUtil.toText("&b---------------------------------------------------"));			         
			    	return CommandResult.success();	
			    }})
			    .child(reload, "reload")
			    .build();

How to do not need to repeat the command reload on /uchat reload reload?

Thanks.

The “reload” child command takes no arguments. Remove this

1 Like

Thanks, fixed ^^