47 private const FORMAT_STRING_REGEX =
'/\G\$(\$)?((?!0)+\d+)(-)?/';
54 private array $formatStrings
56 parent::__construct($alias, KnownTranslationFactory::pocketmine_command_userDefined_description());
63 foreach($this->formatStrings as $formatString){
65 $formatArgs = CommandStringHelper::parseQuoteAware($formatString);
68 foreach($formatArgs as $formatArg){
69 $processedArg = $this->buildCommand($formatArg, $args);
70 if($processedArg ===
null){
71 $unresolved[] = $formatArg;
72 }elseif(count($unresolved) !== 0){
74 throw new \InvalidArgumentException(
"Unable to resolve format arguments (" . implode(
", ", $unresolved) .
") in command string \"$formatString\" due to missing arguments");
76 $processedArgs[] = $processedArg;
79 $commands[] = $processedArgs;
80 }
catch(\InvalidArgumentException $e){
81 $sender->sendMessage(TextFormat::RED . $e->getMessage());
86 $commandMap = $sender->getServer()->getCommandMap();
87 foreach($commands as $commandArgs){
93 $commandLabel = array_shift($commandArgs);
94 if($commandLabel ===
null){
98 if(($target = $commandMap->getCommand($commandLabel)) !==
null){
99 $timings = Timings::getCommandDispatchTimings($target->getLabel());
100 $timings->startTiming();
103 $target->execute($sender, $commandLabel, $commandArgs);
105 $sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage())));
107 $timings->stopTiming();
110 $sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_notFound($commandLabel,
"/help")->prefix(TextFormat::RED)));
125 private function buildCommand(
string $formatString, array $args) : ?string{
127 while(($index = strpos($formatString,
'$', $index)) !==
false){
129 if($index > 0 && $formatString[$start - 1] ===
"\\"){
130 $formatString = substr($formatString, 0, $start - 1) . substr($formatString, $start);
135 $info = self::extractPlaceholderInfo($formatString, $index);
137 throw new \InvalidArgumentException(
"Invalid replacement token");
139 [$fullPlaceholder, $required, $position, $rest] = $info;
142 if($required && $position >= count($args)){
143 throw new \InvalidArgumentException(
"Missing required argument " . ($position + 1));
146 $replacement = self::buildReplacement($args, $position, $rest);
147 if($replacement ===
null){
151 $end = $index + strlen($fullPlaceholder);
152 $formatString = substr($formatString, 0, $start) . $replacement . substr($formatString, $end);
154 $index = $start + strlen($replacement);
157 return $formatString;
164 private static function buildReplacement(array $args,
int $position,
bool $rest) : ?string{
165 if($rest && $position < count($args)){
167 for($i = $position, $c = count($args); $i < $c; ++$i){
168 if($i !== $position){
172 $replacement .= $args[$i];
176 }elseif($position < count($args)){
177 return $args[$position];
186 private static function extractPlaceholderInfo(
string $commandString,
int $offset) : ?array{
187 if(preg_match(self::FORMAT_STRING_REGEX, $commandString, $matches, 0, $offset) !== 1){
191 $fullPlaceholder = $matches[0];
193 $required = ($matches[1] ??
"") !==
"";
194 $position = (int) $matches[2];
195 $variadic = ($matches[3] ??
"") !==
"";
197 return [$fullPlaceholder, $required, $position, $variadic];