Command API

Since it seems it has been somewhat suggested that I do the command API, here’s what I got.

First off, I have Intake, which is a command processing library I spun off from WorldEdit 6. It’s usable, but it’s not finished by any means. The original pull request to pull it into WorldEdit is over a year old, never went through, and eventually was salvaged for a newer branch.

That said, it supports annotations, but usage of it is optional. An example of annotation usage is:

@Command(aliases = "age", desc = "Set age")
@Require("example.age")
public void setAge(Player player, @Optional @Default("20") int age) {
    player.setAge(age);
    player.message("Your age was set to: " + age);
}

Intake can automatically inject a Player rather than, for example from Bukkit, a CommandSender based on the type of parameter. It can also inject your own type of objects.

However, while that annotation code is a substantial part of Intake, you can still implement a command as a class:

class MyCommand implements CommandCallable {
    void call(String arguments, CommandContext locals, String[] parentCommands) {
        // do stuff
    }
    
    // ...
}

There are some “design designs” that had to be made at the time but could be up to discussion.

  1. Intake does not pass specifically the “command sender” as a special parameter. Rather, it has a “context” object the caller of the command can store several objects. A command would then, for example, get a sender using context.get(Sender.class). The downside is that you do not have compile-time safety really, because there’s no guarantee that the context object contains, for example, the sender. The upside, however, is that you can store a bunch of different things on the context object. What’s everyone opinion on this?
  2. It is not possible to know the name of parameters easily using the annotation method unless I add a @Named("age") annotation. The first problem is, prior to Java 8, Java did not store the names of parameters in compiled .class files. However, a workaround is to make use of the Paranamer library. While that works, parameter names aren’t great for documentation purposes, and localization is an issue. I’m really not sure what to do about this problem.

There are more of such decisions to make, but they will come to me at a later time. Ultimately, I’m not sure whether I plan to bundle in the annotation part, especially if I can’t resolve those problems.

10 Likes

I think if people want to use annotations they can create their own system or use a library.
For simple plugins to just add a command or two it’s nice to have but there are quite a lot limitations by using the annotations compared to using your own system.
And I think the majority would rather have more control over it.
I’d say only do annotations for the command and aliases and maybe description.
But leave handling the arguments and permissions to developers.

1 Like

hes right, i dont really like this annotation system :x

3 Likes

What would be the difference in just having the full version of Intake and allowing class based commands as well? @sk89q already stated both would be options, you could still implement your own system on top of it could you not? Some people may still see this as a valuable tool, and may prefer it over creating their own system. So if it is optional, personally I would rather see all of the features or none, not just a watered down version.

I am currently leaning towards not including the annotation stuff because it’s not really finished and I probably won’t get time to “finish” them.

Rather the focus will be that the command API is sufficiently flexible, at least considerably more so than Bukkit’s.

2 Likes

I like the idea of registering annotated commands. Being able to configure aliases, description, required perms, etc. in annotation is convenient. Could you perhaps remove the object injection and leave the rest?

3 Likes

There are atm many pull requests/ideas for commands:

My suggestion list:

  • Player vs CommandSender (not all commands are for console)
  • Annotation System for
    • Aliases
    • Permissions
    • min Arguments
    • CommandContextObject instead of a String[]
    • Description/help/usage

Basic stuff from you “old” Command Library.

PR’s:

Ideas:

Alternatives (for ideas):
http://forums.bukkit.org/threads/lib-annotation-based-command-system.195554/
http://forums.bukkit.org/threads/util-simple-command-library.145182/

The object injection is optional.

The old command framework in WorldEdit didn’t support parameter injection (all methods took sender, arguments), but I was able to slap the new framework onto the old code with minimal changes.

Remember that “sender” and “arguments” are also objects that can be injected.

I’m not a fan of those PRs, either due to

  • Verbose naming (getCommandUsage())
  • API design issues
    • Notably, why is CommandManager not a Command itself? Then you can easily have sub-commands.
    • Not a fan that arguments is a String[] personally.
1 Like

I like this library.

Does the optional and default argument stuff work without annotations? Is everything the annotations do available in the normal API? Not saying I don’t like annotations, I think the extensive use of those in general for stuff that is registered in some way is great. Just because you said you’d probably not include them, I like the argument injection though.

Intake has:

  • Basic command manager / interfaces
  • Annotation command builder

The latter depends on the former, but it doesn’t go the other way. Without annotation / parameter injection, then all you get is an argument string and a context object.

If you parse the arguments yourself, you have to figure out optional parameters, default parameters, etc. yourself.

I would vote to keep the annotation stuff then. As it is optional I see no problem including it.
If it’s not finished, that’s another problem of course.

1 Like

I post them only to close the circle.
You are right, I forgot subcommands.
Maybe we provide a multi-level command system:

  1. old Bukkit like command-system with, Sender and String[], plugin must register all commands
  2. command registration over class and annotations, wrap a CommandContext around and possiblity for subcommands, playeronly commands, …
  3. Intake

Start with 1 and build other systems on top of it?

The CommandCallable class offers exactly that.

Not sure why you list Intake separately here because as far as I can tell, Intake does exactly what you described in points 1 and 2.
I also don’t see why you would like to have a Bukkit style command API. This thing was so lacking and I’d rather have all plugins use as many features as possible of this Intake lib because it will make things more consistent. Plugins still have the option to implement their own command system if they like though.

2 Likes

second level use the CommandContext wrapper for arguments, flags, …
Intake uses a annotation system to pass variables direct into the method params.

My wish list / requirements for the new command api:

  • Easy to use

  • Commands can be disabled / removed / registered / overwritten while the server is running

  • Changeable command prefix per command. (default: “/”, but maybe I want to use “!”)

  • Possibility of localized description and help, maybe by adding a localization handler?

    @Command( desc = “myplugin.command.test.desc” )
    @LocalizationHandler( handler = myStaticHandler )
    public void …

  • Commands only visible for console or only for players.

The permission requirement @Require("example.age") is a little bit confusing.
At first I didn’t knew what this meant, maybe changing to @Permission("example.age") would be better.

The library Intake looks good.
I never used it so i’m wondering what its performance is like?

3 Likes

Intake doesn’t “use” an annotation system.

The annotation system, as mentioned previously, is a separate part of Intake. You can even mix annotated commands next to non-annotated commands.

Just wanted to show off you a system I’ve made in the past.

A simple command would look like this:

@Command(identifier = "test")
@Children(children = {
        TestCommand.SubCommand.class,
})
public class TestCommand {

    @Command(identifier = "sub")
    public static class SubCommand implements Executor<Sender> {

        @Argument(name = "name", description = "The name")
        String name;

        @Option(name = "option")
        int option = Default.DEFAULT;

        @Override
        public void execute(CommandContext<Sender> ctx, Sender sender) {
            sender.send("Hello!");
        }
    }
}

But, It’s absolutly not reflection dependent. All reflection stuff is in an extra package unter /reflect. So you could use it like the old bukkit system, even if it’s not recommented. Also I tried to adopt the default unix command line style with optional parameters and required arguments.

Example:

/test sub --option 5

More examples with custom formatting.

I’ve also implemented some parsers to support integers, booleans or any other data type.
Another neat feature allows you to create very simple tables.

There’s still some work which has to be done, like a better code coverage and ofc I would move it to an extra library.

Even if you do write your own bukkit-like system, please leave us the option to implement our own command system. It should be very easy to give us a generic entry point with a sender and a string.

EDIT: Also need to mention that it is completly thread safe and allows you to handle commands async and sync.

Annotations for commands = <3
Command class = no

I’d suggest implementing both though, due to the fact that most people are used to doing it “the old way” and don’t want to switch. Annotations would be a cleaner way of doing things.

While it is very intriguing to have an annotation command API in Sponge, I think you also need to consider if it is the right way to go for Sponge. It all depends on what the ultimate goal of Sponge is, is more of an abstraction layer upon Minecraft or an framework for developers to develop in. This project will of course be of the both types, but where will the focus be?

The question is, should Sponge have optional APIs that is not abstraction-layers/entry-points that developers can use if they want?

If the answer is yes, I think it is a feature that should be implemented later. Since this project is in its infancy still I think it is good to keep the scope small and focus on getting a working non-conflicting, robust and dynamic entry-point system that you can then use to build this annotation system upon.

However, I really like the CommandContext class. It feels like bukkit CommandExecutor “simplé” but with an included powerful parser. That will dramatically reduce “parse code” on its own.

One question I have though is, how will we register commands? Will it be something similar to Bukkit’s plugin.yml? How will it handle conflicting commands when two plugins register the same command? If this is already sorted out though I would love to hear it.