Skip to main content

Flags

A flag is a named modifier (--silent, -s), as opposed to a positional argument. Flags can be given in any order among themselves, but must come after every positional argument and sub-command.

Flags covers Brigadier primitives and Paper's argument types — intFlag, doubleFlag, boolFlag, stringFlag, worldFlag, playerFlag, itemStackFlag, componentFlag and more. Anything not covered has Flags.argFlag(name, argumentType).

Declaring​

this.addFlag(Flags.boolFlag("silent").alias("s"));
this.addFlag(Flags.intFlag("count", 1, 64).alias("c").defaultTo(1).suggests("1", "16", "64"));

Suggestions can carry hover tooltips, the way vanilla annotates registry keys:

Flags.stringFlag("mode").suggests(new LinkedHashMap<>() {{
put("fast", Component.text("Skips validation"));
put("safe", Component.text("Checks every entry"));
}});

Reading them back​

dispatch.hasFlag("verbose"); // explicitly provided?
dispatch.getFlagValue("count", Integer.class); // value or declared default
dispatch.getFlagValue("count", Integer.class, 5); // value or this fallback
dispatch.getOptionalFlagValue("message", String.class); // Optional

getFlagValue is @Nullable — a flag may declare an explicit null default — so prefer the three-argument overload when you are about to unbox a primitive.

Two limits worth knowing​

Flags must come last​

Flag nodes branch only into other flag nodes. So /cmd sub --verbose parses and /cmd --verbose sub does not.

Keep the flag count low​

Every ordered subset of the flag set becomes a real Brigadier node, so flags can be given in any order — and that tree is sent to every client. Measured, for a single command:

Boolean flagsNodes (no positional args)Nodes (2 positional args)
31633
465131
5326653
61957—

Growth is faster than n!. Past four flags the library logs a warning at startup.

Prefer a single argFlag carrying several options over many boolean flags:

// Instead of --fast --safe --verbose --dry-run
Flags.stringFlag("mode").suggests("fast", "safe", "verbose", "dry-run");
Why the tree is not collapsed

Brigadier redirects would make this O(n), but a redirect creates a child CommandContext, so every argument lookup would have to walk getChild(). It is a real change and deliberately deferred.

Ordering is load-bearing internally​

Relevant if you are modifying the library itself: ArgumentBuilder#then(child) calls child.build() immediately and stores an immutable node. Anything added to a builder after it has been then()'d onto a parent is silently discarded — no error, just missing nodes. Flag branches are therefore attached to every argument-level builder before those builders are chained together.

This is the easiest thing in the library to break, and it fails silently.