need help in syncing texture2D

I am a beginner in unity netcode and multiplayer in general.

trying to make a project where a user can upload image files and those files are then placed on a 3d cube as its texture. I handled loading the image using the NativeGallery package , but it is not syncing over the network.

I have attached NetworkObject and NetworkTransform components to the cube and also added it to the NetworkPrefabList

i tried using serverrpc but im pretty sure i messed it up.:sweat_smile:

here is my code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;
using UnityEngine.Networking;

public class FilePicker : NetworkBehaviour
{
    public string finalPath;
    public GameObject ImageCube;
    Material ImageCubeMat;
    Texture2D ImageCubeTexture;
  
    public void PickImage()
    {
        NativeGallery.Permission permission = NativeGallery.GetImageFromGallery((path) =>
        {
            Debug.Log("Image path: " + path);
            if (path != null)
            {
                // Create Texture from selected image
                Texture2D texture = NativeGallery.LoadImageAtPath(path);
                if (texture == null)
                {
                    Debug.Log("Couldn't load texture from " + path);
                    return;
                }
                ImageCubeTexture = texture;
                Material material = ImageCube.GetComponent<Renderer>().material;
                if (!material.shader.isSupported)
                    material.shader = Shader.Find("Legacy Shaders/Diffuse");
                ImageCubeMat = material;

                SetImageServerRpc();

            }
        });

        Debug.Log("Permission result: " + permission);
    }

    [ServerRpc]
    public void SetImageServerRpc()
    {
        ImageCubeMat.mainTexture = ImageCubeTexture;
    }
  


}

Note: the texture is only changing on the user’s side.

please give me suggestions for fixing this and making the texture change sync . :(:frowning:

You can’t (easily) sync a Texture2D object. Instead you should send the image itself. Use File.ReadAllBytes to load it into a byte[ ] and send that as a parameter in the ServerRPC method.

Note that this may fail depending on the size of the byte[ ]. In earlier versions of NGO this limit was about 30k but I’ve heard this has been raised, but I don’t know by how much. If sending fails you may have to send it in chunks via repeated RPC calls, or use an external webserver or service to upload the image to, then just send the URL with the ServerRPC respectively ClientRPC so every client can download that image by itself.

1 Like

Thank you for the suggestion, i tried to implemented it but it unfortunately did not work
and yes it was only taking up images upto 60kb

here is the updated code:

    public void PickImage()
    {
        NativeGallery.Permission permission = NativeGallery.GetImageFromGallery((path) =>
        {
            Debug.Log("Image path: " + path);
            if (path != null)
            {
                // Create Texture from selected image
                Texture2D texture = NativeGallery.LoadImageAtPath(path);
                byte[] textureBytes = File.ReadAllBytes(path);

                if (texture == null)
                {
                    Debug.Log("Couldn't load texture from " + path);
                    return;
                }
                //ImageCubeTexture = texture;
                Material material = ImageCube.GetComponent<Renderer>().material;
                if (!material.shader.isSupported)
                    material.shader = Shader.Find("Legacy Shaders/Diffuse");
                ImageCubeMat = material;

                SetImageServerRpc(textureBytes);

            }
        });

        Debug.Log("Permission result: " + permission);
    }

    [ServerRpc]
    public void SetImageServerRpc(byte[] bytes)
    {
        Texture2D texture = new Texture2D((int)ImageCube.GetComponent<MeshRenderer>().bounds.size.x, (int)ImageCube.GetComponent<MeshRenderer>().bounds.size.y);
        texture.LoadImage(bytes);

        ImageCubeMat.mainTexture = texture;
       

    }

here is the screenshot from my android which i used as host:
9408098--1317296--WhatsApp Image 2023-10-14 at 1.12.52 AM.jpeg
Note the error in my debug console is because i tried to load a 100kb image

and here is the editor:
9408098--1317299--WhatsApp Image 2023-10-14 at 1.12.34 AM.jpeg

please tell me if i did something wrong :face_with_spiral_eyes:

This looks alright. The limit is probably the max size of a single large packet, close to 65k. So not enough for large file transfers.

I once made a chunk-based file transfer tool for NGO. This may still work today with some changes to the current version (it was made with 1.0 or 1.1): UnityNetcodeBiteSizeExamples/Assets/Plugins/CodeSmile/Netcode/FileTransfer at main · CodeSmile-0000011110110111/UnityNetcodeBiteSizeExamples · GitHub

Essentially it initiates a transfer and sends the expected size, then sends a small chunk and waits for the acknowledgment. Repeat until all bytes are transferred.

Btw, once a file is transferred you should save it locally. That way you can avoid retransmitting existing files by first sending the file’s CRC32 hash to the server, and having the server ask every client to check whether they have a file with the same hash.

Outside of chunking the data you could take a look at Custom Messages, they can handle the fragmenting of large messages for you. Here’s an example:

 public class BigDataSceneController : MonoBehaviour
    {
        [SerializeField] int dataSize;
        byte[] data;

        void Start()
        {
            Application.targetFrameRate = 15;

            if (!ParrelSync.ClonesManager.IsClone())
            {
                NetworkManager.Singleton.OnServerStarted += OnServerStarted;
                NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnected;
                NetworkManager.Singleton.StartHost();
            }
            else
            {
                NetworkManager.Singleton.StartClient();
                NetworkManager.Singleton.CustomMessagingManager.OnUnnamedMessage += OnUnnamedMessage;
            }
        }

        private void OnUnnamedMessage(ulong clientId, FastBufferReader reader)
        {
            int dataLength = reader.Length;

            Debug.Log("BigDataSceneController OnUnnamedMessage reader length: " + dataLength);

            data = new byte[dataLength];
            reader.ReadBytesSafe(ref data, dataLength);

            // can be a bit slow
            for (int i = 0; i < data.Length; i++)
            {
                Debug.Log($"Data byte[{i}] is {data[i]}");
            }
        }

        private void OnClientConnected(ulong clientId)
        {
            if(clientId != NetworkManager.ServerClientId)
            {
                var writer = new FastBufferWriter(dataSize, Allocator.Temp);
                var customMessagingManager = NetworkManager.Singleton.CustomMessagingManager;

                using (writer)
                {
                    writer.WriteBytesSafe(data);

                    customMessagingManager.SendUnnamedMessage(clientId, writer, NetworkDelivery.ReliableFragmentedSequenced);
                }
            }
        }

        private void OnServerStarted()
        {
            data = new byte[dataSize];

            for (int i = 0; i < dataSize; i++)
            {
                data[i] = (byte)i;
            }
        }
    }
2 Likes

Thanks! That was the thing that lifted the transfer limit that I couldn‘t remember.

I couldn’t remember either, I had to do a search as I knew it was Fragmented something or other. :smile:

1 Like