54 public const CURRENT_STORAGE_VERSION = WorldDataVersions::STORAGE;
55 public const CURRENT_STORAGE_NETWORK_VERSION = WorldDataVersions::NETWORK;
56 public const CURRENT_CLIENT_VERSION_TARGET = WorldDataVersions::LAST_OPENED_IN;
58 public const GENERATOR_LIMITED = 0;
59 public const GENERATOR_INFINITE = 1;
60 public const GENERATOR_FLAT = 2;
62 private const TAG_DAY_CYCLE_STOP_TIME =
"DayCycleStopTime";
63 private const TAG_DIFFICULTY =
"Difficulty";
64 private const TAG_FORCE_GAME_TYPE =
"ForceGameType";
65 private const TAG_GAME_TYPE =
"GameType";
66 private const TAG_GENERATOR =
"Generator";
67 private const TAG_LAST_PLAYED =
"LastPlayed";
68 private const TAG_NETWORK_VERSION =
"NetworkVersion";
69 private const TAG_STORAGE_VERSION =
"StorageVersion";
70 private const TAG_IS_EDU =
"eduLevel";
71 private const TAG_FALL_DAMAGE_ENABLED =
"falldamage";
72 private const TAG_FIRE_DAMAGE_ENABLED =
"firedamage";
73 private const TAG_ACHIEVEMENTS_DISABLED =
"hasBeenLoadedInCreative";
74 private const TAG_IMMUTABLE_WORLD =
"immutableWorld";
75 private const TAG_LIGHTNING_LEVEL =
"lightningLevel";
76 private const TAG_LIGHTNING_TIME =
"lightningTime";
77 private const TAG_PVP_ENABLED =
"pvp";
78 private const TAG_RAIN_LEVEL =
"rainLevel";
79 private const TAG_RAIN_TIME =
"rainTime";
80 private const TAG_SPAWN_MOBS =
"spawnMobs";
81 private const TAG_TEXTURE_PACKS_REQUIRED =
"texturePacksRequired";
82 private const TAG_LAST_OPENED_WITH_VERSION =
"lastOpenedWithVersion";
83 private const TAG_COMMANDS_ENABLED =
"commandsEnabled";
85 public static function generate(
string $path,
string $name,
WorldCreationOptions $options) :
void{
88 $generatorType = self::GENERATOR_FLAT;
91 $generatorType = self::GENERATOR_INFINITE;
95 $worldData = CompoundTag::create()
97 ->setInt(self::TAG_DAY_CYCLE_STOP_TIME, -1)
98 ->setInt(self::TAG_DIFFICULTY, $options->getDifficulty())
99 ->setByte(self::TAG_FORCE_GAME_TYPE, 0)
100 ->setInt(self::TAG_GAME_TYPE, 0)
101 ->setInt(self::TAG_GENERATOR, $generatorType)
102 ->setLong(self::TAG_LAST_PLAYED, time())
103 ->setString(self::TAG_LEVEL_NAME, $name)
104 ->setInt(self::TAG_NETWORK_VERSION, self::CURRENT_STORAGE_NETWORK_VERSION)
106 ->setLong(self::TAG_RANDOM_SEED, $options->getSeed())
107 ->setInt(self::TAG_SPAWN_X, $options->getSpawnPosition()->getFloorX())
108 ->setInt(self::TAG_SPAWN_Y, $options->getSpawnPosition()->getFloorY())
109 ->setInt(self::TAG_SPAWN_Z, $options->getSpawnPosition()->getFloorZ())
110 ->setInt(self::TAG_STORAGE_VERSION, self::CURRENT_STORAGE_VERSION)
111 ->setLong(self::TAG_TIME, 0)
112 ->setByte(self::TAG_IS_EDU, 0)
113 ->setByte(self::TAG_FALL_DAMAGE_ENABLED, 1)
114 ->setByte(self::TAG_FIRE_DAMAGE_ENABLED, 1)
115 ->setByte(self::TAG_ACHIEVEMENTS_DISABLED, 1)
116 ->setByte(self::TAG_IMMUTABLE_WORLD, 0)
117 ->setFloat(self::TAG_LIGHTNING_LEVEL, 0.0)
118 ->setInt(self::TAG_LIGHTNING_TIME, 0)
119 ->setByte(self::TAG_PVP_ENABLED, 1)
120 ->setFloat(self::TAG_RAIN_LEVEL, 0.0)
121 ->setInt(self::TAG_RAIN_TIME, 0)
122 ->setByte(self::TAG_SPAWN_MOBS, 1)
123 ->setByte(self::TAG_TEXTURE_PACKS_REQUIRED, 0)
124 ->setByte(self::TAG_COMMANDS_ENABLED, 1)
125 ->setTag(self::TAG_LAST_OPENED_WITH_VERSION,
new ListTag(array_map(fn(
int $v) =>
new IntTag($v), self::CURRENT_CLIENT_VERSION_TARGET)))
128 ->setString(self::TAG_GENERATOR_NAME, GeneratorManager::getInstance()->getGeneratorName($options->
getGeneratorClass()))
129 ->setString(self::TAG_GENERATOR_OPTIONS, $options->getGeneratorOptions());
132 $buffer = $nbt->write(
new TreeRoot($worldData));
133 file_put_contents(Path::join($path,
"level.dat"), Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer);
138 $rawLevelData =
Filesystem::fileGetContents($this->dataPath);
139 }
catch(\RuntimeException $e){
142 if(strlen($rawLevelData) <= 8){
145 $nbt =
new LittleEndianNbtSerializer();
147 $worldData = $nbt->read(substr($rawLevelData, 8))->mustGetCompoundTag();
148 }
catch(NbtDataException $e){
152 $version = $worldData->getInt(self::TAG_STORAGE_VERSION, Limits::INT32_MAX);
153 if($version === Limits::INT32_MAX){
154 throw new CorruptedWorldException(sprintf(
"Missing '%s' tag in level.dat", self::TAG_STORAGE_VERSION));
156 if($version > self::CURRENT_STORAGE_VERSION){
157 throw new UnsupportedWorldFormatException(
"LevelDB world format version $version is currently unsupported");
161 $protocolVersion = $worldData->getInt(self::TAG_NETWORK_VERSION, Limits::INT32_MAX);
162 if($protocolVersion === Limits::INT32_MAX){
163 throw new CorruptedWorldException(sprintf(
"Missing '%s' tag in level.dat", self::TAG_NETWORK_VERSION));
165 if($protocolVersion > self::CURRENT_STORAGE_NETWORK_VERSION){
166 throw new UnsupportedWorldFormatException(
"LevelDB world protocol version $protocolVersion is currently unsupported");
172 protected function fix() : void{
173 $generatorNameTag = $this->compoundTag->getTag(self::TAG_GENERATOR_NAME);
174 if(!($generatorNameTag instanceof
StringTag)){
175 if(($mcpeGeneratorTypeTag = $this->compoundTag->getTag(self::TAG_GENERATOR)) instanceof
IntTag){
176 switch($mcpeGeneratorTypeTag->getValue()){
177 case self::GENERATOR_FLAT:
178 $this->compoundTag->setString(self::TAG_GENERATOR_NAME,
"flat");
179 $this->compoundTag->setString(self::TAG_GENERATOR_OPTIONS,
"2;7,3,3,2;1");
181 case self::GENERATOR_INFINITE:
183 $this->compoundTag->setString(self::TAG_GENERATOR_NAME,
"default");
184 $this->compoundTag->setString(self::TAG_GENERATOR_OPTIONS,
"");
186 case self::GENERATOR_LIMITED:
192 $this->compoundTag->setString(self::TAG_GENERATOR_NAME,
"default");
194 }elseif(($generatorName = self::hackyFixForGeneratorClasspathInLevelDat($generatorNameTag->getValue())) !==
null){
195 $this->compoundTag->setString(self::TAG_GENERATOR_NAME, $generatorName);
198 if(!($this->compoundTag->getTag(self::TAG_GENERATOR_OPTIONS)) instanceof
StringTag){
199 $this->compoundTag->setString(self::TAG_GENERATOR_OPTIONS,
"");
204 $this->compoundTag->setInt(self::TAG_NETWORK_VERSION, self::CURRENT_STORAGE_NETWORK_VERSION);
205 $this->compoundTag->setInt(self::TAG_STORAGE_VERSION, self::CURRENT_STORAGE_VERSION);
206 $this->compoundTag->setTag(self::TAG_LAST_OPENED_WITH_VERSION,
new ListTag(array_map(fn(
int $v) =>
new IntTag($v), self::CURRENT_CLIENT_VERSION_TARGET)));
207 $this->compoundTag->setLong(VersionInfo::TAG_WORLD_DATA_VERSION, VersionInfo::WORLD_DATA_VERSION);
210 $buffer = $nbt->write(
new TreeRoot($this->compoundTag));
211 Filesystem::safeFilePutContents($this->dataPath, Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer);
215 return $this->compoundTag->getInt(self::TAG_DIFFICULTY,
World::DIFFICULTY_NORMAL);
219 $this->compoundTag->setInt(self::TAG_DIFFICULTY, $difficulty);
223 return $this->compoundTag->getInt(self::TAG_RAIN_TIME, 0);
227 $this->compoundTag->setInt(self::TAG_RAIN_TIME, $ticks);
231 return $this->compoundTag->getFloat(self::TAG_RAIN_LEVEL, 0.0);
235 $this->compoundTag->setFloat(self::TAG_RAIN_LEVEL, $level);
239 return $this->compoundTag->getInt(self::TAG_LIGHTNING_TIME, 0);
243 $this->compoundTag->setInt(self::TAG_LIGHTNING_TIME, $ticks);
247 return $this->compoundTag->getFloat(self::TAG_LIGHTNING_LEVEL, 0.0);
251 $this->compoundTag->setFloat(self::TAG_LIGHTNING_LEVEL, $level);