Command API

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.

Totally agree with you @Ranzdo! It shouldn’t be the target of sponge to handle everything! Just a abstraction layer. Commands and even configs shouldn’t be a part of sponge. Probably a default recommendation for an implementation to use would be good, but there are too many individual developers out there to support everyone’s needs.

plugin.yml will not exists in Sponge:
https://github.com/SpongePowered/SpongeAPI/blob/master/src/main/example/main/java/org/spongepowered/example/ExamplePlugin.java

Read more here:

2 Likes

I hope not! With Bukkit I hate to predefine all commands, the most time it is a repetitive work.
And then you have to register them again in your plugin if you used CommandExecutor.

I would propose just to overwrite the old command and log a warning. Simple but effective.

I didn’t like plugin.yml and I am happy that this project will take another approach, but that approach needs to solve the same problems that the plugin.yml solved. The approach to override the command in a state of conflict and tell the user through the console does not supply also a solution to make the plugins stop conflicting.

One solution I can think of is to tell the user that two plugins use the same command when they try to use the command. Then they get asked to rename one of the plugins commands so they don’t conflict anymore. That way it will be clear to the Server Admin what is wrong and a easy solution to fix the problem.

Or if there is a conflict in commands, when you try to run the command the server will bring this to your attention
'There is a conflict with this command by the plugins [plugin1, plugin2… etc]
‘Please suffix your command with the plugin name’
‘E.g /plugin1:command’

Something along those lines maybe

2 Likes

I agree, there are too many individuals out there! (me too)
But if plugins import their own config parser library you have big plugin files, so a “default” config library would be nice.
This default would help the lazy developers or newcomers who just want to throw together a new plugin.
I would suggest for migration a yaml based default config library.


Not good. As I know my server owner, he executes commands on players in his plugins.
Maybe another solution would be to blacklist commands in the server config?

commands:
  blacklist:
    myPlugin: [send, test]

Back to the topic: Where will be the commands registered / replaced / removed?
@sk89q Would there be with Intake in Sponge one “master” dispatcher?
Or had you another design in mind?

I like the annotations more than the concept of passing a context object to methods.
Passing down a context object may risk the type safety.

I like the idea of annotations, just not how it’s implemented here.

It was already decided to use HOCON as a config format, since it’s much more forgiving than YAML. It also has a few nifty features.

I think you meant HOCON :slight_smile:

It is implemented already though. I would like it to be a complete command API on the first run, because else a plugin dev would have to do more work early, and probably have to rewrite later.

I don’t see why everyone is against more features in this project. Give them time and we’ll have something awesome :slight_smile:

you’re right :stuck_out_tongue:

I’m the one who proposed http://www.reddit.com/r/spongeproject/comments/2g2b22/command_api_idea_use_of_annotations_and_dynamic/
I love the annotations, so please include them. I would like to see 2 or 3 ways of adding commands:

//class, description, aliases
//class uses String[] args
plugin.registerCommand(commandClass, "Worldedit Help", "/help", "worldedit help")


//class with annotated methods, annotation defines description, aliases, permissions
plugin.registerCommands(annotatedCommandsClass)

and the annotation looks like this:


//This argument could also be of type OnlinePlayer or ConsoleSender, for player/console-only commands
@Command(aliases=["mail send", "ms"], permissions=["perm.a", "perm.b"], "desc")
public void myCommand(
    CommandSender sender, 
    @Arg(name="player") Player receipient, 
    @Wilcard @Arg(name="message") String[] message
)
{ 
//command logic...
}

My Code is based on this implementation: Home · Ranzdo/MethodCommand Wiki · GitHub
More examples can be found there…

A nice idea, especially when there are muliple command aliases (/cmd1, /cmd2) and you want to know which one the player used.
(Let’s say for paging commands, the help text would say “Page 1 of x. Use /cmd2 ” instead of “/cmd1 ”)

It’s always an issue, and i think it’s quite important. Maybe something like gettext is a good solution: Localization support suggestion

Yeah, bring me more features by all means! I’m all for new and beneficial things :slight_smile:

  1. We will be registering commands dynamically most likely. Convention > configuration
  2. Intake is extremely modular:
    1. CommandCallable is an interface that (essentially) takes callable.execute(sender, arguments).
    2. Dispatcher (an interface) is also a CommandCallable, but it lets you register commands on it, so callable.execute(sender, "addmember") would call an “addmember” subcommand.
    3. If you want a “command manager,” you just use a Dispatcher at the top.
    4. If you want subcommands, then you use a Dispatcher somewhere below (commandManager.register(dispatcher, "region", "rg")).
    5. The annotation code builds CommandCallables. You basically give ParametricBuilder your .class file and it will give you a List<CommandCallable>. manager.registerCommands(YourClass.class) does not exist, which means the annotation implementation is 100% separate.
    6. Some of you want to use annotated methods to register commands but don’t want parametric injection (i.e. @Command void myCommand(Sender sender, CommandContext args)). The annotation library supports this 100% too because to it, all it is doing is injecting a Sender object and the arguments.
    7. Intake supports switches, flags, and quote handling (/mycommand -f -w world "hi there"). However, this is also 100% optional because Intake only gives you a String for arguments. (However, the annotation code does mandate this parser).
  3. If you use annotations, it still passes a context object underneath. Annotations are an abstraction on top of the simpler command code.

So basically Intake does everything* while letting you use almost nothing.

*besides one-command-per-class annotated commands.

8 Likes

API draft:

Overview from @Falkreon (thanks):