From 1532b0ef6d48ce5346dea644ca477cb26c087928 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 3 Aug 2018 18:04:56 +0100 Subject: [PATCH 1/2] Level: Remove chunks from chunk send queue on unload When a chunk request task crashes, these can get stuck and never get removed. This allows using /gc to collect the bad chunk in order to fix the bug. --- src/pocketmine/level/Level.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index f7f9c2a0e..6dd1d9763 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -2754,6 +2754,8 @@ class Level implements ChunkManager, Metadatable{ unset($this->chunkCache[$chunkHash]); unset($this->blockCache[$chunkHash]); unset($this->changedBlocks[$chunkHash]); + unset($this->chunkSendQueue[$chunkHash]); + unset($this->chunkSendTasks[$chunkHash]); $this->timings->doChunkUnload->stopTiming(); From 9ff5c65fb613276bb59a710606b898ac1bff73b0 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 3 Aug 2018 18:23:32 +0100 Subject: [PATCH 2/2] Level: Make async chunk sending aware of faults Previously any random error could occur during an AsyncTask preparing a chunk, and the Level would never know about it and thus never send the chunk. I don't know how many invisible-chunk bug cases this fixes, but I expect it's quite a lot. --- src/pocketmine/level/Level.php | 42 +++++++++++++++------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 6dd1d9763..b63cb72d7 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -191,7 +191,7 @@ class Level implements ChunkManager, Metadatable{ /** @var Player[][] */ private $chunkSendQueue = []; - /** @var bool[] */ + /** @var ChunkRequestTask[] */ private $chunkSendTasks = []; /** @var bool[] */ @@ -2456,7 +2456,7 @@ class Level implements ChunkManager, Metadatable{ } private function sendChunkFromCache(int $x, int $z){ - if(isset($this->chunkSendTasks[$index = Level::chunkHash($x, $z)])){ + if(isset($this->chunkSendQueue[$index = Level::chunkHash($x, $z)])){ foreach($this->chunkSendQueue[$index] as $player){ /** @var Player $player */ if($player->isConnected() and isset($player->usedChunks[$index])){ @@ -2464,7 +2464,6 @@ class Level implements ChunkManager, Metadatable{ } } unset($this->chunkSendQueue[$index]); - unset($this->chunkSendTasks[$index]); } } @@ -2473,11 +2472,17 @@ class Level implements ChunkManager, Metadatable{ $this->timings->syncChunkSendTimer->startTiming(); foreach($this->chunkSendQueue as $index => $players){ - if(isset($this->chunkSendTasks[$index])){ - continue; - } Level::getXZ($index, $x, $z); - $this->chunkSendTasks[$index] = true; + + if(isset($this->chunkSendTasks[$index])){ + if($this->chunkSendTasks[$index]->isCrashed()){ + unset($this->chunkSendTasks[$index]); + $this->server->getLogger()->error("Failed to prepare chunk $x $z for sending, retrying"); + }else{ + //Not ready for sending yet + continue; + } + } if(isset($this->chunkCache[$index])){ $this->sendChunkFromCache($x, $z); continue; @@ -2490,7 +2495,8 @@ class Level implements ChunkManager, Metadatable{ } assert($chunk->getX() === $x and $chunk->getZ() === $z, "Chunk coordinate mismatch: expected $x $z, but chunk has coordinates " . $chunk->getX() . " " . $chunk->getZ() . ", did you forget to clone a chunk before setting?"); - $this->server->getAsyncPool()->submitTask(new ChunkRequestTask($this, $x, $z, $chunk)); + $this->server->getAsyncPool()->submitTask($task = new ChunkRequestTask($this, $x, $z, $chunk)); + $this->chunkSendTasks[$index] = $task; $this->timings->syncChunkSendPrepareTimer->stopTiming(); } @@ -2503,24 +2509,14 @@ class Level implements ChunkManager, Metadatable{ $this->timings->syncChunkSendTimer->startTiming(); $index = Level::chunkHash($x, $z); + unset($this->chunkSendTasks[$index]); - if(!isset($this->chunkCache[$index]) and $this->server->getMemoryManager()->canUseChunkCache()){ - $this->chunkCache[$index] = $payload; - $this->sendChunkFromCache($x, $z); - $this->timings->syncChunkSendTimer->stopTiming(); - return; + $this->chunkCache[$index] = $payload; + $this->sendChunkFromCache($x, $z); + if(!$this->server->getMemoryManager()->canUseChunkCache()){ + unset($this->chunkCache[$index]); } - if(isset($this->chunkSendTasks[$index])){ - foreach($this->chunkSendQueue[$index] as $player){ - /** @var Player $player */ - if($player->isConnected() and isset($player->usedChunks[$index])){ - $player->sendChunk($x, $z, $payload); - } - } - unset($this->chunkSendQueue[$index]); - unset($this->chunkSendTasks[$index]); - } $this->timings->syncChunkSendTimer->stopTiming(); }