52 private const FORMAT_STRING_REGEX =
'/\G\$(\$)?((?!0)+\d+)(-)?/';
60 private array $formatStrings
62 parent::__construct($namespace, $name, KnownTranslationFactory::pocketmine_command_userDefined_description());
69 foreach($this->formatStrings as $formatString){
71 $formatArgs = CommandStringHelper::parseQuoteAware($formatString);
74 foreach($formatArgs as $formatArg){
75 $processedArg = $this->buildCommand($formatArg, $args);
76 if($processedArg ===
null){
77 $unresolved[] = $formatArg;
78 }elseif(count($unresolved) !== 0){
80 throw new \InvalidArgumentException(
"Unable to resolve format arguments (" . implode(
", ", $unresolved) .
") in command string \"$formatString\" due to missing arguments");
82 $processedArgs[] = $processedArg;
85 $commands[] = $processedArgs;
86 }
catch(\InvalidArgumentException $e){
87 $sender->sendMessage(TextFormat::RED . $e->getMessage());
92 $commandMap = $sender->getServer()->getCommandMap();
93 foreach($commands as $commandArgs){
99 $commandLabel = array_shift($commandArgs);
100 if($commandLabel ===
null){
106 if(($target = $commandMap->getCommand($commandLabel)) instanceof
Command){
108 $timings = Timings::getCommandDispatchTimings($target->getId());
109 $timings->startTiming();
112 $target->execute($sender, $commandLabel, $commandArgs);
114 $sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage() ??
"/$commandLabel")));
116 $timings->stopTiming();
120 $sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_notFound($commandLabel,
"/help")->prefix(TextFormat::RED)));
136 private function buildCommand(
string $formatString, array $args) : ?string{
138 while(($index = strpos($formatString,
'$', $index)) !==
false){
140 if($index > 0 && $formatString[$start - 1] ===
"\\"){
141 $formatString = substr($formatString, 0, $start - 1) . substr($formatString, $start);
146 $info = self::extractPlaceholderInfo($formatString, $index);
148 throw new \InvalidArgumentException(
"Invalid replacement token");
150 [$fullPlaceholder, $required, $position, $rest] = $info;
153 if($required && $position >= count($args)){
154 throw new \InvalidArgumentException(
"Missing required argument " . ($position + 1));
157 $replacement = self::buildReplacement($args, $position, $rest);
158 if($replacement ===
null){
162 $end = $index + strlen($fullPlaceholder);
163 $formatString = substr($formatString, 0, $start) . $replacement . substr($formatString, $end);
165 $index = $start + strlen($replacement);
168 return $formatString;
175 private static function buildReplacement(array $args,
int $position,
bool $rest) : ?string{
176 if($rest && $position < count($args)){
178 for($i = $position, $c = count($args); $i < $c; ++$i){
179 if($i !== $position){
183 $replacement .= $args[$i];
187 }elseif($position < count($args)){
188 return $args[$position];
197 private static function extractPlaceholderInfo(
string $commandString,
int $offset) : ?array{
198 if(preg_match(self::FORMAT_STRING_REGEX, $commandString, $matches, 0, $offset) !== 1){
202 $fullPlaceholder = $matches[0];
204 $required = ($matches[1] ??
"") !==
"";
205 $position = (int) $matches[2];
206 $variadic = ($matches[3] ??
"") !==
"";
208 return [$fullPlaceholder, $required, $position, $variadic];