124 if($world === $this->getDefaultWorld() && !$forceUnload){
125 throw new \InvalidArgumentException(
"The default world cannot be unloaded while running, please switch worlds.");
127 if($world->isDoingTick()){
128 throw new \InvalidArgumentException(
"Cannot unload a world during world tick");
131 $ev =
new WorldUnloadEvent($world);
134 if(!$forceUnload && $ev->isCancelled()){
138 $this->server->getLogger()->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_unloading($world->getDisplayName())));
139 if(count($world->getPlayers()) !== 0){
141 $safeSpawn = $this->defaultWorld !==
null && $this->defaultWorld !== $world ? $this->defaultWorld->getSafeSpawn() :
null;
142 }
catch(WorldException $e){
145 foreach($world->getPlayers() as $player){
146 if($safeSpawn ===
null){
147 $player->disconnect(
"Forced default world unload");
149 $player->teleport($safeSpawn);
154 if($world === $this->defaultWorld){
155 $this->defaultWorld =
null;
157 unset($this->worlds[$world->getId()]);
170 public function loadWorld(
string $name,
bool $autoUpgrade =
false) : bool{
171 if(trim($name) ===
""){
172 throw new \InvalidArgumentException(
"Invalid empty world name");
174 if($this->isWorldLoaded($name)){
176 }elseif(!$this->isWorldGenerated($name)){
180 $path = $this->getWorldPath($name);
182 $providers = $this->providerManager->getMatchingProviders($path);
183 if(count($providers) !== 1){
184 $this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
186 count($providers) === 0 ?
187 KnownTranslationFactory::pocketmine_level_unknownFormat() :
188 KnownTranslationFactory::pocketmine_level_ambiguousFormat(implode(
", ", array_keys($providers)))
192 $providerClass = array_shift($providers);
195 $provider = $providerClass->fromPath($path,
new \
PrefixedLogger($this->
server->getLogger(),
"World Provider: $name"));
196 }
catch(CorruptedWorldException $e){
197 $this->
server->getLogger()->error($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
199 KnownTranslationFactory::pocketmine_level_corrupted($e->getMessage())
202 }
catch(UnsupportedWorldFormatException $e){
203 $this->
server->getLogger()->error($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
205 KnownTranslationFactory::pocketmine_level_unsupportedFormat($e->getMessage())
210 $generatorEntry = GeneratorManager::getInstance()->getGenerator($provider->getWorldData()->getGenerator());
211 if($generatorEntry ===
null){
212 $this->
server->getLogger()->error($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
214 KnownTranslationFactory::pocketmine_level_unknownGenerator($provider->getWorldData()->getGenerator())
219 $generatorEntry->validateGeneratorOptions($provider->getWorldData()->getGeneratorOptions());
220 }
catch(InvalidGeneratorOptionsException $e){
221 $this->
server->getLogger()->error($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
223 KnownTranslationFactory::pocketmine_level_invalidGeneratorOptions(
224 $provider->getWorldData()->getGeneratorOptions(),
225 $provider->getWorldData()->getGenerator(),
231 if(!($provider instanceof WritableWorldProvider)){
233 throw new UnsupportedWorldFormatException(
"World \"$name\" is in an unsupported format and needs to be upgraded");
235 $this->
server->getLogger()->notice($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_conversion_start($name)));
237 $providerClass = $this->providerManager->getDefault();
238 $converter =
new FormatConverter($provider, $providerClass, Path::join($this->
server->getDataPath(),
"backups",
"worlds"), $this->server->getLogger());
239 $converter->execute();
240 $provider = $providerClass->fromPath($path,
new \
PrefixedLogger($this->
server->getLogger(),
"World Provider: $name"));
242 $this->
server->getLogger()->notice($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_conversion_finish($name, $converter->getBackupPath())));
245 $world =
new World($this->server, $name, $provider, $this->
server->getAsyncPool());
247 $this->worlds[$world->getId()] = $world;
248 $world->setAutoSave($this->autoSave);
250 (
new WorldLoadEvent($world))->call();
261 if(trim($name) ===
"" || $this->isWorldGenerated($name)){
265 $providerEntry = $this->providerManager->getDefault();
267 $path = $this->getWorldPath($name);
268 $providerEntry->generate($path, $name, $options);
270 $world =
new World($this->server, $name, $providerEntry->fromPath($path,
new \
PrefixedLogger($this->
server->getLogger(),
"World Provider: $name")), $this->server->getAsyncPool());
271 $this->worlds[$world->getId()] = $world;
273 $world->setAutoSave($this->autoSave);
279 if($backgroundGeneration){
280 $this->
server->getLogger()->notice($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_backgroundGeneration($name)));
282 $spawnLocation = $world->getSpawnLocation();
283 $centerX = $spawnLocation->getFloorX() >> Chunk::COORD_BIT_SIZE;
284 $centerZ = $spawnLocation->getFloorZ() >> Chunk::COORD_BIT_SIZE;
286 $selected = iterator_to_array((
new ChunkSelector())->selectChunks(8, $centerX, $centerZ), preserve_keys:
false);
288 $total = count($selected);
289 foreach($selected as $index){
290 World::getXZ($index, $chunkX, $chunkZ);
291 $world->orderChunkPopulation($chunkX, $chunkZ,
null)->onCompletion(
292 static function() use ($world, &$done, $total) :
void{
293 $oldProgress = (int) floor(($done / $total) * 100);
294 $newProgress = (int) floor((++$done / $total) * 100);
295 if(intdiv($oldProgress, 10) !== intdiv($newProgress, 10) || $done === $total || $done === 1){
296 $world->getLogger()->info($world->getServer()->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_spawnTerrainGenerationProgress(strval($done), strval($total), strval($newProgress))));
299 static function() :
void{