mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-19 15:35:52 +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\LightArray;
|
||||
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\GeneratorManager;
|
||||
use pocketmine\world\generator\GeneratorManagerEntry;
|
||||
use pocketmine\world\light\BlockLightUpdate;
|
||||
use pocketmine\world\light\LightPopulationTask;
|
||||
use pocketmine\world\light\SkyLightUpdate;
|
||||
@ -306,7 +310,7 @@ class World implements ChunkManager{
|
||||
*/
|
||||
private array $neighbourBlockUpdateQueueIndex = [];
|
||||
|
||||
private readonly ChunkGenerator $chunkGenerator;
|
||||
private readonly GeneratorExecutor $generatorExecutor;
|
||||
|
||||
/**
|
||||
* @var ChunkLockId[]
|
||||
@ -332,9 +336,6 @@ class World implements ChunkManager{
|
||||
|
||||
private bool $doingTick = false;
|
||||
|
||||
/** @phpstan-var class-string<\pocketmine\world\generator\Generator> */
|
||||
private string $generator;
|
||||
|
||||
private bool $unloaded = false;
|
||||
/**
|
||||
* @var \Closure[]
|
||||
@ -471,25 +472,24 @@ class World implements ChunkManager{
|
||||
throw new AssumptionFailedError("WorldManager should already have checked that the generator exists");
|
||||
$generatorOptions = $this->provider->getWorldData()->getGeneratorOptions();
|
||||
$generator->validateGeneratorOptions($generatorOptions);
|
||||
$this->generator = $generator->getGeneratorClass();
|
||||
$generatorClass = $generator->getGeneratorClass();
|
||||
|
||||
$cfg = $this->server->getConfigGroup();
|
||||
$seed = $this->getSeed();
|
||||
if($generator->isFast()){
|
||||
/**
|
||||
* @see Generator::__construct()
|
||||
*/
|
||||
$this->chunkGenerator = new SyncChunkGenerator(new $this->generator($this->getSeed(), $generatorOptions));
|
||||
$this->logger->debug("Using main thread generator system for fast generator " . $this->generator);
|
||||
$this->generatorExecutor = new SyncGeneratorExecutor(GeneratorManagerEntry::make($generatorClass, $seed, $generatorOptions));
|
||||
$this->logger->debug("Using main thread generator system for fast generator " . $generatorClass);
|
||||
}else{
|
||||
$this->chunkGenerator = new AsyncChunkGenerator(
|
||||
$this->generatorExecutor = new AsyncGeneratorExecutor(
|
||||
$this->workerPool,
|
||||
$this->logger,
|
||||
static fn() => GeneratorManagerEntry::make($generatorClass, $seed, $generatorOptions),
|
||||
$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->chunkGenerator->shutdown($this);
|
||||
$this->generatorExecutor->shutdown($this);
|
||||
});
|
||||
|
||||
$this->scheduledBlockUpdateQueue = new ReversePriorityQueue();
|
||||
@ -549,13 +549,6 @@ class World implements ChunkManager{
|
||||
return $this->tickRateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-return class-string<covariant \pocketmine\world\generator\Generator>
|
||||
*/
|
||||
public function getGeneratorClass() : string{
|
||||
return $this->generator;
|
||||
}
|
||||
|
||||
public function getServer() : Server{
|
||||
return $this->server;
|
||||
}
|
||||
@ -791,7 +784,7 @@ class World implements ChunkManager{
|
||||
if(count($this->chunkLoaders[$chunkHash]) === 1){
|
||||
unset($this->chunkLoaders[$chunkHash]);
|
||||
$this->unloadChunkRequest($chunkX, $chunkZ, true);
|
||||
$this->chunkGenerator->cancelChunkPopulation($this, $chunkX, $chunkZ);
|
||||
$this->generatorExecutor->cancelChunkPopulation($this, $chunkX, $chunkZ);
|
||||
}else{
|
||||
unset($this->chunkLoaders[$chunkHash][$loaderId]);
|
||||
}
|
||||
@ -3068,7 +3061,7 @@ class World implements ChunkManager{
|
||||
unset($this->registeredTickingChunks[$chunkHash]);
|
||||
$this->markTickingChunkForRecheck($x, $z);
|
||||
|
||||
$this->chunkGenerator->cancelChunkPopulation($this, $x, $z);
|
||||
$this->generatorExecutor->cancelChunkPopulation($this, $x, $z);
|
||||
|
||||
$this->timings->doChunkUnload->stopTiming();
|
||||
|
||||
@ -3265,7 +3258,7 @@ class World implements ChunkManager{
|
||||
* @phpstan-return Promise<Chunk>
|
||||
*/
|
||||
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>
|
||||
*/
|
||||
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{
|
||||
|
@ -50,4 +50,14 @@ final class GeneratorManagerEntry{
|
||||
public function isFast() : bool{
|
||||
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;
|
||||
|
||||
class GeneratorRegisterTask extends AsyncTask{
|
||||
public int $seed;
|
||||
public int $worldId;
|
||||
public int $worldMinY;
|
||||
public int $worldMaxY;
|
||||
|
||||
/**
|
||||
* @phpstan-param class-string<Generator> $generatorClass
|
||||
* @phpstan-param \Closure() : Generator $generatorFactory
|
||||
*/
|
||||
public function __construct(
|
||||
World $world,
|
||||
public string $generatorClass,
|
||||
public string $generatorSettings
|
||||
private readonly \Closure $generatorFactory
|
||||
){
|
||||
$this->seed = $world->getSeed();
|
||||
$this->worldId = $world->getId();
|
||||
$this->worldMinY = $world->getMinY();
|
||||
$this->worldMaxY = $world->getMaxY();
|
||||
}
|
||||
|
||||
public function onRun() : void{
|
||||
/**
|
||||
* @var Generator $generator
|
||||
* @see Generator::__construct()
|
||||
*/
|
||||
$generator = new $this->generatorClass($this->seed, $this->generatorSettings);
|
||||
$generator = ($this->generatorFactory)();
|
||||
ThreadLocalGeneratorContext::register(new ThreadLocalGeneratorContext($generator, $this->worldMinY, $this->worldMaxY), $this->worldId);
|
||||
}
|
||||
}
|
||||
|
@ -21,16 +21,21 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world;
|
||||
namespace pocketmine\world\generator\executor;
|
||||
|
||||
use pmmp\thread\ThreadSafeArray;
|
||||
use pocketmine\event\world\ChunkPopulateEvent;
|
||||
use pocketmine\promise\Promise;
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\scheduler\AsyncPool;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\ChunkLoader;
|
||||
use pocketmine\world\ChunkLockId;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\generator\Generator;
|
||||
use pocketmine\world\generator\GeneratorRegisterTask;
|
||||
use pocketmine\world\generator\PopulationTask;
|
||||
use pocketmine\world\World;
|
||||
use function array_key_exists;
|
||||
use function assert;
|
||||
use function count;
|
||||
@ -38,7 +43,7 @@ use function count;
|
||||
/**
|
||||
* @phpstan-import-type ChunkPosHash from World
|
||||
*/
|
||||
final class AsyncChunkGenerator implements ChunkGenerator{
|
||||
final class AsyncGeneratorExecutor implements GeneratorExecutor{
|
||||
/**
|
||||
* @var bool[] chunkHash => isValid
|
||||
* @phpstan-var array<ChunkPosHash, bool>
|
||||
@ -71,11 +76,19 @@ final class AsyncChunkGenerator implements ChunkGenerator{
|
||||
/** @phpstan-var \Closure(int) : void */
|
||||
private \Closure $workerStartHook;
|
||||
|
||||
/**
|
||||
* @phpstan-param \Closure() : Generator $generatorFactory Must be a thread-safe closure
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly AsyncPool $workerPool,
|
||||
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();
|
||||
//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{
|
||||
@ -91,8 +104,7 @@ final class AsyncChunkGenerator implements ChunkGenerator{
|
||||
$world->getLogger()->debug("Registering generator on worker $worker");
|
||||
$this->workerPool->submitTaskToWorker(new GeneratorRegisterTask(
|
||||
$world,
|
||||
$world->getGeneratorClass(),
|
||||
$world->getProvider()->getWorldData()->getGeneratorOptions()
|
||||
$this->generatorFactory,
|
||||
), $worker);
|
||||
$this->generatorRegisteredWorkers[$worker] = true;
|
||||
}
|
@ -21,19 +21,25 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world;
|
||||
namespace pocketmine\world\generator\executor;
|
||||
|
||||
use pocketmine\promise\Promise;
|
||||
use pocketmine\world\ChunkLoader;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\World;
|
||||
|
||||
/**
|
||||
* Decides how and when to invoke the world generator.
|
||||
*
|
||||
* @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
|
||||
* so (and if it isn't already generated/populated).
|
||||
* If the generator is busy, the request will be put into a queue and delayed until a better time.
|
||||
* Requests generation/population of the target chunk, if it's currently reasonable to do so (and if it isn't
|
||||
* already generated/populated).
|
||||
* 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
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Initiates asynchronous 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.
|
||||
* Initiates generation/population of the target chunk, if it's not already generated/populated.
|
||||
*
|
||||
* If the chunk is currently locked (for example due to another chunk using it for async generation), the request
|
||||
* will be queued and executed at the earliest opportunity.
|
||||
* This function will begin processing the request immediately, unless any of the adjacent chunks are currently in
|
||||
* use for other generation tasks.
|
||||
*
|
||||
* @see World::isChunkLocked()
|
||||
*
|
||||
* @phpstan-return Promise<Chunk>
|
||||
*/
|
@ -21,20 +21,22 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\world;
|
||||
namespace pocketmine\world\generator\executor;
|
||||
|
||||
use pocketmine\event\world\ChunkPopulateEvent;
|
||||
use pocketmine\promise\Promise;
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\ChunkLoader;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\generator\Generator;
|
||||
use pocketmine\world\World;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
final class SyncChunkGenerator implements ChunkGenerator{
|
||||
final class SyncGeneratorExecutor implements GeneratorExecutor{
|
||||
public function __construct(
|
||||
private readonly Generator $generator
|
||||
){}
|
Loading…
x
Reference in New Issue
Block a user