can you significantly reduce chunk creation speeds and all related processing? generally optimize the code for performance.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using Newtonsoft.Json;
using System.IO;
using System.Linq;
using System;

public class BlockFetcher : MonoBehaviour
{
    private string serverUrl = "http://localhost:8000/blocks";
    public string texturePath = "Assets/Textures";
    public Material blockMaterial;
    [Tooltip(
        "The name of the texture file to use when a block's texture is not found (without .png extension)"
    )]
    public string defaultTextureName = "missing_texture";
    private const int MAX_VERTICES_PER_CHUNK = 65000; // Slightly below Unity's limit of 65,535
    private List<MeshChunk> meshChunks = new List<MeshChunk>();

    public string[] woodTypes;
    public Mesh slabMesh;
    public Mesh fenceMesh;
    public Mesh stairMesh;

    private class MeshChunk
    {
        public List<Vector3> vertices;
        public List<int> triangles;
        public List<Vector2> uvs;
        public bool isFull => vertices.Count + 4 >= MAX_VERTICES_PER_CHUNK; // +4 for next quad

        public MeshChunk()
        {
            int capacity = MAX_VERTICES_PER_CHUNK + 4;
            vertices = new List<Vector3>(capacity);
            uvs = new List<Vector2>(capacity);
            triangles = new List<int>(capacity * 2); // Estimate triangle count
        }
    }

    [System.Serializable]
    public class BlockTextureConfig
    {
        public string blockName;
        public string top;
        public string bottom;
        public string front;
        public string back;
        public string left;
        public string right;
        public string side;
        public bool isBillboard;
        public Mesh customMesh;
        public void SetDefaults(string defaultName)
        {
            if (string.IsNullOrEmpty(top)) top = defaultName;
            if (string.IsNullOrEmpty(bottom)) bottom = defaultName;
            if (!string.IsNullOrEmpty(side))
            {
                front = back = left = right = side;
            }
            else
            {
                if (string.IsNullOrEmpty(front)) front = defaultName;
                if (string.IsNullOrEmpty(back)) back = defaultName;
                if (string.IsNullOrEmpty(left)) left = defaultName;
                if (string.IsNullOrEmpty(right)) right = defaultName;
            }
        }
    }

    [System.Serializable]
    public class BlockTintConfig
    {
        public string blockName;
        public Color tintColor;
        public string maskName;

        public BlockTintConfig(string blockName, Color tintColor, string maskName = null)
        {
            this.blockName = blockName;
            this.tintColor = tintColor;
            this.maskName = maskName;
        }
    }

    public BlockTextureConfig[] blockConfigs;
    public BlockTintConfig[] blockTintConfigs;

    private Texture2D textureAtlas;
    private List<BlockData> blockDataList;
    private Rect[] atlasRects;
    private Dictionary<string, int> blockNameToTextureIndex = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
    private Dictionary<string, BlockTextureConfig> blockConfigMap = new Dictionary<string, BlockTextureConfig>(StringComparer.OrdinalIgnoreCase);
    private int defaultTextureIndex;
    private Dictionary<int, Vector2[]> uvCache = new Dictionary<int, Vector2[]>();

    [System.Serializable]
    public class BlockData
    {
        public string block_name;
        public int x;
        public int y;
        public int z;
        public string direction;
    }

    private static readonly Vector3[,] faceVertices =
    {
        {
            new Vector3(-0.5f, -0.5f, -0.5f),
            new Vector3( 0.5f, -0.5f, -0.5f),
            new Vector3( 0.5f,  0.5f, -0.5f),
            new Vector3(-0.5f,  0.5f, -0.5f)
        },
        {
            new Vector3(-0.5f, -0.5f, 0.5f),
            new Vector3( 0.5f, -0.5f, 0.5f),
            new Vector3( 0.5f,  0.5f, 0.5f),
            new Vector3(-0.5f,  0.5f, 0.5f)
        },
        {
            new Vector3(-0.5f,  0.5f, -0.5f),
            new Vector3( 0.5f,  0.5f, -0.5f),
            new Vector3( 0.5f,  0.5f,  0.5f),
            new Vector3(-0.5f,  0.5f,  0.5f)
        },
        {
            new Vector3(-0.5f, -0.5f, -0.5f),
            new Vector3( 0.5f, -0.5f, -0.5f),
            new Vector3( 0.5f, -0.5f,  0.5f),
            new Vector3(-0.5f, -0.5f,  0.5f)
        },
        {
            new Vector3(-0.5f, -0.5f, -0.5f),
            new Vector3(-0.5f, -0.5f,  0.5f),
            new Vector3(-0.5f,  0.5f,  0.5f),
            new Vector3(-0.5f,  0.5f, -0.5f)
        },
        {
            new Vector3( 0.5f, -0.5f, -0.5f),
            new Vector3( 0.5f, -0.5f,  0.5f),
            new Vector3( 0.5f,  0.5f,  0.5f),
            new Vector3( 0.5f,  0.5f, -0.5f)
        }
    };

    private static readonly Vector3[][] billboardVertices = new Vector3[][]
    {
        new Vector3[]
        {
            new Vector3(-0.5f, -0.5f, -0.5f),
            new Vector3( 0.5f, -0.5f,  0.5f),
            new Vector3( 0.5f,  0.5f,  0.5f),
            new Vector3(-0.5f,  0.5f, -0.5f)
        },
        new Vector3[]
        {
            new Vector3(-0.5f, -0.5f,  0.5f),
            new Vector3( 0.5f, -0.5f, -0.5f),
            new Vector3( 0.5f,  0.5f, -0.5f),
            new Vector3(-0.5f,  0.5f,  0.5f)
        }
    };

    void Start()
    {
        InitializeBlockConfigs();
        CreateTextureAtlas();
        StartCoroutine(FetchBlocks());
    }

    private void InitializeBlockConfigs()
    {
        blockConfigMap.Clear();
        foreach (var config in blockConfigs)
        {
            config.SetDefaults(config.blockName);
            blockConfigMap[config.blockName] = config;
        }
    }

    private void CreateTextureAtlas()
    {
        string[] textureFiles = Directory.GetFiles(texturePath, "*.png");
        List<Texture2D> texturesList = new List<Texture2D>();
        Dictionary<string, Texture2D> maskTextures = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase);

        string maskPath = Path.Combine(texturePath, "Masks");
        if (Directory.Exists(maskPath))
        {
            foreach (string maskFile in Directory.GetFiles(maskPath, "*.png"))
            {
                string maskName = Path.GetFileNameWithoutExtension(maskFile);
                byte[] maskData = File.ReadAllBytes(maskFile);
                Texture2D maskTexture = new Texture2D(2, 2, TextureFormat.RGBA32, false);
                maskTexture.LoadImage(maskData);
                maskTextures[maskName] = maskTexture;
                Debug.Log($"Loaded mask texture: {maskName}");
            }
        }

        // Process default texture
        string defaultTexturePath = Path.Combine(texturePath, defaultTextureName + ".png");
        if (File.Exists(defaultTexturePath))
        {
            byte[] defaultTextureData = File.ReadAllBytes(defaultTexturePath);
            Texture2D defaultTexture = new Texture2D(2, 2, TextureFormat.RGBA32, false);
            defaultTexture.filterMode = FilterMode.Point;
            defaultTexture.LoadImage(defaultTextureData);
            texturesList.Add(defaultTexture);
            blockNameToTextureIndex[defaultTextureName] = 0;
            defaultTextureIndex = 0;
            Debug.Log($"Loaded default texture: {defaultTextureName} at index {defaultTextureIndex}");
        }
        else
        {
            Debug.LogError($"Default texture {defaultTexturePath} not found! Creating fallback texture.");
            Texture2D fallbackTexture = CreateFallbackTexture();
            texturesList.Add(fallbackTexture);
            defaultTextureIndex = 0;
        }

        // Process all other textures
        foreach (string textureFile in textureFiles)
        {
            string fileName = Path.GetFileNameWithoutExtension(textureFile);
            if (fileName.Equals(defaultTextureName, StringComparison.OrdinalIgnoreCase))
                continue; // Already processed default texture
            byte[] fileData = File.ReadAllBytes(textureFile);
            Texture2D texture = new Texture2D(2, 2, TextureFormat.RGBA32, false);
            texture.filterMode = FilterMode.Point;
            texture.LoadImage(fileData);

            foreach (var config in blockTintConfigs)
            {
                if (fileName.Equals(config.blockName, StringComparison.OrdinalIgnoreCase))
                {
                    Color[] pixels = texture.GetPixels();
                    Color[] maskPixels = null;
                    if (!string.IsNullOrEmpty(config.maskName) &&
                        maskTextures.TryGetValue(config.maskName, out Texture2D maskTexture))
                    {
                        if (maskTexture.width == texture.width && maskTexture.height == texture.height)
                        {
                            maskPixels = maskTexture.GetPixels();
                        }
                        else
                        {
                            Debug.LogWarning($"Mask texture {config.maskName} dimensions don't match block texture {fileName}");
                        }
                    }

                    for (int i = 0; i < pixels.Length; i++)
                    {
                        if (maskPixels != null)
                        {
                            float maskIntensity = maskPixels[i].grayscale;
                            pixels[i].r = Mathf.Lerp(pixels[i].r, pixels[i].r * config.tintColor.r, maskIntensity);
                            pixels[i].g = Mathf.Lerp(pixels[i].g, pixels[i].g * config.tintColor.g, maskIntensity);
                            pixels[i].b = Mathf.Lerp(pixels[i].b, pixels[i].b * config.tintColor.b, maskIntensity);
                        }
                        else
                        {
                            pixels[i].r *= config.tintColor.r;
                            pixels[i].g *= config.tintColor.g;
                            pixels[i].b *= config.tintColor.b;
                        }
                        pixels[i].r = Mathf.Clamp01(pixels[i].r);
                        pixels[i].g = Mathf.Clamp01(pixels[i].g);
                        pixels[i].b = Mathf.Clamp01(pixels[i].b);
                    }

                    texture.SetPixels(pixels);
                    texture.Apply();
                    break;
                }
            }

            int textureIndex = texturesList.Count;
            texturesList.Add(texture);
            blockNameToTextureIndex[fileName] = textureIndex;
            Debug.Log($"Mapped texture {fileName} to index {textureIndex}");
        }

        textureAtlas = new Texture2D(2048, 2048, TextureFormat.RGBA32, false);
        textureAtlas.filterMode = FilterMode.Point;
        atlasRects = textureAtlas.PackTextures(texturesList.ToArray(), 0, 2048);
        textureAtlas.Apply(false, true);
        blockMaterial.mainTexture = textureAtlas;
        blockMaterial.mainTexture.filterMode = FilterMode.Point;
        foreach (var maskTexture in maskTextures.Values)
        {
            UnityEngine.Object.Destroy(maskTexture);
        }
    }

    private Texture2D CreateFallbackTexture()
    {
        Texture2D fallback = new Texture2D(16, 16, TextureFormat.RGBA32, false);
        fallback.filterMode = FilterMode.Point;

        Color pink = new Color(1f, 0f, 1f, 1f);
        Color black = Color.black;

        for (int y = 0; y < 16; y++)
        {
            for (int x = 0; x < 16; x++)
            {
                Color color = ((x + y) % 2 == 0) ? pink : black;
                fallback.SetPixel(x, y, color);
            }
        }

        fallback.Apply();
        return fallback;
    }

    private int GetTextureIndexForTextureName(string textureName)
    {
        if (blockNameToTextureIndex.TryGetValue(textureName, out int textureIndex))
        {
            return textureIndex;
        }

        Debug.LogWarning($"No texture found for name: {textureName}. Using default texture.");
        return defaultTextureIndex;
    }

    private Vector2[] GetUVsForTexture(string textureName)
    {
        int textureIndex = GetTextureIndexForTextureName(textureName);

        if (textureIndex >= atlasRects.Length || textureIndex < 0)
        {
            Debug.LogError($"Invalid texture index: {textureIndex} for texture: {textureName}");
            textureIndex = defaultTextureIndex;
        }

        if (!uvCache.TryGetValue(textureIndex, out Vector2[] uvs))
        {
            Rect uvRect = atlasRects[textureIndex];
            uvs = new Vector2[]
            {
                new Vector2(uvRect.x, uvRect.y),
                new Vector2(uvRect.x + uvRect.width, uvRect.y),
                new Vector2(uvRect.x + uvRect.width, uvRect.y + uvRect.height),
                new Vector2(uvRect.x, uvRect.y + uvRect.height)
            };
            uvCache[textureIndex] = uvs;
        }
        return uvs;
    }

    private IEnumerator FetchBlocks()
    {
        using (UnityWebRequest webRequest = UnityWebRequest.Get(serverUrl))
        {
            yield return webRequest.SendWebRequest();

            if (webRequest.result != UnityWebRequest.Result.Success)
            {
                Debug.LogError("Error fetching block data: " + webRequest.error);
            }
            else
            {
                string jsonResponse = webRequest.downloadHandler.text;
                blockDataList = JsonConvert.DeserializeObject<List<BlockData>>(jsonResponse);

                foreach (var block in blockDataList)
                {
                    AddBlockFaces(block);
                }

                CreateMesh();
            }
        }
    }

    private void AddBlockFaces(BlockData block)
    {
        Vector3 blockPosition = new Vector3(block.x, block.y, block.z);

        if (!blockConfigMap.TryGetValue(block.block_name, out BlockTextureConfig config))
        {
            config = new BlockTextureConfig { blockName = block.block_name };
            config.SetDefaults(block.block_name);
        }

        Dictionary<string, Mesh> customMeshes = new Dictionary<string, Mesh>
        {
            { "_slab", slabMesh },
            { "_fence", fenceMesh },
            { "_stairs", stairMesh }
        };
        string replace = null;
        string add = null;

        Mesh selectedMesh = config.customMesh;

        if (selectedMesh == null)
        {
            foreach (var kvp in customMeshes)
            {
                if (config.blockName.Contains(kvp.Key))
                {
                    selectedMesh = kvp.Value;
                    replace = kvp.Key;

                    if (woodTypes.Any(config.front.Contains))
                    {
                        add = "_planks";
                    }
                    break;
                }
            }
        }

        if (selectedMesh != null)
        {
            AddCustomMeshToChunk(blockPosition, selectedMesh, config.front, replace, add);
        }
        else if (config.isBillboard)
        {
            AddBillboardFacesToChunk(blockPosition, config);
        }
        else
        {
            AddDefaultCubeFacesToChunk(blockPosition, config);
        }
    }

    private void AddCustomMeshToChunk(
        Vector3 blockPosition,
        Mesh customMesh,
        string textureName,
        string replace,
        string add
    )
    {
        if (replace != null)
        {
            textureName = textureName.Replace(replace, "") + add;
        }

        MeshChunk chunk = GetCurrentChunk();
        Vector2[] textureUVs = GetUVsForTexture(textureName);
        int vertCount = chunk.vertices.Count;

        Vector3[] vertices = customMesh.vertices;
        int[] triangles = customMesh.triangles;
        int vertexCount = vertices.Length;

        for (int i = 0; i < vertexCount; i++)
        {
            chunk.vertices.Add(vertices[i] + blockPosition);
            chunk.uvs.Add(textureUVs[i % 4]);
        }

        for (int i = 0; i < triangles.Length; i++)
        {
            chunk.triangles.Add(vertCount + triangles[i]);
        }
    }

    private MeshChunk GetCurrentChunk()
    {
        if (meshChunks.Count == 0 || meshChunks[meshChunks.Count - 1].isFull)
        {
            meshChunks.Add(new MeshChunk());
        }
        return meshChunks[meshChunks.Count - 1];
    }

    private void AddBillboardFacesToChunk(Vector3 blockPosition, BlockTextureConfig config)
    {
        Vector2[] textureUVs = GetUVsForTexture(config.front);

        for (int planeIndex = 0; planeIndex < 2; planeIndex++)
        {
            MeshChunk chunk = GetCurrentChunk();
            int vertCount = chunk.vertices.Count;

            for (int i = 0; i < 4; i++)
            {
                chunk.vertices.Add(billboardVertices[planeIndex][i] + blockPosition);
                chunk.uvs.Add(textureUVs[i]);
            }

            chunk.triangles.Add(vertCount + 0);
            chunk.triangles.Add(vertCount + 2);
            chunk.triangles.Add(vertCount + 1);
            chunk.triangles.Add(vertCount + 0);
            chunk.triangles.Add(vertCount + 3);
            chunk.triangles.Add(vertCount + 2);

            chunk = GetCurrentChunk();
            vertCount = chunk.vertices.Count;

            for (int i = 0; i < 4; i++)
            {
                chunk.vertices.Add(billboardVertices[planeIndex][i] + blockPosition);
                chunk.uvs.Add(textureUVs[i]);
            }

            chunk.triangles.Add(vertCount + 0);
            chunk.triangles.Add(vertCount + 1);
            chunk.triangles.Add(vertCount + 2);
            chunk.triangles.Add(vertCount + 0);
            chunk.triangles.Add(vertCount + 2);
            chunk.triangles.Add(vertCount + 3);
        }
    }

    private void AddDefaultCubeFacesToChunk(Vector3 blockPosition, BlockTextureConfig config)
    {
        string[] faceTextures = new string[]
        {
            config.front,
            config.back,
            config.top,
            config.bottom,
            config.left,
            config.right
        };

        for (int faceIndex = 0; faceIndex < 6; faceIndex++)
        {
            MeshChunk chunk = GetCurrentChunk();
            Vector2[] textureUVs = GetUVsForTexture(faceTextures[faceIndex]);
            int vertCount = chunk.vertices.Count;

            for (int i = 0; i < 4; i++)
            {
                chunk.vertices.Add(faceVertices[faceIndex, i] + blockPosition);
                chunk.uvs.Add(textureUVs[i]);
            }

            chunk.triangles.Add(vertCount + 0);
            chunk.triangles.Add(vertCount + 2);
            chunk.triangles.Add(vertCount + 1);
            chunk.triangles.Add(vertCount + 0);
            chunk.triangles.Add(vertCount + 3);
            chunk.triangles.Add(vertCount + 2);
        }
    }

    private void CreateMesh()
    {
        GameObject chunksParent = new GameObject("ChunkedMeshes");
        chunksParent.transform.position = Vector3.zero;

        for (int i = 0; i < meshChunks.Count; i++)
        {
            MeshChunk chunk = meshChunks[i];
            GameObject chunkObject = new GameObject($"MeshChunk_{i}");
            chunkObject.transform.parent = chunksParent.transform;
            chunkObject.transform.localPosition = Vector3.zero;

            MeshFilter meshFilter = chunkObject.AddComponent<MeshFilter>();
            MeshRenderer meshRenderer = chunkObject.AddComponent<MeshRenderer>();
            Mesh mesh = new Mesh();
            mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;

            mesh.SetVertices(chunk.vertices);
            mesh.SetTriangles(chunk.triangles, 0);
            mesh.SetUVs(0, chunk.uvs);

            mesh.RecalculateNormals();

            meshFilter.mesh = mesh;
            meshRenderer.material = blockMaterial;

            chunkObject.AddComponent<MeshCollider>().sharedMesh = mesh;
        }

        Debug.Log($"Created {meshChunks.Count} mesh chunks");
    }
}