mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-07 12:18:46 +00:00
Merge branch 'minor-next' into major-next
This commit is contained in:
commit
02d181d0c8
@ -41,7 +41,7 @@
|
|||||||
"pocketmine/callback-validator": "^1.0.2",
|
"pocketmine/callback-validator": "^1.0.2",
|
||||||
"pocketmine/color": "^0.3.0",
|
"pocketmine/color": "^0.3.0",
|
||||||
"pocketmine/errorhandler": "^0.7.0",
|
"pocketmine/errorhandler": "^0.7.0",
|
||||||
"pocketmine/locale-data": "~2.21.0",
|
"pocketmine/locale-data": "~2.22.0",
|
||||||
"pocketmine/log": "^0.4.0",
|
"pocketmine/log": "^0.4.0",
|
||||||
"pocketmine/math": "~1.0.0",
|
"pocketmine/math": "~1.0.0",
|
||||||
"pocketmine/nbt": "~1.0.0",
|
"pocketmine/nbt": "~1.0.0",
|
||||||
|
14
composer.lock
generated
14
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "476374fb3d22e26a97c1dea8c6736faf",
|
"content-hash": "c57e8f52250edfd03906219fe14fc240",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "adhocore/json-comment",
|
"name": "adhocore/json-comment",
|
||||||
@ -420,16 +420,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/locale-data",
|
"name": "pocketmine/locale-data",
|
||||||
"version": "2.21.1",
|
"version": "2.22.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/pmmp/Language.git",
|
"url": "https://github.com/pmmp/Language.git",
|
||||||
"reference": "fdba0f764d6281f64e5968dca94fdab96bf4e167"
|
"reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/pmmp/Language/zipball/fdba0f764d6281f64e5968dca94fdab96bf4e167",
|
"url": "https://api.github.com/repos/pmmp/Language/zipball/aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d",
|
||||||
"reference": "fdba0f764d6281f64e5968dca94fdab96bf4e167",
|
"reference": "aed64e9ca92ffbb20788b3b3bb75b60e4f0eae2d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
@ -437,9 +437,9 @@
|
|||||||
"description": "Language resources used by PocketMine-MP",
|
"description": "Language resources used by PocketMine-MP",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/pmmp/Language/issues",
|
"issues": "https://github.com/pmmp/Language/issues",
|
||||||
"source": "https://github.com/pmmp/Language/tree/2.21.1"
|
"source": "https://github.com/pmmp/Language/tree/2.22.0"
|
||||||
},
|
},
|
||||||
"time": "2024-11-14T23:11:22+00:00"
|
"time": "2024-11-16T13:28:01+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pocketmine/log",
|
"name": "pocketmine/log",
|
||||||
|
@ -90,6 +90,8 @@ use pocketmine\promise\Promise;
|
|||||||
use pocketmine\promise\PromiseResolver;
|
use pocketmine\promise\PromiseResolver;
|
||||||
use pocketmine\resourcepacks\ResourcePackManager;
|
use pocketmine\resourcepacks\ResourcePackManager;
|
||||||
use pocketmine\scheduler\AsyncPool;
|
use pocketmine\scheduler\AsyncPool;
|
||||||
|
use pocketmine\scheduler\TimingsCollectionTask;
|
||||||
|
use pocketmine\scheduler\TimingsControlTask;
|
||||||
use pocketmine\snooze\SleeperHandler;
|
use pocketmine\snooze\SleeperHandler;
|
||||||
use pocketmine\stats\SendUsageTask;
|
use pocketmine\stats\SendUsageTask;
|
||||||
use pocketmine\thread\log\AttachableThreadSafeLogger;
|
use pocketmine\thread\log\AttachableThreadSafeLogger;
|
||||||
@ -895,7 +897,36 @@ class Server{
|
|||||||
$poolSize = max(1, (int) $poolSize);
|
$poolSize = max(1, (int) $poolSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false));
|
||||||
|
$this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND);
|
||||||
|
|
||||||
$this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt(Yml::MEMORY_ASYNC_WORKER_HARD_LIMIT, 256)), $this->autoloader, $this->logger, $this->tickSleeper);
|
$this->asyncPool = new AsyncPool($poolSize, max(-1, $this->configGroup->getPropertyInt(Yml::MEMORY_ASYNC_WORKER_HARD_LIMIT, 256)), $this->autoloader, $this->logger, $this->tickSleeper);
|
||||||
|
$this->asyncPool->addWorkerStartHook(function(int $i) : void{
|
||||||
|
if(TimingsHandler::isEnabled()){
|
||||||
|
$this->asyncPool->submitTaskToWorker(TimingsControlTask::setEnabled(true), $i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
TimingsHandler::getToggleCallbacks()->add(function(bool $enable) : void{
|
||||||
|
foreach($this->asyncPool->getRunningWorkers() as $workerId){
|
||||||
|
$this->asyncPool->submitTaskToWorker(TimingsControlTask::setEnabled($enable), $workerId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
TimingsHandler::getReloadCallbacks()->add(function() : void{
|
||||||
|
foreach($this->asyncPool->getRunningWorkers() as $workerId){
|
||||||
|
$this->asyncPool->submitTaskToWorker(TimingsControlTask::reload(), $workerId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
TimingsHandler::getCollectCallbacks()->add(function() : array{
|
||||||
|
$promises = [];
|
||||||
|
foreach($this->asyncPool->getRunningWorkers() as $workerId){
|
||||||
|
$resolver = new PromiseResolver();
|
||||||
|
$this->asyncPool->submitTaskToWorker(new TimingsCollectionTask($resolver), $workerId);
|
||||||
|
|
||||||
|
$promises[] = $resolver->getPromise();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $promises;
|
||||||
|
});
|
||||||
|
|
||||||
$netCompressionThreshold = -1;
|
$netCompressionThreshold = -1;
|
||||||
if($this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256) >= 0){
|
if($this->configGroup->getPropertyInt(Yml::NETWORK_BATCH_THRESHOLD, 256) >= 0){
|
||||||
@ -969,9 +1000,6 @@ class Server{
|
|||||||
)));
|
)));
|
||||||
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
|
$this->logger->info($this->language->translate(KnownTranslationFactory::pocketmine_server_license($this->getName())));
|
||||||
|
|
||||||
TimingsHandler::setEnabled($this->configGroup->getPropertyBool(Yml::SETTINGS_ENABLE_PROFILING, false));
|
|
||||||
$this->profilingTickRate = $this->configGroup->getPropertyInt(Yml::SETTINGS_PROFILE_REPORT_TRIGGER, self::TARGET_TICKS_PER_SECOND);
|
|
||||||
|
|
||||||
DefaultPermissions::registerCorePermissions();
|
DefaultPermissions::registerCorePermissions();
|
||||||
|
|
||||||
$this->commandMap = new SimpleCommandMap($this);
|
$this->commandMap = new SimpleCommandMap($this);
|
||||||
|
@ -69,6 +69,10 @@ class Campfire extends Transparent{
|
|||||||
|
|
||||||
private const UPDATE_INTERVAL_TICKS = 10;
|
private const UPDATE_INTERVAL_TICKS = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This was added by mistake. It can't be relied on as the inventory won't be initialized if this block
|
||||||
|
* has never been set in the world.
|
||||||
|
*/
|
||||||
protected CampfireInventory $inventory;
|
protected CampfireInventory $inventory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,6 +133,10 @@ class Campfire extends Transparent{
|
|||||||
return [AxisAlignedBB::one()->trim(Facing::UP, 9 / 16)];
|
return [AxisAlignedBB::one()->trim(Facing::UP, 9 / 16)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This was added by mistake. It can't be relied on as the inventory won't be initialized if this block
|
||||||
|
* has never been set in the world.
|
||||||
|
*/
|
||||||
public function getInventory() : CampfireInventory{
|
public function getInventory() : CampfireInventory{
|
||||||
return $this->inventory;
|
return $this->inventory;
|
||||||
}
|
}
|
||||||
|
@ -26,28 +26,28 @@ namespace pocketmine\command\defaults;
|
|||||||
use pocketmine\command\Command;
|
use pocketmine\command\Command;
|
||||||
use pocketmine\command\CommandSender;
|
use pocketmine\command\CommandSender;
|
||||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||||
|
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||||
use pocketmine\lang\KnownTranslationFactory;
|
use pocketmine\lang\KnownTranslationFactory;
|
||||||
use pocketmine\permission\DefaultPermissionNames;
|
use pocketmine\permission\DefaultPermissionNames;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
use pocketmine\scheduler\BulkCurlTask;
|
use pocketmine\scheduler\BulkCurlTask;
|
||||||
use pocketmine\scheduler\BulkCurlTaskOperation;
|
use pocketmine\scheduler\BulkCurlTaskOperation;
|
||||||
use pocketmine\timings\TimingsHandler;
|
use pocketmine\timings\TimingsHandler;
|
||||||
|
use pocketmine\utils\AssumptionFailedError;
|
||||||
use pocketmine\utils\InternetException;
|
use pocketmine\utils\InternetException;
|
||||||
use pocketmine\utils\InternetRequestResult;
|
use pocketmine\utils\InternetRequestResult;
|
||||||
use pocketmine\utils\Utils;
|
|
||||||
use pocketmine\YmlServerProperties;
|
use pocketmine\YmlServerProperties;
|
||||||
use Symfony\Component\Filesystem\Path;
|
use Symfony\Component\Filesystem\Path;
|
||||||
use function count;
|
use function count;
|
||||||
use function fclose;
|
use function fclose;
|
||||||
use function file_exists;
|
use function file_exists;
|
||||||
use function fopen;
|
use function fopen;
|
||||||
use function fseek;
|
|
||||||
use function fwrite;
|
use function fwrite;
|
||||||
use function http_build_query;
|
use function http_build_query;
|
||||||
|
use function implode;
|
||||||
use function is_array;
|
use function is_array;
|
||||||
use function json_decode;
|
use function json_decode;
|
||||||
use function mkdir;
|
use function mkdir;
|
||||||
use function stream_get_contents;
|
|
||||||
use function strtolower;
|
use function strtolower;
|
||||||
use const CURLOPT_AUTOREFERER;
|
use const CURLOPT_AUTOREFERER;
|
||||||
use const CURLOPT_FOLLOWLOCATION;
|
use const CURLOPT_FOLLOWLOCATION;
|
||||||
@ -101,10 +101,24 @@ class TimingsCommand extends VanillaCommand{
|
|||||||
TimingsHandler::reload();
|
TimingsHandler::reload();
|
||||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_reset());
|
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_reset());
|
||||||
}elseif($mode === "merged" || $mode === "report" || $paste){
|
}elseif($mode === "merged" || $mode === "report" || $paste){
|
||||||
$timings = "";
|
$timingsPromise = TimingsHandler::requestPrintTimings();
|
||||||
if($paste){
|
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_collect());
|
||||||
$fileTimings = Utils::assumeNotFalse(fopen("php://temp", "r+b"), "Opening php://temp should never fail");
|
$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{
|
}else{
|
||||||
|
throw new InvalidCommandSyntaxException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $lines
|
||||||
|
* @phpstan-param list<string> $lines
|
||||||
|
*/
|
||||||
|
private function createReportFile(array $lines, CommandSender $sender) : void{
|
||||||
$index = 0;
|
$index = 0;
|
||||||
$timingFolder = Path::join($sender->getServer()->getDataPath(), "timings");
|
$timingFolder = Path::join($sender->getServer()->getDataPath(), "timings");
|
||||||
|
|
||||||
@ -116,20 +130,24 @@ class TimingsCommand extends VanillaCommand{
|
|||||||
$timings = Path::join($timingFolder, "timings" . (++$index) . ".txt");
|
$timings = Path::join($timingFolder, "timings" . (++$index) . ".txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileTimings = fopen($timings, "a+b");
|
$fileTimings = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => fopen($timings, "a+b"));
|
||||||
}
|
|
||||||
$lines = TimingsHandler::printTimings();
|
|
||||||
foreach($lines as $line){
|
foreach($lines as $line){
|
||||||
fwrite($fileTimings, $line . PHP_EOL);
|
fwrite($fileTimings, $line . PHP_EOL);
|
||||||
}
|
}
|
||||||
|
fclose($fileTimings);
|
||||||
|
|
||||||
if($paste){
|
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings));
|
||||||
fseek($fileTimings, 0);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $lines
|
||||||
|
* @phpstan-param list<string> $lines
|
||||||
|
*/
|
||||||
|
private function uploadReport(array $lines, CommandSender $sender) : void{
|
||||||
$data = [
|
$data = [
|
||||||
"browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
|
"browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
|
||||||
"data" => $content = stream_get_contents($fileTimings)
|
"data" => implode("\n", $lines)
|
||||||
];
|
];
|
||||||
fclose($fileTimings);
|
|
||||||
|
|
||||||
$host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io");
|
$host = $sender->getServer()->getConfigGroup()->getPropertyString(YmlServerProperties::TIMINGS_HOST, "timings.pmmp.io");
|
||||||
|
|
||||||
@ -169,14 +187,5 @@ class TimingsCommand extends VanillaCommand{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
}else{
|
|
||||||
fclose($fileTimings);
|
|
||||||
Command::broadcastCommandMessage($sender, KnownTranslationFactory::pocketmine_command_timings_timingsWrite($timings));
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
throw new InvalidCommandSyntaxException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1441,6 +1441,10 @@ final class KnownTranslationFactory{
|
|||||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_ALREADYENABLED, []);
|
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_ALREADYENABLED, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function pocketmine_command_timings_collect() : Translatable{
|
||||||
|
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_COLLECT, []);
|
||||||
|
}
|
||||||
|
|
||||||
public static function pocketmine_command_timings_description() : Translatable{
|
public static function pocketmine_command_timings_description() : Translatable{
|
||||||
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_DESCRIPTION, []);
|
return new Translatable(KnownTranslationKeys::POCKETMINE_COMMAND_TIMINGS_DESCRIPTION, []);
|
||||||
}
|
}
|
||||||
|
@ -315,6 +315,7 @@ final class KnownTranslationKeys{
|
|||||||
public const POCKETMINE_COMMAND_TIME_DESCRIPTION = "pocketmine.command.time.description";
|
public const POCKETMINE_COMMAND_TIME_DESCRIPTION = "pocketmine.command.time.description";
|
||||||
public const POCKETMINE_COMMAND_TIME_USAGE = "pocketmine.command.time.usage";
|
public const POCKETMINE_COMMAND_TIME_USAGE = "pocketmine.command.time.usage";
|
||||||
public const POCKETMINE_COMMAND_TIMINGS_ALREADYENABLED = "pocketmine.command.timings.alreadyEnabled";
|
public const POCKETMINE_COMMAND_TIMINGS_ALREADYENABLED = "pocketmine.command.timings.alreadyEnabled";
|
||||||
|
public const POCKETMINE_COMMAND_TIMINGS_COLLECT = "pocketmine.command.timings.collect";
|
||||||
public const POCKETMINE_COMMAND_TIMINGS_DESCRIPTION = "pocketmine.command.timings.description";
|
public const POCKETMINE_COMMAND_TIMINGS_DESCRIPTION = "pocketmine.command.timings.description";
|
||||||
public const POCKETMINE_COMMAND_TIMINGS_DISABLE = "pocketmine.command.timings.disable";
|
public const POCKETMINE_COMMAND_TIMINGS_DISABLE = "pocketmine.command.timings.disable";
|
||||||
public const POCKETMINE_COMMAND_TIMINGS_ENABLE = "pocketmine.command.timings.enable";
|
public const POCKETMINE_COMMAND_TIMINGS_ENABLE = "pocketmine.command.timings.enable";
|
||||||
|
@ -27,6 +27,7 @@ use pmmp\thread\Runnable;
|
|||||||
use pmmp\thread\ThreadSafe;
|
use pmmp\thread\ThreadSafe;
|
||||||
use pmmp\thread\ThreadSafeArray;
|
use pmmp\thread\ThreadSafeArray;
|
||||||
use pocketmine\thread\NonThreadSafeValue;
|
use pocketmine\thread\NonThreadSafeValue;
|
||||||
|
use pocketmine\timings\Timings;
|
||||||
use function array_key_exists;
|
use function array_key_exists;
|
||||||
use function igbinary_serialize;
|
use function igbinary_serialize;
|
||||||
use function igbinary_unserialize;
|
use function igbinary_unserialize;
|
||||||
@ -78,7 +79,14 @@ abstract class AsyncTask extends Runnable{
|
|||||||
public function run() : void{
|
public function run() : void{
|
||||||
$this->result = null;
|
$this->result = null;
|
||||||
|
|
||||||
|
$timings = Timings::getAsyncTaskRunTimings($this);
|
||||||
|
$timings->startTiming();
|
||||||
|
|
||||||
|
try{
|
||||||
$this->onRun();
|
$this->onRun();
|
||||||
|
}finally{
|
||||||
|
$timings->stopTiming();
|
||||||
|
}
|
||||||
|
|
||||||
$this->finished = true;
|
$this->finished = true;
|
||||||
AsyncWorker::getNotifier()->wakeupSleeper();
|
AsyncWorker::getNotifier()->wakeupSleeper();
|
||||||
|
60
src/scheduler/TimingsCollectionTask.php
Normal file
60
src/scheduler/TimingsCollectionTask.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\scheduler;
|
||||||
|
|
||||||
|
use pocketmine\promise\PromiseResolver;
|
||||||
|
use pocketmine\timings\TimingsHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-type Resolver PromiseResolver<list<string>>
|
||||||
|
*/
|
||||||
|
final class TimingsCollectionTask extends AsyncTask{
|
||||||
|
private const TLS_KEY_RESOLVER = "resolver";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-param PromiseResolver<list<string>> $promiseResolver
|
||||||
|
*/
|
||||||
|
public function __construct(PromiseResolver $promiseResolver){
|
||||||
|
$this->storeLocal(self::TLS_KEY_RESOLVER, $promiseResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onRun() : void{
|
||||||
|
$this->setResult(TimingsHandler::printCurrentThreadRecords());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onCompletion() : void{
|
||||||
|
/**
|
||||||
|
* @var string[] $result
|
||||||
|
* @phpstan-var list<string> $result
|
||||||
|
*/
|
||||||
|
$result = $this->getResult();
|
||||||
|
/**
|
||||||
|
* @var PromiseResolver $promiseResolver
|
||||||
|
* @phpstan-var PromiseResolver<list<string>> $promiseResolver
|
||||||
|
*/
|
||||||
|
$promiseResolver = $this->fetchLocal(self::TLS_KEY_RESOLVER);
|
||||||
|
|
||||||
|
$promiseResolver->resolve($result);
|
||||||
|
}
|
||||||
|
}
|
60
src/scheduler/TimingsControlTask.php
Normal file
60
src/scheduler/TimingsControlTask.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\scheduler;
|
||||||
|
|
||||||
|
use pocketmine\timings\TimingsHandler;
|
||||||
|
|
||||||
|
final class TimingsControlTask extends AsyncTask{
|
||||||
|
|
||||||
|
private const ENABLE = 1;
|
||||||
|
private const DISABLE = 2;
|
||||||
|
private const RELOAD = 3;
|
||||||
|
|
||||||
|
private function __construct(
|
||||||
|
private int $operation
|
||||||
|
){}
|
||||||
|
|
||||||
|
public static function setEnabled(bool $enable) : self{
|
||||||
|
return new self($enable ? self::ENABLE : self::DISABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function reload() : self{
|
||||||
|
return new self(self::RELOAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onRun() : void{
|
||||||
|
if($this->operation === self::ENABLE){
|
||||||
|
TimingsHandler::setEnabled(true);
|
||||||
|
\GlobalLogger::get()->debug("Enabled timings");
|
||||||
|
}elseif($this->operation === self::DISABLE){
|
||||||
|
TimingsHandler::setEnabled(false);
|
||||||
|
\GlobalLogger::get()->debug("Disabled timings");
|
||||||
|
}elseif($this->operation === self::RELOAD){
|
||||||
|
TimingsHandler::reload();
|
||||||
|
\GlobalLogger::get()->debug("Reset timings");
|
||||||
|
}else{
|
||||||
|
throw new \InvalidArgumentException("Invalid operation $this->operation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,7 @@ use function str_starts_with;
|
|||||||
|
|
||||||
abstract class Timings{
|
abstract class Timings{
|
||||||
public const GROUP_MINECRAFT = "Minecraft";
|
public const GROUP_MINECRAFT = "Minecraft";
|
||||||
|
/** @deprecated No longer used */
|
||||||
public const GROUP_BREAKDOWN = "Minecraft - Breakdown";
|
public const GROUP_BREAKDOWN = "Minecraft - Breakdown";
|
||||||
|
|
||||||
private static bool $initialized = false;
|
private static bool $initialized = false;
|
||||||
@ -124,11 +125,16 @@ abstract class Timings{
|
|||||||
|
|
||||||
/** @var TimingsHandler[] */
|
/** @var TimingsHandler[] */
|
||||||
private static array $asyncTaskProgressUpdate = [];
|
private static array $asyncTaskProgressUpdate = [];
|
||||||
|
|
||||||
/** @var TimingsHandler[] */
|
/** @var TimingsHandler[] */
|
||||||
private static array $asyncTaskCompletion = [];
|
private static array $asyncTaskCompletion = [];
|
||||||
/** @var TimingsHandler[] */
|
/** @var TimingsHandler[] */
|
||||||
private static array $asyncTaskError = [];
|
private static array $asyncTaskError = [];
|
||||||
|
|
||||||
|
private static TimingsHandler $asyncTaskWorkers;
|
||||||
|
/** @var TimingsHandler[] */
|
||||||
|
private static array $asyncTaskRun = [];
|
||||||
|
|
||||||
public static function init() : void{
|
public static function init() : void{
|
||||||
if(self::$initialized){
|
if(self::$initialized){
|
||||||
return;
|
return;
|
||||||
@ -188,6 +194,8 @@ abstract class Timings{
|
|||||||
self::$asyncTaskCompletionParent = new TimingsHandler("Async Tasks - Completion Handlers", self::$schedulerAsync);
|
self::$asyncTaskCompletionParent = new TimingsHandler("Async Tasks - Completion Handlers", self::$schedulerAsync);
|
||||||
self::$asyncTaskErrorParent = new TimingsHandler("Async Tasks - Error Handlers", self::$schedulerAsync);
|
self::$asyncTaskErrorParent = new TimingsHandler("Async Tasks - Error Handlers", self::$schedulerAsync);
|
||||||
|
|
||||||
|
self::$asyncTaskWorkers = new TimingsHandler("Async Task Workers");
|
||||||
|
|
||||||
self::$playerCommand = new TimingsHandler("Player Command");
|
self::$playerCommand = new TimingsHandler("Player Command");
|
||||||
self::$craftingDataCacheRebuild = new TimingsHandler("Build CraftingDataPacket Cache");
|
self::$craftingDataCacheRebuild = new TimingsHandler("Build CraftingDataPacket Cache");
|
||||||
|
|
||||||
@ -344,6 +352,9 @@ abstract class Timings{
|
|||||||
return self::$asyncTaskCompletion[$taskClass];
|
return self::$asyncTaskCompletion[$taskClass];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated No longer used
|
||||||
|
*/
|
||||||
public static function getAsyncTaskErrorTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{
|
public static function getAsyncTaskErrorTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{
|
||||||
$taskClass = $task::class;
|
$taskClass = $task::class;
|
||||||
if(!isset(self::$asyncTaskError[$taskClass])){
|
if(!isset(self::$asyncTaskError[$taskClass])){
|
||||||
@ -357,4 +368,18 @@ abstract class Timings{
|
|||||||
|
|
||||||
return self::$asyncTaskError[$taskClass];
|
return self::$asyncTaskError[$taskClass];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getAsyncTaskRunTimings(AsyncTask $task, string $group = self::GROUP_MINECRAFT) : TimingsHandler{
|
||||||
|
$taskClass = $task::class;
|
||||||
|
if(!isset(self::$asyncTaskRun[$taskClass])){
|
||||||
|
self::init();
|
||||||
|
self::$asyncTaskRun[$taskClass] = new TimingsHandler(
|
||||||
|
"AsyncTask - " . self::shortenCoreClassName($taskClass, "pocketmine\\") . " - Run",
|
||||||
|
self::$asyncTaskWorkers,
|
||||||
|
$group
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$asyncTaskRun[$taskClass];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,20 +23,66 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\timings;
|
namespace pocketmine\timings;
|
||||||
|
|
||||||
|
use pmmp\thread\Thread as NativeThread;
|
||||||
|
use pocketmine\promise\Promise;
|
||||||
|
use pocketmine\promise\PromiseResolver;
|
||||||
use pocketmine\Server;
|
use pocketmine\Server;
|
||||||
|
use pocketmine\utils\ObjectSet;
|
||||||
use pocketmine\utils\Utils;
|
use pocketmine\utils\Utils;
|
||||||
|
use function array_merge;
|
||||||
|
use function array_push;
|
||||||
use function hrtime;
|
use function hrtime;
|
||||||
use function implode;
|
use function implode;
|
||||||
use function spl_object_id;
|
use function spl_object_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-type CollectPromise Promise<list<string>>
|
||||||
|
*/
|
||||||
class TimingsHandler{
|
class TimingsHandler{
|
||||||
private const FORMAT_VERSION = 2; //peak timings fix
|
private const FORMAT_VERSION = 3; //thread timings collection
|
||||||
|
|
||||||
private static bool $enabled = false;
|
private static bool $enabled = false;
|
||||||
private static int $timingStart = 0;
|
private static int $timingStart = 0;
|
||||||
|
|
||||||
/** @return string[] */
|
/** @phpstan-var ObjectSet<\Closure(bool $enable) : void> */
|
||||||
public static function printTimings() : array{
|
private static ?ObjectSet $toggleCallbacks = null;
|
||||||
|
/** @phpstan-var ObjectSet<\Closure() : void> */
|
||||||
|
private static ?ObjectSet $reloadCallbacks = null;
|
||||||
|
/** @phpstan-var ObjectSet<\Closure() : list<CollectPromise>> */
|
||||||
|
private static ?ObjectSet $collectCallbacks = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-template T of object
|
||||||
|
* @phpstan-param ?ObjectSet<T> $where
|
||||||
|
* @phpstan-param-out ObjectSet<T> $where
|
||||||
|
* @phpstan-return ObjectSet<T>
|
||||||
|
*/
|
||||||
|
private static function lazyGetSet(?ObjectSet &$where) : ObjectSet{
|
||||||
|
//workaround for phpstan bug - allows us to ignore 1 error instead of 6 without suppressing other errors
|
||||||
|
return $where ??= new ObjectSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-return ObjectSet<\Closure(bool $enable) : void>
|
||||||
|
*/
|
||||||
|
public static function getToggleCallbacks() : ObjectSet{ return self::lazyGetSet(self::$toggleCallbacks); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-return ObjectSet<\Closure() : void>
|
||||||
|
*/
|
||||||
|
public static function getReloadCallbacks() : ObjectSet{ return self::lazyGetSet(self::$reloadCallbacks); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-return ObjectSet<\Closure() : list<CollectPromise>>
|
||||||
|
*/
|
||||||
|
public static function getCollectCallbacks() : ObjectSet{ return self::lazyGetSet(self::$collectCallbacks); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
* @phpstan-return list<string>
|
||||||
|
*/
|
||||||
|
public static function printCurrentThreadRecords() : array{
|
||||||
|
$threadId = NativeThread::getCurrentThread()?->getThreadId();
|
||||||
$groups = [];
|
$groups = [];
|
||||||
|
|
||||||
foreach(TimingsRecord::getAll() as $timings){
|
foreach(TimingsRecord::getAll() as $timings){
|
||||||
@ -49,7 +95,7 @@ class TimingsHandler{
|
|||||||
|
|
||||||
$avg = $time / $count;
|
$avg = $time / $count;
|
||||||
|
|
||||||
$group = $timings->getGroup();
|
$group = $timings->getGroup() . ($threadId !== null ? " ThreadId: $threadId" : "");
|
||||||
$groups[$group][] = implode(" ", [
|
$groups[$group][] = implode(" ", [
|
||||||
$timings->getName(),
|
$timings->getName(),
|
||||||
"Time: $time",
|
"Time: $time",
|
||||||
@ -72,6 +118,15 @@ class TimingsHandler{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
private static function printFooter() : array{
|
||||||
|
$result = [];
|
||||||
|
|
||||||
$result[] = "# Version " . Server::getInstance()->getVersion();
|
$result[] = "# Version " . Server::getInstance()->getVersion();
|
||||||
$result[] = "# " . Server::getInstance()->getName() . " " . Server::getInstance()->getPocketMineVersion();
|
$result[] = "# " . Server::getInstance()->getName() . " " . Server::getInstance()->getPocketMineVersion();
|
||||||
|
|
||||||
@ -79,29 +134,95 @@ class TimingsHandler{
|
|||||||
|
|
||||||
$sampleTime = hrtime(true) - self::$timingStart;
|
$sampleTime = hrtime(true) - self::$timingStart;
|
||||||
$result[] = "Sample time $sampleTime (" . ($sampleTime / 1000000000) . "s)";
|
$result[] = "Sample time $sampleTime (" . ($sampleTime / 1000000000) . "s)";
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This only collects timings from the main thread. Collecting timings from all threads is an async
|
||||||
|
* operation, so it can't be done synchronously.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public static function printTimings() : array{
|
||||||
|
$records = self::printCurrentThreadRecords();
|
||||||
|
$footer = self::printFooter();
|
||||||
|
|
||||||
|
return [...$records, ...$footer];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects timings asynchronously, allowing timings from multiple threads to be aggregated into a single report.
|
||||||
|
*
|
||||||
|
* NOTE: You need to add a callback to collectCallbacks if you want to include timings from other threads. They
|
||||||
|
* won't be automatically collected if you don't, since the main thread has no way to access them.
|
||||||
|
*
|
||||||
|
* This is an asynchronous operation, and the result is returned as a promise.
|
||||||
|
* The caller must add a callback to the returned promise to get the complete timings report.
|
||||||
|
*
|
||||||
|
* @phpstan-return Promise<list<string>>
|
||||||
|
*/
|
||||||
|
public static function requestPrintTimings() : Promise{
|
||||||
|
$thisThreadRecords = self::printCurrentThreadRecords();
|
||||||
|
|
||||||
|
$otherThreadRecordPromises = [];
|
||||||
|
if(self::$collectCallbacks !== null){
|
||||||
|
foreach(self::$collectCallbacks as $callback){
|
||||||
|
$callbackPromises = $callback();
|
||||||
|
array_push($otherThreadRecordPromises, ...$callbackPromises);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolver = new PromiseResolver();
|
||||||
|
Promise::all($otherThreadRecordPromises)->onCompletion(
|
||||||
|
function(array $promisedRecords) use ($resolver, $thisThreadRecords) : void{
|
||||||
|
$resolver->resolve([...$thisThreadRecords, ...array_merge(...$promisedRecords), ...self::printFooter()]);
|
||||||
|
},
|
||||||
|
function() : void{
|
||||||
|
throw new \AssertionError("This promise is not expected to be rejected");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return $resolver->getPromise();
|
||||||
|
}
|
||||||
|
|
||||||
public static function isEnabled() : bool{
|
public static function isEnabled() : bool{
|
||||||
return self::$enabled;
|
return self::$enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function setEnabled(bool $enable = true) : void{
|
public static function setEnabled(bool $enable = true) : void{
|
||||||
|
if($enable === self::$enabled){
|
||||||
|
return;
|
||||||
|
}
|
||||||
self::$enabled = $enable;
|
self::$enabled = $enable;
|
||||||
self::reload();
|
self::internalReload();
|
||||||
|
if(self::$toggleCallbacks !== null){
|
||||||
|
foreach(self::$toggleCallbacks as $callback){
|
||||||
|
$callback($enable);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getStartTime() : float{
|
public static function getStartTime() : float{
|
||||||
return self::$timingStart;
|
return self::$timingStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function reload() : void{
|
private static function internalReload() : void{
|
||||||
TimingsRecord::reset();
|
TimingsRecord::reset();
|
||||||
if(self::$enabled){
|
if(self::$enabled){
|
||||||
self::$timingStart = hrtime(true);
|
self::$timingStart = hrtime(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function reload() : void{
|
||||||
|
self::internalReload();
|
||||||
|
if(self::$reloadCallbacks !== null){
|
||||||
|
foreach(self::$reloadCallbacks as $callback){
|
||||||
|
$callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function tick(bool $measure = true) : void{
|
public static function tick(bool $measure = true) : void{
|
||||||
if(self::$enabled){
|
if(self::$enabled){
|
||||||
TimingsRecord::tick($measure);
|
TimingsRecord::tick($measure);
|
||||||
|
@ -90,6 +90,11 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: ../../../src/plugin/PluginManager.php
|
path: ../../../src/plugin/PluginManager.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Method pocketmine\\\\timings\\\\TimingsHandler\\:\\:lazyGetSet\\(\\) should return pocketmine\\\\utils\\\\ObjectSet\\<T of object\\> but returns pocketmine\\\\utils\\\\ObjectSet\\<object\\>\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: ../../../src/timings/TimingsHandler.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Casting to int something that's already int\\.$#"
|
message: "#^Casting to int something that's already int\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user