diff --git a/src/world/SyncChunkGenerator.php b/src/world/SyncChunkGenerator.php new file mode 100644 index 000000000..9c58b97ec --- /dev/null +++ b/src/world/SyncChunkGenerator.php @@ -0,0 +1,101 @@ +orderChunkPopulation($world, $chunkX, $chunkZ, $associatedChunkLoader); + } + + public function orderChunkPopulation(World $world, int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{ + $temporaryChunkLoader = new class implements ChunkLoader{}; + $world->registerChunkLoader($temporaryChunkLoader, $chunkX, $chunkZ); + $oldChunk = $world->loadChunk($chunkX, $chunkZ); + + //TODO: the following code is basically identical to PopulationTask + //we should probably generalize this + for($xx = $chunkX - 1; $xx <= $chunkX + 1; ++$xx){ + for($zz = $chunkZ - 1; $zz <= $chunkZ + 1; ++$zz){ + $world->registerChunkLoader($temporaryChunkLoader, $xx, $zz); + $chunk = $world->loadChunk($xx, $zz); + if($chunk === null){ + $this->generator->generateChunk($world, $xx, $zz); + } + } + } + + $this->generator->populateChunk($world, $chunkX, $chunkZ); + $chunk = $world->getChunk($chunkX, $chunkZ); + if($chunk === null){ + throw new AssumptionFailedError("We just generated and populated this, it should not be null"); + } + $chunk->setPopulated(); + + //TODO: this is basically identical to the AsyncChunkGenerator generateChunkCallback side + //we probably ought to generalize this + if(($oldChunk === null || !$oldChunk->isPopulated()) && $chunk->isPopulated()){ + if(ChunkPopulateEvent::hasHandlers()){ + (new ChunkPopulateEvent($world, $chunkX, $chunkZ, $chunk))->call(); + } + + foreach($world->getChunkListeners($chunkX, $chunkZ) as $listener){ + $listener->onChunkPopulated($chunkX, $chunkZ, $chunk); + } + } + + for($xx = $chunkX - 1; $xx <= $chunkX + 1; ++$xx){ + for($zz = $chunkZ - 1; $zz <= $chunkZ + 1; ++$zz){ + $world->unregisterChunkLoader($temporaryChunkLoader, $xx, $zz); + } + } + + /** @phpstan-var PromiseResolver $resolver */ + $resolver = new PromiseResolver(); + $resolver->resolve($chunk); + return $resolver->getPromise(); + } + + public function cancelChunkPopulation(World $world, int $chunkX, int $chunkZ) : void{ + //NOOP + } + + public function shutdown(World $world) : void{ + //NOOP + } +} diff --git a/src/world/World.php b/src/world/World.php index 3ff9169ea..ebced35be 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -92,6 +92,7 @@ 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\Generator; use pocketmine\world\generator\GeneratorManager; use pocketmine\world\light\BlockLightUpdate; use pocketmine\world\light\LightPopulationTask; @@ -468,15 +469,25 @@ class World implements ChunkManager{ $this->server->getLogger()->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_preparing($this->displayName))); $generator = GeneratorManager::getInstance()->getGenerator($this->provider->getWorldData()->getGenerator()) ?? throw new AssumptionFailedError("WorldManager should already have checked that the generator exists"); - $generator->validateGeneratorOptions($this->provider->getWorldData()->getGeneratorOptions()); + $generatorOptions = $this->provider->getWorldData()->getGeneratorOptions(); + $generator->validateGeneratorOptions($generatorOptions); $this->generator = $generator->getGeneratorClass(); $cfg = $this->server->getConfigGroup(); - $this->chunkGenerator = new AsyncChunkGenerator( - $this->workerPool, - $this->logger, - $cfg->getPropertyInt(YmlServerProperties::CHUNK_GENERATION_POPULATION_QUEUE_SIZE, 2) - ); + 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); + }else{ + $this->chunkGenerator = new AsyncChunkGenerator( + $this->workerPool, + $this->logger, + $cfg->getPropertyInt(YmlServerProperties::CHUNK_GENERATION_POPULATION_QUEUE_SIZE, 2) + ); + $this->logger->debug("Using async task generator system for slow generator " . $this->generator); + } $this->addOnUnloadCallback(function() : void{ $this->chunkGenerator->shutdown($this); }); diff --git a/src/world/generator/GeneratorManager.php b/src/world/generator/GeneratorManager.php index 291ea91de..a1b00480e 100644 --- a/src/world/generator/GeneratorManager.php +++ b/src/world/generator/GeneratorManager.php @@ -50,7 +50,7 @@ final class GeneratorManager{ }catch(InvalidGeneratorOptionsException $e){ return $e; } - }); + }, fast: true); $this->addGenerator(Normal::class, "normal", fn() => null); $this->addAlias("normal", "default"); $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 \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 $fast Whether this generator is fast enough to run without async tasks * * @phpstan-param \Closure(string) : ?InvalidGeneratorOptionsException $presetValidator * @@ -69,7 +70,7 @@ final class GeneratorManager{ * * @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); $name = strtolower($name); @@ -77,7 +78,7 @@ final class GeneratorManager{ throw new \InvalidArgumentException("Alias \"$name\" is already assigned"); } - $this->list[$name] = new GeneratorManagerEntry($class, $presetValidator); + $this->list[$name] = new GeneratorManagerEntry($class, $presetValidator, $fast); } /** diff --git a/src/world/generator/GeneratorManagerEntry.php b/src/world/generator/GeneratorManagerEntry.php index 256ed27d5..c514f2fbe 100644 --- a/src/world/generator/GeneratorManagerEntry.php +++ b/src/world/generator/GeneratorManagerEntry.php @@ -31,7 +31,8 @@ final class GeneratorManagerEntry{ */ public function __construct( private string $generatorClass, - private \Closure $presetValidator + private \Closure $presetValidator, + private bool $fast ){} /** @phpstan-return class-string */ @@ -45,4 +46,8 @@ final class GeneratorManagerEntry{ throw $exception; } } + + public function isFast() : bool{ + return $this->fast; + } }