-
Notifications
You must be signed in to change notification settings - Fork 7
Flags
What this page covers: the --name value flag syntax on FinalCMD commands - the syntax a player
or admin types, the declarative @FlagArg pipeline a command author uses to bind it to a method
parameter, and the lower-level manual API (MultiArgumentos.getFlags()/getFlag(...)) for commands
that parse their own raw tokens. Companion pages: Command Framework (the command itself) and
Argument Parsing (@Arg, the context mini-DSL, custom ArgParsers - flags reuse all of it).
A flag is a token starting with one or more -, followed by a name: --force, --page, -n. There
is no other supported form - the old -name:value syntax is gone.
-
A value flag consumes exactly the next token:
--page 2setspageto"2". -
A multi-word value needs quotes, single or double. Without quotes, only the immediately following token is taken and the rest stays positional. These two cases come straight from the tests that pin the tokenizer (
MultiArgumentosSystemTest):Case A (quoted): /dbroad Teste My Friend --title 'Title Message' -> flag title = "Title Message" Case B (unquoted): /dbroad Teste My Friend --title Title Message -> flag title = "Title" (only the next token) -> "Message" remains a positional argument -
A flag with nothing after it (or followed by another flag/
--) is a presence flag: its value is the literal string"true". ABoolean-typed@FlagArgnever looks past this - see below. -
--alone ends flag scanning. It is removed from the input, and every token after it stays positional literally, even one that looks like a flag:set -- --forcetreats--forceas a plain positional argument, not a flag. -
A negative number is never a flag.
-5and--5are left as positional arguments (the check is "dash(es) followed by a non-digit"). -
Flags can sit anywhere on the line - before, between, or after the positional arguments. They are stripped out before the positional parser ever sees the line.
-
Lookup is dash-count- and case-insensitive:
force,-force,--FORCEand--Forceall resolve the same flag. -
A quote glued directly onto the name (no separating space, e.g.
--title'X') is part of the flag's name, not the start of a quoted value - it is a flag literally calledtitle'X'. Always put a space before an opening quote.
An unrecognized flag on a command that declares @FlagArg parameters aborts the command with a message
listing the flags that are available (filtered to the ones the sender has permission for):
Unknown flag
[--froce]! Available flags:--force
Put @FlagArg on a method parameter exactly like @Arg, except a flag parameter is never positional -
it can appear anywhere on the command line, and its presence is always optional.
public @interface FlagArg {
String name(); // canonical spelling, always long form: "--force"
String[] aliases() default {}; // extra spellings, short or long: {"-f"}
String context() default ""; // same mini-DSL as @Arg, applied to the VALUE parser
Class<? extends ArgParser> parser() default ArgParser.class;
FCLocale[] locales() default {}; // hover description, same mechanism as @Arg
String def() default ""; // parsed by the value parser when the flag is absent
String permission() default ""; // required to USE the flag (not to see the command)
boolean showOnUsage() default true; // false hides it from usage+hover; tab/dispatch unaffected
}This is the shipped code (common/src/main/java/.../commands/misc/CMDECCooldown.java) - the fire-test
that collapsed the old 4-command matrix (set/setnetwork/setplayer/setplayernetwork) down to two:
@FinalCMD.SubCMD(subcmd = "set")
public void set(FCommandSender sender,
@Arg(name = "<CooldownID>") String cooldownId,
@Arg(name = "<duration>") String duration,
@FlagArg(name = "--network", aliases = "-n", def = "false", locales = {
@FCLocale(lang = LocaleType.EN_US, text = "Apply network-wide (follows the account across servers)."),
@FCLocale(lang = LocaleType.PT_BR, text = "Aplica na rede inteira (segue a conta entre servidores).")
})
Boolean network) {
Cooldown cooldown = network ? Cooldown.network(cooldownId) : Cooldown.of(cooldownId);
// ...
}/eccooldown set myid 5m sets a local cooldown; /eccooldown set myid 5m --network (or -n) sets a
network one. setPlayer carries the same --network/-n flag alongside its own <player> argument.
-
A flag parameter is never a primitive.
boolean forcefails registration; useBooleanso an absent flag can benull. This is checked fail-fast when the command registers, not at dispatch time. -
Booleanis the presence type: aridade 0, it never consumes the next token. Present ->TRUE. Absent ->null, unlessdef()says otherwise (def = "false"makes absenceFALSEinstead ofnull- seeCMDECCooldownabove). -
Every other type consumes exactly one token (or one quoted group): the flag's value is parsed by
the same
ArgParsera positional@Argof that type would use - built-in or your ownparser = MyParser.classoverride, including thecontextmini-DSL (bounds, choices - see Argument Parsing). -
A flag is never required. There is no
requiredattribute - a mandatory flag is a positional argument in disguise. Handle absence yourself, typically by checking fornull(or relying ondef()). -
def()is parsed like a typed value, not stored raw. An unparseabledef()errors at registration/first-use instead of silently becomingnull- unlike@Arg.def(), whose internalArgInfois intentionallyOPTIONAL(see Argument Parsing), a flag's internalArgInfoisREQUIREDso a typo indef()is never swallowed. - A value the player actually typed always aborts the command on a parse failure, exactly like a bad value on a required positional - the command never runs with a bogus flag value.
Four things fail the command's registration outright (ArgMountException), so a bad @FlagArg
never ships silently:
- A primitive parameter type.
-
namenot in the long form--name(no spaces, non-empty after the dashes). - The same normalized spelling (name or alias, dashes stripped, case-insensitive) claimed twice on the same method - by two different flags, or a flag whose alias collides with another flag's name.
- Both
@Argand@FlagArgon the same parameter - a parameter is one or the other, never both.
permission() gates using the flag, not seeing the command: absent, it is never checked; present
without the permission, the sender gets the standard permission message and the command aborts (the
value, if any, is never parsed). Tab-complete additionally filters out flags the sender cannot use.
ArgParserCommandContext.isFlag() tells a custom ArgParser whether it is currently resolving a
flag's value (true, including its def()) or a positional/contextual value (false) - the same
parser class serves both call sites, so this is the hook to branch on if a parser needs to behave
differently for the two (e.g. a different tab-complete list). It is set once, at construction of the
ArgParserCommandContext for that resolution, and never changes mid-parse.
A visible flag (showOnUsage() == true, the default) renders as a compact [--name] token on the
usage line, in declaration order, after the positional arguments - the value's shape (required-vs-not,
bounds) never appears there, only in the hover.
Unlike a plain @Arg, a flag with no locales() still gets its own hover block - the title alone
(with its aliases) is the only place a player can discover a short alias at all:
[--network | -n] <- usage line: name plus every alias, pipe-separated, when present
✯ [--network | -n] <- hover block title (always present for a visible flag)
● Apply network-wide (follows the account across servers). <- only when locales() is set
showOnUsage = false removes the flag from both the usage line and the hover block entirely, but
it keeps working normally: it still tab-completes and still dispatches. Use permission(), not
showOnUsage, to hide a flag from players who should not even know it exists (a showOnUsage=false
flag with no permission() is still suggested to everyone on tab).
Tab-complete understands flags in three situations:
-
The word being typed looks like a flag (
--,--ne, ...): suggests the declared long names, filtered by permission, prefix, and flags already used earlier on the line. -
The previous token is a declared value-flag: delegates to that flag's own value parser (so
--page <TAB>suggests that argument's own choices, exactly like a positional would). - Otherwise: the raw token index is corrected for whatever flags (and their consumed values) sit earlier on the line, then falls through to the normal positional tab-complete - so a flag typed before a positional argument never throws off which positional is being completed.
A token after a literal -- is never treated as a flag by tab-complete either, matching dispatch.
Some commands parse their own raw tokens instead of declaring @Arg/@FlagArg - the escape hatch is
MultiArgumentos, injected as a contextual parameter. It understands the same --name value syntax,
but in sniffed mode: there is no declared arity, so a value token that itself looks like a flag (or
is --) makes the marker degrade to a presence flag instead of consuming it.
@FinalCMD.SubCMD(subcmd = "set")
public void set(FCommandSender sender, MultiArgumentos argumentos, HelpLine helpLine) {
if (argumentos.emptyArgs(1)) { helpLine.sendTo(sender); return; }
FlagedArgumento force = argumentos.getFlag("force"); // same lookup as "-force"/"--force"
if (force.isSet()) {
// ...
}
}getFlags()/getFlag(name) trigger flagification lazily (and idempotently) the first time either is
called - a plain positional-only command that never touches them pays nothing for it. A missing flag
returns FlagedArgumento.EMPTY_ARG (isSet() == false), never null.
A single MultiArgumentos instance is flagified in exactly one mode: calling the declarative
extractDeclaredFlags(...) (what @FlagArg uses internally) on an instance and then calling
getFlags()/getFlag(...) on the SAME instance sees exactly what was already extracted - the two
modes are mutually exclusive per instance, not something you mix by hand.
A token starting with - is only ever treated as a flag when:
- The method declares at least one
@FlagArgparameter (the declarative pipeline), or - The command calls
MultiArgumentos.getFlags()/getFlag(...)itself (the manual path).
A method with @Arg parameters and no @FlagArg never enters flag mode at all - a token like
--anything stays a plain positional argument, parsed like any other token would be. This is
deliberate: adding a flag to one sub-command can never change how an unrelated sub-command interprets
a dash-prefixed positional value.
-
Command Framework -
@FinalCMD, sub-commands, the command registry, the 2.x -> 3.x migration table. -
Argument Parsing -
@Arg, thecontextmini-DSL,def(), writing a customArgParser.
EverNifeCore · Home · made by Petrus Pradella
Getting Started
Commands & Text
Player Data & Storage
- PlayerData & PDSections
- Accounts
- Storage Backends
- Inline Backends for Plugins
- Legacy Data Migration
- Cooldowns
Config & Minecraft Systems
- Configuration
- Scheduler & Threading
- Items & NBT
- GUI Framework
- Integrations
- Economy
- Version Compatibility
Architecture & Reference