Custom Arguments

Arguments

Arguments are the inputs your commands use. Each argument has:

  • Name: How you reference it in code and command syntax.
  • Type: What kind of data it accepts (string, number, etc).
  • Required/Optional: Is it mandatory or can it be skipped?
  • Default Value: What it defaults to if not provided.
  • Suggestions: Helpful hints for users as they type.

the parsed-value is provided in the executable-pathway-methods for execution logic.

Argument States

Arguments start as unparsed (raw user input) and become parsed (converted to their type). Parsed arguments give you:

  • Raw Value: The original input.
  • Parsed Value: The processed value you use in your code.

Supported Argument Types

Imperat comes shipped with built-in support for native java types, an even specific types per platform. For more details on this, check out Supported-Types

Custom Argument Types

Want more than basic types? Create your own! Pick the right base class for your shape, define how to parse and validate your custom input, and Imperat handles the rest.

Choosing the right base class

Imperat exposes three layers — pick the one that matches what your type consumes:

Base class Consumes Parse method receives Use when
SimpleArgumentType<S, T> A fixed number of tokens (1 by default) a String Most types: numerics, names, IDs, single-word semantics. Multi-token via super(N).
GreedyArgumentType<S, T> All remaining tokens (rest-of-line) a String Free-form messages, descriptions, anything that captures everything left.
ArgumentType<S, T> Variable, peek-driven a Cursor<S> Full control: variable-arity, optional consumption, conditional reads.

Default to SimpleArgumentType. Only drop down to raw ArgumentType when you need direct cursor control.

Example: Custom Rank Argument

Suppose you want a Rank argument that checks user input against a registry and gives helpful errors.

public class Rank {

    private final String name;
    private final List<String> permissions = new ArrayList<>();

    public Rank(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public List<String> getPermissions() {
        return permissions;
    }

    public void addPermission(String permission) {
        permissions.add(permission);
    }
}

And you have a registry of ranks that define the available ranks in your plugin. You want to create a mechanism that when a user inputs a rank name as an argument, it gets parsed into a Rank object that you can use in your command logic. and you would want the input to always be validated as an available rank otherwise an error message is sent to the user.

To handle errors, create a custom exception extending SelfHandledException:

public class UnknownRankException extends SelfHandledException {
    private final String input;
    public UnknownRankException(String input) {
        this.input = input;
    }

    @Override
    public <S extends Source> void handle(
            CommandContext<S> context
    ) {
        var sender = context.source();
        sender.reply("Unknown rank: '" + input + "'");
    }

}

Now, create the parsing logic for your custom argument. A rank is one token, so we extend SimpleArgumentType — the framework reads the token off the cursor and forwards it to us as a String:

public class RankArgumentType extends SimpleArgumentType<PLATFORMSOURCE, Rank> {

    @Override
    public @NotNull Rank parse(
            @NotNull CommandContext<PLATFORMSOURCE> context,
            @NotNull Argument<PLATFORMSOURCE> argument,
            @NotNull String input
    ) throws CommandException {
        Rank rank = yourRankRegistry.getRank(input);
        if (rank == null) {
            throw new UnknownRankException(input);
        }
        return rank;
    }

    @Override //assuming #getAllRanks returns a `List<Rank>`
    public SuggestionProvider<PLATFORMSOURCE> getSuggestionProvider() {
        return (ctx, arg)-> {
            return yourRankRegistry.getAllRanks().stream()
                    .map(Rank::getName)
                    .toList();
        };
    }
}

How It Works

Your custom ArgumentType parses input, checks validity, and throws your exception if needed. Imperat catches errors and shows users clear messages.

Learn more about error handling and custom exceptions in the Error Handling section.

We overridded the getSuggestionProvider method to provide dynamic suggestions based on the available ranks in the registry. This way, when users start typing a rank name, they get real-time suggestions of valid ranks. Learn more about suggestions in the Suggestions section.

Registering Your Argument Type

Register your custom argument type so Imperat knows how to use it:

PLATFORMIMPERAT imperat = PLATFORMIMPERAT.builder()
    .argType(Rank.class, new RankArgumentType())
    .build();

Using Custom Argument Types

Now use your custom argument in commands just like built-in types:

@RootCommand("rank")
public class RankCommand {

    @Execute
    public void exec(PLATFORMSOURCE source) {
        source.reply("/rank <rank>");
    }

    @Execute
    public void exec(PLATFORMSOURCE source, Rank rank) {
        // In a real implementation, you would set the player's rank here.
        source.reply("/rank <rank> setpermission <permission>");
    }

    @SubCommand(value = "setpermission", attachTo = "<rank>")
    public class SetPermission {

        @Execute
        public void setPermission(PLATFORMSOURCE source, @InheritedArg Rank rank, String permission) {
            // In a real implementation, you would set the specified player's rank here.
            rank.addPermission(permission);
            source.reply("Added permission '" + permission + "' to rank '" + rank.getName() + "'");
        }

    }
    
}

And don’t forget to register your command:

PLATFORMIMPERAT imperat = PLATFORMIMPERAT.builder()
    .argType(Rank.class, new RankArgumentType())
    .build();

imperat.registerCommand(RankCommand.class);