From 1d810f8aebc6eb43a92b2ece0b469d0542c76216 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 9 Mar 2019 19:09:05 +0000 Subject: [PATCH 1/8] Backport c3e66e0adc1a0d5ccc2b48dc2ec44e51f5c5383d to 3.6 --- src/pocketmine/Server.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 76bcdcc0c..35dbfb082 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -2450,6 +2450,9 @@ class Server{ $level->doTick($currentTick); $tickMs = (microtime(true) - $levelTime) * 1000; $level->tickRateTime = $tickMs; + if($tickMs >= 50){ + $this->getLogger()->debug(sprintf("World \"%s\" took too long to tick: %gms (%g ticks)", $level->getName(), $tickMs, round($tickMs / 50, 2))); + } if($this->autoTickRate){ if($tickMs < 50 and $level->getTickRate() > $this->baseTickRate){ From 10612acacee15a01556c1c792e5d797423d959d6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 9 Mar 2019 19:10:09 +0000 Subject: [PATCH 2/8] Partial backport of 2bffd5cc1c326670c25386ce78e5c690f4c5fd54 to 3.6 --- src/pocketmine/Server.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 35dbfb082..ed2413e3b 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -2622,8 +2622,10 @@ class Server{ } if($this->autoSave and ++$this->autoSaveTicker >= $this->autoSaveTicks){ - $this->autoSaveTicker = 0; + $this->getLogger()->debug("[Auto Save] Saving worlds..."); + $start = microtime(true); $this->doAutoSave(); + $this->getLogger()->debug("[Auto Save] Save completed in " . round(microtime(true) - $start, 3) . "s"); } if($this->sendUsageTicker > 0 and --$this->sendUsageTicker === 0){ From 0659d2fbefa6f19741fc931bd3f86dda5479b5f3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 3 Mar 2019 13:24:53 +0000 Subject: [PATCH 3/8] Backport 6bd43a8215b9e612478fe789da9a58b36f0f0e73: Firehose auto-tick-rate anti-feature, closes #2665 --- src/pocketmine/Server.php | 43 ------------------- .../command/defaults/StatusCommand.php | 8 ++-- src/pocketmine/entity/Entity.php | 3 +- src/pocketmine/level/Level.php | 17 +++++--- 4 files changed, 15 insertions(+), 56 deletions(-) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index ed2413e3b..0b1b7696f 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -122,7 +122,6 @@ use function file_exists; use function file_get_contents; use function file_put_contents; use function filemtime; -use function floor; use function function_exists; use function get_class; use function getmypid; @@ -295,15 +294,6 @@ class Server{ /** @var int */ public $networkCompressionLevel = 7; - /** @var bool */ - private $autoTickRate = true; - /** @var int */ - private $autoTickRateLimit = 20; - /** @var bool */ - private $alwaysTickPlayers = false; - /** @var int */ - private $baseTickRate = 1; - /** @var int */ private $autoSaveTicker = 0; /** @var int */ @@ -1109,8 +1099,6 @@ class Server{ (new LevelLoadEvent($level))->call(); - $level->setTickRate($this->baseTickRate); - return true; } @@ -1154,8 +1142,6 @@ class Server{ $level = new Level($this, $name, new $providerClass($path)); $this->levels[$level->getId()] = $level; - $level->setTickRate($this->baseTickRate); - (new LevelInitEvent($level))->call(); (new LevelLoadEvent($level))->call(); @@ -1593,11 +1579,6 @@ class Server{ } $this->networkCompressionAsync = (bool) $this->getProperty("network.async-compression", true); - $this->autoTickRate = (bool) $this->getProperty("level-settings.auto-tick-rate", true); - $this->autoTickRateLimit = (int) $this->getProperty("level-settings.auto-tick-rate-limit", 20); - $this->alwaysTickPlayers = (bool) $this->getProperty("level-settings.always-tick-players", false); - $this->baseTickRate = (int) $this->getProperty("level-settings.base-tick-rate", 1); - $this->doTitleTick = ((bool) $this->getProperty("console.title-tick", true)) && Terminal::hasFormattingCodes(); @@ -2431,8 +2412,6 @@ class Server{ foreach($this->players as $p){ if(!$p->loggedIn and ($tickTime - $p->creationTime) >= 10){ $p->close("", "Login timeout"); - }elseif($this->alwaysTickPlayers and $p->spawned){ - $p->onUpdate($currentTick); } } @@ -2442,9 +2421,6 @@ class Server{ // Level unloaded during the tick of a level earlier in this loop, perhaps by plugin continue; } - if($level->getTickRate() > $this->baseTickRate and --$level->tickRateCounter > 0){ - continue; - } $levelTime = microtime(true); $level->doTick($currentTick); @@ -2453,25 +2429,6 @@ class Server{ if($tickMs >= 50){ $this->getLogger()->debug(sprintf("World \"%s\" took too long to tick: %gms (%g ticks)", $level->getName(), $tickMs, round($tickMs / 50, 2))); } - - if($this->autoTickRate){ - if($tickMs < 50 and $level->getTickRate() > $this->baseTickRate){ - $level->setTickRate($r = $level->getTickRate() - 1); - if($r > $this->baseTickRate){ - $level->tickRateCounter = $level->getTickRate(); - } - $this->getLogger()->debug("Raising level \"{$level->getName()}\" tick rate to {$level->getTickRate()} ticks"); - }elseif($tickMs >= 50){ - if($level->getTickRate() === $this->baseTickRate){ - $level->setTickRate(max($this->baseTickRate + 1, min($this->autoTickRateLimit, (int) floor($tickMs / 50)))); - $this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate())); - }elseif(($tickMs / $level->getTickRate()) >= 50 and $level->getTickRate() < $this->autoTickRateLimit){ - $level->setTickRate($level->getTickRate() + 1); - $this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate())); - } - $level->tickRateCounter = $level->getTickRate(); - } - } } } diff --git a/src/pocketmine/command/defaults/StatusCommand.php b/src/pocketmine/command/defaults/StatusCommand.php index 9c258c3cc..d43f7df28 100644 --- a/src/pocketmine/command/defaults/StatusCommand.php +++ b/src/pocketmine/command/defaults/StatusCommand.php @@ -108,13 +108,11 @@ class StatusCommand extends VanillaCommand{ foreach($server->getLevels() as $level){ $levelName = $level->getFolderName() !== $level->getName() ? " (" . $level->getName() . ")" : ""; - $timeColor = ($level->getTickRate() > 1 or $level->getTickRateTime() > 40) ? TextFormat::RED : TextFormat::YELLOW; - $tickRate = $level->getTickRate() > 1 ? " (tick rate " . $level->getTickRate() . ")" : ""; + $timeColor = $level->getTickRateTime() > 40 ? TextFormat::RED : TextFormat::YELLOW; $sender->sendMessage(TextFormat::GOLD . "World \"{$level->getFolderName()}\"$levelName: " . TextFormat::RED . number_format(count($level->getChunks())) . TextFormat::GREEN . " chunks, " . - TextFormat::RED . number_format(count($level->getEntities())) . TextFormat::GREEN . " entities, " . - TextFormat::RED . number_format(count($level->getTiles())) . TextFormat::GREEN . " tiles. " . - "Time $timeColor" . round($level->getTickRateTime(), 2) . "ms" . $tickRate + TextFormat::RED . number_format(count($level->getEntities())) . TextFormat::GREEN . " entities. " . + "Time $timeColor" . round($level->getTickRateTime(), 2) . "ms" ); } diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index d32e04fa0..c72116386 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -1640,7 +1640,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{ assert(abs($dx) <= 20 and abs($dy) <= 20 and abs($dz) <= 20, "Movement distance is excessive: dx=$dx, dy=$dy, dz=$dz"); - $list = $this->level->getCollisionCubes($this, $this->level->getTickRate() > 1 ? $this->boundingBox->offsetCopy($dx, $dy, $dz) : $this->boundingBox->addCoord($dx, $dy, $dz), false); + //TODO: bad hack here will cause unexpected behaviour under heavy lag + $list = $this->level->getCollisionCubes($this, $this->level->getTickRateTime() > 50 ? $this->boundingBox->offsetCopy($dx, $dy, $dz) : $this->boundingBox->addCoord($dx, $dy, $dz), false); foreach($list as $bb){ $dy = $bb->calculateYOffset($this->boundingBox, $dy); diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 040c2bcd1..9a8bc8a5c 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -261,12 +261,8 @@ class Level implements ChunkManager, Metadatable{ /** @var LevelTimings */ public $timings; - /** @var int */ - private $tickRate; /** @var int */ public $tickRateTime = 0; - /** @var int */ - public $tickRateCounter = 0; /** @var bool */ private $doingTick = false; @@ -411,19 +407,26 @@ class Level implements ChunkManager, Metadatable{ $this->timings = new LevelTimings($this); $this->temporalPosition = new Position(0, 0, 0, $this); $this->temporalVector = new Vector3(0, 0, 0); - $this->tickRate = 1; } + /** + * @deprecated + * @return int + */ public function getTickRate() : int{ - return $this->tickRate; + return 1; } public function getTickRateTime() : float{ return $this->tickRateTime; } + /** + * @deprecated does nothing + * @param int $tickRate + */ public function setTickRate(int $tickRate){ - $this->tickRate = $tickRate; + } public function registerGeneratorToWorker(int $worker) : void{ From 4252c5914b77836bf387b9cfbede7874e7ff5765 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 3 Mar 2019 13:28:18 +0000 Subject: [PATCH 4/8] Backport 93cd00ae8ffdfe7f7959b5e2ca5ca20b59d54012: Remove dead settings from pocketmine.yml --- src/pocketmine/resources/pocketmine.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/pocketmine/resources/pocketmine.yml b/src/pocketmine/resources/pocketmine.yml index b81824282..7a605e8ff 100644 --- a/src/pocketmine/resources/pocketmine.yml +++ b/src/pocketmine/resources/pocketmine.yml @@ -109,13 +109,6 @@ player: level-settings: #The default format that levels will use when created default-format: pmanvil - #Automatically change levels tick rate to maintain 20 ticks per second - auto-tick-rate: true - auto-tick-rate-limit: 20 - #Sets the base tick rate (1 = 20 ticks per second, 2 = 10 ticks per second, etc.) - base-tick-rate: 1 - #Tick all players each tick even when other settings disallow this. - always-tick-players: false chunk-sending: #To change server normal render distance, change view-distance in server.properties. From a1d50de12e9845ce182c803f1a47f34db42893a4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 9 Mar 2019 19:31:13 +0000 Subject: [PATCH 5/8] OOF --- src/pocketmine/Server.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 0b1b7696f..84f188ac8 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -2579,6 +2579,7 @@ class Server{ } if($this->autoSave and ++$this->autoSaveTicker >= $this->autoSaveTicks){ + $this->autoSaveTicker = 0; $this->getLogger()->debug("[Auto Save] Saving worlds..."); $start = microtime(true); $this->doAutoSave(); From 99606bbe23c79f4ad1a8746836d6e635349cae8c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 9 Mar 2019 19:33:46 +0000 Subject: [PATCH 6/8] beware possible API break --- src/pocketmine/level/Level.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 9a8bc8a5c..d6167cde4 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -263,6 +263,11 @@ class Level implements ChunkManager, Metadatable{ /** @var int */ public $tickRateTime = 0; + /** + * @deprecated + * @var int + */ + public $tickRateCounter = 0; /** @var bool */ private $doingTick = false; From 5c12bee8740d18000b1aa34a93197256881e658b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 2 Mar 2019 18:20:25 +0000 Subject: [PATCH 7/8] Backport other part of 2bffd5cc1c326670c25386ce78e5c690f4c5fd54: Add timer measurements for autosave --- src/pocketmine/command/defaults/SaveCommand.php | 7 +++++-- src/pocketmine/lang/locale | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pocketmine/command/defaults/SaveCommand.php b/src/pocketmine/command/defaults/SaveCommand.php index a52eabcfc..7347d41cd 100644 --- a/src/pocketmine/command/defaults/SaveCommand.php +++ b/src/pocketmine/command/defaults/SaveCommand.php @@ -26,6 +26,8 @@ namespace pocketmine\command\defaults; use pocketmine\command\Command; use pocketmine\command\CommandSender; use pocketmine\lang\TranslationContainer; +use function microtime; +use function round; class SaveCommand extends VanillaCommand{ @@ -43,7 +45,8 @@ class SaveCommand extends VanillaCommand{ return true; } - Command::broadcastCommandMessage($sender, new TranslationContainer("commands.save.start")); + Command::broadcastCommandMessage($sender, new TranslationContainer("pocketmine.save.start")); + $start = microtime(true); foreach($sender->getServer()->getOnlinePlayers() as $player){ $player->save(); @@ -53,7 +56,7 @@ class SaveCommand extends VanillaCommand{ $level->save(true); } - Command::broadcastCommandMessage($sender, new TranslationContainer("commands.save.success")); + Command::broadcastCommandMessage($sender, new TranslationContainer("pocketmine.save.success", [round(microtime(true) - $start, 3)])); return true; } diff --git a/src/pocketmine/lang/locale b/src/pocketmine/lang/locale index 5dad5db21..73ed1ab3e 160000 --- a/src/pocketmine/lang/locale +++ b/src/pocketmine/lang/locale @@ -1 +1 @@ -Subproject commit 5dad5db214ce0c94be0235e93d8253d0aec19c82 +Subproject commit 73ed1ab3e1f2a1644fe908b439f8cf8ed6c12ab5 From 562179bdd699030fb4f51c53bdbf448281def960 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Feb 2019 15:33:46 +0000 Subject: [PATCH 8/8] Backport 58cafc853f2caa013c65d98ab56f13ac3abe521c: s/level/world (strings only) we should look at doing this for code too, but for now I'm not planning to break everyone's plugins. --- src/pocketmine/Server.php | 10 +++++----- src/pocketmine/level/Explosion.php | 2 +- src/pocketmine/level/Level.php | 20 +++++++++---------- src/pocketmine/level/Position.php | 4 ++-- .../level/format/io/ChunkRequestTask.php | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 84f188ac8..f01273a4f 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1046,7 +1046,7 @@ class Server{ */ public function unloadLevel(Level $level, bool $forceUnload = false) : bool{ if($level === $this->getDefaultLevel() and !$forceUnload){ - throw new \InvalidStateException("The default level cannot be unloaded while running, please switch levels."); + throw new \InvalidStateException("The default world cannot be unloaded while running, please switch worlds."); } return $level->unload($forceUnload); @@ -1072,7 +1072,7 @@ class Server{ */ public function loadLevel(string $name) : bool{ if(trim($name) === ""){ - throw new LevelException("Invalid empty level name"); + throw new LevelException("Invalid empty world name"); } if($this->isLevelLoaded($name)){ return true; @@ -1130,7 +1130,7 @@ class Server{ if(($providerClass = LevelProviderManager::getProviderByName($this->getProperty("level-settings.default-format", "pmanvil"))) === null){ $providerClass = LevelProviderManager::getProviderByName("pmanvil"); if($providerClass === null){ - throw new \InvalidStateException("Default level provider has not been registered"); + throw new \InvalidStateException("Default world provider has not been registered"); } } @@ -2019,7 +2019,7 @@ class Server{ } public function reload(){ - $this->logger->info("Saving levels..."); + $this->logger->info("Saving worlds..."); foreach($this->levels as $level){ $level->save(); @@ -2097,7 +2097,7 @@ class Server{ $player->close($player->getLeaveMessage(), $this->getProperty("settings.shutdown-message", "Server closed")); } - $this->getLogger()->debug("Unloading all levels"); + $this->getLogger()->debug("Unloading all worlds"); foreach($this->getLevels() as $level){ $this->unloadLevel($level, true); } diff --git a/src/pocketmine/level/Explosion.php b/src/pocketmine/level/Explosion.php index 1ff862d78..80772cbe8 100644 --- a/src/pocketmine/level/Explosion.php +++ b/src/pocketmine/level/Explosion.php @@ -74,7 +74,7 @@ class Explosion{ */ public function __construct(Position $center, float $size, $what = null){ if(!$center->isValid()){ - throw new \InvalidArgumentException("Position does not have a valid level"); + throw new \InvalidArgumentException("Position does not have a valid world"); } $this->source = $center; $this->level = $center->getLevel(); diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index d6167cde4..bde1fb9ab 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -474,7 +474,7 @@ class Level implements ChunkManager, Metadatable{ public function close(){ if($this->closed){ - throw new \InvalidStateException("Tried to close a level which is already closed"); + throw new \InvalidStateException("Tried to close a world which is already closed"); } foreach($this->chunks as $chunk){ @@ -588,7 +588,7 @@ class Level implements ChunkManager, Metadatable{ */ public function unload(bool $force = false) : bool{ if($this->doingTick and !$force){ - throw new \InvalidStateException("Cannot unload a level during level tick"); + throw new \InvalidStateException("Cannot unload a world during world tick"); } $ev = new LevelUnloadEvent($this); @@ -607,7 +607,7 @@ class Level implements ChunkManager, Metadatable{ $defaultLevel = $this->server->getDefaultLevel(); foreach($this->getPlayers() as $player){ if($this === $defaultLevel or $defaultLevel === null){ - $player->close($player->getLeaveMessage(), "Forced default level unload"); + $player->close($player->getLeaveMessage(), "Forced default world unload"); }elseif($defaultLevel instanceof Level){ $player->teleport($this->server->getDefaultLevel()->getSafeSpawn()); } @@ -785,7 +785,7 @@ class Level implements ChunkManager, Metadatable{ */ public function doTick(int $currentTick){ if($this->closed){ - throw new \InvalidStateException("Attempted to tick a Level which has been closed"); + throw new \InvalidStateException("Attempted to tick a world which has been closed"); } $this->timings->doTick->startTiming(); @@ -2699,10 +2699,10 @@ class Level implements ChunkManager, Metadatable{ */ public function addEntity(Entity $entity){ if($entity->isClosed()){ - throw new \InvalidArgumentException("Attempted to add a garbage closed Entity to Level"); + throw new \InvalidArgumentException("Attempted to add a garbage closed Entity to world"); } if($entity->getLevel() !== $this){ - throw new LevelException("Invalid Entity level"); + throw new LevelException("Invalid Entity world"); } if($entity instanceof Player){ @@ -2720,7 +2720,7 @@ class Level implements ChunkManager, Metadatable{ */ public function removeEntity(Entity $entity){ if($entity->getLevel() !== $this){ - throw new LevelException("Invalid Entity level"); + throw new LevelException("Invalid Entity world"); } if($entity instanceof Player){ @@ -2739,10 +2739,10 @@ class Level implements ChunkManager, Metadatable{ */ public function addTile(Tile $tile){ if($tile->isClosed()){ - throw new \InvalidArgumentException("Attempted to add a garbage closed Tile to Level"); + throw new \InvalidArgumentException("Attempted to add a garbage closed Tile to world"); } if($tile->getLevel() !== $this){ - throw new LevelException("Invalid Tile level"); + throw new LevelException("Invalid Tile world"); } $chunkX = $tile->getFloorX() >> 4; @@ -2765,7 +2765,7 @@ class Level implements ChunkManager, Metadatable{ */ public function removeTile(Tile $tile){ if($tile->getLevel() !== $this){ - throw new LevelException("Invalid Tile level"); + throw new LevelException("Invalid Tile world"); } unset($this->tiles[$tile->getId()], $this->updateTiles[$tile->getId()]); diff --git a/src/pocketmine/level/Position.php b/src/pocketmine/level/Position.php index bc28a74b6..6bcb599bc 100644 --- a/src/pocketmine/level/Position.php +++ b/src/pocketmine/level/Position.php @@ -64,7 +64,7 @@ class Position extends Vector3{ */ public function getLevel(){ if($this->level !== null and $this->level->isClosed()){ - MainLogger::getLogger()->debug("Position was holding a reference to an unloaded Level"); + MainLogger::getLogger()->debug("Position was holding a reference to an unloaded world"); $this->level = null; } @@ -82,7 +82,7 @@ class Position extends Vector3{ */ public function setLevel(Level $level = null){ if($level !== null and $level->isClosed()){ - throw new \InvalidArgumentException("Specified level has been unloaded and cannot be used"); + throw new \InvalidArgumentException("Specified world has been unloaded and cannot be used"); } $this->level = $level; diff --git a/src/pocketmine/level/format/io/ChunkRequestTask.php b/src/pocketmine/level/format/io/ChunkRequestTask.php index fccdd68d7..52f5419a8 100644 --- a/src/pocketmine/level/format/io/ChunkRequestTask.php +++ b/src/pocketmine/level/format/io/ChunkRequestTask.php @@ -74,10 +74,10 @@ class ChunkRequestTask extends AsyncTask{ $batch->isEncoded = true; $level->chunkRequestCallback($this->chunkX, $this->chunkZ, $batch); }else{ - $server->getLogger()->error("Chunk request for level #" . $this->levelId . ", x=" . $this->chunkX . ", z=" . $this->chunkZ . " doesn't have any result data"); + $server->getLogger()->error("Chunk request for world #" . $this->levelId . ", x=" . $this->chunkX . ", z=" . $this->chunkZ . " doesn't have any result data"); } }else{ - $server->getLogger()->debug("Dropped chunk task due to level not loaded"); + $server->getLogger()->debug("Dropped chunk task due to world not loaded"); } } }