mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-16 08:25:29 +00:00
First step in restructuring command alias handling
This decouples Command from CommandMap, and moves the burden of tracking registered aliases to CommandMap. This allows lots of simplification, and also solves a few weird usage message issues. - Active (registered) aliases are now tracked via CommandMapEntry - Commands overriding other commands' aliases now update the original command's registered alias list properly - Command alias lists now include prefixed aliases - Prefixed aliases are now included in command data provided to the client - Server-side /pocketmine:help is now visible on the client - Permission testing can now provide context that's more relevant to the command invocation - e.g. if a user doesn't have /time set permission, it'll now show a more specific message where previously it would just show that the permission for /time was denied - User-specified label is now used for permission messages instead of command name - this is more consistent with user expectations - /help can now see prefixed aliases - Removed magic alias registration behaviour for VanillaCommand that I don't think anyone expected - Aliases are now provided to CommandMap via register() parameters, instead of being retrieved from the provided Command - Command->get/setAliases(), get/setLabel() and getName() are removed - Command->getName() pushed down to PluginCommand, as it's only useful for CommandExecutors as a command ID and shouldn't be used anywhere else
This commit is contained in:
parent
cb4364f8fd
commit
579aecfad7
@ -38,19 +38,17 @@ final class ClosureCommand extends Command{
|
||||
* @phpstan-param Execute $execute
|
||||
*/
|
||||
public function __construct(
|
||||
string $name,
|
||||
\Closure $execute,
|
||||
array $permissions,
|
||||
Translatable|string $description = "",
|
||||
Translatable|string|null $usageMessage = null,
|
||||
array $aliases = []
|
||||
){
|
||||
Utils::validateCallableSignature(
|
||||
fn(CommandSender $sender, Command $command, string $commandLabel, array $args) : mixed => 1,
|
||||
$execute,
|
||||
);
|
||||
$this->execute = $execute;
|
||||
parent::__construct($name, $description, $usageMessage, $aliases);
|
||||
parent::__construct($description, $usageMessage);
|
||||
$this->setPermissions($permissions);
|
||||
}
|
||||
|
||||
|
@ -33,52 +33,20 @@ use pocketmine\permission\PermissionManager;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\BroadcastLoggerForwarder;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use function array_values;
|
||||
use function explode;
|
||||
use function implode;
|
||||
use function str_replace;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
abstract class Command{
|
||||
|
||||
private string $name;
|
||||
|
||||
private string $nextLabel;
|
||||
private string $label;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
private array $aliases = [];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @phpstan-var list<string>
|
||||
*/
|
||||
private array $activeAliases = [];
|
||||
|
||||
private ?CommandMap $commandMap = null;
|
||||
|
||||
protected Translatable|string $description = "";
|
||||
|
||||
protected Translatable|string $usageMessage;
|
||||
|
||||
/** @var string[] */
|
||||
private array $permission = [];
|
||||
private Translatable|string|null $permissionMessage = null;
|
||||
|
||||
/**
|
||||
* @param string[] $aliases
|
||||
* @phpstan-param list<string> $aliases
|
||||
*/
|
||||
public function __construct(string $name, Translatable|string $description = "", Translatable|string|null $usageMessage = null, array $aliases = []){
|
||||
$this->name = $name;
|
||||
$this->setLabel($name);
|
||||
$this->setDescription($description);
|
||||
$this->usageMessage = $usageMessage ?? ("/" . $name);
|
||||
$this->setAliases($aliases);
|
||||
}
|
||||
public function __construct(
|
||||
private Translatable|string $description = "",
|
||||
private Translatable|string|null $usageMessage = null
|
||||
){}
|
||||
|
||||
/**
|
||||
* @param string[] $args
|
||||
@ -89,10 +57,6 @@ abstract class Command{
|
||||
*/
|
||||
abstract public function execute(CommandSender $sender, string $commandLabel, array $args);
|
||||
|
||||
public function getName() : string{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
@ -117,12 +81,17 @@ abstract class Command{
|
||||
$this->setPermissions($permission === null ? [] : explode(";", $permission, limit: PHP_INT_MAX));
|
||||
}
|
||||
|
||||
public function testPermission(CommandSender $target, ?string $permission = null) : bool{
|
||||
/**
|
||||
* @param string $context usually the command name, but may include extra args if useful (e.g. for subcommands)
|
||||
* @param CommandSender $target the target to check the permission for
|
||||
* @param string|null $permission the permission to check, if null, will check if the target has any of the command's permissions
|
||||
*/
|
||||
public function testPermission(string $context, CommandSender $target, ?string $permission = null) : bool{
|
||||
if($this->testPermissionSilent($target, $permission)){
|
||||
return true;
|
||||
}
|
||||
|
||||
$message = $this->permissionMessage ?? KnownTranslationFactory::pocketmine_command_error_permission($this->name);
|
||||
$message = $this->permissionMessage ?? KnownTranslationFactory::pocketmine_command_error_permission($context);
|
||||
if($message instanceof Translatable){
|
||||
$target->sendMessage($message->prefix(TextFormat::RED));
|
||||
}elseif($message !== ""){
|
||||
@ -143,62 +112,6 @@ abstract class Command{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getLabel() : string{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $name) : bool{
|
||||
$this->nextLabel = $name;
|
||||
if(!$this->isRegistered()){
|
||||
$this->label = $name;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the command into a Command map
|
||||
*/
|
||||
public function register(CommandMap $commandMap) : bool{
|
||||
if($this->allowChangesFrom($commandMap)){
|
||||
$this->commandMap = $commandMap;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function unregister(CommandMap $commandMap) : bool{
|
||||
if($this->allowChangesFrom($commandMap)){
|
||||
$this->commandMap = null;
|
||||
$this->activeAliases = $this->aliases;
|
||||
$this->label = $this->nextLabel;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function allowChangesFrom(CommandMap $commandMap) : bool{
|
||||
return $this->commandMap === null || $this->commandMap === $commandMap;
|
||||
}
|
||||
|
||||
public function isRegistered() : bool{
|
||||
return $this->commandMap !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
public function getAliases() : array{
|
||||
return $this->activeAliases;
|
||||
}
|
||||
|
||||
public function getPermissionMessage() : Translatable|string|null{
|
||||
return $this->permissionMessage;
|
||||
}
|
||||
@ -207,22 +120,10 @@ abstract class Command{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function getUsage() : Translatable|string{
|
||||
public function getUsage() : Translatable|string|null{
|
||||
return $this->usageMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $aliases
|
||||
* @phpstan-param list<string> $aliases
|
||||
*/
|
||||
public function setAliases(array $aliases) : void{
|
||||
$aliases = array_values($aliases); //because plugins can and will pass crap
|
||||
$this->aliases = $aliases;
|
||||
if(!$this->isRegistered()){
|
||||
$this->activeAliases = $aliases;
|
||||
}
|
||||
}
|
||||
|
||||
public function setDescription(Translatable|string $description) : void{
|
||||
$this->description = $description;
|
||||
}
|
||||
@ -231,7 +132,7 @@ abstract class Command{
|
||||
$this->permissionMessage = $permissionMessage;
|
||||
}
|
||||
|
||||
public function setUsage(Translatable|string $usage) : void{
|
||||
public function setUsage(Translatable|string|null $usage) : void{
|
||||
$this->usageMessage = $usage;
|
||||
}
|
||||
|
||||
@ -252,8 +153,4 @@ abstract class Command{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString() : string{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,12 @@ declare(strict_types=1);
|
||||
namespace pocketmine\command;
|
||||
|
||||
interface CommandMap{
|
||||
|
||||
/**
|
||||
* @param Command[] $commands
|
||||
* @param string[] $otherAliases
|
||||
*
|
||||
* @phpstan-param list<string> $otherAliases
|
||||
*/
|
||||
public function registerAll(string $fallbackPrefix, array $commands) : void;
|
||||
|
||||
public function register(string $fallbackPrefix, Command $command, ?string $label = null) : bool;
|
||||
public function register(string $fallbackPrefix, Command $command, string $preferredAlias, array $otherAliases = []) : CommandMapEntry;
|
||||
|
||||
public function dispatch(CommandSender $sender, string $cmdLine) : bool;
|
||||
|
||||
|
48
src/command/CommandMapEntry.php
Normal file
48
src/command/CommandMapEntry.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\command;
|
||||
|
||||
use pocketmine\lang\Translatable;
|
||||
|
||||
final class CommandMapEntry{
|
||||
|
||||
/**
|
||||
* @param string[] $aliases
|
||||
* @phpstan-param non-empty-list<string> $aliases
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly Command $command,
|
||||
public readonly array $aliases
|
||||
){}
|
||||
|
||||
public function getPreferredAlias() : string{
|
||||
return $this->aliases[0];
|
||||
}
|
||||
|
||||
public function getUsage() : Translatable|string{
|
||||
//TODO: usage messages ought to use user-specified alias, not command preferred
|
||||
//command-preferred is confusing if the user used a different alias
|
||||
return $this->command->getUsage() ?? "/" . $this->getPreferredAlias();
|
||||
}
|
||||
}
|
@ -50,10 +50,9 @@ class FormattedCommandAlias extends Command{
|
||||
* @param string[] $formatStrings
|
||||
*/
|
||||
public function __construct(
|
||||
string $alias,
|
||||
private array $formatStrings
|
||||
){
|
||||
parent::__construct($alias, KnownTranslationFactory::pocketmine_command_userDefined_description());
|
||||
parent::__construct(KnownTranslationFactory::pocketmine_command_userDefined_description());
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
@ -95,12 +94,14 @@ class FormattedCommandAlias extends Command{
|
||||
throw new AssumptionFailedError("This should have been checked before construction");
|
||||
}
|
||||
|
||||
if(($target = $commandMap->getCommand($commandLabel)) !== null){
|
||||
$timings = Timings::getCommandDispatchTimings($target->getLabel());
|
||||
if(($target = $commandMap->getEntry($commandLabel)) !== null){
|
||||
//TODO: using labels for command dispatch is problematic - what if the label changes?
|
||||
//maybe this should use command class instead?
|
||||
$timings = Timings::getCommandDispatchTimings($target->getPreferredAlias());
|
||||
$timings->startTiming();
|
||||
|
||||
try{
|
||||
$target->execute($sender, $commandLabel, $commandArgs);
|
||||
$target->command->execute($sender, $commandLabel, $commandArgs);
|
||||
}catch(InvalidCommandSyntaxException $e){
|
||||
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage())));
|
||||
}finally{
|
||||
|
@ -24,17 +24,23 @@ declare(strict_types=1);
|
||||
namespace pocketmine\command;
|
||||
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\Translatable;
|
||||
use pocketmine\plugin\Plugin;
|
||||
use pocketmine\plugin\PluginOwned;
|
||||
|
||||
final class PluginCommand extends Command implements PluginOwned{
|
||||
public function __construct(
|
||||
string $name,
|
||||
private string $name,
|
||||
private Plugin $owner,
|
||||
private CommandExecutor $executor
|
||||
private CommandExecutor $executor,
|
||||
Translatable|string $description = "",
|
||||
Translatable|string|null $usageMessage = null
|
||||
){
|
||||
parent::__construct($name);
|
||||
$this->usageMessage = "";
|
||||
parent::__construct($description, $usageMessage);
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
@ -43,13 +49,11 @@ final class PluginCommand extends Command implements PluginOwned{
|
||||
return false;
|
||||
}
|
||||
|
||||
$success = $this->executor->onCommand($sender, $this, $commandLabel, $args);
|
||||
|
||||
if(!$success && $this->usageMessage !== ""){
|
||||
if(!$this->executor->onCommand($sender, $this, $commandLabel, $args)){
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
return $success;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getOwningPlugin() : Plugin{
|
||||
|
@ -61,7 +61,6 @@ use pocketmine\command\defaults\TimeCommand;
|
||||
use pocketmine\command\defaults\TimingsCommand;
|
||||
use pocketmine\command\defaults\TitleCommand;
|
||||
use pocketmine\command\defaults\TransferServerCommand;
|
||||
use pocketmine\command\defaults\VanillaCommand;
|
||||
use pocketmine\command\defaults\VersionCommand;
|
||||
use pocketmine\command\defaults\WhitelistCommand;
|
||||
use pocketmine\command\defaults\XpCommand;
|
||||
@ -70,12 +69,15 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\lang\KnownTranslationFactory;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\timings\Timings;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_filter;
|
||||
use function array_shift;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function spl_object_id;
|
||||
use function str_contains;
|
||||
use function strcasecmp;
|
||||
use function strtolower;
|
||||
@ -87,122 +89,124 @@ class SimpleCommandMap implements CommandMap{
|
||||
* @var Command[]
|
||||
* @phpstan-var array<string, Command>
|
||||
*/
|
||||
protected array $knownCommands = [];
|
||||
protected array $aliasToCommandMap = [];
|
||||
|
||||
/**
|
||||
* @var CommandMapEntry[]
|
||||
* @phpstan-var array<int, CommandMapEntry>
|
||||
*/
|
||||
private array $uniqueCommands = [];
|
||||
|
||||
public function __construct(private Server $server){
|
||||
$this->setDefaultCommands();
|
||||
}
|
||||
|
||||
private function setDefaultCommands() : void{
|
||||
$this->registerAll("pocketmine", [
|
||||
new BanCommand(),
|
||||
new BanIpCommand(),
|
||||
new BanListCommand(),
|
||||
new ClearCommand(),
|
||||
new DefaultGamemodeCommand(),
|
||||
new DeopCommand(),
|
||||
new DifficultyCommand(),
|
||||
new DumpMemoryCommand(),
|
||||
new EffectCommand(),
|
||||
new EnchantCommand(),
|
||||
new GamemodeCommand(),
|
||||
new GarbageCollectorCommand(),
|
||||
new GiveCommand(),
|
||||
new HelpCommand(),
|
||||
new KickCommand(),
|
||||
new KillCommand(),
|
||||
new ListCommand(),
|
||||
new MeCommand(),
|
||||
new OpCommand(),
|
||||
new PardonCommand(),
|
||||
new PardonIpCommand(),
|
||||
new ParticleCommand(),
|
||||
new PluginsCommand(),
|
||||
new SaveCommand(),
|
||||
new SaveOffCommand(),
|
||||
new SaveOnCommand(),
|
||||
new SayCommand(),
|
||||
new SeedCommand(),
|
||||
new SetWorldSpawnCommand(),
|
||||
new SpawnpointCommand(),
|
||||
new StatusCommand(),
|
||||
new StopCommand(),
|
||||
new TeleportCommand(),
|
||||
new TellCommand(),
|
||||
new TimeCommand(),
|
||||
new TimingsCommand(),
|
||||
new TitleCommand(),
|
||||
new TransferServerCommand(),
|
||||
new VersionCommand(),
|
||||
new WhitelistCommand(),
|
||||
new XpCommand(),
|
||||
]);
|
||||
$pmPrefix = "pocketmine";
|
||||
$this->register($pmPrefix, new BanCommand(), "ban");
|
||||
$this->register($pmPrefix, new BanIpCommand(), "ban-ip");
|
||||
$this->register($pmPrefix, new BanListCommand(), "banlist");
|
||||
$this->register($pmPrefix, new ClearCommand(), "clear");
|
||||
$this->register($pmPrefix, new DefaultGamemodeCommand(), "defaultgamemode");
|
||||
$this->register($pmPrefix, new DeopCommand(), "deop");
|
||||
$this->register($pmPrefix, new DifficultyCommand(), "difficulty");
|
||||
$this->register($pmPrefix, new DumpMemoryCommand(), "dumpmemory");
|
||||
$this->register($pmPrefix, new EffectCommand(), "effect");
|
||||
$this->register($pmPrefix, new EnchantCommand(), "enchant");
|
||||
$this->register($pmPrefix, new GamemodeCommand(), "gamemode");
|
||||
$this->register($pmPrefix, new GarbageCollectorCommand(), "gc");
|
||||
$this->register($pmPrefix, new GiveCommand(), "give");
|
||||
$this->register($pmPrefix, new HelpCommand(), "help", ["?"]);
|
||||
$this->register($pmPrefix, new KickCommand(), "kick");
|
||||
$this->register($pmPrefix, new KillCommand(), "kill", ["suicide"]);
|
||||
$this->register($pmPrefix, new ListCommand(), "list");
|
||||
$this->register($pmPrefix, new MeCommand(), "me");
|
||||
$this->register($pmPrefix, new OpCommand(), "op");
|
||||
$this->register($pmPrefix, new PardonCommand(), "pardon", ["unban"]);
|
||||
$this->register($pmPrefix, new PardonIpCommand(), "pardon-ip", ["unban-ip"]);
|
||||
$this->register($pmPrefix, new ParticleCommand(), "particle");
|
||||
$this->register($pmPrefix, new PluginsCommand(), "plugins", ["pl"]);
|
||||
$this->register($pmPrefix, new SaveCommand(), "save-all");
|
||||
$this->register($pmPrefix, new SaveOffCommand(), "save-off");
|
||||
$this->register($pmPrefix, new SaveOnCommand(), "save-on");
|
||||
$this->register($pmPrefix, new SayCommand(), "say");
|
||||
$this->register($pmPrefix, new SeedCommand(), "seed");
|
||||
$this->register($pmPrefix, new SetWorldSpawnCommand(), "setworldspawn");
|
||||
$this->register($pmPrefix, new SpawnpointCommand(), "spawnpoint");
|
||||
$this->register($pmPrefix, new StatusCommand(), "status");
|
||||
$this->register($pmPrefix, new StopCommand(), "stop");
|
||||
$this->register($pmPrefix, new TeleportCommand(), "tp", ["teleport"]);
|
||||
$this->register($pmPrefix, new TellCommand(), "tell", ["w", "msg"]);
|
||||
$this->register($pmPrefix, new TimeCommand(), "time");
|
||||
$this->register($pmPrefix, new TimingsCommand(), "timings");
|
||||
$this->register($pmPrefix, new TitleCommand(), "title");
|
||||
$this->register($pmPrefix, new TransferServerCommand(), "transferserver");
|
||||
$this->register($pmPrefix, new VersionCommand(), "version", ["ver", "about"]);
|
||||
$this->register($pmPrefix, new WhitelistCommand(), "whitelist");
|
||||
$this->register($pmPrefix, new XpCommand(), "xp");
|
||||
}
|
||||
|
||||
public function registerAll(string $fallbackPrefix, array $commands) : void{
|
||||
foreach($commands as $command){
|
||||
$this->register($fallbackPrefix, $command);
|
||||
}
|
||||
}
|
||||
|
||||
public function register(string $fallbackPrefix, Command $command, ?string $label = null) : bool{
|
||||
public function register(string $fallbackPrefix, Command $command, string $preferredAlias, array $otherAliases = []) : CommandMapEntry{
|
||||
if(count($command->getPermissions()) === 0){
|
||||
throw new \InvalidArgumentException("Commands must have a permission set");
|
||||
}
|
||||
|
||||
if($label === null){
|
||||
$label = $command->getLabel();
|
||||
}
|
||||
$label = trim($label);
|
||||
$preferredAlias = trim($preferredAlias);
|
||||
$fallbackPrefix = strtolower(trim($fallbackPrefix));
|
||||
|
||||
$registered = $this->registerAlias($command, false, $fallbackPrefix, $label);
|
||||
$registeredAliases = [];
|
||||
//primary labels take precedence over any existing registrations
|
||||
$this->mapAlias($preferredAlias, $command, $registeredAliases);
|
||||
$this->mapAlias($fallbackPrefix . ":" . $preferredAlias, $command, $registeredAliases);
|
||||
|
||||
$aliases = $command->getAliases();
|
||||
foreach($aliases as $index => $alias){
|
||||
if(!$this->registerAlias($command, true, $fallbackPrefix, $alias)){
|
||||
unset($aliases[$index]);
|
||||
foreach($otherAliases as $alias){
|
||||
$this->mapAlias($fallbackPrefix . ":" . $alias, $command, $registeredAliases);
|
||||
if(!isset($this->aliasToCommandMap[$alias])){
|
||||
$this->mapAlias($alias, $command, $registeredAliases);
|
||||
}
|
||||
}
|
||||
$command->setAliases(array_values($aliases));
|
||||
|
||||
if(!$registered){
|
||||
$command->setLabel($fallbackPrefix . ":" . $label);
|
||||
$entry = new CommandMapEntry($command, $registeredAliases);
|
||||
$this->uniqueCommands[spl_object_id($command)] = $entry;
|
||||
|
||||
return $entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] &$registeredAliases
|
||||
* @phpstan-param list<string> &$registeredAliases
|
||||
* @phpstan-param-out non-empty-list<string> $registeredAliases
|
||||
*/
|
||||
private function mapAlias(string $alias, Command $command, array &$registeredAliases) : void{
|
||||
$this->unmapAlias($alias);
|
||||
$this->aliasToCommandMap[$alias] = $command;
|
||||
$registeredAliases[] = $alias;
|
||||
}
|
||||
|
||||
private function unmapAlias(string $alias) : void{
|
||||
$oldCommand = $this->aliasToCommandMap[$alias] ?? null;
|
||||
if($oldCommand !== null){
|
||||
unset($this->aliasToCommandMap[$alias]);
|
||||
$oldCommandKey = spl_object_id($oldCommand);
|
||||
$oldCommandEntry = $this->uniqueCommands[$oldCommandKey];
|
||||
$filteredAliases = array_values(array_filter($oldCommandEntry->aliases, fn(string $oldAlias) => $oldAlias !== $alias));
|
||||
if(count($filteredAliases) > 0){
|
||||
$this->uniqueCommands[$oldCommandKey] = new CommandMapEntry($oldCommand, $filteredAliases);
|
||||
}else{
|
||||
unset($this->uniqueCommands[$oldCommandKey]);
|
||||
}
|
||||
}
|
||||
|
||||
$command->register($this);
|
||||
|
||||
return $registered;
|
||||
}
|
||||
|
||||
public function unregister(Command $command) : bool{
|
||||
foreach(Utils::promoteKeys($this->knownCommands) as $lbl => $cmd){
|
||||
if($cmd === $command){
|
||||
unset($this->knownCommands[$lbl]);
|
||||
$entry = $this->uniqueCommands[spl_object_id($command)] ?? null;
|
||||
if($entry !== null){
|
||||
unset($this->uniqueCommands[spl_object_id($command)]);
|
||||
foreach($entry->aliases as $alias){
|
||||
unset($this->aliasToCommandMap[$alias]);
|
||||
}
|
||||
}
|
||||
|
||||
$command->unregister($this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function registerAlias(Command $command, bool $isAlias, string $fallbackPrefix, string $label) : bool{
|
||||
$this->knownCommands[$fallbackPrefix . ":" . $label] = $command;
|
||||
if(($command instanceof VanillaCommand || $isAlias) && isset($this->knownCommands[$label])){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(isset($this->knownCommands[$label]) && $this->knownCommands[$label]->getLabel() === $label){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!$isAlias){
|
||||
$command->setLabel($label);
|
||||
}
|
||||
|
||||
$this->knownCommands[$label] = $command;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -210,13 +214,15 @@ class SimpleCommandMap implements CommandMap{
|
||||
$args = CommandStringHelper::parseQuoteAware($commandLine);
|
||||
|
||||
$sentCommandLabel = array_shift($args);
|
||||
if($sentCommandLabel !== null && ($target = $this->getCommand($sentCommandLabel)) !== null){
|
||||
$timings = Timings::getCommandDispatchTimings($target->getLabel());
|
||||
if($sentCommandLabel !== null && ($target = $this->getEntry($sentCommandLabel)) !== null){
|
||||
//TODO: using labels for command dispatch is problematic - what if the label changes?
|
||||
//maybe this should use command class instead?
|
||||
$timings = Timings::getCommandDispatchTimings($target->getPreferredAlias());
|
||||
$timings->startTiming();
|
||||
|
||||
try{
|
||||
if($target->testPermission($sender)){
|
||||
$target->execute($sender, $sentCommandLabel, $args);
|
||||
if($target->command->testPermission($sentCommandLabel, $sender)){
|
||||
$target->command->execute($sender, $sentCommandLabel, $args);
|
||||
}
|
||||
}catch(InvalidCommandSyntaxException $e){
|
||||
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage())));
|
||||
@ -231,23 +237,36 @@ class SimpleCommandMap implements CommandMap{
|
||||
}
|
||||
|
||||
public function clearCommands() : void{
|
||||
foreach($this->knownCommands as $command){
|
||||
$command->unregister($this);
|
||||
}
|
||||
$this->knownCommands = [];
|
||||
$this->aliasToCommandMap = [];
|
||||
$this->uniqueCommands = [];
|
||||
$this->setDefaultCommands();
|
||||
}
|
||||
|
||||
public function getCommand(string $name) : ?Command{
|
||||
return $this->knownCommands[$name] ?? null;
|
||||
return $this->aliasToCommandMap[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Command[]
|
||||
* @phpstan-return array<string, Command>
|
||||
*/
|
||||
public function getCommands() : array{
|
||||
return $this->knownCommands;
|
||||
public function getAliasToCommandMap() : array{
|
||||
return $this->aliasToCommandMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommandMapEntry[]
|
||||
* @phpstan-return array<int, CommandMapEntry>
|
||||
*/
|
||||
public function getUniqueCommands() : array{
|
||||
return $this->uniqueCommands;
|
||||
}
|
||||
|
||||
public function getEntry(string $name) : ?CommandMapEntry{
|
||||
$command = $this->getCommand($name);
|
||||
return $command !== null ?
|
||||
$this->uniqueCommands[spl_object_id($command)] ?? throw new AssumptionFailedError("This should never be unset") :
|
||||
null;
|
||||
}
|
||||
|
||||
public function registerServerAliases() : void{
|
||||
@ -289,12 +308,13 @@ class SimpleCommandMap implements CommandMap{
|
||||
|
||||
//These registered commands have absolute priority
|
||||
$lowerAlias = strtolower($alias);
|
||||
$this->unmapAlias($lowerAlias);
|
||||
if(count($targets) > 0){
|
||||
$this->knownCommands[$lowerAlias] = new FormattedCommandAlias($lowerAlias, $targets);
|
||||
}else{
|
||||
unset($this->knownCommands[$lowerAlias]);
|
||||
$aliasInstance = new FormattedCommandAlias($targets);
|
||||
$registeredAliases = [];
|
||||
$this->mapAlias($lowerAlias, $aliasInstance, $registeredAliases);
|
||||
$this->uniqueCommands[spl_object_id($aliasInstance)] = new CommandMapEntry($aliasInstance, $registeredAliases);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ class BanCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"ban",
|
||||
KnownTranslationFactory::pocketmine_command_ban_player_description(),
|
||||
KnownTranslationFactory::commands_ban_usage()
|
||||
);
|
||||
|
@ -38,7 +38,6 @@ class BanIpCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"ban-ip",
|
||||
KnownTranslationFactory::pocketmine_command_ban_ip_description(),
|
||||
KnownTranslationFactory::commands_banip_usage()
|
||||
);
|
||||
|
@ -39,7 +39,6 @@ class BanListCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"banlist",
|
||||
KnownTranslationFactory::pocketmine_command_banlist_description(),
|
||||
KnownTranslationFactory::commands_banlist_usage()
|
||||
);
|
||||
|
@ -41,7 +41,6 @@ class ClearCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"clear",
|
||||
KnownTranslationFactory::pocketmine_command_clear_description(),
|
||||
KnownTranslationFactory::pocketmine_command_clear_usage()
|
||||
);
|
||||
@ -53,7 +52,7 @@ class ClearCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$target = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_CLEAR_SELF, DefaultPermissionNames::COMMAND_CLEAR_OTHER);
|
||||
$target = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_CLEAR_SELF, DefaultPermissionNames::COMMAND_CLEAR_OTHER);
|
||||
if($target === null){
|
||||
return true;
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ class DefaultGamemodeCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"defaultgamemode",
|
||||
KnownTranslationFactory::pocketmine_command_defaultgamemode_description(),
|
||||
KnownTranslationFactory::commands_defaultgamemode_usage()
|
||||
);
|
||||
|
@ -37,7 +37,6 @@ class DeopCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"deop",
|
||||
KnownTranslationFactory::pocketmine_command_deop_description(),
|
||||
KnownTranslationFactory::commands_deop_usage()
|
||||
);
|
||||
|
@ -36,7 +36,6 @@ class DifficultyCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"difficulty",
|
||||
KnownTranslationFactory::pocketmine_command_difficulty_description(),
|
||||
KnownTranslationFactory::commands_difficulty_usage()
|
||||
);
|
||||
|
@ -33,7 +33,6 @@ class DumpMemoryCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"dumpmemory",
|
||||
KnownTranslationFactory::pocketmine_command_dumpmemory_description(),
|
||||
"/dumpmemory [path]"
|
||||
);
|
||||
|
@ -38,7 +38,6 @@ class EffectCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"effect",
|
||||
KnownTranslationFactory::pocketmine_command_effect_description(),
|
||||
KnownTranslationFactory::commands_effect_usage()
|
||||
);
|
||||
@ -53,7 +52,7 @@ class EffectCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_EFFECT_SELF, DefaultPermissionNames::COMMAND_EFFECT_OTHER);
|
||||
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0], DefaultPermissionNames::COMMAND_EFFECT_SELF, DefaultPermissionNames::COMMAND_EFFECT_OTHER);
|
||||
if($player === null){
|
||||
return true;
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ class EnchantCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"enchant",
|
||||
KnownTranslationFactory::pocketmine_command_enchant_description(),
|
||||
KnownTranslationFactory::commands_enchant_usage()
|
||||
);
|
||||
@ -51,7 +50,7 @@ class EnchantCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_ENCHANT_SELF, DefaultPermissionNames::COMMAND_ENCHANT_OTHER);
|
||||
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0], DefaultPermissionNames::COMMAND_ENCHANT_SELF, DefaultPermissionNames::COMMAND_ENCHANT_OTHER);
|
||||
if($player === null){
|
||||
return true;
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ class GamemodeCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"gamemode",
|
||||
KnownTranslationFactory::pocketmine_command_gamemode_description(),
|
||||
KnownTranslationFactory::commands_gamemode_usage()
|
||||
);
|
||||
@ -56,7 +55,7 @@ class GamemodeCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
$target = $this->fetchPermittedPlayerTarget($sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_GAMEMODE_SELF, DefaultPermissionNames::COMMAND_GAMEMODE_OTHER);
|
||||
$target = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_GAMEMODE_SELF, DefaultPermissionNames::COMMAND_GAMEMODE_OTHER);
|
||||
if($target === null){
|
||||
return true;
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ class GarbageCollectorCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"gc",
|
||||
KnownTranslationFactory::pocketmine_command_gc_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_GC);
|
||||
|
@ -43,7 +43,6 @@ class GiveCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"give",
|
||||
KnownTranslationFactory::pocketmine_command_give_description(),
|
||||
KnownTranslationFactory::pocketmine_command_give_usage()
|
||||
);
|
||||
@ -58,7 +57,7 @@ class GiveCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_GIVE_SELF, DefaultPermissionNames::COMMAND_GIVE_OTHER);
|
||||
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0], DefaultPermissionNames::COMMAND_GIVE_SELF, DefaultPermissionNames::COMMAND_GIVE_OTHER);
|
||||
if($player === null){
|
||||
return true;
|
||||
}
|
||||
|
@ -47,10 +47,8 @@ class HelpCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"help",
|
||||
KnownTranslationFactory::pocketmine_command_help_description(),
|
||||
KnownTranslationFactory::commands_help_usage(),
|
||||
["?"]
|
||||
KnownTranslationFactory::commands_help_usage()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_HELP);
|
||||
}
|
||||
@ -72,11 +70,13 @@ class HelpCommand extends VanillaCommand{
|
||||
|
||||
$pageHeight = $sender->getScreenLineHeight();
|
||||
|
||||
//TODO: maybe inject this in the constructor instead of assuming the server's command map?
|
||||
$commandMap = $sender->getServer()->getCommandMap();
|
||||
if($commandName === ""){
|
||||
$commands = [];
|
||||
foreach($sender->getServer()->getCommandMap()->getCommands() as $command){
|
||||
if($command->testPermissionSilent($sender)){
|
||||
$commands[$command->getLabel()] = $command;
|
||||
foreach($commandMap->getUniqueCommands() as $commandEntry){
|
||||
if($commandEntry->command->testPermissionSilent($sender)){
|
||||
$commands[$commandEntry->getPreferredAlias()] = $commandEntry;
|
||||
}
|
||||
}
|
||||
ksort($commands, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
@ -88,31 +88,31 @@ class HelpCommand extends VanillaCommand{
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_help_header((string) $pageNumber, (string) count($commands)));
|
||||
$lang = $sender->getLanguage();
|
||||
if(isset($commands[$pageNumber - 1])){
|
||||
foreach($commands[$pageNumber - 1] as $command){
|
||||
$description = $command->getDescription();
|
||||
foreach($commands[$pageNumber - 1] as $commandEntry){
|
||||
$description = $commandEntry->command->getDescription();
|
||||
$descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description;
|
||||
$sender->sendMessage(TextFormat::DARK_GREEN . "/" . $command->getLabel() . ": " . TextFormat::RESET . $descriptionString);
|
||||
$sender->sendMessage(TextFormat::DARK_GREEN . "/" . $commandEntry->getPreferredAlias() . ": " . TextFormat::RESET . $descriptionString);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}else{
|
||||
if(($cmd = $sender->getServer()->getCommandMap()->getCommand(strtolower($commandName))) instanceof Command){
|
||||
if($cmd->testPermissionSilent($sender)){
|
||||
if(($commandEntry = $commandMap->getEntry(strtolower($commandName))) !== null){
|
||||
if($commandEntry->command->testPermissionSilent($sender)){
|
||||
$lang = $sender->getLanguage();
|
||||
$description = $cmd->getDescription();
|
||||
$description = $commandEntry->command->getDescription();
|
||||
$descriptionString = $description instanceof Translatable ? $lang->translate($description) : $description;
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_header($cmd->getLabel())
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_header($commandEntry->getPreferredAlias())
|
||||
->format(TextFormat::YELLOW . "--------- " . TextFormat::RESET, TextFormat::YELLOW . " ---------"));
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_description(TextFormat::RESET . $descriptionString)
|
||||
->prefix(TextFormat::GOLD));
|
||||
|
||||
$usage = $cmd->getUsage();
|
||||
$usage = $commandEntry->getUsage();
|
||||
$usageString = $usage instanceof Translatable ? $lang->translate($usage) : $usage;
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_usage(TextFormat::RESET . implode("\n" . TextFormat::RESET, explode("\n", $usageString, limit: PHP_INT_MAX)))
|
||||
->prefix(TextFormat::GOLD));
|
||||
|
||||
$aliases = $cmd->getAliases();
|
||||
$aliases = $commandEntry->aliases;
|
||||
sort($aliases, SORT_NATURAL);
|
||||
$sender->sendMessage(KnownTranslationFactory::pocketmine_command_help_specificCommand_aliases(TextFormat::RESET . implode(", ", $aliases))
|
||||
->prefix(TextFormat::GOLD));
|
||||
|
@ -39,7 +39,6 @@ class KickCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"kick",
|
||||
KnownTranslationFactory::pocketmine_command_kick_description(),
|
||||
KnownTranslationFactory::commands_kick_usage()
|
||||
);
|
||||
|
@ -35,10 +35,8 @@ class KillCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"kill",
|
||||
KnownTranslationFactory::pocketmine_command_kill_description(),
|
||||
KnownTranslationFactory::pocketmine_command_kill_usage(),
|
||||
["suicide"]
|
||||
KnownTranslationFactory::pocketmine_command_kill_usage()
|
||||
);
|
||||
$this->setPermissions([DefaultPermissionNames::COMMAND_KILL_SELF, DefaultPermissionNames::COMMAND_KILL_OTHER]);
|
||||
}
|
||||
@ -48,7 +46,7 @@ class KillCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_KILL_SELF, DefaultPermissionNames::COMMAND_KILL_OTHER);
|
||||
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_KILL_SELF, DefaultPermissionNames::COMMAND_KILL_OTHER);
|
||||
if($player === null){
|
||||
return true;
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ class ListCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"list",
|
||||
KnownTranslationFactory::pocketmine_command_list_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_LIST);
|
||||
|
@ -36,7 +36,6 @@ class MeCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"me",
|
||||
KnownTranslationFactory::pocketmine_command_me_description(),
|
||||
KnownTranslationFactory::commands_me_usage()
|
||||
);
|
||||
|
@ -37,7 +37,6 @@ class OpCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"op",
|
||||
KnownTranslationFactory::pocketmine_command_op_description(),
|
||||
KnownTranslationFactory::commands_op_usage()
|
||||
);
|
||||
|
@ -34,10 +34,8 @@ class PardonCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"pardon",
|
||||
KnownTranslationFactory::pocketmine_command_unban_player_description(),
|
||||
KnownTranslationFactory::commands_unban_usage(),
|
||||
["unban"]
|
||||
KnownTranslationFactory::commands_unban_usage()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_UNBAN_PLAYER);
|
||||
}
|
||||
|
@ -35,10 +35,8 @@ class PardonIpCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"pardon-ip",
|
||||
KnownTranslationFactory::pocketmine_command_unban_ip_description(),
|
||||
KnownTranslationFactory::commands_unbanip_usage(),
|
||||
["unban-ip"]
|
||||
KnownTranslationFactory::commands_unbanip_usage()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_UNBAN_IP);
|
||||
}
|
||||
|
@ -76,7 +76,6 @@ class ParticleCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"particle",
|
||||
KnownTranslationFactory::pocketmine_command_particle_description(),
|
||||
KnownTranslationFactory::pocketmine_command_particle_usage()
|
||||
);
|
||||
|
@ -38,10 +38,8 @@ class PluginsCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"plugins",
|
||||
KnownTranslationFactory::pocketmine_command_plugins_description(),
|
||||
null,
|
||||
["pl"]
|
||||
null
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_PLUGINS);
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ class SaveCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"save-all",
|
||||
KnownTranslationFactory::pocketmine_command_save_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_SAVE_PERFORM);
|
||||
|
@ -32,7 +32,6 @@ class SaveOffCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"save-off",
|
||||
KnownTranslationFactory::pocketmine_command_saveoff_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_SAVE_DISABLE);
|
||||
|
@ -32,7 +32,6 @@ class SaveOnCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"save-on",
|
||||
KnownTranslationFactory::pocketmine_command_saveon_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_SAVE_ENABLE);
|
||||
|
@ -37,7 +37,6 @@ class SayCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"say",
|
||||
KnownTranslationFactory::pocketmine_command_say_description(),
|
||||
KnownTranslationFactory::commands_say_usage()
|
||||
);
|
||||
|
@ -32,7 +32,6 @@ class SeedCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"seed",
|
||||
KnownTranslationFactory::pocketmine_command_seed_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_SEED);
|
||||
|
@ -38,7 +38,6 @@ class SetWorldSpawnCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"setworldspawn",
|
||||
KnownTranslationFactory::pocketmine_command_setworldspawn_description(),
|
||||
KnownTranslationFactory::commands_setworldspawn_usage()
|
||||
);
|
||||
|
@ -38,7 +38,6 @@ class SpawnpointCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"spawnpoint",
|
||||
KnownTranslationFactory::pocketmine_command_spawnpoint_description(),
|
||||
KnownTranslationFactory::commands_spawnpoint_usage()
|
||||
);
|
||||
@ -49,7 +48,7 @@ class SpawnpointCommand extends VanillaCommand{
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
$target = $this->fetchPermittedPlayerTarget($sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_SPAWNPOINT_SELF, DefaultPermissionNames::COMMAND_SPAWNPOINT_OTHER);
|
||||
$target = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0] ?? null, DefaultPermissionNames::COMMAND_SPAWNPOINT_SELF, DefaultPermissionNames::COMMAND_SPAWNPOINT_OTHER);
|
||||
if($target === null){
|
||||
return true;
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ class StatusCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"status",
|
||||
KnownTranslationFactory::pocketmine_command_status_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_STATUS);
|
||||
|
@ -32,7 +32,6 @@ class StopCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"stop",
|
||||
KnownTranslationFactory::pocketmine_command_stop_description()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_STOP);
|
||||
|
@ -41,10 +41,8 @@ class TeleportCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"tp",
|
||||
KnownTranslationFactory::pocketmine_command_tp_description(),
|
||||
KnownTranslationFactory::commands_tp_usage(),
|
||||
["teleport"]
|
||||
KnownTranslationFactory::commands_tp_usage()
|
||||
);
|
||||
$this->setPermissions([
|
||||
DefaultPermissionNames::COMMAND_TELEPORT_SELF,
|
||||
@ -77,7 +75,7 @@ class TeleportCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$subject = $this->fetchPermittedPlayerTarget($sender, $subjectName, DefaultPermissionNames::COMMAND_TELEPORT_SELF, DefaultPermissionNames::COMMAND_TELEPORT_OTHER);
|
||||
$subject = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $subjectName, DefaultPermissionNames::COMMAND_TELEPORT_SELF, DefaultPermissionNames::COMMAND_TELEPORT_OTHER);
|
||||
if($subject === null){
|
||||
return true;
|
||||
}
|
||||
|
@ -38,10 +38,8 @@ class TellCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"tell",
|
||||
KnownTranslationFactory::pocketmine_command_tell_description(),
|
||||
KnownTranslationFactory::commands_message_usage(),
|
||||
["w", "msg"]
|
||||
KnownTranslationFactory::commands_message_usage()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_TELL);
|
||||
}
|
||||
|
@ -36,7 +36,6 @@ class TimeCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"time",
|
||||
KnownTranslationFactory::pocketmine_command_time_description(),
|
||||
KnownTranslationFactory::pocketmine_command_time_usage()
|
||||
);
|
||||
@ -53,9 +52,10 @@ class TimeCommand extends VanillaCommand{
|
||||
if(count($args) < 1){
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
$testPermissionCtx = $commandLabel . " " . $args[0];
|
||||
|
||||
if($args[0] === "start"){
|
||||
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_TIME_START)){
|
||||
if(!$this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_TIME_START)){
|
||||
return true;
|
||||
}
|
||||
foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){
|
||||
@ -64,7 +64,7 @@ class TimeCommand extends VanillaCommand{
|
||||
Command::broadcastCommandMessage($sender, "Restarted the time");
|
||||
return true;
|
||||
}elseif($args[0] === "stop"){
|
||||
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_TIME_STOP)){
|
||||
if(!$this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_TIME_STOP)){
|
||||
return true;
|
||||
}
|
||||
foreach($sender->getServer()->getWorldManager()->getWorlds() as $world){
|
||||
@ -73,7 +73,7 @@ class TimeCommand extends VanillaCommand{
|
||||
Command::broadcastCommandMessage($sender, "Stopped the time");
|
||||
return true;
|
||||
}elseif($args[0] === "query"){
|
||||
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_TIME_QUERY)){
|
||||
if(!$this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_TIME_QUERY)){
|
||||
return true;
|
||||
}
|
||||
if($sender instanceof Player){
|
||||
@ -90,7 +90,7 @@ class TimeCommand extends VanillaCommand{
|
||||
}
|
||||
|
||||
if($args[0] === "set"){
|
||||
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_TIME_SET)){
|
||||
if(!$this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_TIME_SET)){
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -123,7 +123,7 @@ class TimeCommand extends VanillaCommand{
|
||||
}
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_time_set((string) $value));
|
||||
}elseif($args[0] === "add"){
|
||||
if(!$this->testPermission($sender, DefaultPermissionNames::COMMAND_TIME_ADD)){
|
||||
if(!$this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_TIME_ADD)){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,6 @@ class TimingsCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"timings",
|
||||
KnownTranslationFactory::pocketmine_command_timings_description(),
|
||||
KnownTranslationFactory::pocketmine_command_timings_usage()
|
||||
);
|
||||
|
@ -35,7 +35,6 @@ class TitleCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"title",
|
||||
KnownTranslationFactory::pocketmine_command_title_description(),
|
||||
KnownTranslationFactory::commands_title_usage()
|
||||
);
|
||||
@ -50,7 +49,7 @@ class TitleCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[0], DefaultPermissionNames::COMMAND_TITLE_SELF, DefaultPermissionNames::COMMAND_TITLE_OTHER);
|
||||
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[0], DefaultPermissionNames::COMMAND_TITLE_SELF, DefaultPermissionNames::COMMAND_TITLE_OTHER);
|
||||
if($player === null){
|
||||
return true;
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ class TransferServerCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"transferserver",
|
||||
KnownTranslationFactory::pocketmine_command_transferserver_description(),
|
||||
KnownTranslationFactory::pocketmine_command_transferserver_usage()
|
||||
);
|
||||
|
@ -36,7 +36,13 @@ abstract class VanillaCommand extends Command{
|
||||
public const MAX_COORD = 30000000;
|
||||
public const MIN_COORD = -30000000;
|
||||
|
||||
protected function fetchPermittedPlayerTarget(CommandSender $sender, ?string $target, string $selfPermission, string $otherPermission) : ?Player{
|
||||
protected function fetchPermittedPlayerTarget(
|
||||
string $testPermissionContext,
|
||||
CommandSender $sender,
|
||||
?string $target,
|
||||
string $selfPermission,
|
||||
string $otherPermission
|
||||
) : ?Player{
|
||||
if($target !== null){
|
||||
$player = $sender->getServer()->getPlayerByPrefix($target);
|
||||
}elseif($sender instanceof Player){
|
||||
@ -49,9 +55,12 @@ abstract class VanillaCommand extends Command{
|
||||
$sender->sendMessage(KnownTranslationFactory::commands_generic_player_notFound()->prefix(TextFormat::RED));
|
||||
return null;
|
||||
}
|
||||
//TODO: using loud testPermission here will generate misleading messages
|
||||
//e.g. if the sender has self permission and tries to use the command on another player, it will give them a
|
||||
//generic message saying that they don't have permission to use the command, which is not correct
|
||||
if(
|
||||
($player === $sender && $this->testPermission($sender, $selfPermission)) ||
|
||||
($player !== $sender && $this->testPermission($sender, $otherPermission))
|
||||
($player === $sender && $this->testPermission($testPermissionContext, $sender, $selfPermission)) ||
|
||||
($player !== $sender && $this->testPermission($testPermissionContext, $sender, $otherPermission))
|
||||
){
|
||||
return $player;
|
||||
}
|
||||
|
@ -42,10 +42,8 @@ class VersionCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"version",
|
||||
KnownTranslationFactory::pocketmine_command_version_description(),
|
||||
KnownTranslationFactory::pocketmine_command_version_usage(),
|
||||
["ver", "about"]
|
||||
KnownTranslationFactory::pocketmine_command_version_usage()
|
||||
);
|
||||
$this->setPermission(DefaultPermissionNames::COMMAND_VERSION);
|
||||
}
|
||||
|
@ -41,7 +41,6 @@ class WhitelistCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"whitelist",
|
||||
KnownTranslationFactory::pocketmine_command_whitelist_description(),
|
||||
KnownTranslationFactory::commands_whitelist_usage()
|
||||
);
|
||||
@ -56,10 +55,14 @@ class WhitelistCommand extends VanillaCommand{
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, string $commandLabel, array $args){
|
||||
if(count($args) === 0){
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
$testPermissionCtx = $commandLabel . " " . $args[0];
|
||||
if(count($args) === 1){
|
||||
switch(strtolower($args[0])){
|
||||
case "reload":
|
||||
if($this->testPermission($sender, DefaultPermissionNames::COMMAND_WHITELIST_RELOAD)){
|
||||
if($this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_WHITELIST_RELOAD)){
|
||||
$server = $sender->getServer();
|
||||
$server->getWhitelisted()->reload();
|
||||
if($server->hasWhitelist()){
|
||||
@ -70,7 +73,7 @@ class WhitelistCommand extends VanillaCommand{
|
||||
|
||||
return true;
|
||||
case "on":
|
||||
if($this->testPermission($sender, DefaultPermissionNames::COMMAND_WHITELIST_ENABLE)){
|
||||
if($this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_WHITELIST_ENABLE)){
|
||||
$server = $sender->getServer();
|
||||
$server->getConfigGroup()->setConfigBool(ServerProperties::WHITELIST, true);
|
||||
$this->kickNonWhitelistedPlayers($server);
|
||||
@ -79,14 +82,14 @@ class WhitelistCommand extends VanillaCommand{
|
||||
|
||||
return true;
|
||||
case "off":
|
||||
if($this->testPermission($sender, DefaultPermissionNames::COMMAND_WHITELIST_DISABLE)){
|
||||
if($this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_WHITELIST_DISABLE)){
|
||||
$sender->getServer()->getConfigGroup()->setConfigBool(ServerProperties::WHITELIST, false);
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_disabled());
|
||||
}
|
||||
|
||||
return true;
|
||||
case "list":
|
||||
if($this->testPermission($sender, DefaultPermissionNames::COMMAND_WHITELIST_LIST)){
|
||||
if($this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_WHITELIST_LIST)){
|
||||
$entries = $sender->getServer()->getWhitelisted()->getAll(true);
|
||||
sort($entries, SORT_STRING);
|
||||
$result = implode(", ", $entries);
|
||||
@ -112,14 +115,14 @@ class WhitelistCommand extends VanillaCommand{
|
||||
}
|
||||
switch(strtolower($args[0])){
|
||||
case "add":
|
||||
if($this->testPermission($sender, DefaultPermissionNames::COMMAND_WHITELIST_ADD)){
|
||||
if($this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_WHITELIST_ADD)){
|
||||
$sender->getServer()->addWhitelist($args[1]);
|
||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::commands_whitelist_add_success($args[1]));
|
||||
}
|
||||
|
||||
return true;
|
||||
case "remove":
|
||||
if($this->testPermission($sender, DefaultPermissionNames::COMMAND_WHITELIST_REMOVE)){
|
||||
if($this->testPermission($testPermissionCtx, $sender, DefaultPermissionNames::COMMAND_WHITELIST_REMOVE)){
|
||||
$server = $sender->getServer();
|
||||
$server->removeWhitelist($args[1]);
|
||||
if(!$server->isWhitelisted($args[1])){
|
||||
|
@ -40,7 +40,6 @@ class XpCommand extends VanillaCommand{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct(
|
||||
"xp",
|
||||
KnownTranslationFactory::pocketmine_command_xp_description(),
|
||||
KnownTranslationFactory::pocketmine_command_xp_usage()
|
||||
);
|
||||
@ -55,7 +54,7 @@ class XpCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$player = $this->fetchPermittedPlayerTarget($sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_XP_SELF, DefaultPermissionNames::COMMAND_XP_OTHER);
|
||||
$player = $this->fetchPermittedPlayerTarget($commandLabel, $sender, $args[1] ?? null, DefaultPermissionNames::COMMAND_XP_SELF, DefaultPermissionNames::COMMAND_XP_OTHER);
|
||||
if($player === null){
|
||||
return true;
|
||||
}
|
||||
|
@ -117,13 +117,14 @@ use pocketmine\world\format\io\GlobalItemDataHandlers;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\World;
|
||||
use pocketmine\YmlServerProperties;
|
||||
use function array_filter;
|
||||
use function array_map;
|
||||
use function array_values;
|
||||
use function base64_encode;
|
||||
use function bin2hex;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use function implode;
|
||||
use function in_array;
|
||||
use function is_string;
|
||||
use function json_encode;
|
||||
use function ord;
|
||||
@ -1088,23 +1089,23 @@ class NetworkSession{
|
||||
|
||||
public function syncAvailableCommands() : void{
|
||||
$commandData = [];
|
||||
foreach($this->server->getCommandMap()->getCommands() as $command){
|
||||
if(isset($commandData[$command->getLabel()]) || $command->getLabel() === "help" || !$command->testPermissionSilent($this->player)){
|
||||
foreach($this->server->getCommandMap()->getUniqueCommands() as $commandEntry){
|
||||
if(!$commandEntry->command->testPermissionSilent($this->player)){
|
||||
continue;
|
||||
}
|
||||
|
||||
$lname = strtolower($command->getLabel());
|
||||
$aliases = $command->getAliases();
|
||||
$aliasObj = null;
|
||||
if(count($aliases) > 0){
|
||||
if(!in_array($lname, $aliases, true)){
|
||||
//work around a client bug which makes the original name not show when aliases are used
|
||||
$aliases[] = $lname;
|
||||
}
|
||||
$aliasObj = new CommandEnum(ucfirst($command->getLabel()) . "Aliases", $aliases);
|
||||
//the client doesn't like it when we override /help
|
||||
$aliases = array_values(array_filter($commandEntry->aliases, fn(string $alias) => $alias !== "help" && $alias !== "?"));
|
||||
if(count($aliases) === 0){
|
||||
continue;
|
||||
}
|
||||
$firstNetworkAlias = $aliases[0];
|
||||
//use filtered aliases for command name discovery - this allows /help to still be shown as /pocketmine:help
|
||||
//on the client without conflicting with the client's built-in /help command
|
||||
$lname = strtolower($firstNetworkAlias);
|
||||
$aliasObj = new CommandEnum(ucfirst($firstNetworkAlias) . "Aliases", $aliases);
|
||||
|
||||
$description = $command->getDescription();
|
||||
$description = $commandEntry->command->getDescription();
|
||||
$data = new CommandData(
|
||||
$lname, //TODO: commands containing uppercase letters in the name crash 1.9.0 client
|
||||
$description instanceof Translatable ? $this->player->getLanguage()->translate($description) : $description,
|
||||
@ -1117,7 +1118,7 @@ class NetworkSession{
|
||||
chainedSubCommandData: []
|
||||
);
|
||||
|
||||
$commandData[$command->getLabel()] = $data;
|
||||
$commandData[] = $data;
|
||||
}
|
||||
|
||||
$this->sendDataPacket(AvailableCommandsPacket::create($commandData, [], [], []));
|
||||
|
@ -34,7 +34,6 @@ use pocketmine\utils\Config;
|
||||
use pocketmine\utils\Utils;
|
||||
use Symfony\Component\Filesystem\Path;
|
||||
use function copy;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function file_exists;
|
||||
use function fopen;
|
||||
@ -146,23 +145,12 @@ abstract class PluginBase implements Plugin, CommandExecutor{
|
||||
* Registers commands declared in the plugin manifest
|
||||
*/
|
||||
private function registerYamlCommands() : void{
|
||||
$pluginCmds = [];
|
||||
|
||||
foreach(Utils::stringifyKeys($this->description->getCommands()) as $key => $data){
|
||||
if(str_contains($key, ":")){
|
||||
$this->logger->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_plugin_commandError($key, $this->description->getFullName(), ":")));
|
||||
continue;
|
||||
}
|
||||
|
||||
$newCmd = new PluginCommand($key, $this, $this);
|
||||
if(($description = $data->getDescription()) !== null){
|
||||
$newCmd->setDescription($description);
|
||||
}
|
||||
|
||||
if(($usageMessage = $data->getUsageMessage()) !== null){
|
||||
$newCmd->setUsage($usageMessage);
|
||||
}
|
||||
|
||||
$aliasList = [];
|
||||
foreach($data->getAliases() as $alias){
|
||||
if(str_contains($alias, ":")){
|
||||
@ -172,7 +160,13 @@ abstract class PluginBase implements Plugin, CommandExecutor{
|
||||
$aliasList[] = $alias;
|
||||
}
|
||||
|
||||
$newCmd->setAliases($aliasList);
|
||||
$newCmd = new PluginCommand(
|
||||
$key,
|
||||
$this,
|
||||
$this,
|
||||
$data->getDescription() ?? "",
|
||||
$data->getUsageMessage() ?? ""
|
||||
);
|
||||
|
||||
$newCmd->setPermission($data->getPermission());
|
||||
|
||||
@ -180,11 +174,7 @@ abstract class PluginBase implements Plugin, CommandExecutor{
|
||||
$newCmd->setPermissionMessage($permissionDeniedMessage);
|
||||
}
|
||||
|
||||
$pluginCmds[] = $newCmd;
|
||||
}
|
||||
|
||||
if(count($pluginCmds) > 0){
|
||||
$this->server->getCommandMap()->registerAll($this->description->getName(), $pluginCmds);
|
||||
$this->server->getCommandMap()->register($this->description->getName(), $newCmd, $key, $aliasList);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user