-
Notifications
You must be signed in to change notification settings - Fork 1
Description
LibCommand
LibCommand is the new in-house command framework for EasyLibrary 2.0. Each command is implemented as a class (extending Command or SubCommand), making the design modular and easy to extend. Command metadata (name, description, aliases, permission node, arguments, subcommands, etc.) is defined in the onBuild() method, execution logic goes in onExecute(), and error handling is done in onFailure(). All arguments are strongly typed and validated before execution; constraints (like permissions or context checks) are provided via separate constraint classes (for example, InGameConstraint).
-
Modular architecture: Each command is a self-contained class. You can add, remove, or extend commands by creating new classes.
-
Clear separation of concerns:
- Definition: In
onBuild(), return an array that declares the command’s name, aliases, description, permission, arguments, subcommands, and constraints. - Execution: Implement the command’s behavior in
onExecute(). This method only runs if argument parsing and constraints pass. - Failure handling: In
onFailure(), handle any validation failures (missing/invalid arguments, failed constraints, execution errors) and provide user feedback.
- Definition: In
-
Typed arguments & constraints: Define input parameters using specific argument classes (e.g.
IntegerArgument,PlayerArgument) which can include range or format constraints. Declare permission/context checks in the'constraints'array ofonBuild()(e.g. usingnew InGameConstraint()).
Important
The onBuild() method must return an associative array with all command properties. This is where you set the command name, aliases, description, permission node, argument definitions, subcommands, and constraint checks.
Tip
When retrieving arguments in onExecute(), you can supply a default value. For example, $result->getArgumentsList()->get('page', 1) fetches the page argument or returns 1 if it was omitted.
Note
The CommandFailure object provides detailed error information. For constraint failures, getData()['failed_constraints'] may list which checks failed. For argument errors, getData()['argument_errors'] contains messages for each invalid argument. Use these details in onFailure() to tailor your error messages.
Examples
The example below demonstrates a top-level command (MyInfoCommand) with an optional integer argument (page) and an in-game constraint, plus a subcommand (PlayerSubCommand) to display player info. Both classes follow the pattern: define properties in onBuild(), implement logic in onExecute(), and handle errors in onFailure().
Command Class
<?php
use imperazim\command\Command;
use imperazim\command\constraint\InGameConstraint;
use imperazim\command\result\CommandResult;
use imperazim\command\result\CommandFailure;
use imperazim\command\argument\IntegerArgument;
class MyInfoCommand extends Command {
public function onBuild(): array {
return [
'name' => 'serverinfo',
'aliases' => ['sinfo', 'server'],
'description' => 'Displays server information',
'permission' => DefaultPermissions::ROOT_USER,
'arguments' => [
new IntegerArgument(
name: 'page',
optional: true,
min: 1,
max: 3
)
],
'subcommands' => [new PlayerSubCommand($this)],
'constraints' => [new InGameConstraint()]
];
}
public function onExecute(CommandResult $result): void {
$sender = $result->getSender();
$page = $result->getArgumentsList()->get('page', 1);
$server = Server::getInstance();
$tps = $server->getTicksPerSecond();
// Generate server information based on the requested page
$serverInfo = match($page) {
1 => [
"Players: §6" . count($server->getOnlinePlayers()) . "§r/" . $server->getMaxPlayers(),
"World: §6" . ($server->getWorldManager()->getDefaultWorld()?->getDisplayName() ?? "Unknown"),
"TPS: " . match(true) {
$tps > 18 => "§a" . number_format($tps, 1) . "§r",
$tps > 15 => "§e" . number_format($tps, 1) . "§r",
default => "§c" . number_format($tps, 1) . "§r"
}
],
2 => [
"Version: §6" . $server->getVersion(),
"API: §6" . $server->getApiVersion(),
"Plugins: §6" . count($server->getPluginManager()->getPlugins())
],
3 => [
"IP: §6" . $server->getIp(),
"Port: §6" . $server->getPort(),
"View Distance: §6" . $server->getViewDistance()
],
default => ['§cInvalid page! Use 1, 2 or 3']
};
// Send formatted information to the player
$sender->sendMessage("§l§3=== §r§aServer Information§3 (Page $page) §l===");
$sender->sendMessage(implode("\n§7", $serverInfo));
}
public function onFailure(CommandFailure $failure): void {
$sender = $failure->getSender();
switch ($failure->getReason()) {
case CommandFailure::CONSTRAINT_FAILED:
// Handle constraint failures (e.g., player not in-game)
// $failedConstraints = $failure->getData()['failed_constraints'] ?? [];
break;
case CommandFailure::MISSING_ARGUMENT:
// Handle missing required arguments
$sender->sendMessage("§eUsage: /serverinfo [page: 1-3]");
break;
case CommandFailure::INVALID_ARGUMENT:
// Handle invalid argument values
$sender->sendMessage("§eUsage: /serverinfo [page: 1-3]");
break;
case CommandFailure::EXECUTION_ERROR:
// Handle unexpected execution errors
$sender->sendMessage("§cAn error occurred while executing the command");
break;
}
}
}SubCommand Class
<?php
use imperazim\command\SubCommand;
use imperazim\command\result\CommandResult;
use imperazim\command\result\CommandFailure;
use imperazim\command\argument\PlayerArgument;
class PlayerSubCommand extends SubCommand {
public function onBuild(): array {
return [
'name' => 'player',
'description' => 'Displays information about a player',
'arguments' => [new PlayerArgument('target', false)]
];
}
public function onExecute(CommandResult $result): void {
$sender = $result->getSender();
$target = $result->getArgumentsList()->get('target');
if (!$target instanceof Player) {
$sender->sendMessage(TextFormat::RED . "Player not found or not online");
return;
}
$this->displayPlayerInfo($sender, $target);
}
private function displayPlayerInfo(CommandSender $sender, Player $target): void {
$health = $target->getHealth();
$maxHealth = $target->getMaxHealth();
$position = $target->getPosition();
$world = $target->getWorld()->getDisplayName();
$sender->sendMessage(TextFormat::GOLD . "=== Player Information ===");
$sender->sendMessage(TextFormat::YELLOW . "Name: " . TextFormat::WHITE . $target->getName());
$sender->sendMessage(TextFormat::YELLOW . "Health: " . $this->formatHealthBar($health, $maxHealth));
$sender->sendMessage(TextFormat::YELLOW . "Position: " . TextFormat::WHITE . "X: {$position->getX()}, Y: {$position->getY()}, Z: {$position->getZ()}");
$sender->sendMessage(TextFormat::YELLOW . "World: " . TextFormat::WHITE . $world);
$sender->sendMessage(TextFormat::YELLOW . "Gamemode: " . TextFormat::WHITE . $this->getGamemodeName($target->getGamemode()));
}
private function formatHealthBar(float $current, float $max): string {
$percentage = $current / $max;
$color = match(true) {
$percentage < 0.25 => TextFormat::RED,
$percentage < 0.5 => TextFormat::GOLD,
default => TextFormat::GREEN
};
return $color . number_format($current, 1) . TextFormat::GRAY . "/" .
TextFormat::WHITE . number_format($max, 1) . "❤";
}
private function getGamemodeName(int $mode): string {
return match($mode) {
Player::SURVIVAL => "Survival",
Player::CREATIVE => "Creative",
Player::ADVENTURE => "Adventure",
Player::SPECTATOR => "Spectator",
default => "Unknown"
};
}
public function onFailure(CommandFailure $failure): void {
$sender = $failure->getSender();
switch ($failure->getReason()) {
case CommandFailure::MISSING_ARGUMENT:
$sender->sendMessage(TextFormat::RED . "Missing player name");
$sender->sendMessage(TextFormat::YELLOW . "Usage: /info player <playerName>");
break;
case CommandFailure::INVALID_ARGUMENT:
$errors = $failure->getData()['argument_errors'] ?? [];
$firstError = $errors[0] ?? null;
if ($firstError && $firstError['argument'] === 'target') {
$sender->sendMessage(TextFormat::RED . "Invalid player: " . $firstError['message']);
}
$sender->sendMessage(TextFormat::YELLOW . "Usage: /info player <playerName>");
break;
case CommandFailure::EXECUTION_ERROR:
$sender->sendMessage(TextFormat::RED . "An error occurred while fetching player information");
break;
}
}
}Tip
Use subcommands (like PlayerSubCommand) to extend a main command with additional functionality. Register subcommands by instantiating them in the parent’s 'subcommands' array (e.g. new PlayerSubCommand($this) in the example). These classes follow the same onBuild(), onExecute(), and onFailure() structure as regular commands.