FormattedCommandAlias: Invoke commands directly with pre-parsed arguments

this resolves a range of issues with quoted arguments when using placeholders, as well as improving performance (no redundant combine -> re-parse needed).
This commit is contained in:
Dylan K. Taylor 2022-05-10 16:03:09 +01:00
parent 4c29f98292
commit 3e1246acff
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
2 changed files with 50 additions and 7 deletions

View File

@ -23,8 +23,13 @@ declare(strict_types=1);
namespace pocketmine\command;
use pocketmine\Server;
use pocketmine\command\utils\CommandStringHelper;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\TextFormat;
use function array_map;
use function array_shift;
use function count;
use function preg_match;
use function strlen;
@ -52,24 +57,52 @@ class FormattedCommandAlias extends Command{
}
public function execute(CommandSender $sender, string $commandLabel, array $args){
$commands = [];
$result = false;
$result = true;
foreach($this->formatStrings as $formatString){
try{
$commands[] = $this->buildCommand($formatString, $args);
$formatArgs = CommandStringHelper::parseQuoteAware($formatString);
$commands[] = array_map(fn(string $formatArg) => $this->buildCommand($formatArg, $args), $formatArgs);
}catch(\InvalidArgumentException $e){
$sender->sendMessage(TextFormat::RED . $e->getMessage());
return false;
}
}
foreach($commands as $command){
$result |= Server::getInstance()->dispatchCommand($sender, $command, true);
$commandMap = $sender->getServer()->getCommandMap();
foreach($commands as $commandArgs){
//this approximately duplicates the logic found in SimpleCommandMap::dispatch()
//this is to allow directly invoking the commands without having to rebuild a command string and parse it
//again for no reason
//TODO: a method on CommandMap to invoke a command with pre-parsed arguments would probably be a good idea
//for a future major version
$commandLabel = array_shift($commandArgs);
if($commandLabel === null){
throw new AssumptionFailedError("This should have been checked before construction");
}
if(($target = $commandMap->getCommand($commandLabel)) !== null){
$target->timings->startTiming();
try{
$target->execute($sender, $commandLabel, $args);
}catch(InvalidCommandSyntaxException $e){
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage())));
}finally{
$target->timings->stopTiming();
}
}else{
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_notFound($commandLabel, "/help")->prefix(TextFormat::RED)));
//to match the behaviour of SimpleCommandMap::dispatch()
//this shouldn't normally happen, but might happen if the command was unregistered or modified after
//the alias was installed
$result = false;
}
}
return (bool) $result;
return $result;
}
/**

View File

@ -440,6 +440,16 @@ parameters:
count: 1
path: ../../../src/command/Command.php
-
message: "#^Cannot call method startTiming\\(\\) on pocketmine\\\\timings\\\\TimingsHandler\\|null\\.$#"
count: 1
path: ../../../src/command/FormattedCommandAlias.php
-
message: "#^Cannot call method stopTiming\\(\\) on pocketmine\\\\timings\\\\TimingsHandler\\|null\\.$#"
count: 1
path: ../../../src/command/FormattedCommandAlias.php
-
message: "#^Cannot call method startTiming\\(\\) on pocketmine\\\\timings\\\\TimingsHandler\\|null\\.$#"
count: 1