rather limited/bad command api
Find a file
kento2 850a9ab107
Some checks failed
Build and Deploy artifact / build (push) Failing after 13s
.
2026-08-05 13:22:44 +02:00
.forgejo/workflows . 2026-08-05 13:22:44 +02:00
.idea . 2026-08-05 11:11:26 +02:00
bukkit refactor ig 2026-08-04 02:30:41 +02:00
core refactor ig 2026-08-04 02:30:41 +02:00
velocity refactor ig 2026-08-04 02:30:41 +02:00
.bazelrc ci/cd maybe?? 2026-08-05 02:42:30 +02:00
.gitignore BAZEL + some code 2026-08-02 21:23:36 +02:00
MODULE.bazel ci/cd maybe?? 2026-08-05 02:42:30 +02:00
README.md . 2026-08-05 03:01:10 +02:00

TODO

CI/CD:

  • keep ~/.cache (avoid analyzing targets)
  • publish package

Build

after installing bazel:

bazel build //bukkit
bazel build //core
bazel build //velocity

Limitations

  • argument's can't require specific permissions.

Design

Commands are trees of Literals. Literals can have arguments. Literals can also have Executors.

arguments can be optional by giving them a default value. arguments can have suggestions.

/feed <user>:

  • feed is the root literal
  • <user> is an argument: it's value is a User Object

To allow the use of /feed without giving a user's name, i.e. to make the argument optional, we need to assign the user-argument a default value.

An example command

Let's say we want to make a command that allows seeing how many coins another User has. If we don't specify a user, it should instead show the coins the sender has.

As a tree

Visualized as a tree, we have a Node/Literal coins with an alias balance with an optional argument called user. To make an argument optional, it must have a default value.

literal:coins/balance
- has argument:user
- has executor:getCoins

As code

import de.kentoj.kencommandapi.api.literal.RootLiteral;

/**
 * Show the amount coins a user has.
 * If no user is given, it should show the amount the sender of the command has.
 */
public class CoinsCommand {

    private final RootLiteral<User> rootLiteral;
    private final CommandArgumentSpec<User, User> userArg;

    public CoinsCommand() {
        userArg = CommandArgumentSpec.builder("user", new UserArgumentType<User>())
                .withRequirement(p -> !p.hasPermission("economy.immune.getcoins"),
                        "you can't see that player's balance")
                .withDefaultValue(CommandContext::sender)
                .build();
        rootLiteral = Literal.<User>builder("coins", "balance")
                .withPermission("economy.command.coins")
                .withArgument(userArg)
                .withSyncExecutor(this::getCoins)
                .asRootLiteral(MessageStyle.PLAIN);
    }

    private Result<Object, String> getCoins(CommandContext<User> ctx) {
        var user = ctx.getArg(userArg);
        ctx.getSender().sendMessage(user.name() + " has " + user.coins() + " coins");
        return Results.success(new Object());
    }

    public RootLiteral<User> rootLiteral() {
        return rootLiteral;
    }
}

If we wanted to report an error, we could do for example return Results.failure("That user doesn't exist"). However, if we have a smart UserArgumentType, it automatically makes sure that the user exists. Additionally, we can also a Requirement in the CommandArgumentSpec.

The registration of the command is platform-dependant. Typically, a class called CommandAPI can be used.

CommandAPI api = new CommandAPI();
register(new CoinsCommand().rootLiteral());