From 1ebb2067627c298834a60b992ee42a98091a38fd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 3 Nov 2021 14:58:58 +0000 Subject: [PATCH] World: fixed yet another edge case in drainPopulationRequestQueue() leading to assertion failure really had to go fucking nuclear on it :( --- src/world/World.php | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index 978bd8a42..a06f2ad5e 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -263,6 +263,12 @@ class World implements ChunkManager{ * @phpstan-var \SplQueue */ private \SplQueue $chunkPopulationRequestQueue; + /** + * @var true[] chunkHash => dummy + * @phpstan-var array + */ + private array $chunkPopulationRequestQueueIndex = []; + /** @var bool[] */ private $generatorRegisteredWorkers = []; @@ -2733,12 +2739,19 @@ class World implements ChunkManager{ } } + private function addChunkHashToPopulationRequestQueue(int $chunkHash) : void{ + if(!isset($this->chunkPopulationRequestQueueIndex[$chunkHash])){ + $this->chunkPopulationRequestQueue->enqueue($chunkHash); + $this->chunkPopulationRequestQueueIndex[$chunkHash] = true; + } + } + /** * @phpstan-return Promise */ private function enqueuePopulationRequest(int $chunkX, int $chunkZ, ?ChunkLoader $associatedChunkLoader) : Promise{ $chunkHash = World::chunkHash($chunkX, $chunkZ); - $this->chunkPopulationRequestQueue->enqueue($chunkHash); + $this->addChunkHashToPopulationRequestQueue($chunkHash); $resolver = $this->chunkPopulationRequestMap[$chunkHash] = new PromiseResolver(); if($associatedChunkLoader === null){ $temporaryLoader = new class implements ChunkLoader{}; @@ -2753,17 +2766,9 @@ class World implements ChunkManager{ private function drainPopulationRequestQueue() : void{ $failed = []; - $seen = []; while(count($this->activeChunkPopulationTasks) < $this->maxConcurrentChunkPopulationTasks && !$this->chunkPopulationRequestQueue->isEmpty()){ $nextChunkHash = $this->chunkPopulationRequestQueue->dequeue(); - if(isset($seen[$nextChunkHash])){ - //We may have previously requested this, decided we didn't want it, and then decided we did want it - //again, all before the generation request got executed. In that case, the chunk hash may appear in the - //queue multiple times (it can't be quickly removed from the queue when the request is cancelled, so we - //leave it in the queue). - continue; - } - $seen[$nextChunkHash] = true; + unset($this->chunkPopulationRequestQueueIndex[$nextChunkHash]); World::getXZ($nextChunkHash, $nextChunkX, $nextChunkZ); if(isset($this->chunkPopulationRequestMap[$nextChunkHash])){ assert(!isset($this->activeChunkPopulationTasks[$nextChunkHash]), "Population for chunk $nextChunkX $nextChunkZ already running"); @@ -2779,7 +2784,7 @@ class World implements ChunkManager{ //these requests failed even though they weren't rate limited; we can't directly re-add them to the back of the //queue because it would result in an infinite loop foreach($failed as $hash){ - $this->chunkPopulationRequestQueue->enqueue($hash); + $this->addChunkHashToPopulationRequestQueue($hash); } } @@ -2978,7 +2983,7 @@ class World implements ChunkManager{ //request failed, stick it back on the queue //we didn't resolve the promise or touch it in any way, so any fake chunk loaders are still valid and //don't need to be added a second time. - $this->chunkPopulationRequestQueue->enqueue($index); + $this->addChunkHashToPopulationRequestQueue($index); } $this->drainPopulationRequestQueue();