128 if($world === $this->getDefaultWorld() && !$forceUnload){
129 throw new \InvalidArgumentException(
"The default world cannot be unloaded while running, please switch worlds.");
131 if($world->isDoingTick()){
132 throw new \InvalidArgumentException(
"Cannot unload a world during world tick");
135 $ev =
new WorldUnloadEvent($world);
138 if(!$forceUnload && $ev->isCancelled()){
142 $this->server->getLogger()->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_unloading($world->getDisplayName())));
143 if(count($world->getPlayers()) !== 0){
145 $safeSpawn = $this->defaultWorld !==
null && $this->defaultWorld !== $world ? $this->defaultWorld->getSafeSpawn() :
null;
146 }
catch(WorldException $e){
149 foreach($world->getPlayers() as $player){
150 if($safeSpawn ===
null){
151 $player->disconnect(
"Forced default world unload");
153 $player->teleport($safeSpawn);
158 if($world === $this->defaultWorld){
159 $this->defaultWorld =
null;
161 unset($this->worlds[$world->getId()]);
174 public function loadWorld(
string $name,
bool $autoUpgrade =
false) : bool{
175 if(trim($name) ===
""){
176 throw new \InvalidArgumentException(
"Invalid empty world name");
178 if($this->isWorldLoaded($name)){
180 }elseif(!$this->isWorldGenerated($name)){
184 $path = $this->getWorldPath($name);
186 $providers = $this->providerManager->getMatchingProviders($path);
187 if(count($providers) !== 1){
188 $this->server->getLogger()->error($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
190 count($providers) === 0 ?
191 KnownTranslationFactory::pocketmine_level_unknownFormat() :
192 KnownTranslationFactory::pocketmine_level_ambiguousFormat(implode(
", ", array_keys($providers)))
196 $providerClass = array_shift($providers);
199 $provider = $providerClass->fromPath($path,
new \
PrefixedLogger($this->
server->getLogger(),
"World Provider: $name"));
200 }
catch(CorruptedWorldException $e){
201 $this->
server->getLogger()->error($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
203 KnownTranslationFactory::pocketmine_level_corrupted($e->getMessage())
206 }
catch(UnsupportedWorldFormatException $e){
207 $this->
server->getLogger()->error($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
209 KnownTranslationFactory::pocketmine_level_unsupportedFormat($e->getMessage())
214 $generatorEntry = GeneratorManager::getInstance()->getGenerator($provider->getWorldData()->getGenerator());
215 if($generatorEntry ===
null){
216 $this->
server->getLogger()->error($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
218 KnownTranslationFactory::pocketmine_level_unknownGenerator($provider->getWorldData()->getGenerator())
223 $generatorEntry->validateGeneratorOptions($provider->getWorldData()->getGeneratorOptions());
224 }
catch(InvalidGeneratorOptionsException $e){
225 $this->
server->getLogger()->error($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_loadError(
227 KnownTranslationFactory::pocketmine_level_invalidGeneratorOptions(
228 $provider->getWorldData()->getGeneratorOptions(),
229 $provider->getWorldData()->getGenerator(),
235 if(!($provider instanceof WritableWorldProvider)){
237 throw new UnsupportedWorldFormatException(
"World \"$name\" is in an unsupported format and needs to be upgraded");
239 $this->
server->getLogger()->notice($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_conversion_start($name)));
241 $providerClass = $this->providerManager->getDefault();
242 $converter =
new FormatConverter($provider, $providerClass, Path::join($this->
server->getDataPath(),
"backups",
"worlds"), $this->server->getLogger());
243 $converter->execute();
244 $provider = $providerClass->fromPath($path,
new \
PrefixedLogger($this->
server->getLogger(),
"World Provider: $name"));
246 $this->
server->getLogger()->notice($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_conversion_finish($name, $converter->getBackupPath())));
249 $world =
new World($this->server, $name, $provider, $this->
server->getAsyncPool());
251 $this->worlds[$world->getId()] = $world;
252 $world->setAutoSave($this->autoSave);
254 (
new WorldLoadEvent($world))->call();
265 if(trim($name) ===
"" || $this->isWorldGenerated($name)){
269 $providerEntry = $this->providerManager->getDefault();
271 $path = $this->getWorldPath($name);
272 $providerEntry->generate($path, $name, $options);
274 $world =
new World($this->server, $name, $providerEntry->fromPath($path,
new \
PrefixedLogger($this->
server->getLogger(),
"World Provider: $name")), $this->server->getAsyncPool());
275 $this->worlds[$world->getId()] = $world;
277 $world->setAutoSave($this->autoSave);
283 if($backgroundGeneration){
284 $this->
server->getLogger()->notice($this->
server->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_backgroundGeneration($name)));
286 $spawnLocation = $world->getSpawnLocation();
287 $centerX = $spawnLocation->getFloorX() >> Chunk::COORD_BIT_SIZE;
288 $centerZ = $spawnLocation->getFloorZ() >> Chunk::COORD_BIT_SIZE;
290 $selected = iterator_to_array((
new ChunkSelector())->selectChunks(8, $centerX, $centerZ), preserve_keys:
false);
292 $total = count($selected);
293 foreach($selected as $index){
294 World::getXZ($index, $chunkX, $chunkZ);
295 $world->orderChunkPopulation($chunkX, $chunkZ,
null)->onCompletion(
296 static function() use ($world, &$done, $total) :
void{
297 $oldProgress = (int) floor(($done / $total) * 100);
298 $newProgress = (int) floor((++$done / $total) * 100);
299 if(intdiv($oldProgress, 10) !== intdiv($newProgress, 10) || $done === $total || $done === 1){
300 $world->getLogger()->info($world->getServer()->getLanguage()->translate(KnownTranslationFactory::pocketmine_level_spawnTerrainGenerationProgress(strval($done), strval($total), strval($newProgress))));
303 static function() :
void{