Skip to content

Custom Command Arguments

CommandArguments is the object passed to command and completer methods. By default, the framework creates the built-in class for every execution, but you can now provide your own subclass and make every command receive richer helper methods.

This is useful when you repeatedly write the same validation, formatting, player lookup, or plugin-specific utility code inside commands.

The simplest approach is to extend CommandArguments and expose a copy constructor.

PluginArguments.java
public class PluginArguments extends CommandArguments {
public PluginArguments(CommandArguments arguments) {
super(arguments);
}
public Player requirePlayer() {
if (!isSenderPlayer()) {
sendMessage("<red>This command can only be used by players.");
throw new IllegalStateException("Sender is not a player.");
}
return getSender();
}
public void success(String message) {
sendMessage("<green>" + message);
}
public void error(String message) {
sendMessage("<red>" + message);
}
}

The copy constructor receives the framework-created CommandArguments instance and passes its internal sender, command, label, and argument array to the parent constructor.

Use setDefaultArguments(Class) when your custom class has either a copy constructor or the full constructor signature.

Main.java
public class Main extends JavaPlugin {
private CommandFramework commandFramework;
@Override
public void onEnable() {
commandFramework = new CommandFramework(this);
// Must be configured before command registration.
commandFramework.setDefaultArguments(PluginArguments.class);
commandFramework.registerCommands(this);
}
@Command(name = "profile", senderType = Command.SenderType.PLAYER)
public void profileCommand(PluginArguments arguments) {
Player player = arguments.requirePlayer();
arguments.success("Opening profile for " + player.getName() + ".");
}
}

After this setup, command methods can request PluginArguments directly. You do not need to cast the base CommandArguments object manually.

If your custom arguments object needs dependencies, use the factory overload instead of reflection.

Main.java
public class Main extends JavaPlugin {
private Economy economy;
@Override
public void onEnable() {
CommandFramework commandFramework = new CommandFramework(this);
commandFramework.setDefaultArguments(arguments ->
new EconomyArguments(arguments, economy)
);
commandFramework.registerCommands(this);
}
}
EconomyArguments.java
public class EconomyArguments extends CommandArguments {
private final Economy economy;
public EconomyArguments(CommandArguments arguments, Economy economy) {
super(arguments);
this.economy = economy;
}
public double getBalance() {
return economy.getBalance(getSender().getName());
}
}

The factory receives the base CommandArguments for the current command execution and must return a non-null subclass instance.

The class-based overload supports two constructor styles.

public PluginArguments(CommandArguments arguments) {
super(arguments);
}

If neither constructor exists, the framework throws an IllegalArgumentException while configuring the default arguments class.

The same default arguments configuration is also used for tab completers.

@Completer(name = "warp")
public List<String> warpCompletion(PluginArguments arguments) {
if (!arguments.hasPermission("example.warp")) {
return List.of();
}
return List.of("spawn", "market", "arena");
}

This lets you share the same permission helpers, lookup methods, and formatting shortcuts across command execution and completion code.

Keep custom arguments focused on command-context utilities.

Good examples include:

  • sender helpers
  • common permission checks
  • message formatting shortcuts
  • player or plugin service lookups
  • argument validation helpers

Avoid placing long command workflows inside the arguments class. Command behavior is usually easier to maintain inside the command method or a dedicated service.