PocketMine-MP 5.23.3 git-fbaa125d0ce21ffef98fc1630881a92bedfbaa73
Loading...
Searching...
No Matches
TimingsCommand.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20 */
21
22declare(strict_types=1);
23
24namespace pocketmine\command\defaults;
25
40use Symfony\Component\Filesystem\Path;
41use function count;
42use function fclose;
43use function file_exists;
44use function fopen;
45use function fwrite;
46use function http_build_query;
47use function implode;
48use function is_array;
49use function json_decode;
50use function mkdir;
51use function strtolower;
52use const CURLOPT_AUTOREFERER;
53use const CURLOPT_FOLLOWLOCATION;
54use const CURLOPT_HTTPHEADER;
55use const CURLOPT_POST;
56use const CURLOPT_POSTFIELDS;
57use const PHP_EOL;
58
60
61 public function __construct(){
62 parent::__construct(
63 "timings",
64 KnownTranslationFactory::pocketmine_command_timings_description(),
65 KnownTranslationFactory::pocketmine_command_timings_usage()
66 );
67 $this->setPermission(DefaultPermissionNames::COMMAND_TIMINGS);
68 }
69
70 public function execute(CommandSender $sender, string $commandLabel, array $args){
71 if(count($args) !== 1){
73 }
74
75 $mode = strtolower($args[0]);
76
77 if($mode === "on"){
78 if(TimingsHandler::isEnabled()){
79 $sender->sendMessage(KnownTranslationFactory::pocketmine_command_timings_alreadyEnabled());
80 return true;
81 }
82 TimingsHandler::setEnabled();
83 Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_enable());
84
85 return true;
86 }elseif($mode === "off"){
87 TimingsHandler::setEnabled(false);
88 Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_disable());
89 return true;
90 }
91
92 if(!TimingsHandler::isEnabled()){
93 $sender->sendMessage(KnownTranslationFactory::pocketmine_command_timings_timingsDisabled());
94
95 return true;
96 }
97
98 $paste = $mode === "paste";
99
100 if($mode === "reset"){
101 TimingsHandler::reload();
102 Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_reset());
103 }elseif($mode === "merged" || $mode === "report" || $paste){
104 $timingsPromise = TimingsHandler::requestPrintTimings();
105 Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_collect());
106 $timingsPromise->onCompletion(
107 fn(array $lines) => $paste ? $this->uploadReport($lines, $sender) : $this->createReportFile($lines, $sender),
108 fn() => throw new AssumptionFailedError("This promise is not expected to be rejected")
109 );
110 }else{
112 }
113
114 return true;
115 }
116
121 private function createReportFile(array $lines, CommandSender $sender) : void{
122 $index = 0;
123 $timingFolder = Path::join($sender->getServer()->getDataPath(), "timings");
124
125 if(!file_exists($timingFolder)){
126 mkdir($timingFolder, 0777);
127 }
128 $timings = Path::join($timingFolder, "timings.txt");
129 while(file_exists($timings)){
130 $timings = Path::join($timingFolder, "timings" . (++$index) . ".txt");
131 }
132
133 $fileTimings = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => fopen($timings, "a+b"));
134 foreach($lines as $line){
135 fwrite($fileTimings, $line . PHP_EOL);
136 }
137 fclose($fileTimings);
138
139 Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings));
140 }
141
146 private function uploadReport(array $lines, CommandSender $sender) : void{
147 $data = [
148 "browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
149 "data" => implode("\n", $lines)
150 ];
151
152 $host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io");
153
154 $sender->getServer()->getAsyncPool()->submitTask(new BulkCurlTask(
155 [new BulkCurlTaskOperation(
156 "https://$host?upload=true",
157 10,
158 [],
159 [
160 CURLOPT_HTTPHEADER => [
161 "User-Agent: $agent",
162 "Content-Type: application/x-www-form-urlencoded"
163 ],
164 CURLOPT_POST => true,
165 CURLOPT_POSTFIELDS => http_build_query($data),
166 CURLOPT_AUTOREFERER => false,
167 CURLOPT_FOLLOWLOCATION => false
168 ]
169 )],
170 function(array $results) use ($sender, $host) : void{
172 if($sender instanceof Player && !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender
173 return;
174 }
175 $result = $results[0];
176 if($result instanceof InternetException){
177 $sender->getServer()->getLogger()->logException($result);
178 return;
179 }
180 $response = json_decode($result->getBody(), true);
181 if(is_array($response) && isset($response["id"])){
182 Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsRead(
183 "https://" . $host . "/?id=" . $response["id"]));
184 }else{
185 $sender->getServer()->getLogger()->debug("Invalid response from timings server (" . $result->getCode() . "): " . $result->getBody());
186 Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_pasteError());
187 }
188 }
189 ));
190 }
191}
execute(CommandSender $sender, string $commandLabel, array $args)
static trapAndRemoveFalse(\Closure $closure, int $levels=E_WARNING|E_NOTICE)