Commands and typed parameters
Extend AbstractCommand, declare the accepted shape with CommandArguments,
and register the command through PluginContext::commands(). Bedriox parses,
validates, resolves selectors, and converts values before execute() runs.
Register a command
public function onEnable(): void
{
$this->context()->commands()->register(new ArenaCommand());
}
Text, numbers, and booleans
public function defineArguments(): CommandArguments
{
return CommandArguments::create()
->addArgument(CommandParameter::string('arena'))
->addArgument(CommandParameter::integer('rounds')->minimum(1)->maximum(20))
->addArgument(CommandParameter::float('damage')->minimum(0.0)->maximum(40.0))
->addArgument(CommandParameter::boolean('friendlyFire'));
}
public function execute(CommandContext $context): CommandResult
{
$values = $context->values();
$arena = $values->string('arena');
$rounds = $values->integer('rounds');
$damage = $values->float('damage');
$friendlyFire = $values->boolean('friendlyFire');
return $this->success("Configured {$arena} for {$rounds} rounds.");
}
Use minimum() and maximum() on integer or float parameters. Invalid values
never reach the command handler.
Choices, enums, and changing choices
Use choice() for a small fixed string list:
CommandParameter::choice('mode', ['solo', 'teams'])
Use a backed PHP enum when the plugin owns the fixed values. Calling enum()
returns the enum case from CommandValues::enum():
enum ArenaMode: string
{
case SOLO = 'solo';
case TEAMS = 'teams';
}
CommandParameter::enum('mode', ArenaMode::class);
$mode = $context->values()->enum('mode', ArenaMode::class);
Use softEnum() when suggestions may change while the server runs, such as
loaded arena names or kits. Keep the CommandSoftEnum instance and update its
values instead of unregistering the command.
$arenas = $this->context()->commands()->registerSoftEnum('arenas', ['duel', 'teams']);
$parameter = CommandParameter::softEnum('arena', $arenas);
$arenas->add('capture');
$arenas->remove('duel');
Players and entity selectors
CommandParameter::onlinePlayer('player'); // one connected player
CommandParameter::players('targets'); // player name or bounded selectors
CommandParameter::entity('target'); // one player or entity
CommandParameter::entities('targets'); // bounded player/entity selection
Read them with the matching typed accessor:
$player = $context->values()->player('player');
$players = $context->values()->players('targets');
$entity = $context->values()->entity('target');
$entities = $context->values()->entities('targets');
Selectors are resolved by Bedriox. Do not parse @a, @s, or other selector
syntax yourself.
Positions and block positions
position() accepts world coordinates, including relative coordinates when the
sender has a position. blockPosition() returns integer block coordinates.
CommandParameter::position('destination');
CommandParameter::blockPosition('block');
$destination = $context->values()->position('destination');
$block = $context->values()->blockPosition('block');
Messages, raw text, and JSON
These parameters consume structured or remaining input and therefore belong at the end of an overload:
CommandParameter::message('message');
CommandParameter::rawText('text');
CommandParameter::json('payload');
Read them with message(), rawText(), or json(). Validate the shape of a
decoded JSON value before using it.
Optional values and defaults
CommandParameter::integer('rounds')->optional(3);
CommandParameter::onlinePlayer('player')->optional();
An explicit default is returned by the typed accessor. For an optional value
without a default, check CommandValues::has() before reading it.
Literal routes and overloads
public function defineArguments(): CommandArguments
{
return CommandArguments::create()
->addOverload(CommandOverload::create()
->addArgument(CommandParameter::literal('join'))
->addArgument(CommandParameter::string('arena')))
->addOverload(CommandOverload::create()
->addArgument(CommandParameter::literal('teleport'))
->addArgument(CommandParameter::position('destination')));
}
Literals cannot be optional. Use another overload when a route is optional.
Sender and result policy
Use AllowedCommandSenders::PLAYER_ONLY when the command fundamentally needs a
player. Otherwise inspect CommandContext::sender() and deliberately support
both player and console senders. Return $this->success() for accepted work and
$this->failure() for a readable command error.
