Star Command System {Thoughts?}

Preface
Back in the olden days when the Sponge command system (CommandSpec and the like) were just getting started, the SpongeAPI changed the name of one of the classes associated with this system. Being the usual idiot I am, I assumed they removed command functionality and wrote my own parser. Three months later, I re-wrote it using my configuration language. (Basically, my config language will accept values separated by spaces, because reasons (It also accepts JSON input)). Now it’s evolved quite a bit more than I expected, and I was wondering if anyone had any thoughts on how it works or how I could improve it.

Base Principle
My command system, unlike Sponge’s, does not parse values into just any object (although you could do some weird stuff and get it to, it’s not supported in the default implementation). What it does do, however, is allow you to assume certain things are true. For example, you could ask the system to ensure a certain argument is a string representation of a UUID, but you’d still be accessing the String object.

Fancy Features
The system uses a “CommandHandler” which defines the aliases, help, and description all in that class. I personally find that easy to work with. Furthermore, usage strings are automatically generated based on expected arguments. The CommandHandlers can then be used for two things, they can either be translated to a sponge CommandCallable, or to a Conversation. Some of you may remember Bukkit’s conversation API, I implemented a basic Conversation API in my plugin. The conversation allows the user to enter each argument individually, this can be incredibly useful for long commands. Another feature is the ease with which command structures can be built; since all the information pertaining to help and aliases exist within the command class itself, a “CommandTree” can be built like so:

        CommandTree tree = CommandTree.builder(Text.of("Main StarAPI command"), Text.of("Main StarAPI command"), "star")
                .node(Text.of("Main StarAPI enchantments command"), Text.of("Main StarAPI enchantments command"), "enchant")
                    .node(new EnchantsCommand())
                    .node(new ClearEnchantsCommand())
                    .node(new EnchantCommand())
                .parent()
                .designateHelp()
                .forcePluginHelp()
                .build();

        CommandTree conversation = CommandTree.builder(Text.of("Main StarAPI Conversation command"), Text.of("Main StarAPI Conversation command"), "conversation")
                .node(new ConversationStartCommand())
                .node(new ConversationEndCommand())
                .designateHelp()
                .build();
        
        PluginCommandTree.builder(this)
                .node(tree)
                .node(conversation)
                .help("help")
                .build()
                .register();

The above structure builds two CommandTrees, and one PluginCommandTree. The idea is that a CommandTree represents a single command, with potential sub-commands. In theory, only one PluginCommandTree should exist per plugin, and it servers the purpose of collecting up all the plugin’s commands and registering. In addition, the trees automatically generate help commands wherever you ask them to - the option “designateHelp” on a CommandTree will register a help-command as a sub-command of that specific tree, and “forcePluginHelp” will ensure the help command refers to the root plugin help. The option “help” on PluginCommandTree specifies the aliases to use for the various help commands that are to be registered. You can also register help commands directly on PluginCommandTree, as flat commands (with no parent command). The nice thing about the help commands is the interface the generate for help. I got the idea from @TrenTech’s plugins, but I wrote all the code for my system without looking at his code. An example of the interface is below:

Caveats
A system like this is not without its problems. A couple are:

  • Unlike CommandSpec, MainCommands cannot have their own action if no sub-command was found
  • The error messages can sometimes be unclear
  • Without CommandTree, it’s hell to write help and main command structures

The End (Or Is It?)
So, if you made it this far, you must be willing to leave some thoughts behind! I wanted to get input on this because I don’t really like deviated from the sponge system, but I like my own system even more. I might make another post like this discussing my configuration language, because, although it accepts JSON input, it’s quite complex.

For anyone interested, the code is on github: https://github.com/SocraticPhoenix/StarAPI/tree/master/src/main/java/com/gmail/socraticphoenix/sponge/star/chat

2 Likes

I’m looking over your codebase. It seems you have more than a simple command API.

Maybe just a bit more… :wink:

Edit: I’ve been working on cleaning it up recently, but there are still large portions I haven’t gotten to yet.

I saw a template class for a plugin object with a bit more than normal.

:worried:
I have it set up so it can do everything for me. Including dynamically define config directories, use a compressed data format (which is an unreadable form of my config format), load the logger with a nice name, and more. It also automatically backs up configs if an error has occurred, I know that the three methods that lookup the data, language mapping, and config are virtually identical, but I’d rather not fix something that isn’t broken. It should also (theoretically) have the capability to auto-update the config, but I’m yet to test that feature.

THis could be interesting

What part?

15 15 15 15 chars

Not sure. Just getting that kind of vibe.

Well, it the library certainly is interesting… I know parts of it still aren’t updated, finished, or functional. That minigame API has been there since I first started on sponge, and so far all it can do is copy worlds. All that remains to be seen is if its usable. I doubt other people will use it to its “full potential” because the command system and plugin template class both deviate from sponge conventions (CommandSpec, etc), but it fits my needs perfectly. If I’ve got something I think would be useful in future plugins I make, I throw it in some package in StarAPI.