mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-30 23:29:54 +00:00
Tidy, allow using closures to setup generators instead of class strings
This commit is contained in:
parent
09c074ed22
commit
a4ae1991c6
@ -92,8 +92,12 @@ 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\SyncGeneratorExecutor;
|
||||||
use pocketmine\world\generator\Generator;
|
use pocketmine\world\generator\Generator;
|
||||||
use pocketmine\world\generator\GeneratorManager;
|
use pocketmine\world\generator\GeneratorManager;
|
||||||
|
use pocketmine\world\generator\GeneratorManagerEntry;
|
||||||
use pocketmine\world\light\BlockLightUpdate;
|
use pocketmine\world\light\BlockLightUpdate;
|
||||||
use pocketmine\world\light\LightPopulationTask;
|
use pocketmine\world\light\LightPopulationTask;
|
||||||
use pocketmine\world\light\SkyLightUpdate;
|
use pocketmine\world\light\SkyLightUpdate;
|
||||||
@ -306,7 +310,7 @@ class World implements ChunkManager{
|
|||||||
*/
|
*/
|
||||||
private array $neighbourBlockUpdateQueueIndex = [];
|
private array $neighbourBlockUpdateQueueIndex = [];
|
||||||
|
|
||||||
private readonly ChunkGenerator $chunkGenerator;
|
private readonly GeneratorExecutor $generatorExecutor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ChunkLockId[]
|
* @var ChunkLockId[]
|
||||||
@ -332,9 +336,6 @@ class World implements ChunkManager{
|
|||||||
|
|
||||||
private bool $doingTick = false;
|
private bool $doingTick = false;
|
||||||
|
|
||||||
/** @phpstan-var class-string<\pocketmine\world\generator\Generator> */
|
|
||||||
private string $generator;
|
|
||||||
|
|
||||||
private bool $unloaded = false;
|
private bool $unloaded = false;
|
||||||
/**
|
/**
|
||||||
* @var \Closure[]
|
* @var \Closure[]
|
||||||
@ -471,25 +472,24 @@ class World implements ChunkManager{
|
|||||||
throw new AssumptionFailedError("WorldManager should already have checked that the generator exists");
|
throw new AssumptionFailedError("WorldManager should already have checked that the generator exists");
|
||||||
$generatorOptions = $this->provider->getWorldData()->getGeneratorOptions();
|
$generatorOptions = $this->provider->getWorldData()->getGeneratorOptions();
|
||||||
$generator->validateGeneratorOptions($generatorOptions);
|
$generator->validateGeneratorOptions($generatorOptions);
|
||||||
$this->generator = $generator->getGeneratorClass();
|
$generatorClass = $generator->getGeneratorClass();
|
||||||
|
|
||||||
$cfg = $this->server->getConfigGroup();
|
$cfg = $this->server->getConfigGroup();
|
||||||
|
$seed = $this->getSeed();
|
||||||
if($generator->isFast()){
|
if($generator->isFast()){
|
||||||
/**
|
$this->generatorExecutor = new SyncGeneratorExecutor(GeneratorManagerEntry::make($generatorClass, $seed, $generatorOptions));
|
||||||
* @see Generator::__construct()
|
$this->logger->debug("Using main thread generator system for fast generator " . $generatorClass);
|
||||||
*/
|
|
||||||
$this->chunkGenerator = new SyncChunkGenerator(new $this->generator($this->getSeed(), $generatorOptions));
|
|
||||||
$this->logger->debug("Using main thread generator system for fast generator " . $this->generator);
|
|
||||||
}else{
|
}else{
|
||||||
$this->chunkGenerator = new AsyncChunkGenerator(
|
$this->generatorExecutor = new AsyncGeneratorExecutor(
|
||||||
$this->workerPool,
|
$this->workerPool,
|
||||||
$this->logger,
|
$this->logger,
|
||||||
|
static fn() => GeneratorManagerEntry::make($generatorClass, $seed, $generatorOptions),
|
||||||
$cfg->getPropertyInt(YmlServerProperties::CHUNK_GENERATION_POPULATION_QUEUE_SIZE, 2)
|
$cfg->getPropertyInt(YmlServerProperties::CHUNK_GENERATION_POPULATION_QUEUE_SIZE, 2)
|
||||||
);
|
);
|
||||||
$this->logger->debug("Using async task generator system for slow generator " . $this->generator);
|
$this->logger->debug("Using async task generator system for slow generator " . $generatorClass);
|
||||||
}
|
}
|
||||||
$this->addOnUnloadCallback(function() : void{
|
$this->addOnUnloadCallback(function() : void{
|
||||||
$this->chunkGenerator->shutdown($this);
|
$this->generatorExecutor->shutdown($this);
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->scheduledBlockUpdateQueue = new ReversePriorityQueue();
|
$this->scheduledBlockUpdateQueue = new ReversePriorityQueue();
|
||||||
@ -549,13 +549,6 @@ class World implements ChunkManager{
|
|||||||
return $this->tickRateTime;
|
return $this->tickRateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @phpstan-return class-string<covariant \pocketmine\world\generator\Generator>
|
|
||||||
*/
|
|
||||||
public function getGeneratorClass() : string{
|
|
||||||
return $this->generator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getServer() : Server{
|
public function getServer() : Server{
|
||||||
return $this->server;
|
return $this->server;
|
||||||
}
|
}
|
||||||
@ -791,7 +784,7 @@ class World implements ChunkManager{
|
|||||||
if(count($this->chunkLoaders[$chunkHash]) === 1){
|
if(count($this->chunkLoaders[$chunkHash]) === 1){
|
||||||
unset($this->chunkLoaders[$chunkHash]);
|
unset($this->chunkLoaders[$chunkHash]);
|
||||||
$this->unloadChunkRequest($chunkX, $chunkZ, true);
|
$this->unloadChunkRequest($chunkX, $chunkZ, true);
|
||||||
$this->chunkGenerator->cancelChunkPopulation($this, $chunkX, $chunkZ);
|
$this->generatorExecutor->cancelChunkPopulation($this, $chunkX, $chunkZ);
|
||||||
}else{
|
}else{
|
||||||
unset($this->chunkLoaders[$chunkHash][$loaderId]);
|
unset($this->chunkLoaders[$chunkHash][$loaderId]);
|
||||||
}
|
}
|
||||||
@ -3068,7 +3061,7 @@ class World implements ChunkManager{
|
|||||||
unset($this->registeredTickingChunks[$chunkHash]);
|
unset($this->registeredTickingChunks[$chunkHash]);
|
||||||
$this->markTickingChunkForRecheck($x, $z);
|
$this->markTickingChunkForRecheck($x, $z);
|
||||||
|
|
||||||
$this->chunkGenerator->cancelChunkPopulation($this, $x, $z);
|
$this->generatorExecutor->cancelChunkPopulation($this, $x, $z);
|
||||||
|
|
||||||
$this->timings->doChunkUnload->stopTiming();
|
$this->timings->doChunkUnload->stopTiming();
|
||||||
|
|
||||||
@ -3265,7 +3258,7 @@ class World implements ChunkManager{
|
|||||||
* @phpstan-return Promise<Chunk>
|
* @phpstan-return Promise<Chunk>
|
||||||
*/
|
*/
|
||||||
public function requestChunkPopulation(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{
|
public function requestChunkPopulation(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{
|
||||||
return $this->chunkGenerator->requestChunkPopulation($this, $chunkX, $chunkZ, $associatedChunkLoader);
|
return $this->generatorExecutor->requestChunkPopulation($this, $chunkX, $chunkZ, $associatedChunkLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3279,7 +3272,7 @@ class World implements ChunkManager{
|
|||||||
* @phpstan-return Promise<Chunk>
|
* @phpstan-return Promise<Chunk>
|
||||||
*/
|
*/
|
||||||
public function orderChunkPopulation(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{
|
public function orderChunkPopulation(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{
|
||||||
return $this->chunkGenerator->orderChunkPopulation($this, $chunkX, $chunkZ, $associatedChunkLoader);
|
return $this->generatorExecutor->orderChunkPopulation($this, $chunkX, $chunkZ, $associatedChunkLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doChunkGarbageCollection() : void{
|
public function doChunkGarbageCollection() : void{
|
||||||
|
@ -50,4 +50,14 @@ final class GeneratorManagerEntry{
|
|||||||
public function isFast() : bool{
|
public function isFast() : bool{
|
||||||
return $this->fast;
|
return $this->fast;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-param class-string<covariant Generator> $class
|
||||||
|
*/
|
||||||
|
public static function make(string $class, int $seed, string $options) : Generator{
|
||||||
|
/**
|
||||||
|
* @see Generator::__construct()
|
||||||
|
*/
|
||||||
|
return new $class($seed, $options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,31 +27,24 @@ use pocketmine\scheduler\AsyncTask;
|
|||||||
use pocketmine\world\World;
|
use pocketmine\world\World;
|
||||||
|
|
||||||
class GeneratorRegisterTask extends AsyncTask{
|
class GeneratorRegisterTask extends AsyncTask{
|
||||||
public int $seed;
|
|
||||||
public int $worldId;
|
public int $worldId;
|
||||||
public int $worldMinY;
|
public int $worldMinY;
|
||||||
public int $worldMaxY;
|
public int $worldMaxY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phpstan-param class-string<Generator> $generatorClass
|
* @phpstan-param \Closure() : Generator $generatorFactory
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
World $world,
|
World $world,
|
||||||
public string $generatorClass,
|
private readonly \Closure $generatorFactory
|
||||||
public string $generatorSettings
|
|
||||||
){
|
){
|
||||||
$this->seed = $world->getSeed();
|
|
||||||
$this->worldId = $world->getId();
|
$this->worldId = $world->getId();
|
||||||
$this->worldMinY = $world->getMinY();
|
$this->worldMinY = $world->getMinY();
|
||||||
$this->worldMaxY = $world->getMaxY();
|
$this->worldMaxY = $world->getMaxY();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onRun() : void{
|
public function onRun() : void{
|
||||||
/**
|
$generator = ($this->generatorFactory)();
|
||||||
* @var Generator $generator
|
|
||||||
* @see Generator::__construct()
|
|
||||||
*/
|
|
||||||
$generator = new $this->generatorClass($this->seed, $this->generatorSettings);
|
|
||||||
ThreadLocalGeneratorContext::register(new ThreadLocalGeneratorContext($generator, $this->worldMinY, $this->worldMaxY), $this->worldId);
|
ThreadLocalGeneratorContext::register(new ThreadLocalGeneratorContext($generator, $this->worldMinY, $this->worldMaxY), $this->worldId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,16 +21,21 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace pocketmine\world;
|
namespace pocketmine\world\generator\executor;
|
||||||
|
|
||||||
|
use pmmp\thread\ThreadSafeArray;
|
||||||
use pocketmine\event\world\ChunkPopulateEvent;
|
use pocketmine\event\world\ChunkPopulateEvent;
|
||||||
use pocketmine\promise\Promise;
|
use pocketmine\promise\Promise;
|
||||||
use pocketmine\promise\PromiseResolver;
|
use pocketmine\promise\PromiseResolver;
|
||||||
use pocketmine\scheduler\AsyncPool;
|
use pocketmine\scheduler\AsyncPool;
|
||||||
use pocketmine\utils\AssumptionFailedError;
|
use pocketmine\utils\AssumptionFailedError;
|
||||||
|
use pocketmine\world\ChunkLoader;
|
||||||
|
use pocketmine\world\ChunkLockId;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
|
use pocketmine\world\generator\Generator;
|
||||||
use pocketmine\world\generator\GeneratorRegisterTask;
|
use pocketmine\world\generator\GeneratorRegisterTask;
|
||||||
use pocketmine\world\generator\PopulationTask;
|
use pocketmine\world\generator\PopulationTask;
|
||||||
|
use pocketmine\world\World;
|
||||||
use function array_key_exists;
|
use function array_key_exists;
|
||||||
use function assert;
|
use function assert;
|
||||||
use function count;
|
use function count;
|
||||||
@ -38,7 +43,7 @@ use function count;
|
|||||||
/**
|
/**
|
||||||
* @phpstan-import-type ChunkPosHash from World
|
* @phpstan-import-type ChunkPosHash from World
|
||||||
*/
|
*/
|
||||||
final class AsyncChunkGenerator implements ChunkGenerator{
|
final class AsyncGeneratorExecutor implements GeneratorExecutor{
|
||||||
/**
|
/**
|
||||||
* @var bool[] chunkHash => isValid
|
* @var bool[] chunkHash => isValid
|
||||||
* @phpstan-var array<ChunkPosHash, bool>
|
* @phpstan-var array<ChunkPosHash, bool>
|
||||||
@ -71,11 +76,19 @@ final class AsyncChunkGenerator implements ChunkGenerator{
|
|||||||
/** @phpstan-var \Closure(int) : void */
|
/** @phpstan-var \Closure(int) : void */
|
||||||
private \Closure $workerStartHook;
|
private \Closure $workerStartHook;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-param \Closure() : Generator $generatorFactory Must be a thread-safe closure
|
||||||
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly AsyncPool $workerPool,
|
private readonly AsyncPool $workerPool,
|
||||||
private readonly \Logger $logger,
|
private readonly \Logger $logger,
|
||||||
private readonly int $maxConcurrentChunkPopulationTasks = 2,
|
private readonly \Closure $generatorFactory,
|
||||||
|
private readonly int $maxConcurrentChunkPopulationTasks = 2
|
||||||
){
|
){
|
||||||
|
//TODO: we really need a better way to check if a closure is thread-safe :(
|
||||||
|
$temp = new ThreadSafeArray();
|
||||||
|
$temp["dummy"] = $this->generatorFactory;
|
||||||
|
|
||||||
$this->chunkPopulationRequestQueue = new \SplQueue();
|
$this->chunkPopulationRequestQueue = new \SplQueue();
|
||||||
//TODO: don't love the circular reference here, but we need to make sure this gets cleaned up on shutdown
|
//TODO: don't love the circular reference here, but we need to make sure this gets cleaned up on shutdown
|
||||||
$this->workerStartHook = function(int $workerId) : void{
|
$this->workerStartHook = function(int $workerId) : void{
|
||||||
@ -91,8 +104,7 @@ final class AsyncChunkGenerator implements ChunkGenerator{
|
|||||||
$world->getLogger()->debug("Registering generator on worker $worker");
|
$world->getLogger()->debug("Registering generator on worker $worker");
|
||||||
$this->workerPool->submitTaskToWorker(new GeneratorRegisterTask(
|
$this->workerPool->submitTaskToWorker(new GeneratorRegisterTask(
|
||||||
$world,
|
$world,
|
||||||
$world->getGeneratorClass(),
|
$this->generatorFactory,
|
||||||
$world->getProvider()->getWorldData()->getGeneratorOptions()
|
|
||||||
), $worker);
|
), $worker);
|
||||||
$this->generatorRegisteredWorkers[$worker] = true;
|
$this->generatorRegisteredWorkers[$worker] = true;
|
||||||
}
|
}
|
@ -21,19 +21,25 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace pocketmine\world;
|
namespace pocketmine\world\generator\executor;
|
||||||
|
|
||||||
use pocketmine\promise\Promise;
|
use pocketmine\promise\Promise;
|
||||||
|
use pocketmine\world\ChunkLoader;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
|
use pocketmine\world\World;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Decides how and when to invoke the world generator.
|
||||||
|
*
|
||||||
* @phpstan-import-type ChunkPosHash from World
|
* @phpstan-import-type ChunkPosHash from World
|
||||||
*/
|
*/
|
||||||
interface ChunkGenerator{
|
interface GeneratorExecutor{
|
||||||
/**
|
/**
|
||||||
* Attempts to initiate asynchronous generation/population of the target chunk, if it's currently reasonable to do
|
* Requests generation/population of the target chunk, if it's currently reasonable to do so (and if it isn't
|
||||||
* so (and if it isn't already generated/populated).
|
* already generated/populated).
|
||||||
* If the generator is busy, the request will be put into a queue and delayed until a better time.
|
* The executor may decide not to process this request immediately based on internal conditions (e.g. the number of
|
||||||
|
* concurrently active generation tasks may have reached a limit). If this happens, it will be queued and processed
|
||||||
|
* as soon as possible.
|
||||||
*
|
*
|
||||||
* A ChunkLoader can be associated with the generation request to ensure that the generation request is cancelled if
|
* A ChunkLoader can be associated with the generation request to ensure that the generation request is cancelled if
|
||||||
* no loaders are attached to the target chunk. If no loader is provided, one will be assigned (and automatically
|
* no loaders are attached to the target chunk. If no loader is provided, one will be assigned (and automatically
|
||||||
@ -44,12 +50,12 @@ interface ChunkGenerator{
|
|||||||
public function requestChunkPopulation(World $world, int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise;
|
public function requestChunkPopulation(World $world, int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initiates asynchronous generation/population of the target chunk, if it's not already generated/populated.
|
* Initiates generation/population of the target chunk, if it's not already generated/populated.
|
||||||
* If generation has already been requested for the target chunk, the promise for the already active request will be
|
|
||||||
* returned directly.
|
|
||||||
*
|
*
|
||||||
* If the chunk is currently locked (for example due to another chunk using it for async generation), the request
|
* This function will begin processing the request immediately, unless any of the adjacent chunks are currently in
|
||||||
* will be queued and executed at the earliest opportunity.
|
* use for other generation tasks.
|
||||||
|
*
|
||||||
|
* @see World::isChunkLocked()
|
||||||
*
|
*
|
||||||
* @phpstan-return Promise<Chunk>
|
* @phpstan-return Promise<Chunk>
|
||||||
*/
|
*/
|
@ -21,20 +21,22 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace pocketmine\world;
|
namespace pocketmine\world\generator\executor;
|
||||||
|
|
||||||
use pocketmine\event\world\ChunkPopulateEvent;
|
use pocketmine\event\world\ChunkPopulateEvent;
|
||||||
use pocketmine\promise\Promise;
|
use pocketmine\promise\Promise;
|
||||||
use pocketmine\promise\PromiseResolver;
|
use pocketmine\promise\PromiseResolver;
|
||||||
use pocketmine\utils\AssumptionFailedError;
|
use pocketmine\utils\AssumptionFailedError;
|
||||||
|
use pocketmine\world\ChunkLoader;
|
||||||
use pocketmine\world\format\Chunk;
|
use pocketmine\world\format\Chunk;
|
||||||
use pocketmine\world\generator\Generator;
|
use pocketmine\world\generator\Generator;
|
||||||
|
use pocketmine\world\World;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Very simple chunk generator which runs everything immediately on the main thread.
|
* Very simple chunk generator which runs everything immediately on the main thread.
|
||||||
* Useful if your generator is very fast and doesn't benefit from async tasks or threading.
|
* Useful if your generator is very fast and doesn't benefit from async tasks or threading.
|
||||||
*/
|
*/
|
||||||
final class SyncChunkGenerator implements ChunkGenerator{
|
final class SyncGeneratorExecutor implements GeneratorExecutor{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly Generator $generator
|
private readonly Generator $generator
|
||||||
){}
|
){}
|
Loading…
x
Reference in New Issue
Block a user