mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 16:00:20 +00:00
Run Flat world generators on the main thread
This commit is contained in:
parent
d338e7624c
commit
664af7ad7c
101
src/world/SyncChunkGenerator.php
Normal file
101
src/world/SyncChunkGenerator.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?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;
|
||||
|
||||
use pocketmine\event\world\ChunkPopulateEvent;
|
||||
use pocketmine\promise\Promise;
|
||||
use pocketmine\promise\PromiseResolver;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\generator\Generator;
|
||||
|
||||
/**
|
||||
* 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{
|
||||
public function __construct(
|
||||
private readonly Generator $generator
|
||||
){}
|
||||
|
||||
public function requestChunkPopulation(World $world, int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{
|
||||
return $this->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<Chunk> $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
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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<Generator> */
|
||||
@ -45,4 +46,8 @@ final class GeneratorManagerEntry{
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
public function isFast() : bool{
|
||||
return $this->fast;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user