///// PUBLIC FUNCTIONS

        /// <summary>
        /// Initilize this chunk controller for it's provided level.
        /// </summary>
        public void initialize()
        {
            if (chunkObjectPrefab == null)
            {
                Debug.LogError("UnityLevelController Missing chunk prefab, can't work");
            }
            else if (level == null)
            {
                Debug.LogError("No level provided by world. Did you hook this level controller up to the world controller?");
            }
            else
            {
                chunkControllerActivationQueue = new ConcurrentQueue <UnityChunkController>();
                loadedChunkLocations           = new ConcurrentBag <Vector3>();
                chunkControllerPool            = new UnityChunkController[Level <IVoxelStorage> .MeshedChunkDiameter * Level <IVoxelStorage> .MeshedChunkDiameter * level.chunkBounds.y];
                isLoaded = true;
                for (int index = 0; index < chunkControllerPool.Length; index++)
                {
                    // for each chunk we want to be able to render at once, create a new pooled gameobject for it with the prefab that has a unitu chunk controller on it
                    GameObject chunkObject = Instantiate(chunkObjectPrefab);
                    chunkObject.transform.parent = gameObject.transform;
                    UnityChunkController chunkController = chunkObject.GetComponent <UnityChunkController>();
                    if (chunkController == null)
                    {
                        Debug.LogError($"No chunk controller on {chunkObject.name}");
                    }
                    else
                    {
                        chunkControllerPool[index]      = chunkController;
                        chunkController.levelController = this;
                        chunkObject.SetActive(false);
                    }
                }
            }
        }
        /// <summary>
        /// Get notifications from other observers, EX:
        ///   block breaking and placing
        ///   player chunk location changes
        /// </summary>
        /// <param name="event">The event to notify this observer of</param>
        /// <param name="origin">(optional) the source of the event</param>
        public void notifyOf(IEvent @event, IObserver origin = null)
        {
            // ignore events if we have no level to control
            if (!isLoaded || level == null)
            {
                return;
            }

            switch (@event)
            {
            // when a player spawns in the level
            case Player.SpawnEvent pse:
                level.initializeAround(pse.spawnLocation.toChunkLocation());
                break;

            // When the player moves to a new chunk, adjust the loaded level focus
            case Player.ChangeChunkLocationEvent pccle:
                level.adjustFocusTo(pccle.newChunkLocation);
                break;

            // when the level finishes loading a chunk's mesh. Render it in world
            case Level <VoxelDictionary> .ChunkMeshGenerationFinishedEvent lcmgfe:
                UnityChunkController unusedChunkController = getUnusedChunkController();
                if (unusedChunkController == null)
                {
                    Debug.LogError($"No free chunk controller found for {lcmgfe.chunkLocation.ToString()}");
                }
                else
                {
                    IVoxelChunk chunk = level.getChunk(lcmgfe.chunkLocation, true);
                    if (unusedChunkController.setChunkToRender(chunk, lcmgfe.chunkLocation.vec3))
                    {
                        chunkControllerActivationQueue.Enqueue(unusedChunkController);
                    }
                }
                break;

            case Level <VoxelDictionary> .ChunkDataLoadingFinishedEvent lcdlfe:
                loadedChunkLocations.Add(lcdlfe.chunkLocation.vec3);
                break;

            // ignore other events
            default:
                return;
            }
        }