A tiny Paper plugin/library that wraps Paper's modern, boilerplate-heavy Brigadier command API behind a clean, Minestom-style command API
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-07-20 12:26:23 +02:00
gradle/wrapper initial commit 2026-07-20 12:10:37 +02:00
src/main why did i not try to build before commit again 2026-07-20 12:20:26 +02:00
.gitignore initial commit 2026-07-20 12:10:37 +02:00
build.gradle.kts why did i not try to build before commit again 2026-07-20 12:20:26 +02:00
gradle.properties initial commit 2026-07-20 12:10:37 +02:00
gradlew initial commit 2026-07-20 12:10:37 +02:00
gradlew.bat initial commit 2026-07-20 12:10:37 +02:00
LICENSE initial commit 2026-07-20 12:10:37 +02:00
README.md typo 2026-07-20 12:26:23 +02:00
settings.gradle.kts initial commit 2026-07-20 12:10:37 +02:00

ComplexCommands

Disclaimer: this is entirely written by ai because im lazy to write something like this but i want to use it

Add to your project

repositories {
    maven("https://api.modrinth.com/maven")
}

dependencies {
    compileOnly("maven.modrinth:complexcommands:+")
}

A tiny Paper plugin/library that wraps Paper's modern, boilerplate-heavy Brigadier command API behind a clean, Minestom-style command API.

Instead of writing raw LiteralArgumentBuilder / RequiredArgumentBuilder trees by hand, you extend ComplexCommand (the equivalent of Minestom's Command) and declare syntaxes just like you would in Minestom - while every argument still compiles down to Paper's real, native Brigadier argument types under the hood (so you keep vanilla client-side validation, colouring and tab-completion for free).

Quick start

public final class PingCommand extends ComplexCommand {
    public PingCommand() {
        super("ping");
        setDefaultExecutor((sender, context) -> sender.sendMessage("Pong!"));
    }
}
public final class TeleportCommand extends ComplexCommand {
    public TeleportCommand() {
        super("teleport", "tp");
        setCondition((sender, name) -> sender.hasPermission("complexcommands.teleport"));

        var targets = ComplexArgumentType.Entities("targets");
        var destination = ComplexArgumentType.Player("destination");

        addSyntax((sender, context) -> {
            for (Entity entity : context.get(targets)) {
                entity.teleport(context.get(destination).getLocation());
            }
        }, targets, destination);
    }
}

Register commands in your plugin's onEnable():

@Override
public void onEnable() {
    ComplexCommandManager.register(this, new PingCommand(), new TeleportCommand());
}

That's it - no paper-plugin.yml command declarations, no manual Brigadier tree building.

API overview

Minestom concept ComplexCommands equivalent
Command ComplexCommand
CommandManager ComplexCommandManager
CommandContext ComplexCommandContext
CommandExecutor ComplexCommandExecutor
CommandCondition ComplexCommandCondition
ArgumentType ComplexArgumentType
Argument<T> ComplexArgument<T>

ComplexCommand building blocks

  • setDefaultExecutor(executor) - runs when no arguments are given.
  • addSyntax(executor, arg1, arg2, ...) - registers one branch of arguments. You may add as many syntaxes as you like; they are merged into a single command tree automatically.
  • setCondition(condition) - hides/denies the command for senders that fail the check.
  • setDescription(text) - shown in client-side command info.
  • addSubcommand(otherCommand) - nests a command as a literal child, e.g. /give gamemode ....

Optional arguments (argument.setDefaultValue(x)) must be the trailing arguments of a syntax, exactly like Minestom. The generated command tree will let players stop typing at any point from the last mandatory argument onward, filling in defaults for whatever wasn't supplied.

ComplexArgumentType - every argument type

Primitives Boolean, Word, String, StringArray, Command (raw remainder), Integer (+ranged), Long (+ranged), Float (+ranged), Double (+ranged), Literal (fixed choices), Enum

Text Component, Color (NamedTextColor)

Identifiers / misc Time (ticks, e.g. 5s, 2d), UUID, NamespacedKey, Resource (any Bukkit Registry, e.g. RegistryKey.ENCHANTMENT), Enchantment, PotionEffect

World / block / item World, GameMode, BlockState (full block-state + NBT parsing), ItemStack (full item + NBT parsing)

Positions RelativeBlockPosition (~ ~-1 ~), RelativeVec3 (~ ~ ~1.5)

Entities Entity (single), Entities (multiple), Player (single), Players (multiple) - all backed by real vanilla entity/player selectors (@e, @a, @r, @s, @p, player names, UUIDs...).

Every factory method returns a ComplexArgument<T>, so context.get(argument) is fully typed - no casting needed.

Custom suggestions

var color = ComplexArgumentType.Word("color")
    .setSuggestionProvider((ctx, builder) -> {
        builder.suggest("red");
        builder.suggest("blue");
        return builder.buildFuture();
    });

Known limitations

This library focuses on the arguments developers reach for most often. A few of Minestom's more exotic argument types (NBT compound/tag arguments, argument "loops"/"groups" for open-ended repeating syntaxes) aren't included, since Paper's Brigadier layer doesn't expose a 1:1 native equivalent - you can still fall back to ComplexArgumentType.Command(id) (a greedy raw-string capture) and parse those cases yourself.