diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 20eb8071f..13cec009e 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -200,6 +200,8 @@ class Level implements ChunkManager, Metadatable{ private $chunkPopulationLock = []; /** @var int */ private $chunkPopulationQueueSize = 2; + /** @var bool[] */ + private $generatorRegisteredWorkers = []; /** @var bool */ private $autoSave = true; @@ -245,9 +247,6 @@ class Level implements ChunkManager, Metadatable{ /** @var bool */ private $closed = false; - /** @var \Closure */ - private $asyncPoolStartHook; - public static function chunkHash(int $x, int $z) : int{ return (($x & 0xFFFFFFFF) << 32) | ($z & 0xFFFFFFFF); } @@ -363,10 +362,6 @@ class Level implements ChunkManager, Metadatable{ $this->temporalPosition = new Position(0, 0, 0, $this); $this->temporalVector = new Vector3(0, 0, 0); $this->tickRate = 1; - - $this->server->getAsyncPool()->addWorkerStartHook($this->asyncPoolStartHook = function(int $worker) : void{ - $this->registerGeneratorToWorker($worker); - }); } public function getTickRate() : int{ @@ -382,15 +377,16 @@ class Level implements ChunkManager, Metadatable{ } public function registerGeneratorToWorker(int $worker) : void{ + $this->generatorRegisteredWorkers[$worker] = true; $this->server->getAsyncPool()->submitTaskToWorker(new GeneratorRegisterTask($this, $this->generator, $this->provider->getGeneratorOptions()), $worker); } public function unregisterGenerator(){ $pool = $this->server->getAsyncPool(); - $pool->removeWorkerStartHook($this->asyncPoolStartHook); - foreach($pool->getRunningWorkers() as $i){ + foreach($this->generatorRegisteredWorkers as $i => $bool){ $pool->submitTaskToWorker(new GeneratorUnregisterTask($this), $i); } + $this->generatorRegisteredWorkers = []; } public function getBlockMetadata() : BlockMetadataStore{ @@ -2955,7 +2951,11 @@ class Level implements ChunkManager, Metadatable{ } } $task = new PopulationTask($this, $chunk); - $this->server->getAsyncPool()->submitTask($task); + $workerId = $this->server->getAsyncPool()->selectWorker(); + if(!isset($this->generatorRegisteredWorkers[$workerId])){ + $this->registerGeneratorToWorker($workerId); + } + $this->server->getAsyncPool()->submitTaskToWorker($task, $workerId); } } diff --git a/src/pocketmine/level/generator/Flat.php b/src/pocketmine/level/generator/Flat.php index e738baecf..fe4f0e0fd 100644 --- a/src/pocketmine/level/generator/Flat.php +++ b/src/pocketmine/level/generator/Flat.php @@ -26,11 +26,13 @@ namespace pocketmine\level\generator; use pocketmine\block\Block; use pocketmine\block\BlockFactory; use pocketmine\item\ItemFactory; +use pocketmine\level\ChunkManager; use pocketmine\level\format\Chunk; use pocketmine\level\generator\object\OreType; use pocketmine\level\generator\populator\Ore; use pocketmine\level\generator\populator\Populator; use pocketmine\math\Vector3; +use pocketmine\utils\Random; class Flat extends Generator{ /** @var Chunk */ @@ -41,6 +43,8 @@ class Flat extends Generator{ private $structure; /** @var int */ private $floorLevel; + /** @var int */ + private $biome; /** @var mixed[] */ private $options; /** @var string */ @@ -55,9 +59,15 @@ class Flat extends Generator{ } public function __construct(array $options = []){ - $this->preset = "2;7,2x3,2;1;"; - //$this->preset = "2;7,59x1,3x3,2;1;spawn(radius=10 block=89),decoration(treecount=80 grasscount=45)"; $this->options = $options; + if(isset($this->options["preset"]) and $this->options["preset"] != ""){ + $this->preset = $this->options["preset"]; + }else{ + $this->preset = "2;7,2x3,2;1;"; + //$this->preset = "2;7,59x1,3x3,2;1;spawn(radius=10 block=89),decoration(treecount=80 grasscount=45)"; + } + + $this->parsePreset(); if(isset($this->options["decoration"])){ $ores = new Ore(); @@ -90,39 +100,14 @@ class Flat extends Generator{ return $result; } - protected function generateBaseChunk(string $preset) : void{ - $this->preset = $preset; - $preset = explode(";", $preset); - $version = (int) $preset[0]; + protected function parsePreset() : void{ + $preset = explode(";", $this->preset); $blocks = (string) ($preset[1] ?? ""); - $biome = (int) ($preset[2] ?? 1); + $this->biome = (int) ($preset[2] ?? 1); $options = (string) ($preset[3] ?? ""); $this->structure = self::parseLayers($blocks); - $this->floorLevel = $y = count($this->structure); - - $this->chunk = new Chunk(0, 0); - $this->chunk->setGenerated(); - - for($Z = 0; $Z < 16; ++$Z){ - for($X = 0; $X < 16; ++$X){ - $this->chunk->setBiomeId($X, $Z, $biome); - } - } - - $count = count($this->structure); - for($sy = 0; $sy < $count; $sy += 16){ - $subchunk = $this->chunk->getSubChunk($sy >> 4, true); - for($y = 0; $y < 16 and isset($this->structure[$y | $sy]); ++$y){ - list($id, $meta) = $this->structure[$y | $sy]; - - for($Z = 0; $Z < 16; ++$Z){ - for($X = 0; $X < 16; ++$X){ - $subchunk->setBlock($X, $y, $Z, $id, $meta); - } - } - } - } + $this->floorLevel = count($this->structure); preg_match_all('#(([0-9a-z_]{1,})\(?([0-9a-z_ =:]{0,})\)?),?#', $options, $matches); foreach($matches[2] as $i => $option){ @@ -141,14 +126,37 @@ class Flat extends Generator{ } } - public function generateChunk(int $chunkX, int $chunkZ) : void{ - if($this->chunk === null){ - if(isset($this->options["preset"]) and $this->options["preset"] != ""){ - $this->generateBaseChunk($this->options["preset"]); - }else{ - $this->generateBaseChunk($this->preset); + protected function generateBaseChunk() : void{ + $this->chunk = new Chunk(0, 0); + $this->chunk->setGenerated(); + + for($Z = 0; $Z < 16; ++$Z){ + for($X = 0; $X < 16; ++$X){ + $this->chunk->setBiomeId($X, $Z, $this->biome); } } + + $count = count($this->structure); + for($sy = 0; $sy < $count; $sy += 16){ + $subchunk = $this->chunk->getSubChunk($sy >> 4, true); + for($y = 0; $y < 16 and isset($this->structure[$y | $sy]); ++$y){ + list($id, $meta) = $this->structure[$y | $sy]; + + for($Z = 0; $Z < 16; ++$Z){ + for($X = 0; $X < 16; ++$X){ + $subchunk->setBlock($X, $y, $Z, $id, $meta); + } + } + } + } + } + + public function init(ChunkManager $level, Random $random) : void{ + parent::init($level, $random); + $this->generateBaseChunk(); + } + + public function generateChunk(int $chunkX, int $chunkZ) : void{ $chunk = clone $this->chunk; $chunk->setX($chunkX); $chunk->setZ($chunkZ); diff --git a/src/pocketmine/scheduler/AsyncPool.php b/src/pocketmine/scheduler/AsyncPool.php index f833c4813..dbdb1de9d 100644 --- a/src/pocketmine/scheduler/AsyncPool.php +++ b/src/pocketmine/scheduler/AsyncPool.php @@ -168,18 +168,15 @@ class AsyncPool{ } /** - * Submits an AsyncTask to the worker with the least load. If all workers are busy and the pool is not full, a new - * worker may be started. + * Selects a worker ID to run a task. * - * @param AsyncTask $task + * - if an idle worker is found, it will be selected + * - else, if the worker pool is not full, a new worker will be selected + * - else, the worker with the smallest backlog is chosen. * * @return int */ - public function submitTask(AsyncTask $task) : int{ - if($task->getTaskId() !== null){ - throw new \InvalidArgumentException("Cannot submit the same AsyncTask instance more than once"); - } - + public function selectWorker() : int{ $worker = null; $minUsage = PHP_INT_MAX; foreach($this->workerUsage as $i => $usage){ @@ -202,7 +199,23 @@ class AsyncPool{ } assert($worker !== null); + return $worker; + } + /** + * Submits an AsyncTask to the worker with the least load. If all workers are busy and the pool is not full, a new + * worker may be started. + * + * @param AsyncTask $task + * + * @return int + */ + public function submitTask(AsyncTask $task) : int{ + if($task->getTaskId() !== null){ + throw new \InvalidArgumentException("Cannot submit the same AsyncTask instance more than once"); + } + + $worker = $this->selectWorker(); $this->submitTaskToWorker($task, $worker); return $worker; }