I'm curious as to what the best way is to unload a level from a running server, and cleaning up its directory. I'm calling unload() on the level, but shortly after, the server crashes. This is being called from a PluginTask, if that helps. The crash seems "deferred". A few ticks later, it crashes... To limit questions, I'll fully explain what I'm working on. I've implemented "Player Vaults" by using a template Vault world, and making a copy for each player. While this isn't the most efficient from a disk usage standpoint (although I'm archiving them when not in use), it's very efficient for performance. The alternative would be to parse the contents of each chest, and save that, which would be horrible on performance or track the changes to the chests as they're modified... For now, I'm storing a copy of the vault for each player, including their changes. When the player leaves the Vault, the Vault is uploaded to a database, and completely erased from the server. This is where the error occurs, although before any cleanup is attempted (shortly after unload). If there's a better way to implement this entire thing, I'm all ears. Right now, the crash I'm getting looks like this: Code: [23:59:49] [Server thread/CRITICAL]: Error: "Call to a member function getName() on null" (EXCEPTION) in "src/pocketmine/level/Level" at line 2681 [23:59:49] [Server thread/DEBUG]: #0 src/pocketmine/Server(2365): pocketmine\level\Level->getName() [23:59:49] [Server thread/DEBUG]: #1 src/pocketmine/Server(2497): pocketmine\Server->checkTickUpdates(integer 358, double 1506311989.3806) [23:59:49] [Server thread/DEBUG]: #2 src/pocketmine/Server(2238): pocketmine\Server->tick() [23:59:49] [Server thread/DEBUG]: #3 src/pocketmine/Server(2117): pocketmine\Server->tickProcessor() [23:59:49] [Server thread/DEBUG]: #4 src/pocketmine/Server(1699): pocketmine\Server->start() [23:59:49] [Server thread/DEBUG]: #5 src/pocketmine/PocketMine(553): pocketmine\Server->__construct(BaseClassLoader object, pocketmine\utils\MainLogger object, string phar:///Volumes/APFS_HACK_DRIVE/MCPE_Stuff/MCPEPlanetFiles/PocketMine-MP.phar/, string /Volumes/APFS_HACK_DRIVE/MCPE_Stuff/MCPEPlanetFiles/, string /Volumes/APFS_HACK_DRIVE/MCPE_Stuff/MCPEPlanetFiles/plugins/) [23:59:49] [Server thread/DEBUG]: #6 /Volumes/APFS_HACK_DRIVE/MCPE_Stuff/MCPEPlanetFiles/PocketMine-MP.phar(1): require_once(string phar:///Volumes/APFS_HACK_DRIVE/MCPE_Stuff/MCPEPlanetFiles/PocketMine-MP.phar/src/pocketmine/PocketMine.php) [23:59:49] [Server thread/EMERGENCY]: An unrecoverable error has occurred and the server has crashed. Creating a crash dump [23:59:49] [Server thread/EMERGENCY]: Please upload the "/Volumes/APFS_HACK_DRIVE/MCPE_Stuff/MCPEPlanetFiles/crashdumps/Sun_Sep_24-23.59.49-EDT_2017.log" file to the Crash Archive and submit the link to the Bug Reporting page. Give as much info as you can.
Sure. The code that I believe is behind this crash isn't really sensitive. PHP: class UnloadTask extends PluginTask { /** * @var string */ private $levelName; /** * UnloadTask constructor. * @param string $levelName * @param Plugin $owner */ public function __construct(string $levelName, Plugin $owner) { $this->levelName = $levelName; parent::__construct($owner); } /** * @param int $currentTick */ public function onRun(int $currentTick) { $plugin = $this->getOwner(); if ($plugin instanceof Vault) { $level = $plugin->getServer()->getLevelByName($this->levelName); $level->unload(); } }} If this doesn't do anything for you, I can send you my full code in a private message. You'll have to configure a MySQL database as specified in the code if you want to run it, though. Although, it could always be indirectly related to any one of the many plugins I run. I'll try disabling them all, and report back. Probably should have done that first. :3 EDIT: Nah, crashes even with just one plugin loaded. Also note that this task is being set up as a delayed task after 20 ticks.
your problem is that you're using `Level->unload()`... that's an internal method, it doesn't remove the level from the Server. Use `Server->unloadLevel()` instead. (This is also a documentation problem... it'll be corrected.)
...and like that, the error's gone. Wow. I could have sworn I tried that method too, but I guess I tried it somewhere else, in some other context. Thanks. EDIT: Ah, I see. PHP: unset($this->levels[$level->getId()]); isn't run if you use the internal method. Gotcha.