private async Task <CaveChunk> GenerateAndAddChunkAsync(Vector3Int chunkCoordinate)
        {
            Task <CaveChunk> newChunkTask;
            bool             isTaskInQueue;

            lock (GeneratedChunks)
            {
                if (GeneratedChunks.TryGetValue(chunkCoordinate, out CaveChunk generatedChunk))
                {
                    return(generatedChunk);
                }

                isTaskInQueue = _chunkGenerationQueue.TryGetValue(chunkCoordinate, out newChunkTask);

                if (!isTaskInQueue)
                {
                    newChunkTask = GenerateChunkAsync(chunkCoordinate);
                    _chunkGenerationQueue.Add(chunkCoordinate, newChunkTask);
                }
            }

            if (!isTaskInQueue)
            {
                CaveChunk chunk = await newChunkTask;

                lock (GeneratedChunks)
                {
                    GeneratedChunks.Add(chunkCoordinate, chunk);
                    _chunkGenerationQueue.Remove(chunkCoordinate);
                }
            }

            return(await newChunkTask);
        }
        private async Task <CaveChunk> GenerateChunkAsync(Vector3Int chunkCoordinate)
        {
            ChunkCellData cellData = new ChunkCellData(GeneratorSettings, this, chunkCoordinate);
            CaveChunk     chunk    = CreateChunk(cellData.ChunkCoordinate.ToString());

            await chunk.Generate(cellData, this);

            return(chunk);
        }
        private CaveChunk CreateChunk(string chunkName)
        {
            GameObject newChunkObject = Instantiate(ChunkPrefab.gameObject, ChunkHolder.transform);

            newChunkObject.name = chunkName;
            newChunkObject.SetActive(false);

            CaveChunk newChunk = newChunkObject.GetComponent <CaveChunk>();

            return(newChunk);
        }
        public async Task <CaveChunk> CreateChunkAsync(Vector3Int chunkCoordinate)
        {
            Task <CaveChunk> mainChunkTask = GenerateAndAddChunkAsync(chunkCoordinate);

            _ = mainChunkTask.ContinueWith(ChunkGenerationExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);

            List <Task <CaveChunk> > nearbyChunkTasks = new List <Task <CaveChunk> >(9);

            nearbyChunkTasks.Add(mainChunkTask);

            for (int i = chunkCoordinate.x - 1; i <= chunkCoordinate.x + 1; i++)
            {
                for (int j = chunkCoordinate.y - 1; j <= chunkCoordinate.y + 1; j++)
                {
                    for (int k = chunkCoordinate.z - 1; k <= chunkCoordinate.z + 1; k++)
                    {
                        Vector3Int nearbyChunkCoordinate = new Vector3Int(i, j, k);

                        if (nearbyChunkCoordinate == chunkCoordinate)
                        {
                            continue;
                        }

                        Task <CaveChunk> chunkTask = GenerateAndAddChunkAsync(nearbyChunkCoordinate);
                        _ = chunkTask.ContinueWith(ChunkGenerationExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);

                        nearbyChunkTasks.Add(chunkTask);
                    }
                }
            }

            await Task.WhenAll(nearbyChunkTasks);

            CaveChunk chunk = mainChunkTask.Result;

            Task finalizationTask = FinalizeChunkAsync(chunk);

            _ = finalizationTask.ContinueWith(ChunkFinalizationExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
            await finalizationTask;

            return(chunk);
        }
        private async Task FinalizeChunkAsync(CaveChunk chunk)
        {
            //Debug.Log($"Finalizing chunk: {chunk.ChunkCoordinate}");

            await chunk.FinalizeGenerationAsync();
        }