From 1e17d86421ed26b71cced6ab4126540fb562dbcb Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 13 Jan 2023 16:03:15 +0000 Subject: [PATCH 1/3] Constify server TPS and server tick time this makes it significantly easier to perform experiments involving the server TPS. --- src/Server.php | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/Server.php b/src/Server.php index 6d762d538..3b1c22a32 100644 --- a/src/Server.php +++ b/src/Server.php @@ -181,6 +181,21 @@ class Server{ public const DEFAULT_PORT_IPV6 = 19133; public const DEFAULT_MAX_VIEW_DISTANCE = 16; + /** + * Worlds, network, commands and most other things are polled this many times per second on average. + * Between ticks, the server will sleep to ensure that the average tick rate is maintained. + * It may wake up between ticks if a Snooze notification source is triggered (e.g. to process network packets). + */ + public const TARGET_TICKS_PER_SECOND = 20; + /** + * The average time between ticks, in seconds. + */ + public const TARGET_SECONDS_PER_TICK = 1 / self::TARGET_TICKS_PER_SECOND; + /** + * The TPS threshold below which the server will generate log warnings. + */ + public const TPS_OVERLOAD_WARNING_THRESHOLD = self::TARGET_TICKS_PER_SECOND * 0.6; + private static ?Server $instance = null; private SleeperHandler $tickSleeper; @@ -199,7 +214,7 @@ class Server{ private PluginManager $pluginManager; - private float $profilingTickRate = 20; + private float $profilingTickRate = self::TARGET_TICKS_PER_SECOND; private UpdateChecker $updater; @@ -209,10 +224,10 @@ class Server{ private int $tickCounter = 0; private float $nextTick = 0; /** @var float[] */ - private array $tickAverage = [20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20]; + private array $tickAverage; /** @var float[] */ - private array $useAverage = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - private float $currentTPS = 20; + private array $useAverage; + private float $currentTPS = self::TARGET_TICKS_PER_SECOND; private float $currentUse = 0; private float $startTime; @@ -750,6 +765,8 @@ class Server{ } self::$instance = $this; $this->startTime = microtime(true); + $this->tickAverage = array_fill(0, self::TARGET_TICKS_PER_SECOND, self::TARGET_TICKS_PER_SECOND); + $this->useAverage = array_fill(0, self::TARGET_TICKS_PER_SECOND, 0); $this->tickSleeper = new SleeperHandler(); @@ -933,7 +950,7 @@ class Server{ Timings::init(); TimingsHandler::setEnabled($this->configGroup->getPropertyBool("settings.enable-profiling", false)); - $this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", 20); + $this->profilingTickRate = $this->configGroup->getPropertyInt("settings.profile-report-trigger", self::TARGET_TICKS_PER_SECOND); DefaultPermissions::registerCorePermissions(); @@ -1793,11 +1810,11 @@ class Server{ $this->network->tick(); Timings::$connection->stopTiming(); - if(($this->tickCounter % 20) === 0){ + if(($this->tickCounter % self::TARGET_TICKS_PER_SECOND) === 0){ if($this->doTitleTick){ $this->titleTick(); } - $this->currentTPS = 20; + $this->currentTPS = self::TARGET_TICKS_PER_SECOND; $this->currentUse = 0; $queryRegenerateEvent = new QueryRegenerateEvent(new QueryInfo($this)); @@ -1818,7 +1835,7 @@ class Server{ $world->clearCache(); } - if($this->getTicksPerSecondAverage() < 12){ + if($this->getTicksPerSecondAverage() < self::TPS_OVERLOAD_WARNING_THRESHOLD){ $this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_tickOverload())); } } @@ -1837,19 +1854,19 @@ class Server{ Timings::$serverTick->stopTiming(); $now = microtime(true); - $this->currentTPS = min(20, 1 / max(0.001, $now - $tickTime)); - $this->currentUse = min(1, ($now - $tickTime) / 0.05); + $this->currentTPS = min(self::TARGET_TICKS_PER_SECOND, 1 / max(0.001, $now - $tickTime)); + $this->currentUse = min(1, ($now - $tickTime) / self::TARGET_SECONDS_PER_TICK); TimingsHandler::tick($this->currentTPS <= $this->profilingTickRate); - $idx = $this->tickCounter % 20; + $idx = $this->tickCounter % self::TARGET_TICKS_PER_SECOND; $this->tickAverage[$idx] = $this->currentTPS; $this->useAverage[$idx] = $this->currentUse; if(($this->nextTick - $tickTime) < -1){ $this->nextTick = $tickTime; }else{ - $this->nextTick += 0.05; + $this->nextTick += self::TARGET_SECONDS_PER_TICK; } } } From dff3f45d22b65e8fb03ca684ad2edc0f80a3f2d5 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 13 Jan 2023 16:29:09 +0000 Subject: [PATCH 2/3] Constify more tick-related things --- src/MemoryManager.php | 10 +++++++--- src/Server.php | 23 +++++++++++++++-------- src/timings/TimingsRecord.php | 5 +++-- src/world/WorldManager.php | 8 +++++--- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/MemoryManager.php b/src/MemoryManager.php index 14ecc03cd..0162f0be0 100644 --- a/src/MemoryManager.php +++ b/src/MemoryManager.php @@ -69,6 +69,10 @@ use const JSON_UNESCAPED_SLASHES; use const SORT_NUMERIC; class MemoryManager{ + private const DEFAULT_CHECK_RATE = Server::TARGET_TICKS_PER_SECOND; + private const DEFAULT_CONTINUOUS_TRIGGER_RATE = Server::TARGET_TICKS_PER_SECOND * 2; + private const DEEFAULT_TICKS_PER_GC = 30 * 60 * Server::TARGET_TICKS_PER_SECOND; + private int $memoryLimit; private int $globalMemoryLimit; private int $checkRate; @@ -131,11 +135,11 @@ class MemoryManager{ } $this->globalMemoryLimit = $config->getPropertyInt("memory.global-limit", 0) * 1024 * 1024; - $this->checkRate = $config->getPropertyInt("memory.check-rate", 20); + $this->checkRate = $config->getPropertyInt("memory.check-rate", self::DEFAULT_CHECK_RATE); $this->continuousTrigger = $config->getPropertyBool("memory.continuous-trigger", true); - $this->continuousTriggerRate = $config->getPropertyInt("memory.continuous-trigger-rate", 30); + $this->continuousTriggerRate = $config->getPropertyInt("memory.continuous-trigger-rate", self::DEFAULT_CONTINUOUS_TRIGGER_RATE); - $this->garbageCollectionPeriod = $config->getPropertyInt("memory.garbage-collection.period", 36000); + $this->garbageCollectionPeriod = $config->getPropertyInt("memory.garbage-collection.period", self::DEEFAULT_TICKS_PER_GC); $this->garbageCollectionTrigger = $config->getPropertyBool("memory.garbage-collection.low-memory-trigger", true); $this->garbageCollectionAsync = $config->getPropertyBool("memory.garbage-collection.collect-async-worker", true); diff --git a/src/Server.php b/src/Server.php index 3b1c22a32..8f6d732e6 100644 --- a/src/Server.php +++ b/src/Server.php @@ -117,6 +117,7 @@ use pocketmine\world\WorldCreationOptions; use pocketmine\world\WorldManager; use Ramsey\Uuid\UuidInterface; use Symfony\Component\Filesystem\Path; +use function array_fill; use function array_sum; use function base64_encode; use function cli_set_process_title; @@ -191,10 +192,16 @@ class Server{ * The average time between ticks, in seconds. */ public const TARGET_SECONDS_PER_TICK = 1 / self::TARGET_TICKS_PER_SECOND; + public const TARGET_NANOSECONDS_PER_TICK = 1_000_000_000 / self::TARGET_TICKS_PER_SECOND; + /** * The TPS threshold below which the server will generate log warnings. */ - public const TPS_OVERLOAD_WARNING_THRESHOLD = self::TARGET_TICKS_PER_SECOND * 0.6; + private const TPS_OVERLOAD_WARNING_THRESHOLD = self::TARGET_TICKS_PER_SECOND * 0.6; + + private const TICKS_PER_WORLD_CACHE_CLEAR = 5 * self::TARGET_TICKS_PER_SECOND; + private const TICKS_PER_TPS_OVERLOAD_WARNING = 5 * self::TARGET_TICKS_PER_SECOND; + private const TICKS_PER_STATS_REPORT = 300 * self::TARGET_TICKS_PER_SECOND; private static ?Server $instance = null; @@ -988,7 +995,7 @@ class Server{ $this->worldManager = new WorldManager($this, Path::join($this->dataPath, "worlds"), $providerManager); $this->worldManager->setAutoSave($this->configGroup->getConfigBool("auto-save", $this->worldManager->getAutoSave())); - $this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt("ticks-per.autosave", 6000)); + $this->worldManager->setAutoSaveInterval($this->configGroup->getPropertyInt("ticks-per.autosave", $this->worldManager->getAutoSaveInterval())); $this->updater = new UpdateChecker($this, $this->configGroup->getPropertyString("auto-updater.host", "update.pmmp.io")); @@ -1028,7 +1035,7 @@ class Server{ } if($this->configGroup->getPropertyBool("anonymous-statistics.enabled", true)){ - $this->sendUsageTicker = 6000; + $this->sendUsageTicker = self::TICKS_PER_STATS_REPORT; $this->sendUsage(SendUsageTask::TYPE_OPEN); } @@ -1826,18 +1833,18 @@ class Server{ } if($this->sendUsageTicker > 0 && --$this->sendUsageTicker === 0){ - $this->sendUsageTicker = 6000; + $this->sendUsageTicker = self::TICKS_PER_STATS_REPORT; $this->sendUsage(SendUsageTask::TYPE_STATUS); } - if(($this->tickCounter % 100) === 0){ + if(($this->tickCounter % self::TICKS_PER_WORLD_CACHE_CLEAR) === 0){ foreach($this->worldManager->getWorlds() as $world){ $world->clearCache(); } + } - if($this->getTicksPerSecondAverage() < self::TPS_OVERLOAD_WARNING_THRESHOLD){ - $this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_tickOverload())); - } + if(($this->tickCounter % self::TICKS_PER_TPS_OVERLOAD_WARNING) === 0 && $this->getTicksPerSecondAverage() < self::TPS_OVERLOAD_WARNING_THRESHOLD){ + $this->logger->warning($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_server_tickOverload())); } $this->getMemoryManager()->check(); diff --git a/src/timings/TimingsRecord.php b/src/timings/TimingsRecord.php index 0ba03d522..8ee752ae2 100644 --- a/src/timings/TimingsRecord.php +++ b/src/timings/TimingsRecord.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\timings; +use pocketmine\Server; use function round; use function spl_object_id; @@ -54,8 +55,8 @@ final class TimingsRecord{ public static function tick(bool $measure = true) : void{ if($measure){ foreach(self::$records as $record){ - if($record->curTickTotal > 50000000){ - $record->violations += (int) round($record->curTickTotal / 50000000); + if($record->curTickTotal > Server::TARGET_NANOSECONDS_PER_TICK){ + $record->violations += (int) round($record->curTickTotal / Server::TARGET_NANOSECONDS_PER_TICK); } $record->curTickTotal = 0; $record->curCount = 0; diff --git a/src/world/WorldManager.php b/src/world/WorldManager.php index 80561298d..9ee68cc0e 100644 --- a/src/world/WorldManager.php +++ b/src/world/WorldManager.php @@ -55,12 +55,14 @@ use function strval; use function trim; class WorldManager{ + public const TICKS_PER_AUTOSAVE = 300 * Server::TARGET_TICKS_PER_SECOND; + /** @var World[] */ private array $worlds = []; private ?World $defaultWorld = null; private bool $autoSave = true; - private int $autoSaveTicks = 6000; + private int $autoSaveTicks = self::TICKS_PER_AUTOSAVE; private int $autoSaveTicker = 0; public function __construct( @@ -348,8 +350,8 @@ class WorldManager{ $world->doTick($currentTick); $tickMs = (microtime(true) - $worldTime) * 1000; $world->tickRateTime = $tickMs; - if($tickMs >= 50){ - $world->getLogger()->debug(sprintf("Tick took too long: %gms (%g ticks)", $tickMs, round($tickMs / 50, 2))); + if($tickMs >= Server::TARGET_SECONDS_PER_TICK){ + $world->getLogger()->debug(sprintf("Tick took too long: %gms (%g ticks)", $tickMs, round($tickMs / Server::TARGET_SECONDS_PER_TICK, 2))); } } From a9f06fc5f4a81306e6d6009a0826c3b2148e0b2f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 13 Jan 2023 17:27:57 +0000 Subject: [PATCH 3/3] Replaced hardcoded record.nowPlaying with KnownTranslationKeys --- composer.json | 2 +- composer.lock | 14 +++++++------- src/block/Jukebox.php | 3 ++- src/lang/KnownTranslationFactory.php | 6 ++++++ src/lang/KnownTranslationKeys.php | 1 + 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index 0bee48852..7a9779a00 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,7 @@ "pocketmine/classloader": "^0.2.0", "pocketmine/color": "^0.3.0", "pocketmine/errorhandler": "^0.6.0", - "pocketmine/locale-data": "~2.16.0", + "pocketmine/locale-data": "~2.17.0", "pocketmine/log": "^0.4.0", "pocketmine/log-pthreads": "^0.4.0", "pocketmine/math": "^0.4.0", diff --git a/composer.lock b/composer.lock index 8345acd3a..b07c17156 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ed6e310514b168db7eef4ae5d84c8baf", + "content-hash": "b96f3344335da3824bd4640210c9499e", "packages": [ { "name": "adhocore/json-comment", @@ -537,16 +537,16 @@ }, { "name": "pocketmine/locale-data", - "version": "2.16.0", + "version": "2.17.0", "source": { "type": "git", "url": "https://github.com/pmmp/Language.git", - "reference": "b3bf9029c112414fdb7cd9de778df191565d3038" + "reference": "a2c7071117c98ccc0e333994271cab1072eb3c06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/Language/zipball/b3bf9029c112414fdb7cd9de778df191565d3038", - "reference": "b3bf9029c112414fdb7cd9de778df191565d3038", + "url": "https://api.github.com/repos/pmmp/Language/zipball/a2c7071117c98ccc0e333994271cab1072eb3c06", + "reference": "a2c7071117c98ccc0e333994271cab1072eb3c06", "shasum": "" }, "type": "library", @@ -554,9 +554,9 @@ "description": "Language resources used by PocketMine-MP", "support": { "issues": "https://github.com/pmmp/Language/issues", - "source": "https://github.com/pmmp/Language/tree/2.16.0" + "source": "https://github.com/pmmp/Language/tree/2.17.0" }, - "time": "2023-01-04T20:27:52+00:00" + "time": "2023-01-13T17:22:45+00:00" }, { "name": "pocketmine/log", diff --git a/src/block/Jukebox.php b/src/block/Jukebox.php index 185426600..b8cbf489b 100644 --- a/src/block/Jukebox.php +++ b/src/block/Jukebox.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Jukebox as JukeboxTile; use pocketmine\item\Item; use pocketmine\item\Record; +use pocketmine\lang\KnownTranslationKeys; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\sound\RecordSound; @@ -44,7 +45,7 @@ class Jukebox extends Opaque{ if($this->record !== null){ $this->ejectRecord(); }elseif($item instanceof Record){ - $player->sendJukeboxPopup("record.nowPlaying", [$player->getLanguage()->translate($item->getRecordType()->getTranslatableName())]); + $player->sendJukeboxPopup(KnownTranslationKeys::RECORD_NOWPLAYING, [$player->getLanguage()->translate($item->getRecordType()->getTranslatableName())]); $this->insertRecord($item->pop()); } } diff --git a/src/lang/KnownTranslationFactory.php b/src/lang/KnownTranslationFactory.php index 0a690236a..c114ae5de 100644 --- a/src/lang/KnownTranslationFactory.php +++ b/src/lang/KnownTranslationFactory.php @@ -2586,6 +2586,12 @@ final class KnownTranslationFactory{ return new Translatable(KnownTranslationKeys::QUERY_WARNING2, []); } + public static function record_nowPlaying(Translatable|string $param0) : Translatable{ + return new Translatable(KnownTranslationKeys::RECORD_NOWPLAYING, [ + 0 => $param0, + ]); + } + public static function server_port() : Translatable{ return new Translatable(KnownTranslationKeys::SERVER_PORT, []); } diff --git a/src/lang/KnownTranslationKeys.php b/src/lang/KnownTranslationKeys.php index d826a5a10..0dd623f60 100644 --- a/src/lang/KnownTranslationKeys.php +++ b/src/lang/KnownTranslationKeys.php @@ -549,6 +549,7 @@ final class KnownTranslationKeys{ public const QUERY_DISABLE = "query_disable"; public const QUERY_WARNING1 = "query_warning1"; public const QUERY_WARNING2 = "query_warning2"; + public const RECORD_NOWPLAYING = "record.nowPlaying"; public const SERVER_PORT = "server_port"; public const SERVER_PORT_V4 = "server_port_v4"; public const SERVER_PORT_V6 = "server_port_v6";