setPermission(DefaultPermissionNames::COMMAND_TIMINGS); } public function execute(CommandSender $sender, string $commandLabel, array $args){ if(count($args) !== 1){ throw new InvalidCommandSyntaxException(); } $mode = strtolower($args[0]); if($mode === "on"){ if(TimingsHandler::isEnabled()){ $sender->sendMessage(KnownTranslationFactory::pocketmine_command_timings_alreadyEnabled()); return true; } TimingsHandler::setEnabled(); Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_enable()); return true; }elseif($mode === "off"){ TimingsHandler::setEnabled(false); Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_disable()); return true; } if(!TimingsHandler::isEnabled()){ $sender->sendMessage(KnownTranslationFactory::pocketmine_command_timings_timingsDisabled()); return true; } $paste = $mode === "paste"; if($mode === "reset"){ TimingsHandler::reload(); Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_reset()); }elseif($mode === "merged" || $mode === "report" || $paste){ $timingsPromise = TimingsHandler::requestPrintTimings(); Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_collect()); $timingsPromise->onCompletion( fn(array $lines) => $paste ? $this->uploadReport($lines, $sender) : $this->createReportFile($lines, $sender), fn() => throw new AssumptionFailedError("This promise is not expected to be rejected") ); }else{ throw new InvalidCommandSyntaxException(); } return true; } /** * @param string[] $lines * @phpstan-param list $lines */ private function createReportFile(array $lines, CommandSender $sender) : void{ $index = 0; $timingFolder = Path::join($sender->getServer()->getDataPath(), "timings"); if(!file_exists($timingFolder)){ mkdir($timingFolder, 0777); } $timings = Path::join($timingFolder, "timings.txt"); while(file_exists($timings)){ $timings = Path::join($timingFolder, "timings" . (++$index) . ".txt"); } $fileTimings = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => fopen($timings, "a+b")); foreach($lines as $line){ fwrite($fileTimings, $line . PHP_EOL); } fclose($fileTimings); Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings)); } /** * @param string[] $lines * @phpstan-param list $lines */ private function uploadReport(array $lines, CommandSender $sender) : void{ $data = [ "browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(), "data" => implode("\n", $lines) ]; $host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io"); $sender->getServer()->getAsyncPool()->submitTask(new BulkCurlTask( [new BulkCurlTaskOperation( "https://$host?upload=true", 10, [], [ CURLOPT_HTTPHEADER => [ "User-Agent: $agent", "Content-Type: application/x-www-form-urlencoded" ], CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_AUTOREFERER => false, CURLOPT_FOLLOWLOCATION => false ] )], function(array $results) use ($sender, $host) : void{ /** @phpstan-var array $results */ if($sender instanceof Player && !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender return; } $result = $results[0]; if($result instanceof InternetException){ $sender->getServer()->getLogger()->logException($result); return; } $response = json_decode($result->getBody(), true); if(is_array($response) && isset($response["id"])){ Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead( "https://" . $host . "/?id=" . $response["id"])); }else{ $sender->getServer()->getLogger()->debug("Invalid response from timings server (" . $result->getCode() . "): " . $result->getBody()); Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_pasteError()); } } )); } }