mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-06 17:59:48 +00:00
Separate level data handling from the main LevelProvider
This commit is contained in:
@ -26,17 +26,14 @@ namespace pocketmine\level\format\io\leveldb;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\io\BaseLevelProvider;
|
||||
use pocketmine\level\format\io\ChunkUtils;
|
||||
use pocketmine\level\format\io\data\BedrockLevelData;
|
||||
use pocketmine\level\format\io\exception\UnsupportedChunkFormatException;
|
||||
use pocketmine\level\format\io\LevelData;
|
||||
use pocketmine\level\format\SubChunk;
|
||||
use pocketmine\level\generator\Flat;
|
||||
use pocketmine\level\generator\GeneratorManager;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\LevelException;
|
||||
use pocketmine\nbt\LittleEndianNBTStream;
|
||||
use pocketmine\nbt\tag\{
|
||||
ByteTag, CompoundTag, FloatTag, IntTag, LongTag, StringTag
|
||||
};
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
|
||||
@ -65,11 +62,6 @@ class LevelDB extends BaseLevelProvider{
|
||||
|
||||
public const ENTRY_FLAT_WORLD_LAYERS = "game_flatworldlayers";
|
||||
|
||||
public const GENERATOR_LIMITED = 0;
|
||||
public const GENERATOR_INFINITE = 1;
|
||||
public const GENERATOR_FLAT = 2;
|
||||
|
||||
public const CURRENT_STORAGE_VERSION = 6; //Current MCPE level format version
|
||||
public const CURRENT_LEVEL_CHUNK_VERSION = 7;
|
||||
public const CURRENT_LEVEL_SUBCHUNK_VERSION = 0;
|
||||
|
||||
@ -99,62 +91,8 @@ class LevelDB extends BaseLevelProvider{
|
||||
$this->db = self::createDB($path);
|
||||
}
|
||||
|
||||
protected function loadLevelData() : void{
|
||||
$levelDatPath = $this->getPath() . "level.dat";
|
||||
if(!file_exists($levelDatPath)){
|
||||
throw new LevelException("level.dat not found");
|
||||
}
|
||||
$nbt = new LittleEndianNBTStream();
|
||||
$levelData = $nbt->read(substr(file_get_contents($levelDatPath), 8));
|
||||
if($levelData instanceof CompoundTag){
|
||||
$this->levelData = $levelData;
|
||||
}else{
|
||||
throw new LevelException("Invalid level.dat");
|
||||
}
|
||||
|
||||
$version = $this->levelData->getInt("StorageVersion", INT32_MAX, true);
|
||||
if($version > self::CURRENT_STORAGE_VERSION){
|
||||
throw new LevelException("Specified LevelDB world format version ($version) is not supported by " . \pocketmine\NAME);
|
||||
}
|
||||
}
|
||||
|
||||
protected function fixLevelData() : void{
|
||||
$db = self::createDB($this->path);
|
||||
|
||||
if(!$this->levelData->hasTag("generatorName", StringTag::class)){
|
||||
if($this->levelData->hasTag("Generator", IntTag::class)){
|
||||
switch($this->levelData->getInt("Generator")){ //Detect correct generator from MCPE data
|
||||
case self::GENERATOR_FLAT:
|
||||
$this->levelData->setString("generatorName", "flat");
|
||||
if(($layers = $db->get(self::ENTRY_FLAT_WORLD_LAYERS)) !== false){ //Detect existing custom flat layers
|
||||
$layers = trim($layers, "[]");
|
||||
}else{
|
||||
$layers = "7,3,3,2";
|
||||
}
|
||||
$this->levelData->setString("generatorOptions", "2;" . $layers . ";1");
|
||||
break;
|
||||
case self::GENERATOR_INFINITE:
|
||||
//TODO: add a null generator which does not generate missing chunks (to allow importing back to MCPE and generating more normal terrain without PocketMine messing things up)
|
||||
$this->levelData->setString("generatorName", "default");
|
||||
$this->levelData->setString("generatorOptions", "");
|
||||
break;
|
||||
case self::GENERATOR_LIMITED:
|
||||
throw new LevelException("Limited worlds are not currently supported");
|
||||
default:
|
||||
throw new LevelException("Unknown LevelDB world format type, this level cannot be loaded");
|
||||
}
|
||||
}else{
|
||||
$this->levelData->setString("generatorName", "default");
|
||||
}
|
||||
}elseif(($generatorName = self::hackyFixForGeneratorClasspathInLevelDat($this->levelData->getString("generatorName"))) !== null){
|
||||
$this->levelData->setString("generatorName", $generatorName);
|
||||
}
|
||||
|
||||
if(!$this->levelData->hasTag("generatorOptions", StringTag::class)){
|
||||
$this->levelData->setString("generatorOptions", "");
|
||||
}
|
||||
|
||||
$db->close();
|
||||
protected function loadLevelData() : LevelData{
|
||||
return new BedrockLevelData($this->getPath() . "level.dat");
|
||||
}
|
||||
|
||||
public function getWorldHeight() : int{
|
||||
@ -172,130 +110,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
mkdir($path . "/db", 0777, true);
|
||||
}
|
||||
|
||||
switch($generator){
|
||||
case Flat::class:
|
||||
$generatorType = self::GENERATOR_FLAT;
|
||||
break;
|
||||
default:
|
||||
$generatorType = self::GENERATOR_INFINITE;
|
||||
//TODO: add support for limited worlds
|
||||
}
|
||||
|
||||
$levelData = new CompoundTag("", [
|
||||
//Vanilla fields
|
||||
new IntTag("DayCycleStopTime", -1),
|
||||
new IntTag("Difficulty", Level::getDifficultyFromString((string) ($options["difficulty"] ?? "normal"))),
|
||||
new ByteTag("ForceGameType", 0),
|
||||
new IntTag("GameType", 0),
|
||||
new IntTag("Generator", $generatorType),
|
||||
new LongTag("LastPlayed", time()),
|
||||
new StringTag("LevelName", $name),
|
||||
new IntTag("NetworkVersion", ProtocolInfo::CURRENT_PROTOCOL),
|
||||
//new IntTag("Platform", 2), //TODO: find out what the possible values are for
|
||||
new LongTag("RandomSeed", $seed),
|
||||
new IntTag("SpawnX", 0),
|
||||
new IntTag("SpawnY", 32767),
|
||||
new IntTag("SpawnZ", 0),
|
||||
new IntTag("StorageVersion", self::CURRENT_STORAGE_VERSION),
|
||||
new LongTag("Time", 0),
|
||||
new ByteTag("eduLevel", 0),
|
||||
new ByteTag("falldamage", 1),
|
||||
new ByteTag("firedamage", 1),
|
||||
new ByteTag("hasBeenLoadedInCreative", 1), //badly named, this actually determines whether achievements can be earned in this world...
|
||||
new ByteTag("immutableWorld", 0),
|
||||
new FloatTag("lightningLevel", 0.0),
|
||||
new IntTag("lightningTime", 0),
|
||||
new ByteTag("pvp", 1),
|
||||
new FloatTag("rainLevel", 0.0),
|
||||
new IntTag("rainTime", 0),
|
||||
new ByteTag("spawnMobs", 1),
|
||||
new ByteTag("texturePacksRequired", 0), //TODO
|
||||
|
||||
//Additional PocketMine-MP fields
|
||||
new CompoundTag("GameRules", []),
|
||||
new ByteTag("hardcore", ($options["hardcore"] ?? false) === true ? 1 : 0),
|
||||
new StringTag("generatorName", GeneratorManager::getGeneratorName($generator)),
|
||||
new StringTag("generatorOptions", $options["preset"] ?? "")
|
||||
]);
|
||||
|
||||
$nbt = new LittleEndianNBTStream();
|
||||
$buffer = $nbt->write($levelData);
|
||||
file_put_contents($path . "level.dat", Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer);
|
||||
|
||||
|
||||
$db = self::createDB($path);
|
||||
|
||||
if($generatorType === self::GENERATOR_FLAT and isset($options["preset"])){
|
||||
$layers = explode(";", $options["preset"])[1] ?? "";
|
||||
if($layers !== ""){
|
||||
$out = "[";
|
||||
foreach(Flat::parseLayers($layers) as $result){
|
||||
$out .= $result[0] . ","; //only id, meta will unfortunately not survive :(
|
||||
}
|
||||
$out = rtrim($out, ",") . "]"; //remove trailing comma
|
||||
$db->put(self::ENTRY_FLAT_WORLD_LAYERS, $out); //Add vanilla flatworld layers to allow terrain generation by MCPE to continue seamlessly
|
||||
}
|
||||
}
|
||||
|
||||
$db->close();
|
||||
|
||||
}
|
||||
|
||||
public function saveLevelData(){
|
||||
$this->levelData->setInt("NetworkVersion", ProtocolInfo::CURRENT_PROTOCOL);
|
||||
$this->levelData->setInt("StorageVersion", self::CURRENT_STORAGE_VERSION);
|
||||
|
||||
$nbt = new LittleEndianNBTStream();
|
||||
$buffer = $nbt->write($this->levelData);
|
||||
file_put_contents($this->getPath() . "level.dat", Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer);
|
||||
}
|
||||
|
||||
public function getGenerator() : string{
|
||||
return $this->levelData->getString("generatorName", "");
|
||||
}
|
||||
|
||||
public function getGeneratorOptions() : array{
|
||||
return ["preset" => $this->levelData->getString("generatorOptions", "")];
|
||||
}
|
||||
|
||||
public function getDifficulty() : int{
|
||||
return $this->levelData->getInt("Difficulty", Level::DIFFICULTY_NORMAL);
|
||||
}
|
||||
|
||||
public function setDifficulty(int $difficulty){
|
||||
$this->levelData->setInt("Difficulty", $difficulty); //yes, this is intended! (in PE: int, PC: byte)
|
||||
}
|
||||
|
||||
public function getRainTime() : int{
|
||||
return $this->levelData->getInt("rainTime", 0);
|
||||
}
|
||||
|
||||
public function setRainTime(int $ticks) : void{
|
||||
$this->levelData->setInt("rainTime", $ticks);
|
||||
}
|
||||
|
||||
public function getRainLevel() : float{
|
||||
return $this->levelData->getFloat("rainLevel", 0.0);
|
||||
}
|
||||
|
||||
public function setRainLevel(float $level) : void{
|
||||
$this->levelData->setFloat("rainLevel", $level);
|
||||
}
|
||||
|
||||
public function getLightningTime() : int{
|
||||
return $this->levelData->getInt("lightningTime", 0);
|
||||
}
|
||||
|
||||
public function setLightningTime(int $ticks) : void{
|
||||
$this->levelData->setInt("lightningTime", $ticks);
|
||||
}
|
||||
|
||||
public function getLightningLevel() : float{
|
||||
return $this->levelData->getFloat("lightningLevel", 0.0);
|
||||
}
|
||||
|
||||
public function setLightningLevel(float $level) : void{
|
||||
$this->levelData->setFloat("lightningLevel", $level);
|
||||
BedrockLevelData::generate($path, $name, $seed, $generator, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user