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)));
 
 
  126    private function buildCommand(
string $formatString, array $args) : ?string{
 
  128        while(($index = strpos($formatString, 
'$', $index)) !== 
false){
 
  130            if($index > 0 && $formatString[$start - 1] === 
"\\"){
 
  131                $formatString = substr($formatString, 0, $start - 1) . substr($formatString, $start);
 
  136            $info = self::extractPlaceholderInfo($formatString, $index);
 
  138                throw new \InvalidArgumentException(
"Invalid replacement token");
 
  140            [$fullPlaceholder, $required, $position, $rest] = $info;
 
  143            if($required && $position >= count($args)){
 
  144                throw new \InvalidArgumentException(
"Missing required argument " . ($position + 1));
 
  147            $replacement = self::buildReplacement($args, $position, $rest);
 
  148            if($replacement === 
null){
 
  152            $end = $index + strlen($fullPlaceholder);
 
  153            $formatString = substr($formatString, 0, $start) . $replacement . substr($formatString, $end);
 
  155            $index = $start + strlen($replacement);
 
  158        return $formatString;
 
  165    private static function buildReplacement(array $args, 
int $position, 
bool $rest) : ?string{
 
  166        if($rest && $position < count($args)){
 
  168            for($i = $position, $c = count($args); $i < $c; ++$i){
 
  169                if($i !== $position){
 
  173                $replacement .= $args[$i];
 
  177        }elseif($position < count($args)){
 
  178            return $args[$position];
 
  187    private static function extractPlaceholderInfo(
string $commandString, 
int $offset) : ?array{
 
  188        if(preg_match(self::FORMAT_STRING_REGEX, $commandString, $matches, 0, $offset) !== 1){
 
  192        $fullPlaceholder = $matches[0];
 
  194        $required = ($matches[1] ?? 
"") !== 
"";
 
  195        $position = (int) $matches[2];
 
  196        $variadic = ($matches[3] ?? 
"") !== 
"";
 
  198        return [$fullPlaceholder, $required, $position, $variadic];