mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-08 20:58:34 +00:00
Extract GeneratorExecutor system from World, v2 (#6682)
- `AsyncGeneratorExecutor` class added that encapsulates the logic of generating chunks using async tasks as previously - `GeneratorExecutor` interface added that can be implemented to provide chunks in other ways - `SyncGeneratorExecutor` which invokes the generator directly on the main thread, useful for simple generators like `Flat` where async tasks are not needed - Some redundant APIs were removed from `World` (these will probably come back as deprecated stubs for the remainder of 5.x, but I was having too much fun deleting code) - Removed internal `World->registerGeneratorToWorker()` (no longer useful) - `World` now invokes generator executor instead of posting AsyncTasks directly - Some internal classes moved to `pocketmine\world\generator\executor` (PopulationTask excluded because plugins use it in lieu of being able to regenerate chunks - Generators can opt into main-thread execution by setting the `$fast` parameter to `true` in `GeneratorManager::register()`
This commit is contained in:
parent
dd17adeaaf
commit
059f4ee7bf
@ -93,9 +93,11 @@ use pocketmine\world\format\io\GlobalBlockStateHandlers;
|
|||||||
use pocketmine\world\format\io\WritableWorldProvider;
|
use pocketmine\world\format\io\WritableWorldProvider;
|
||||||
use pocketmine\world\format\LightArray;
|
use pocketmine\world\format\LightArray;
|
||||||
use pocketmine\world\format\SubChunk;
|
use pocketmine\world\format\SubChunk;
|
||||||
|
use pocketmine\world\generator\executor\AsyncGeneratorExecutor;
|
||||||
|
use pocketmine\world\generator\executor\GeneratorExecutor;
|
||||||
|
use pocketmine\world\generator\executor\GeneratorExecutorSetupParameters;
|
||||||
|
use pocketmine\world\generator\executor\SyncGeneratorExecutor;
|
||||||
use pocketmine\world\generator\GeneratorManager;
|
use pocketmine\world\generator\GeneratorManager;
|
||||||
use pocketmine\world\generator\GeneratorRegisterTask;
|
|
||||||
use pocketmine\world\generator\GeneratorUnregisterTask;
|
|
||||||
use pocketmine\world\generator\PopulationTask;
|
use pocketmine\world\generator\PopulationTask;
|
||||||
use pocketmine\world\light\BlockLightUpdate;
|
use pocketmine\world\light\BlockLightUpdate;
|
||||||
use pocketmine\world\light\LightPopulationTask;
|
use pocketmine\world\light\LightPopulationTask;
|
||||||
@ -336,11 +338,7 @@ class World implements ChunkManager{
|
|||||||
*/
|
*/
|
||||||
private array $chunkPopulationRequestQueueIndex = [];
|
private array $chunkPopulationRequestQueueIndex = [];
|
||||||
|
|
||||||
/**
|
private readonly GeneratorExecutor $generatorExecutor;
|
||||||
* @var true[]
|
|
||||||
* @phpstan-var array<int, true>
|
|
||||||
*/
|
|
||||||
private array $generatorRegisteredWorkers = [];
|
|
||||||
|
|
||||||
private bool $autoSave = true;
|
private bool $autoSave = true;
|
||||||
|
|
||||||
@ -360,9 +358,6 @@ class World implements ChunkManager{
|
|||||||
|
|
||||||
private bool $doingTick = false;
|
private bool $doingTick = false;
|
||||||
|
|
||||||
/** @phpstan-var class-string<generator\Generator> */
|
|
||||||
private string $generator;
|
|
||||||
|
|
||||||
private bool $unloaded = false;
|
private bool $unloaded = false;
|
||||||
/**
|
/**
|
||||||
* @var \Closure[]
|
* @var \Closure[]
|
||||||
@ -498,7 +493,23 @@ class World implements ChunkManager{
|
|||||||
$generator = GeneratorManager::getInstance()->getGenerator($this->provider->getWorldData()->getGenerator()) ??
|
$generator = GeneratorManager::getInstance()->getGenerator($this->provider->getWorldData()->getGenerator()) ??
|
||||||
throw new AssumptionFailedError("WorldManager should already have checked that the generator exists");
|
throw new AssumptionFailedError("WorldManager should already have checked that the generator exists");
|
||||||
$generator->validateGeneratorOptions($this->provider->getWorldData()->getGeneratorOptions());
|
$generator->validateGeneratorOptions($this->provider->getWorldData()->getGeneratorOptions());
|
||||||
$this->generator = $generator->getGeneratorClass();
|
|
||||||
|
$executorSetupParameters = new GeneratorExecutorSetupParameters(
|
||||||
|
worldMinY: $this->minY,
|
||||||
|
worldMaxY: $this->maxY,
|
||||||
|
generatorSeed: $this->getSeed(),
|
||||||
|
generatorClass: $generator->getGeneratorClass(),
|
||||||
|
generatorSettings: $this->provider->getWorldData()->getGeneratorOptions()
|
||||||
|
);
|
||||||
|
$this->generatorExecutor = $generator->isFast() ?
|
||||||
|
new SyncGeneratorExecutor($executorSetupParameters) :
|
||||||
|
new AsyncGeneratorExecutor(
|
||||||
|
$this->logger,
|
||||||
|
$this->workerPool,
|
||||||
|
$executorSetupParameters,
|
||||||
|
$this->worldId
|
||||||
|
);
|
||||||
|
|
||||||
$this->chunkPopulationRequestQueue = new \SplQueue();
|
$this->chunkPopulationRequestQueue = new \SplQueue();
|
||||||
$this->addOnUnloadCallback(function() : void{
|
$this->addOnUnloadCallback(function() : void{
|
||||||
$this->logger->debug("Cancelling unfulfilled generation requests");
|
$this->logger->debug("Cancelling unfulfilled generation requests");
|
||||||
@ -534,17 +545,6 @@ class World implements ChunkManager{
|
|||||||
$this->initRandomTickBlocksFromConfig($cfg);
|
$this->initRandomTickBlocksFromConfig($cfg);
|
||||||
|
|
||||||
$this->timings = new WorldTimings($this);
|
$this->timings = new WorldTimings($this);
|
||||||
|
|
||||||
$this->workerPool->addWorkerStartHook($workerStartHook = function(int $workerId) : void{
|
|
||||||
if(array_key_exists($workerId, $this->generatorRegisteredWorkers)){
|
|
||||||
$this->logger->debug("Worker $workerId with previously registered generator restarted, flagging as unregistered");
|
|
||||||
unset($this->generatorRegisteredWorkers[$workerId]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$workerPool = $this->workerPool;
|
|
||||||
$this->addOnUnloadCallback(static function() use ($workerPool, $workerStartHook) : void{
|
|
||||||
$workerPool->removeWorkerStartHook($workerStartHook);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function initRandomTickBlocksFromConfig(ServerConfigGroup $cfg) : void{
|
private function initRandomTickBlocksFromConfig(ServerConfigGroup $cfg) : void{
|
||||||
@ -585,21 +585,6 @@ class World implements ChunkManager{
|
|||||||
return $this->tickRateTime;
|
return $this->tickRateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerGeneratorToWorker(int $worker) : void{
|
|
||||||
$this->logger->debug("Registering generator on worker $worker");
|
|
||||||
$this->workerPool->submitTaskToWorker(new GeneratorRegisterTask($this, $this->generator, $this->provider->getWorldData()->getGeneratorOptions()), $worker);
|
|
||||||
$this->generatorRegisteredWorkers[$worker] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unregisterGenerator() : void{
|
|
||||||
foreach($this->workerPool->getRunningWorkers() as $i){
|
|
||||||
if(isset($this->generatorRegisteredWorkers[$i])){
|
|
||||||
$this->workerPool->submitTaskToWorker(new GeneratorUnregisterTask($this), $i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->generatorRegisteredWorkers = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getServer() : Server{
|
public function getServer() : Server{
|
||||||
return $this->server;
|
return $this->server;
|
||||||
}
|
}
|
||||||
@ -657,7 +642,7 @@ class World implements ChunkManager{
|
|||||||
|
|
||||||
$this->save();
|
$this->save();
|
||||||
|
|
||||||
$this->unregisterGenerator();
|
$this->generatorExecutor->shutdown();
|
||||||
|
|
||||||
$this->provider->close();
|
$this->provider->close();
|
||||||
$this->blockCache = [];
|
$this->blockCache = [];
|
||||||
@ -3486,8 +3471,8 @@ class World implements ChunkManager{
|
|||||||
|
|
||||||
$centerChunk = $this->loadChunk($chunkX, $chunkZ);
|
$centerChunk = $this->loadChunk($chunkX, $chunkZ);
|
||||||
$adjacentChunks = $this->getAdjacentChunks($chunkX, $chunkZ);
|
$adjacentChunks = $this->getAdjacentChunks($chunkX, $chunkZ);
|
||||||
$task = new PopulationTask(
|
|
||||||
$this->worldId,
|
$this->generatorExecutor->populate(
|
||||||
$chunkX,
|
$chunkX,
|
||||||
$chunkZ,
|
$chunkZ,
|
||||||
$centerChunk,
|
$centerChunk,
|
||||||
@ -3500,15 +3485,6 @@ class World implements ChunkManager{
|
|||||||
$this->generateChunkCallback($chunkPopulationLockId, $chunkX, $chunkZ, $centerChunk, $adjacentChunks, $temporaryChunkLoader);
|
$this->generateChunkCallback($chunkPopulationLockId, $chunkX, $chunkZ, $centerChunk, $adjacentChunks, $temporaryChunkLoader);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
$workerId = $this->workerPool->selectWorker();
|
|
||||||
if(!isset($this->workerPool->getRunningWorkers()[$workerId]) && isset($this->generatorRegisteredWorkers[$workerId])){
|
|
||||||
$this->logger->debug("Selected worker $workerId previously had generator registered, but is now offline");
|
|
||||||
unset($this->generatorRegisteredWorkers[$workerId]);
|
|
||||||
}
|
|
||||||
if(!isset($this->generatorRegisteredWorkers[$workerId])){
|
|
||||||
$this->registerGeneratorToWorker($workerId);
|
|
||||||
}
|
|
||||||
$this->workerPool->submitTaskToWorker($task, $workerId);
|
|
||||||
|
|
||||||
return $resolver->getPromise();
|
return $resolver->getPromise();
|
||||||
}finally{
|
}finally{
|
||||||
|
@ -50,7 +50,7 @@ final class GeneratorManager{
|
|||||||
}catch(InvalidGeneratorOptionsException $e){
|
}catch(InvalidGeneratorOptionsException $e){
|
||||||
return $e;
|
return $e;
|
||||||
}
|
}
|
||||||
});
|
}, fast: true);
|
||||||
$this->addGenerator(Normal::class, "normal", fn() => null);
|
$this->addGenerator(Normal::class, "normal", fn() => null);
|
||||||
$this->addAlias("normal", "default");
|
$this->addAlias("normal", "default");
|
||||||
$this->addGenerator(Nether::class, "nether", fn() => null);
|
$this->addGenerator(Nether::class, "nether", fn() => null);
|
||||||
@ -62,6 +62,7 @@ final class GeneratorManager{
|
|||||||
* @param string $name Alias for this generator type that can be written in configs
|
* @param string $name Alias for this generator type that can be written in configs
|
||||||
* @param \Closure $presetValidator Callback to validate generator options for new worlds
|
* @param \Closure $presetValidator Callback to validate generator options for new worlds
|
||||||
* @param bool $overwrite Whether to force overwriting any existing registered generator with the same name
|
* @param bool $overwrite Whether to force overwriting any existing registered generator with the same name
|
||||||
|
* @param bool $fast Whether this generator is fast enough to run without async tasks
|
||||||
*
|
*
|
||||||
* @phpstan-param \Closure(string) : ?InvalidGeneratorOptionsException $presetValidator
|
* @phpstan-param \Closure(string) : ?InvalidGeneratorOptionsException $presetValidator
|
||||||
*
|
*
|
||||||
@ -69,7 +70,7 @@ final class GeneratorManager{
|
|||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException
|
* @throws \InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
public function addGenerator(string $class, string $name, \Closure $presetValidator, bool $overwrite = false) : void{
|
public function addGenerator(string $class, string $name, \Closure $presetValidator, bool $overwrite = false, bool $fast = false) : void{
|
||||||
Utils::testValidInstance($class, Generator::class);
|
Utils::testValidInstance($class, Generator::class);
|
||||||
|
|
||||||
$name = strtolower($name);
|
$name = strtolower($name);
|
||||||
@ -77,7 +78,7 @@ final class GeneratorManager{
|
|||||||
throw new \InvalidArgumentException("Alias \"$name\" is already assigned");
|
throw new \InvalidArgumentException("Alias \"$name\" is already assigned");
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->list[$name] = new GeneratorManagerEntry($class, $presetValidator);
|
$this->list[$name] = new GeneratorManagerEntry($class, $presetValidator, $fast);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,12 +31,15 @@ final class GeneratorManagerEntry{
|
|||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private string $generatorClass,
|
private string $generatorClass,
|
||||||
private \Closure $presetValidator
|
private \Closure $presetValidator,
|
||||||
|
private readonly bool $fast
|
||||||
){}
|
){}
|
||||||
|
|
||||||
/** @phpstan-return class-string<Generator> */
|
/** @phpstan-return class-string<Generator> */
|
||||||
public function getGeneratorClass() : string{ return $this->generatorClass; }
|
public function getGeneratorClass() : string{ return $this->generatorClass; }
|
||||||
|
|
||||||
|
public function isFast() : bool{ return $this->fast; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws InvalidGeneratorOptionsException
|
* @throws InvalidGeneratorOptionsException
|
||||||
*/
|
*/
|
||||||
|
@ -27,11 +27,18 @@ use pocketmine\scheduler\AsyncTask;
|
|||||||
use pocketmine\utils\AssumptionFailedError;
|
use pocketmine\utils\AssumptionFailedError;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
use pocketmine\world\format\io\FastChunkSerializer;
|
use pocketmine\world\format\io\FastChunkSerializer;
|
||||||
|
use pocketmine\world\generator\executor\ThreadLocalGeneratorContext;
|
||||||
use function array_map;
|
use function array_map;
|
||||||
use function igbinary_serialize;
|
use function igbinary_serialize;
|
||||||
use function igbinary_unserialize;
|
use function igbinary_unserialize;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* TODO: this should be moved to the executor namespace, but plugins have unfortunately used it directly due to the
|
||||||
|
* difficulty of regenerating chunks. This should be addressed in the future.
|
||||||
|
* For the remainder of PM5, we can't relocate this class.
|
||||||
|
*
|
||||||
* @phpstan-type OnCompletion \Closure(Chunk $centerChunk, array<int, Chunk> $adjacentChunks) : void
|
* @phpstan-type OnCompletion \Closure(Chunk $centerChunk, array<int, Chunk> $adjacentChunks) : void
|
||||||
*/
|
*/
|
||||||
class PopulationTask extends AsyncTask{
|
class PopulationTask extends AsyncTask{
|
||||||
|
106
src/world/generator/executor/AsyncGeneratorExecutor.php
Normal file
106
src/world/generator/executor/AsyncGeneratorExecutor.php
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<?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\world\generator\executor;
|
||||||
|
|
||||||
|
use pocketmine\scheduler\AsyncPool;
|
||||||
|
use pocketmine\world\format\Chunk;
|
||||||
|
use pocketmine\world\generator\PopulationTask;
|
||||||
|
use function array_key_exists;
|
||||||
|
|
||||||
|
final class AsyncGeneratorExecutor implements GeneratorExecutor{
|
||||||
|
private static int $nextAsyncContextId = 1;
|
||||||
|
|
||||||
|
private readonly \Logger $logger;
|
||||||
|
|
||||||
|
/** @phpstan-var \Closure(int) : void */
|
||||||
|
private readonly \Closure $workerStartHook;
|
||||||
|
|
||||||
|
private readonly int $asyncContextId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var true[]
|
||||||
|
* @phpstan-var array<int, true>
|
||||||
|
*/
|
||||||
|
private array $generatorRegisteredWorkers = [];
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
\Logger $logger,
|
||||||
|
private readonly AsyncPool $workerPool,
|
||||||
|
private readonly GeneratorExecutorSetupParameters $setupParameters,
|
||||||
|
int $asyncContextId = null
|
||||||
|
){
|
||||||
|
$this->logger = new \PrefixedLogger($logger, "AsyncGeneratorExecutor");
|
||||||
|
|
||||||
|
//TODO: we only allow setting this for PM5 because of PopulationTask uses in plugins
|
||||||
|
$this->asyncContextId = $asyncContextId ?? self::$nextAsyncContextId++;
|
||||||
|
|
||||||
|
$this->workerStartHook = function(int $workerId) : void{
|
||||||
|
if(array_key_exists($workerId, $this->generatorRegisteredWorkers)){
|
||||||
|
$this->logger->debug("Worker $workerId with previously registered generator restarted, flagging as unregistered");
|
||||||
|
unset($this->generatorRegisteredWorkers[$workerId]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$this->workerPool->addWorkerStartHook($this->workerStartHook);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function registerGeneratorToWorker(int $worker) : void{
|
||||||
|
$this->logger->debug("Registering generator on worker $worker");
|
||||||
|
$this->workerPool->submitTaskToWorker(new AsyncGeneratorRegisterTask($this->setupParameters, $this->asyncContextId), $worker);
|
||||||
|
$this->generatorRegisteredWorkers[$worker] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function unregisterGenerator() : void{
|
||||||
|
foreach($this->workerPool->getRunningWorkers() as $i){
|
||||||
|
if(isset($this->generatorRegisteredWorkers[$i])){
|
||||||
|
$this->workerPool->submitTaskToWorker(new AsyncGeneratorUnregisterTask($this->asyncContextId), $i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->generatorRegisteredWorkers = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function populate(int $chunkX, int $chunkZ, ?Chunk $centerChunk, array $adjacentChunks, \Closure $onCompletion) : void{
|
||||||
|
$task = new PopulationTask(
|
||||||
|
$this->asyncContextId,
|
||||||
|
$chunkX,
|
||||||
|
$chunkZ,
|
||||||
|
$centerChunk,
|
||||||
|
$adjacentChunks,
|
||||||
|
$onCompletion
|
||||||
|
);
|
||||||
|
$workerId = $this->workerPool->selectWorker();
|
||||||
|
if(!isset($this->workerPool->getRunningWorkers()[$workerId]) && isset($this->generatorRegisteredWorkers[$workerId])){
|
||||||
|
$this->logger->debug("Selected worker $workerId previously had generator registered, but is now offline");
|
||||||
|
unset($this->generatorRegisteredWorkers[$workerId]);
|
||||||
|
}
|
||||||
|
if(!isset($this->generatorRegisteredWorkers[$workerId])){
|
||||||
|
$this->registerGeneratorToWorker($workerId);
|
||||||
|
}
|
||||||
|
$this->workerPool->submitTaskToWorker($task, $workerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shutdown() : void{
|
||||||
|
$this->unregisterGenerator();
|
||||||
|
$this->workerPool->removeWorkerStartHook($this->workerStartHook);
|
||||||
|
}
|
||||||
|
}
|
@ -21,37 +21,20 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace pocketmine\world\generator;
|
namespace pocketmine\world\generator\executor;
|
||||||
|
|
||||||
use pocketmine\scheduler\AsyncTask;
|
use pocketmine\scheduler\AsyncTask;
|
||||||
use pocketmine\world\World;
|
|
||||||
|
|
||||||
class GeneratorRegisterTask extends AsyncTask{
|
class AsyncGeneratorRegisterTask extends AsyncTask{
|
||||||
public int $seed;
|
|
||||||
public int $worldId;
|
|
||||||
public int $worldMinY;
|
|
||||||
public int $worldMaxY;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @phpstan-param class-string<Generator> $generatorClass
|
|
||||||
*/
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
World $world,
|
private readonly GeneratorExecutorSetupParameters $setupParameters,
|
||||||
public string $generatorClass,
|
private readonly int $contextId
|
||||||
public string $generatorSettings
|
){}
|
||||||
){
|
|
||||||
$this->seed = $world->getSeed();
|
|
||||||
$this->worldId = $world->getId();
|
|
||||||
$this->worldMinY = $world->getMinY();
|
|
||||||
$this->worldMaxY = $world->getMaxY();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onRun() : void{
|
public function onRun() : void{
|
||||||
/**
|
$setupParameters = $this->setupParameters;
|
||||||
* @var Generator $generator
|
$generator = $setupParameters->createGenerator();
|
||||||
* @see Generator::__construct()
|
ThreadLocalGeneratorContext::register(new ThreadLocalGeneratorContext($generator, $setupParameters->worldMinY, $setupParameters->worldMaxY), $this->contextId);
|
||||||
*/
|
|
||||||
$generator = new $this->generatorClass($this->seed, $this->generatorSettings);
|
|
||||||
ThreadLocalGeneratorContext::register(new ThreadLocalGeneratorContext($generator, $this->worldMinY, $this->worldMaxY), $this->worldId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -21,19 +21,16 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace pocketmine\world\generator;
|
namespace pocketmine\world\generator\executor;
|
||||||
|
|
||||||
use pocketmine\scheduler\AsyncTask;
|
use pocketmine\scheduler\AsyncTask;
|
||||||
use pocketmine\world\World;
|
|
||||||
|
|
||||||
class GeneratorUnregisterTask extends AsyncTask{
|
class AsyncGeneratorUnregisterTask extends AsyncTask{
|
||||||
public int $worldId;
|
public function __construct(
|
||||||
|
private readonly int $contextId
|
||||||
public function __construct(World $world){
|
){}
|
||||||
$this->worldId = $world->getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onRun() : void{
|
public function onRun() : void{
|
||||||
ThreadLocalGeneratorContext::unregister($this->worldId);
|
ThreadLocalGeneratorContext::unregister($this->contextId);
|
||||||
}
|
}
|
||||||
}
|
}
|
38
src/world/generator/executor/GeneratorExecutor.php
Normal file
38
src/world/generator/executor/GeneratorExecutor.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?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\world\generator\executor;
|
||||||
|
|
||||||
|
use pocketmine\world\format\Chunk;
|
||||||
|
|
||||||
|
interface GeneratorExecutor{
|
||||||
|
/**
|
||||||
|
* @param Chunk[]|null[] $adjacentChunks
|
||||||
|
* @phpstan-param array<int, Chunk|null> $adjacentChunks
|
||||||
|
* @phpstan-param \Closure(Chunk $centerChunk, array<int, Chunk> $adjacentChunks) : void $onCompletion
|
||||||
|
*/
|
||||||
|
public function populate(int $chunkX, int $chunkZ, ?Chunk $centerChunk, array $adjacentChunks, \Closure $onCompletion) : void;
|
||||||
|
|
||||||
|
public function shutdown() : void;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
<?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\world\generator\executor;
|
||||||
|
|
||||||
|
use pmmp\thread\ThreadSafe;
|
||||||
|
use pocketmine\world\generator\Generator;
|
||||||
|
|
||||||
|
final class GeneratorExecutorSetupParameters extends ThreadSafe{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-param class-string<covariant \pocketmine\world\generator\Generator> $generatorClass
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public readonly int $worldMinY,
|
||||||
|
public readonly int $worldMaxY,
|
||||||
|
public readonly int $generatorSeed,
|
||||||
|
public readonly string $generatorClass,
|
||||||
|
public readonly string $generatorSettings,
|
||||||
|
){}
|
||||||
|
|
||||||
|
public function createGenerator() : Generator{
|
||||||
|
/**
|
||||||
|
* @var Generator $generator
|
||||||
|
* @see Generator::__construct()
|
||||||
|
*/
|
||||||
|
$generator = new $this->generatorClass($this->generatorSeed, $this->generatorSettings);
|
||||||
|
return $generator;
|
||||||
|
}
|
||||||
|
}
|
61
src/world/generator/executor/SyncGeneratorExecutor.php
Normal file
61
src/world/generator/executor/SyncGeneratorExecutor.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?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\world\generator\executor;
|
||||||
|
|
||||||
|
use pocketmine\world\format\Chunk;
|
||||||
|
use pocketmine\world\generator\Generator;
|
||||||
|
use pocketmine\world\generator\PopulationUtils;
|
||||||
|
|
||||||
|
final class SyncGeneratorExecutor implements GeneratorExecutor{
|
||||||
|
|
||||||
|
private readonly Generator $generator;
|
||||||
|
private readonly int $worldMinY;
|
||||||
|
private readonly int $worldMaxY;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
GeneratorExecutorSetupParameters $setupParameters
|
||||||
|
){
|
||||||
|
$this->generator = $setupParameters->createGenerator();
|
||||||
|
$this->worldMinY = $setupParameters->worldMinY;
|
||||||
|
$this->worldMaxY = $setupParameters->worldMaxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function populate(int $chunkX, int $chunkZ, ?Chunk $centerChunk, array $adjacentChunks, \Closure $onCompletion) : void{
|
||||||
|
[$centerChunk, $adjacentChunks] = PopulationUtils::populateChunkWithAdjacents(
|
||||||
|
$this->worldMinY,
|
||||||
|
$this->worldMaxY,
|
||||||
|
$this->generator,
|
||||||
|
$chunkX,
|
||||||
|
$chunkZ,
|
||||||
|
$centerChunk,
|
||||||
|
$adjacentChunks
|
||||||
|
);
|
||||||
|
|
||||||
|
$onCompletion($centerChunk, $adjacentChunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shutdown() : void{
|
||||||
|
//NOOP
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,9 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace pocketmine\world\generator;
|
namespace pocketmine\world\generator\executor;
|
||||||
|
|
||||||
|
use pocketmine\world\generator\Generator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages thread-local caches for generators and the things needed to support them
|
* Manages thread-local caches for generators and the things needed to support them
|
@ -1272,18 +1272,18 @@ parameters:
|
|||||||
count: 1
|
count: 1
|
||||||
path: ../../../src/world/format/io/region/RegionLoader.php
|
path: ../../../src/world/format/io/region/RegionLoader.php
|
||||||
|
|
||||||
-
|
|
||||||
message: '#^Dynamic new is not allowed\.$#'
|
|
||||||
identifier: pocketmine.new.dynamic
|
|
||||||
count: 1
|
|
||||||
path: ../../../src/world/generator/GeneratorRegisterTask.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: '#^Method pocketmine\\world\\generator\\biome\\BiomeSelector\:\:pickBiome\(\) should return pocketmine\\world\\biome\\Biome but returns pocketmine\\world\\biome\\Biome\|null\.$#'
|
message: '#^Method pocketmine\\world\\generator\\biome\\BiomeSelector\:\:pickBiome\(\) should return pocketmine\\world\\biome\\Biome but returns pocketmine\\world\\biome\\Biome\|null\.$#'
|
||||||
identifier: return.type
|
identifier: return.type
|
||||||
count: 1
|
count: 1
|
||||||
path: ../../../src/world/generator/biome/BiomeSelector.php
|
path: ../../../src/world/generator/biome/BiomeSelector.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: '#^Dynamic new is not allowed\.$#'
|
||||||
|
identifier: pocketmine.new.dynamic
|
||||||
|
count: 1
|
||||||
|
path: ../../../src/world/generator/executor/GeneratorExecutorSetupParameters.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: '#^Cannot call method getBiomeId\(\) on pocketmine\\world\\format\\Chunk\|null\.$#'
|
message: '#^Cannot call method getBiomeId\(\) on pocketmine\\world\\format\\Chunk\|null\.$#'
|
||||||
identifier: method.nonObject
|
identifier: method.nonObject
|
||||||
|
Loading…
x
Reference in New Issue
Block a user