From 9c1b2744993c2eb442307a8c3fdafdd43d351a81 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 13 Apr 2021 21:03:25 +0100 Subject: [PATCH] WorldManager::createWorld() now accepts WorldCreationOptions instead of mixed[] --- src/Server.php | 39 +++++--- src/world/WorldCreationOptions.php | 98 +++++++++++++++++++ src/world/WorldManager.php | 18 +--- src/world/format/io/FormatConverter.php | 9 +- src/world/format/io/WritableWorldProvider.php | 8 +- src/world/format/io/data/BedrockWorldData.php | 29 +++--- src/world/format/io/data/JavaWorldData.php | 29 +++--- src/world/format/io/leveldb/LevelDB.php | 8 +- .../format/io/region/RegionWorldProvider.php | 2 +- 9 files changed, 165 insertions(+), 75 deletions(-) create mode 100644 src/world/WorldCreationOptions.php diff --git a/src/Server.php b/src/Server.php index c4c6c4be31..b38111dd63 100644 --- a/src/Server.php +++ b/src/Server.php @@ -100,8 +100,8 @@ use pocketmine\world\format\io\WorldProviderManager; use pocketmine\world\format\io\WritableWorldProvider; use pocketmine\world\generator\Generator; use pocketmine\world\generator\GeneratorManager; -use pocketmine\world\generator\normal\Normal; use pocketmine\world\World; +use pocketmine\world\WorldCreationOptions; use pocketmine\world\WorldManager; use Ramsey\Uuid\UuidInterface; use function array_shift; @@ -1031,17 +1031,30 @@ class Server{ continue; } if(!$this->worldManager->loadWorld($name, true)){ + $creationOptions = WorldCreationOptions::create(); + //TODO: error checking + if(isset($options["generator"])){ $generatorOptions = explode(":", $options["generator"]); - $generator = GeneratorManager::getInstance()->getGenerator(array_shift($generatorOptions)); + $creationOptions->setGeneratorClass(GeneratorManager::getInstance()->getGenerator(array_shift($generatorOptions))); if(count($generatorOptions) > 0){ - $options["preset"] = implode(":", $generatorOptions); + $creationOptions->setGeneratorOptions(implode(":", $generatorOptions)); + } + } + if(isset($options["difficulty"]) && is_string($options["difficulty"])){ + $creationOptions->setDifficulty(World::getDifficultyFromString($options["difficulty"])); + } + if(isset($options["preset"]) && is_string($options["preset"])){ + $creationOptions->setGeneratorOptions($options["preset"]); + } + if(isset($options["seed"])){ + $convertedSeed = Generator::convertSeed((string) ($options["seed"] ?? "")); + if($convertedSeed !== null){ + $creationOptions->setSeed($convertedSeed); } - }else{ - $generator = Normal::class; } - $this->worldManager->generateWorld($name, Generator::convertSeed((string) ($options["seed"] ?? "")), $generator, $options); + $this->worldManager->generateWorld($name, $creationOptions); } } @@ -1053,12 +1066,14 @@ class Server{ $this->configGroup->setConfigString("level-name", "world"); } if(!$this->worldManager->loadWorld($default, true)){ - $this->worldManager->generateWorld( - $default, - Generator::convertSeed($this->configGroup->getConfigString("level-seed")), - GeneratorManager::getInstance()->getGenerator($this->configGroup->getConfigString("level-type")), - ["preset" => $this->configGroup->getConfigString("generator-settings")] - ); + $creationOptions = WorldCreationOptions::create() + ->setGeneratorClass(GeneratorManager::getInstance()->getGenerator($this->configGroup->getConfigString("level-type"))) + ->setGeneratorOptions($this->configGroup->getConfigString("generator-settings")); + $convertedSeed = Generator::convertSeed($this->configGroup->getConfigString("level-seed")); + if($convertedSeed !== null){ + $creationOptions->setSeed($convertedSeed); + } + $this->worldManager->generateWorld($default, $creationOptions); } $world = $this->worldManager->getWorldByName($default); diff --git a/src/world/WorldCreationOptions.php b/src/world/WorldCreationOptions.php new file mode 100644 index 0000000000..87304dd5ef --- /dev/null +++ b/src/world/WorldCreationOptions.php @@ -0,0 +1,98 @@ + */ + private string $generatorClass = Normal::class; + private int $seed; + private int $difficulty = World::DIFFICULTY_NORMAL; + private string $generatorOptions = ""; + private Vector3 $spawnPosition; + + public function __construct(){ + $this->seed = random_int(Limits::INT32_MIN, Limits::INT32_MAX); + $this->spawnPosition = new Vector3(256, 70, 256); + } + + public static function create() : self{ + return new self; + } + + /** @phpstan-return class-string */ + public function getGeneratorClass() : string{ return $this->generatorClass; } + + /** + * @phpstan-param class-string $generatorClass + * @return $this + */ + public function setGeneratorClass(string $generatorClass) : self{ + Utils::testValidInstance($generatorClass, Generator::class); + $this->generatorClass = $generatorClass; + return $this; + } + + public function getSeed() : int{ return $this->seed; } + + /** @return $this */ + public function setSeed(int $seed) : self{ + $this->seed = $seed; + return $this; + } + + public function getDifficulty() : int{ return $this->difficulty; } + + /** @return $this */ + public function setDifficulty(int $difficulty) : self{ + $this->difficulty = $difficulty; + return $this; + } + + public function getGeneratorOptions() : string{ return $this->generatorOptions; } + + /** @return $this */ + public function setGeneratorOptions(string $generatorOptions) : self{ + $this->generatorOptions = $generatorOptions; + return $this; + } + + public function getSpawnPosition() : Vector3{ return $this->spawnPosition; } + + /** @return $this */ + public function setSpawnPosition(Vector3 $spawnPosition) : self{ + $this->spawnPosition = $spawnPosition; + return $this; + } +} diff --git a/src/world/WorldManager.php b/src/world/WorldManager.php index 86fb3f6ba6..fa3f3c94ce 100644 --- a/src/world/WorldManager.php +++ b/src/world/WorldManager.php @@ -30,24 +30,19 @@ use pocketmine\event\world\WorldUnloadEvent; use pocketmine\player\ChunkSelector; use pocketmine\Server; use pocketmine\timings\Timings; -use pocketmine\utils\Limits; -use pocketmine\utils\Utils; use pocketmine\world\format\io\exception\CorruptedWorldException; use pocketmine\world\format\io\exception\UnsupportedWorldFormatException; use pocketmine\world\format\io\FormatConverter; use pocketmine\world\format\io\WorldProvider; use pocketmine\world\format\io\WorldProviderManager; use pocketmine\world\format\io\WritableWorldProvider; -use pocketmine\world\generator\Generator; use pocketmine\world\generator\GeneratorManager; -use pocketmine\world\generator\normal\Normal; use function array_keys; use function array_shift; use function assert; use function count; use function implode; use function microtime; -use function random_int; use function round; use function sprintf; use function trim; @@ -244,27 +239,18 @@ class WorldManager{ /** * Generates a new world if it does not exist * - * @param string $generator Class name that extends pocketmine\world\generator\Generator - * @param mixed[] $options - * @phpstan-param class-string $generator - * @phpstan-param array $options - * * @throws \InvalidArgumentException */ - public function generateWorld(string $name, ?int $seed = null, string $generator = Normal::class, array $options = [], bool $backgroundGeneration = true) : bool{ + public function generateWorld(string $name, ?WorldCreationOptions $options = null, bool $backgroundGeneration = true) : bool{ if(trim($name) === "" or $this->isWorldGenerated($name)){ return false; } - $seed = $seed ?? random_int(Limits::INT32_MIN, Limits::INT32_MAX); - - Utils::testValidInstance($generator, Generator::class); - $providerClass = $this->providerManager->getDefault(); $path = $this->getWorldPath($name); /** @var WritableWorldProvider $providerClass */ - $providerClass::generate($path, $name, $seed, $generator, $options); + $providerClass::generate($path, $name, $options); /** @see WritableWorldProvider::__construct() */ $world = new World($this->server, $name, new $providerClass($path), $this->server->getAsyncPool()); diff --git a/src/world/format/io/FormatConverter.php b/src/world/format/io/FormatConverter.php index a2ddf9fd25..4d5f3eba31 100644 --- a/src/world/format/io/FormatConverter.php +++ b/src/world/format/io/FormatConverter.php @@ -26,6 +26,7 @@ namespace pocketmine\world\format\io; use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; use pocketmine\world\generator\GeneratorManager; +use pocketmine\world\WorldCreationOptions; use function basename; use function crc32; use function file_exists; @@ -105,7 +106,13 @@ class FormatConverter{ $this->logger->info("Found previous conversion attempt, deleting..."); Filesystem::recursiveUnlink($convertedOutput); } - $this->newProvider::generate($convertedOutput, $data->getName(), $data->getSeed(), GeneratorManager::getInstance()->getGenerator($data->getGenerator()), ["preset" => $data->getGeneratorOptions()]); + $this->newProvider::generate($convertedOutput, $data->getName(), WorldCreationOptions::create() + ->setGeneratorClass(GeneratorManager::getInstance()->getGenerator($data->getGenerator())) + ->setGeneratorOptions($data->getGeneratorOptions()) + ->setSeed($data->getSeed()) + ->setSpawnPosition($data->getSpawn()) + ->setDifficulty($data->getDifficulty()) + ); /** * @see WritableWorldProvider::__construct() diff --git a/src/world/format/io/WritableWorldProvider.php b/src/world/format/io/WritableWorldProvider.php index 58ceef4426..de3a69b739 100644 --- a/src/world/format/io/WritableWorldProvider.php +++ b/src/world/format/io/WritableWorldProvider.php @@ -24,17 +24,13 @@ declare(strict_types=1); namespace pocketmine\world\format\io; use pocketmine\world\format\Chunk; -use pocketmine\world\generator\Generator; +use pocketmine\world\WorldCreationOptions; interface WritableWorldProvider extends WorldProvider{ /** * Generate the needed files in the path given - * - * @param mixed[] $options - * @phpstan-param class-string $generator - * @phpstan-param array $options */ - public static function generate(string $path, string $name, int $seed, string $generator, array $options = []) : void; + public static function generate(string $path, string $name, ?WorldCreationOptions $options = null) : void; /** * Saves a chunk (usually to disk). diff --git a/src/world/format/io/data/BedrockWorldData.php b/src/world/format/io/data/BedrockWorldData.php index 6ae3103081..73a6a5be94 100644 --- a/src/world/format/io/data/BedrockWorldData.php +++ b/src/world/format/io/data/BedrockWorldData.php @@ -32,13 +32,13 @@ use pocketmine\nbt\TreeRoot; use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\utils\Binary; use pocketmine\utils\Limits; -use pocketmine\utils\Utils; use pocketmine\world\format\io\exception\CorruptedWorldException; use pocketmine\world\format\io\exception\UnsupportedWorldFormatException; use pocketmine\world\generator\Flat; use pocketmine\world\generator\Generator; use pocketmine\world\generator\GeneratorManager; use pocketmine\world\World; +use pocketmine\world\WorldCreationOptions; use function file_get_contents; use function file_put_contents; use function strlen; @@ -53,14 +53,9 @@ class BedrockWorldData extends BaseNbtWorldData{ public const GENERATOR_INFINITE = 1; public const GENERATOR_FLAT = 2; - /** - * @param mixed[] $options - * @phpstan-param class-string $generator - * @phpstan-param array $options - */ - public static function generate(string $path, string $name, int $seed, string $generator, array $options = []) : void{ - Utils::testValidInstance($generator, Generator::class); - switch($generator){ + public static function generate(string $path, string $name, ?WorldCreationOptions $options = null) : void{ + $options ??= WorldCreationOptions::create(); + switch($options->getGeneratorClass()){ case Flat::class: $generatorType = self::GENERATOR_FLAT; break; @@ -72,7 +67,7 @@ class BedrockWorldData extends BaseNbtWorldData{ $worldData = CompoundTag::create() //Vanilla fields ->setInt("DayCycleStopTime", -1) - ->setInt("Difficulty", World::getDifficultyFromString((string) ($options["difficulty"] ?? "normal"))) + ->setInt("Difficulty", $options->getDifficulty()) ->setByte("ForceGameType", 0) ->setInt("GameType", 0) ->setInt("Generator", $generatorType) @@ -80,10 +75,10 @@ class BedrockWorldData extends BaseNbtWorldData{ ->setString("LevelName", $name) ->setInt("NetworkVersion", ProtocolInfo::CURRENT_PROTOCOL) //->setInt("Platform", 2) //TODO: find out what the possible values are for - ->setLong("RandomSeed", $seed) - ->setInt("SpawnX", 0) - ->setInt("SpawnY", 32767) - ->setInt("SpawnZ", 0) + ->setLong("RandomSeed", $options->getSeed()) + ->setInt("SpawnX", $options->getSpawnPosition()->getFloorX()) + ->setInt("SpawnY", $options->getSpawnPosition()->getFloorY()) + ->setInt("SpawnZ", $options->getSpawnPosition()->getFloorZ()) ->setInt("StorageVersion", self::CURRENT_STORAGE_VERSION) ->setLong("Time", 0) ->setByte("eduLevel", 0) @@ -101,9 +96,9 @@ class BedrockWorldData extends BaseNbtWorldData{ //Additional PocketMine-MP fields ->setTag("GameRules", new CompoundTag()) - ->setByte("hardcore", ($options["hardcore"] ?? false) === true ? 1 : 0) - ->setString("generatorName", GeneratorManager::getInstance()->getGeneratorName($generator)) - ->setString("generatorOptions", $options["preset"] ?? ""); + ->setByte("hardcore", 0) + ->setString("generatorName", GeneratorManager::getInstance()->getGeneratorName($options->getGeneratorClass())) + ->setString("generatorOptions", $options->getGeneratorOptions()); $nbt = new LittleEndianNbtSerializer(); $buffer = $nbt->write(new TreeRoot($worldData)); diff --git a/src/world/format/io/data/JavaWorldData.php b/src/world/format/io/data/JavaWorldData.php index 197b62fa9c..301dce6a56 100644 --- a/src/world/format/io/data/JavaWorldData.php +++ b/src/world/format/io/data/JavaWorldData.php @@ -29,11 +29,10 @@ use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\FloatTag; use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\TreeRoot; -use pocketmine\utils\Utils; use pocketmine\world\format\io\exception\CorruptedWorldException; -use pocketmine\world\generator\Generator; use pocketmine\world\generator\GeneratorManager; use pocketmine\world\World; +use pocketmine\world\WorldCreationOptions; use function ceil; use function file_get_contents; use function file_put_contents; @@ -44,31 +43,27 @@ use const ZLIB_ENCODING_GZIP; class JavaWorldData extends BaseNbtWorldData{ - /** - * @param mixed[] $options - * @phpstan-param class-string $generator - * @phpstan-param array $options - */ - public static function generate(string $path, string $name, int $seed, string $generator, array $options = [], int $version = 19133) : void{ - Utils::testValidInstance($generator, Generator::class); + public static function generate(string $path, string $name, ?WorldCreationOptions $options = null, int $version = 19133) : void{ //TODO, add extra details + + $options ??= WorldCreationOptions::create(); $worldData = CompoundTag::create() - ->setByte("hardcore", ($options["hardcore"] ?? false) === true ? 1 : 0) - ->setByte("Difficulty", World::getDifficultyFromString((string) ($options["difficulty"] ?? "normal"))) + ->setByte("hardcore", 0) + ->setByte("Difficulty", $options->getDifficulty()) ->setByte("initialized", 1) ->setInt("GameType", 0) ->setInt("generatorVersion", 1) //2 in MCPE - ->setInt("SpawnX", 256) - ->setInt("SpawnY", 70) - ->setInt("SpawnZ", 256) + ->setInt("SpawnX", $options->getSpawnPosition()->getFloorX()) + ->setInt("SpawnY", $options->getSpawnPosition()->getFloorY()) + ->setInt("SpawnZ", $options->getSpawnPosition()->getFloorZ()) ->setInt("version", $version) ->setInt("DayTime", 0) ->setLong("LastPlayed", (int) (microtime(true) * 1000)) - ->setLong("RandomSeed", $seed) + ->setLong("RandomSeed", $options->getSeed()) ->setLong("SizeOnDisk", 0) ->setLong("Time", 0) - ->setString("generatorName", GeneratorManager::getInstance()->getGeneratorName($generator)) - ->setString("generatorOptions", $options["preset"] ?? "") + ->setString("generatorName", GeneratorManager::getInstance()->getGeneratorName($options->getGeneratorClass())) + ->setString("generatorOptions", $options->getGeneratorOptions()) ->setString("LevelName", $name) ->setTag("GameRules", new CompoundTag()); diff --git a/src/world/format/io/leveldb/LevelDB.php b/src/world/format/io/leveldb/LevelDB.php index a84391543c..d7837a7d27 100644 --- a/src/world/format/io/leveldb/LevelDB.php +++ b/src/world/format/io/leveldb/LevelDB.php @@ -32,7 +32,6 @@ use pocketmine\nbt\TreeRoot; use pocketmine\utils\Binary; use pocketmine\utils\BinaryDataException; use pocketmine\utils\BinaryStream; -use pocketmine\utils\Utils; use pocketmine\world\format\BiomeArray; use pocketmine\world\format\Chunk; use pocketmine\world\format\io\BaseWorldProvider; @@ -46,7 +45,7 @@ use pocketmine\world\format\io\WorldData; use pocketmine\world\format\io\WritableWorldProvider; use pocketmine\world\format\PalettedBlockArray; use pocketmine\world\format\SubChunk; -use pocketmine\world\generator\Generator; +use pocketmine\world\WorldCreationOptions; use function array_map; use function array_values; use function chr; @@ -144,15 +143,14 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{ return file_exists($path . "/level.dat") and is_dir($path . "/db/"); } - public static function generate(string $path, string $name, int $seed, string $generator, array $options = []) : void{ - Utils::testValidInstance($generator, Generator::class); + public static function generate(string $path, string $name, ?WorldCreationOptions $options = null) : void{ self::checkForLevelDBExtension(); if(!file_exists($path . "/db")){ mkdir($path . "/db", 0777, true); } - BedrockWorldData::generate($path, $name, $seed, $generator, $options); + BedrockWorldData::generate($path, $name, $options); } protected function deserializePaletted(BinaryStream $stream) : PalettedBlockArray{ diff --git a/src/world/format/io/region/RegionWorldProvider.php b/src/world/format/io/region/RegionWorldProvider.php index ebb0f8088e..c15d3139fb 100644 --- a/src/world/format/io/region/RegionWorldProvider.php +++ b/src/world/format/io/region/RegionWorldProvider.php @@ -90,7 +90,7 @@ abstract class RegionWorldProvider extends BaseWorldProvider{ mkdir($path . "/region", 0777); } - JavaWorldData::generate($path, $name, $seed, $generator, $options, static::getPcWorldFormatVersion()); + JavaWorldData::generate($path, $name, null, static::getPcWorldFormatVersion()); } /** @var RegionLoader[] */