Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b82d446
auto paste on shutdown
remminiscent Oct 20, 2025
7b839f7
Merge branch 'minor-next' into auto-paste-timings
remminiscent Oct 20, 2025
6f9f0b7
fix cs
remminiscent Oct 20, 2025
97ef3e6
Merge branch 'auto-paste-timings' of github-remminiscent:remminiscent…
remminiscent Oct 20, 2025
f28e983
use shortened name
remminiscent Oct 20, 2025
f933056
blank line
remminiscent Oct 20, 2025
a5c722f
refactor timings auto paste and API
remminiscent Oct 20, 2025
4f7bfc5
plot's requests
remminiscent Oct 21, 2025
492b487
Merge branch 'minor-next' into auto-paste-timings
remminiscent Oct 21, 2025
ce4edf3
cs
remminiscent Oct 21, 2025
c80e0f3
dylan's suggestions
remminiscent Oct 21, 2025
7a92d95
changes
remminiscent Oct 21, 2025
0e65a87
fix file name
remminiscent Oct 21, 2025
6be39b0
maybe fix phpstan?
remminiscent Oct 21, 2025
43511d8
fix cs
remminiscent Oct 21, 2025
fbb3057
bruh
remminiscent Oct 21, 2025
d7a1c28
changes
remminiscent Oct 21, 2025
a47a3fd
stupid cs
remminiscent Oct 21, 2025
b4fbf0b
phpstan
remminiscent Oct 21, 2025
fdc49f9
i forgot ts
remminiscent Oct 21, 2025
dd84aae
changes
remminiscent Oct 22, 2025
b5887dc
Merge branch 'minor-next' into auto-paste-timings
remminiscent Oct 22, 2025
4b1eed3
cs
remminiscent Oct 22, 2025
32b34cd
Merge branch 'auto-paste-timings' of github-remminiscent:remminiscent…
remminiscent Oct 22, 2025
13cffe6
reject
remminiscent Oct 22, 2025
24a3f8b
cs ig
remminiscent Oct 22, 2025
1dde477
changes
remminiscent Oct 23, 2025
6e0bb89
Merge branch 'minor-next' into auto-paste-timings
remminiscent Oct 23, 2025
d66b0fc
fix cs
remminiscent Oct 23, 2025
ba27756
Merge branch 'auto-paste-timings' of github-remminiscent:remminiscent…
remminiscent Oct 23, 2025
8a6f3cf
reverted upload stuff, fixed report stuff
remminiscent Oct 24, 2025
f232a45
Merge branch 'minor-next' into auto-paste-timings
remminiscent Oct 24, 2025
8c21d69
fix CS
remminiscent Oct 24, 2025
a728465
Merge branch 'auto-paste-timings' of github-remminiscent:remminiscent…
remminiscent Oct 24, 2025
b279381
phpstan?
remminiscent Oct 24, 2025
e91d86e
Simplify code
dktapps Oct 24, 2025
50d0df2
error logging
remminiscent Oct 24, 2025
68d6d49
use AssumptionFailedError
remminiscent Oct 24, 2025
5a37e84
use Path
remminiscent Oct 24, 2025
fa33b1e
there
remminiscent Oct 24, 2025
bbdf512
like this?
remminiscent Oct 24, 2025
2af3888
Merge branch 'minor-next' into auto-paste-timings
remminiscent Oct 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -1500,6 +1500,16 @@ public function shutdown() : void{
if($this->isRunning){
$this->isRunning = false;
$this->signalHandler->unregister();

if(TimingsHandler::isEnabled()){
TimingsHandler::createReportFile(Path::join($this->getDataPath(), "timings"))->onCompletion(
function(string $timingsFile) : void{
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timingsFile)));
TimingsHandler::setEnabled(false);
},
fn() => $this->logger->error("Failed to create timings report file")
);
}
}
}

Expand Down
53 changes: 15 additions & 38 deletions src/command/defaults/TimingsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
use pocketmine\command\Command;
use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\permission\DefaultPermissionNames;
use pocketmine\player\Player;
Expand All @@ -39,24 +38,18 @@
use pocketmine\YmlServerProperties;
use Symfony\Component\Filesystem\Path;
use function count;
use function fclose;
use function file_exists;
use function fopen;
use function fwrite;
use function http_build_query;
use function implode;
use function is_array;
use function is_int;
use function is_string;
use function json_decode;
use function mkdir;
use function strtolower;
use const CURLOPT_AUTOREFERER;
use const CURLOPT_FOLLOWLOCATION;
use const CURLOPT_HTTPHEADER;
use const CURLOPT_POST;
use const CURLOPT_POSTFIELDS;
use const PHP_EOL;

class TimingsCommand extends VanillaCommand{

Expand Down Expand Up @@ -103,44 +96,28 @@ public function execute(CommandSender $sender, string $commandLabel, array $args
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")
);
if($paste){
$timingsPromise = TimingsHandler::requestPrintTimings();
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_collect());
$timingsPromise->onCompletion(
fn(array $lines) => $this->uploadReport($lines, $sender),
fn() => throw new AssumptionFailedError("This promise is not expected to be rejected")
);
}else{
TimingsHandler::createReportFile(Path::join($sender->getServer()->getDataPath(), "timings"))->onCompletion(
function(string $timingsFile) use ($sender) : void{
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timingsFile));
},
fn() => $sender->getServer()->getLogger()->error("Failed to create timings report file")
);
}
}else{
throw new InvalidCommandSyntaxException();
}

return true;
}

/**
* @param string[] $lines
* @phpstan-param list<string> $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<string> $lines
Expand Down
56 changes: 56 additions & 0 deletions src/timings/TimingsHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,26 @@
namespace pocketmine\timings;

use pmmp\thread\Thread as NativeThread;
use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\promise\Promise;
use pocketmine\promise\PromiseResolver;
use pocketmine\Server;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\ObjectSet;
use pocketmine\utils\Utils;
use Symfony\Component\Filesystem\Path;
use function array_merge;
use function array_push;
use function date;
use function fclose;
use function fopen;
use function fwrite;
use function hrtime;
use function implode;
use function is_dir;
use function mkdir;
use function spl_object_id;
use const PHP_EOL;

/**
* @phpstan-type CollectPromise Promise<list<string>>
Expand Down Expand Up @@ -332,4 +342,50 @@ public function reset() : void{
$this->recordsByParent = [];
$this->timingDepth = 0;
}

/**
* Creates a timings report file locally in the provided file path.
* Collects timings data and returns a promise that resolves with the created file.
*
* @param string $directory directory path to the timings folder.
* @param string|null $fileName Optional custom file name, If null, uses default timings file naming
*
* @phpstan-return Promise<string>
*/
public static function createReportFile(string $directory, ?string $fileName = null) : Promise{
$timingsPromise = self::requestPrintTimings();

/** @var PromiseResolver<string> $resolver */
$resolver = new PromiseResolver();

$timingsPromise->onCompletion(
function(array $lines) use ($fileName, $directory, $resolver) : void{
if($fileName === null){
$date = date('Y-m-d_H.i.s_T');
$fileName = "timings_{$date}";
}
if(!@mkdir($directory, 0777, true) && !is_dir($directory)){
$resolver->reject();
return;
}
$timingsFile = Path::join($directory, $fileName . ".txt");
try{
$handle = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => fopen($timingsFile, "x+b"));
}catch(\ErrorException){
//TODO: it'd be better if we could report this to the promise callback
$resolver->reject();
return;
}
foreach($lines as $line){
fwrite($handle, $line . PHP_EOL);
}
fclose($handle);

$resolver->resolve($timingsFile);
},
fn() => throw new AssumptionFailedError("This promise is not expected to be rejected")
);

return $resolver->getPromise();
}
}