/// <summary>
        /// Set array of blocks
        /// </summary>
        /// <param name="blockDatas">array containing data of each block you want to place</param>
        /// <param name="destroy">spawn destroy particle</param>
        public void SetBlocks(BlockData[] blockDatas, SetBlockSettings blockSettings)
        {
            List <JobHandle> jobHandles    = new List <JobHandle>();
            List <Chunk>     chunksToBuild = new List <Chunk>();

            bool[] neighboursToBuild = new bool[4];

            for (int i = 0; i < blockDatas.Length; i++)
            {
                BlockPosition blockPosition = blockDatas[i].position;
                BlockType     blockType     = blockDatas[i].blockType;

                SetBlockWithoutRebuild(blockPosition, blockType, blockSettings);

                // check neighbours
                if (blockPosition.x == 16)
                {
                    Chunk neighbourChunk = NeighbourChunks[0];
                    if (neighbourChunk)
                    {
                        neighbourChunk.blocks[Utils.BlockPosition3DtoIndex(0, blockPosition.y, blockPosition.z)] = blockType;
                        neighbourChunk.isTerrainModified = true;
                        neighboursToBuild[0]             = true;
                    }
                }
                else if (blockPosition.x == 1)
                {
                    Chunk neighbourChunk = NeighbourChunks[1];
                    if (neighbourChunk)
                    {
                        neighbourChunk.blocks[Utils.BlockPosition3DtoIndex(17, blockPosition.y, blockPosition.z)] = blockType;
                        neighbourChunk.isTerrainModified = true;
                        neighboursToBuild[1]             = true;
                    }
                }

                if (blockPosition.z == 16)
                {
                    Chunk neighbourChunk = NeighbourChunks[2];
                    if (neighbourChunk)
                    {
                        neighbourChunk.blocks[Utils.BlockPosition3DtoIndex(blockPosition.x, blockPosition.y, 0)] = blockType;
                        neighbourChunk.isTerrainModified = true;
                        neighboursToBuild[2]             = true;
                    }
                }
                else if (blockPosition.z == 1)
                {
                    Chunk neighbourChunk = NeighbourChunks[3];
                    if (neighbourChunk)
                    {
                        neighbourChunk.blocks[Utils.BlockPosition3DtoIndex(blockPosition.x, blockPosition.y, 17)] = blockType;
                        neighbourChunk.isTerrainModified = true;
                        neighboursToBuild[3]             = true;
                    }
                }

                UpdateNeighbourBlocks(blockDatas[i].position, 10);
            }

            // add current chunk
            BuildMesh(jobHandles);
            chunksToBuild.Add(this);

            for (int j = 0; j < 4; j++)
            {
                if (neighboursToBuild[j])
                {
                    Chunk tc = NeighbourChunks[j];
                    tc.BuildMesh(jobHandles);
                    chunksToBuild.Add(tc);
                }
            }

            NativeArray <JobHandle> njobHandles = new NativeArray <JobHandle>(jobHandles.ToArray(), Allocator.Temp);

            JobHandle.CompleteAll(njobHandles);

            // build meshes
            foreach (Chunk tc in chunksToBuild)
            {
                tc.ApplyMesh();
            }

            // clear & dispose
            jobHandles.Clear();
            chunksToBuild.Clear();

            njobHandles.Dispose();
        }
        /// <summary>
        /// Set block at position and rebuild mesh - use when you want to place one block, else take a look at
        /// <see cref="SetBlockWithoutRebuild(int, int, int, BlockType, bool)"/> or <see cref="SetBlocks(BlockData[], bool)"/>
        /// </summary>
        /// <param name="x">x position of block</param>
        /// <param name="y">y position of block</param>
        /// <param name="z">z position of block</param>
        /// <param name="blockType">type of block you want to place</param>
        /// <param name="destroy">spawn destroy particle</param>
        public void SetBlock(BlockPosition blockPosition, BlockType blockType, SetBlockSettings blockSettings)
        {
            List <JobHandle> jobHandles    = new List <JobHandle>();
            List <Chunk>     chunksToBuild = new List <Chunk>();

            SetBlockWithoutRebuild(blockPosition, blockType, blockSettings);

            // add current chunk
            BuildMesh(jobHandles);
            chunksToBuild.Add(this);

            // check neighbours
            if (blockPosition.x == 16)
            {
                Chunk neighbourChunk = NeighbourChunks[0];
                if (neighbourChunk)
                {
                    neighbourChunk.blocks[Utils.BlockPosition3DtoIndex(0, blockPosition.y, blockPosition.z)] = blockType;
                    neighbourChunk.isTerrainModified = true;
                    neighbourChunk.BuildMesh(jobHandles);
                    chunksToBuild.Add(neighbourChunk);
                }
            }
            else if (blockPosition.x == 1)
            {
                Chunk neighbourChunk = NeighbourChunks[1];
                if (neighbourChunk)
                {
                    neighbourChunk.blocks[Utils.BlockPosition3DtoIndex(17, blockPosition.y, blockPosition.z)] = blockType;
                    neighbourChunk.isTerrainModified = true;
                    neighbourChunk.BuildMesh(jobHandles);
                    chunksToBuild.Add(neighbourChunk);
                }
            }

            if (blockPosition.z == 16)
            {
                Chunk neighbourChunk = NeighbourChunks[2];
                if (neighbourChunk)
                {
                    neighbourChunk.blocks[Utils.BlockPosition3DtoIndex(blockPosition.x, blockPosition.y, 0)] = blockType;
                    neighbourChunk.isTerrainModified = true;
                    neighbourChunk.BuildMesh(jobHandles);
                    chunksToBuild.Add(neighbourChunk);
                }
            }
            else if (blockPosition.z == 1)
            {
                Chunk neighbourChunk = NeighbourChunks[3];
                if (neighbourChunk)
                {
                    neighbourChunk.blocks[Utils.BlockPosition3DtoIndex(blockPosition.x, blockPosition.y, 17)] = blockType;
                    neighbourChunk.isTerrainModified = true;
                    neighbourChunk.BuildMesh(jobHandles);
                    chunksToBuild.Add(neighbourChunk);
                }
            }

            NativeArray <JobHandle> njobHandles = new NativeArray <JobHandle>(jobHandles.ToArray(), Allocator.TempJob);

            JobHandle.CompleteAll(njobHandles);

            // build meshes
            foreach (Chunk tc in chunksToBuild)
            {
                tc.ApplyMesh();
            }

            // clear & dispose
            jobHandles.Clear();
            chunksToBuild.Clear();
            njobHandles.Dispose();

            UpdateNeighbourBlocks(blockPosition, 10);
        }
 private void SetNeighbour(int index, Chunk neighbour)
 {
     NeighbourChunks[index] = neighbour;
 }
        /// <summary>
        /// Set parameter of block
        /// </summary>
        /// <param name="parameter">parameter type</param>
        /// <param name="value">parameter value</param>
        public void SetParameters(BlockParameter parameter, short value)
        {
            int3 blockPos = parameter.blockPos;

            // check neighbours
            if (blockPos.x == 16)
            {
                Chunk chunk = NeighbourChunks[0];
                if (chunk)
                {
                    BlockParameter neighbourParameter = parameter;
                    neighbourParameter.blockPos = new int3(0, blockPos.y, blockPos.z);

                    if (chunk.blockParameters.ContainsKey(neighbourParameter))
                    {
                        chunk.blockParameters[neighbourParameter] = value;
                    }
                    else
                    {
                        chunk.blockParameters.Add(neighbourParameter, value);
                    }
                }
            }
            else if (blockPos.x == 1)
            {
                Chunk chunk = NeighbourChunks[1];
                if (chunk)
                {
                    BlockParameter neighbourParameter = parameter;
                    neighbourParameter.blockPos = new int3(17, blockPos.y, blockPos.z);

                    if (chunk.blockParameters.ContainsKey(neighbourParameter))
                    {
                        chunk.blockParameters[neighbourParameter] = value;
                    }
                    else
                    {
                        chunk.blockParameters.Add(neighbourParameter, value);
                    }
                }
            }

            if (blockPos.z == 16)
            {
                Chunk chunk = NeighbourChunks[2];
                if (chunk)
                {
                    BlockParameter neighbourParameter = parameter;
                    neighbourParameter.blockPos = new int3(blockPos.x, blockPos.y, 0);

                    if (chunk.blockParameters.ContainsKey(neighbourParameter))
                    {
                        chunk.blockParameters[neighbourParameter] = value;
                    }
                    else
                    {
                        chunk.blockParameters.Add(neighbourParameter, value);
                    }
                }
            }
            else if (blockPos.z == 1)
            {
                Chunk chunk = NeighbourChunks[3];
                if (chunk)
                {
                    BlockParameter neighbourParameter = parameter;
                    neighbourParameter.blockPos = new int3(blockPos.x, blockPos.y, 17);

                    if (chunk.blockParameters.ContainsKey(neighbourParameter))
                    {
                        chunk.blockParameters[neighbourParameter] = value;
                    }
                    else
                    {
                        chunk.blockParameters.Add(neighbourParameter, value);
                    }
                }
            }

            if (blockParameters.ContainsKey(parameter))
            {
                blockParameters[parameter] = value;
            }
            else
            {
                blockParameters.Add(parameter, value);
            }
        }
        /// <summary>
        /// Remove all parameters from block
        /// </summary>
        /// <param name="blockPos">position of block</param>
        public void ClearParameters(int3 blockPos)
        {
            BlockParameter key = new BlockParameter(blockPos);

            // check neighbours
            // TODO: add check neighbours method taking position as param and returning neighbour chunk and it's index
            if (blockPos.x == ChunkSizeXZ)
            {
                Chunk chunk = NeighbourChunks[0];
                if (chunk)
                {
                    BlockParameter neighbourKey = new BlockParameter(new int3(0, blockPos.y, blockPos.z));
                    while (chunk.blockParameters.ContainsKey(neighbourKey))
                    {
                        chunk.blockParameters.Remove(neighbourKey);
                    }
                }
            }
            else if (blockPos.x == 1)
            {
                Chunk chunk = NeighbourChunks[1];
                if (chunk)
                {
                    BlockParameter neighbourKey = new BlockParameter(new int3(ChunkSizeXZ + 1, blockPos.y, blockPos.z));
                    while (chunk.blockParameters.ContainsKey(neighbourKey))
                    {
                        chunk.blockParameters.Remove(neighbourKey);
                    }
                }
            }

            if (blockPos.z == ChunkSizeXZ)
            {
                Chunk chunk = NeighbourChunks[2];
                if (chunk)
                {
                    BlockParameter neighbourKey = new BlockParameter(new int3(blockPos.x, blockPos.y, 0));
                    while (chunk.blockParameters.ContainsKey(neighbourKey))
                    {
                        chunk.blockParameters.Remove(neighbourKey);
                    }
                }
            }
            else if (blockPos.z == 1)
            {
                Chunk chunk = NeighbourChunks[3];
                if (chunk)
                {
                    BlockParameter neighbourKey = new BlockParameter(new int3(blockPos.x, blockPos.y, ChunkSizeXZ + 1));
                    while (chunk.blockParameters.ContainsKey(neighbourKey))
                    {
                        chunk.blockParameters.Remove(neighbourKey);
                    }
                }
            }

            while (blockParameters.ContainsKey(key))
            {
                blockParameters.Remove(key);
            }
        }