53 public const CURRENT_STORAGE_VERSION = 10;
54 public const CURRENT_STORAGE_NETWORK_VERSION = 748;
55 public const CURRENT_CLIENT_VERSION_TARGET = [
63 public const GENERATOR_LIMITED = 0;
64 public const GENERATOR_INFINITE = 1;
65 public const GENERATOR_FLAT = 2;
67 private const TAG_DAY_CYCLE_STOP_TIME =
"DayCycleStopTime";
68 private const TAG_DIFFICULTY =
"Difficulty";
69 private const TAG_FORCE_GAME_TYPE =
"ForceGameType";
70 private const TAG_GAME_TYPE =
"GameType";
71 private const TAG_GENERATOR =
"Generator";
72 private const TAG_LAST_PLAYED =
"LastPlayed";
73 private const TAG_NETWORK_VERSION =
"NetworkVersion";
74 private const TAG_STORAGE_VERSION =
"StorageVersion";
75 private const TAG_IS_EDU =
"eduLevel";
76 private const TAG_FALL_DAMAGE_ENABLED =
"falldamage";
77 private const TAG_FIRE_DAMAGE_ENABLED =
"firedamage";
78 private const TAG_ACHIEVEMENTS_DISABLED =
"hasBeenLoadedInCreative";
79 private const TAG_IMMUTABLE_WORLD =
"immutableWorld";
80 private const TAG_LIGHTNING_LEVEL =
"lightningLevel";
81 private const TAG_LIGHTNING_TIME =
"lightningTime";
82 private const TAG_PVP_ENABLED =
"pvp";
83 private const TAG_RAIN_LEVEL =
"rainLevel";
84 private const TAG_RAIN_TIME =
"rainTime";
85 private const TAG_SPAWN_MOBS =
"spawnMobs";
86 private const TAG_TEXTURE_PACKS_REQUIRED =
"texturePacksRequired";
87 private const TAG_LAST_OPENED_WITH_VERSION =
"lastOpenedWithVersion";
88 private const TAG_COMMANDS_ENABLED =
"commandsEnabled";
90 public static function generate(
string $path,
string $name,
WorldCreationOptions $options) :
void{
93 $generatorType = self::GENERATOR_FLAT;
96 $generatorType = self::GENERATOR_INFINITE;
100 $worldData = CompoundTag::create()
102 ->setInt(self::TAG_DAY_CYCLE_STOP_TIME, -1)
103 ->setInt(self::TAG_DIFFICULTY, $options->getDifficulty())
104 ->setByte(self::TAG_FORCE_GAME_TYPE, 0)
105 ->setInt(self::TAG_GAME_TYPE, 0)
106 ->setInt(self::TAG_GENERATOR, $generatorType)
107 ->setLong(self::TAG_LAST_PLAYED, time())
108 ->setString(self::TAG_LEVEL_NAME, $name)
109 ->setInt(self::TAG_NETWORK_VERSION, self::CURRENT_STORAGE_NETWORK_VERSION)
111 ->setLong(self::TAG_RANDOM_SEED, $options->getSeed())
112 ->setInt(self::TAG_SPAWN_X, $options->getSpawnPosition()->getFloorX())
113 ->setInt(self::TAG_SPAWN_Y, $options->getSpawnPosition()->getFloorY())
114 ->setInt(self::TAG_SPAWN_Z, $options->getSpawnPosition()->getFloorZ())
115 ->setInt(self::TAG_STORAGE_VERSION, self::CURRENT_STORAGE_VERSION)
116 ->setLong(self::TAG_TIME, 0)
117 ->setByte(self::TAG_IS_EDU, 0)
118 ->setByte(self::TAG_FALL_DAMAGE_ENABLED, 1)
119 ->setByte(self::TAG_FIRE_DAMAGE_ENABLED, 1)
120 ->setByte(self::TAG_ACHIEVEMENTS_DISABLED, 1)
121 ->setByte(self::TAG_IMMUTABLE_WORLD, 0)
122 ->setFloat(self::TAG_LIGHTNING_LEVEL, 0.0)
123 ->setInt(self::TAG_LIGHTNING_TIME, 0)
124 ->setByte(self::TAG_PVP_ENABLED, 1)
125 ->setFloat(self::TAG_RAIN_LEVEL, 0.0)
126 ->setInt(self::TAG_RAIN_TIME, 0)
127 ->setByte(self::TAG_SPAWN_MOBS, 1)
128 ->setByte(self::TAG_TEXTURE_PACKS_REQUIRED, 0)
129 ->setByte(self::TAG_COMMANDS_ENABLED, 1)
130 ->setTag(self::TAG_LAST_OPENED_WITH_VERSION,
new ListTag(array_map(fn(
int $v) =>
new IntTag($v), self::CURRENT_CLIENT_VERSION_TARGET)))
133 ->setString(self::TAG_GENERATOR_NAME, GeneratorManager::getInstance()->getGeneratorName($options->
getGeneratorClass()))
134 ->setString(self::TAG_GENERATOR_OPTIONS, $options->getGeneratorOptions());
137 $buffer = $nbt->write(
new TreeRoot($worldData));
138 file_put_contents(Path::join($path,
"level.dat"), Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer);
143 $rawLevelData =
Filesystem::fileGetContents($this->dataPath);
144 }
catch(\RuntimeException $e){
147 if(strlen($rawLevelData) <= 8){
150 $nbt =
new LittleEndianNbtSerializer();
152 $worldData = $nbt->read(substr($rawLevelData, 8))->mustGetCompoundTag();
153 }
catch(NbtDataException $e){
157 $version = $worldData->getInt(self::TAG_STORAGE_VERSION, Limits::INT32_MAX);
158 if($version === Limits::INT32_MAX){
159 throw new CorruptedWorldException(sprintf(
"Missing '%s' tag in level.dat", self::TAG_STORAGE_VERSION));
161 if($version > self::CURRENT_STORAGE_VERSION){
162 throw new UnsupportedWorldFormatException(
"LevelDB world format version $version is currently unsupported");
166 $protocolVersion = $worldData->getInt(self::TAG_NETWORK_VERSION, Limits::INT32_MAX);
167 if($protocolVersion === Limits::INT32_MAX){
168 throw new CorruptedWorldException(sprintf(
"Missing '%s' tag in level.dat", self::TAG_NETWORK_VERSION));
170 if($protocolVersion > self::CURRENT_STORAGE_NETWORK_VERSION){
171 throw new UnsupportedWorldFormatException(
"LevelDB world protocol version $protocolVersion is currently unsupported");
177 protected function fix() : void{
178 $generatorNameTag = $this->compoundTag->getTag(self::TAG_GENERATOR_NAME);
179 if(!($generatorNameTag instanceof
StringTag)){
180 if(($mcpeGeneratorTypeTag = $this->compoundTag->getTag(self::TAG_GENERATOR)) instanceof
IntTag){
181 switch($mcpeGeneratorTypeTag->getValue()){
182 case self::GENERATOR_FLAT:
183 $this->compoundTag->setString(self::TAG_GENERATOR_NAME,
"flat");
184 $this->compoundTag->setString(self::TAG_GENERATOR_OPTIONS,
"2;7,3,3,2;1");
186 case self::GENERATOR_INFINITE:
188 $this->compoundTag->setString(self::TAG_GENERATOR_NAME,
"default");
189 $this->compoundTag->setString(self::TAG_GENERATOR_OPTIONS,
"");
191 case self::GENERATOR_LIMITED:
197 $this->compoundTag->setString(self::TAG_GENERATOR_NAME,
"default");
199 }elseif(($generatorName = self::hackyFixForGeneratorClasspathInLevelDat($generatorNameTag->getValue())) !==
null){
200 $this->compoundTag->setString(self::TAG_GENERATOR_NAME, $generatorName);
203 if(!($this->compoundTag->getTag(self::TAG_GENERATOR_OPTIONS)) instanceof
StringTag){
204 $this->compoundTag->setString(self::TAG_GENERATOR_OPTIONS,
"");
209 $this->compoundTag->setInt(self::TAG_NETWORK_VERSION, self::CURRENT_STORAGE_NETWORK_VERSION);
210 $this->compoundTag->setInt(self::TAG_STORAGE_VERSION, self::CURRENT_STORAGE_VERSION);
211 $this->compoundTag->setTag(self::TAG_LAST_OPENED_WITH_VERSION,
new ListTag(array_map(fn(
int $v) =>
new IntTag($v), self::CURRENT_CLIENT_VERSION_TARGET)));
212 $this->compoundTag->setLong(VersionInfo::TAG_WORLD_DATA_VERSION, VersionInfo::WORLD_DATA_VERSION);
215 $buffer = $nbt->write(
new TreeRoot($this->compoundTag));
216 Filesystem::safeFilePutContents($this->dataPath, Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer);
220 return $this->compoundTag->getInt(self::TAG_DIFFICULTY,
World::DIFFICULTY_NORMAL);
224 $this->compoundTag->setInt(self::TAG_DIFFICULTY, $difficulty);
228 return $this->compoundTag->getInt(self::TAG_RAIN_TIME, 0);
232 $this->compoundTag->setInt(self::TAG_RAIN_TIME, $ticks);
236 return $this->compoundTag->getFloat(self::TAG_RAIN_LEVEL, 0.0);
240 $this->compoundTag->setFloat(self::TAG_RAIN_LEVEL, $level);
244 return $this->compoundTag->getInt(self::TAG_LIGHTNING_TIME, 0);
248 $this->compoundTag->setInt(self::TAG_LIGHTNING_TIME, $ticks);
252 return $this->compoundTag->getFloat(self::TAG_LIGHTNING_LEVEL, 0.0);
256 $this->compoundTag->setFloat(self::TAG_LIGHTNING_LEVEL, $level);