Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Resolved need help in syncing texture2D

Discussion in 'Multiplayer' started by Lev1athn, Oct 13, 2023.

  1. Lev1athn

    Lev1athn

    Joined:
    Jan 26, 2023
    Posts:
    2
    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.:oops:

    here is my code:


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Netcode;
    5. using UnityEngine.Networking;
    6.  
    7. public class FilePicker : NetworkBehaviour
    8. {
    9.     public string finalPath;
    10.     public GameObject ImageCube;
    11.     Material ImageCubeMat;
    12.     Texture2D ImageCubeTexture;
    13.  
    14.     public void PickImage()
    15.     {
    16.         NativeGallery.Permission permission = NativeGallery.GetImageFromGallery((path) =>
    17.         {
    18.             Debug.Log("Image path: " + path);
    19.             if (path != null)
    20.             {
    21.                 // Create Texture from selected image
    22.                 Texture2D texture = NativeGallery.LoadImageAtPath(path);
    23.                 if (texture == null)
    24.                 {
    25.                     Debug.Log("Couldn't load texture from " + path);
    26.                     return;
    27.                 }
    28.                 ImageCubeTexture = texture;
    29.                 Material material = ImageCube.GetComponent<Renderer>().material;
    30.                 if (!material.shader.isSupported)
    31.                     material.shader = Shader.Find("Legacy Shaders/Diffuse");
    32.                 ImageCubeMat = material;
    33.  
    34.                 SetImageServerRpc();
    35.  
    36.             }
    37.         });
    38.  
    39.         Debug.Log("Permission result: " + permission);
    40.     }
    41.  
    42.     [ServerRpc]
    43.     public void SetImageServerRpc()
    44.     {
    45.         ImageCubeMat.mainTexture = ImageCubeTexture;
    46.     }
    47.  
    48.  
    49.  
    50. }
    51.  
    Note: the texture is only changing on the user's side.

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

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,014
    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.
     
    Lev1athn likes this.
  3. Lev1athn

    Lev1athn

    Joined:
    Jan 26, 2023
    Posts:
    2
    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:

    Code (CSharp):
    1.     public void PickImage()
    2.     {
    3.         NativeGallery.Permission permission = NativeGallery.GetImageFromGallery((path) =>
    4.         {
    5.             Debug.Log("Image path: " + path);
    6.             if (path != null)
    7.             {
    8.                 // Create Texture from selected image
    9.                 Texture2D texture = NativeGallery.LoadImageAtPath(path);
    10.                 byte[] textureBytes = File.ReadAllBytes(path);
    11.  
    12.                 if (texture == null)
    13.                 {
    14.                     Debug.Log("Couldn't load texture from " + path);
    15.                     return;
    16.                 }
    17.                 //ImageCubeTexture = texture;
    18.                 Material material = ImageCube.GetComponent<Renderer>().material;
    19.                 if (!material.shader.isSupported)
    20.                     material.shader = Shader.Find("Legacy Shaders/Diffuse");
    21.                 ImageCubeMat = material;
    22.  
    23.                 SetImageServerRpc(textureBytes);
    24.  
    25.             }
    26.         });
    27.  
    28.         Debug.Log("Permission result: " + permission);
    29.     }
    30.  
    31.     [ServerRpc]
    32.     public void SetImageServerRpc(byte[] bytes)
    33.     {
    34.         Texture2D texture = new Texture2D((int)ImageCube.GetComponent<MeshRenderer>().bounds.size.x, (int)ImageCube.GetComponent<MeshRenderer>().bounds.size.y);
    35.         texture.LoadImage(bytes);
    36.  
    37.         ImageCubeMat.mainTexture = texture;
    38.        
    39.  
    40.     }
    here is the screenshot from my android which i used as host:
    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:
    WhatsApp Image 2023-10-14 at 1.12.34 AM.jpeg


    please tell me if i did something wrong :confused:
     
  4. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,014
    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): https://github.com/CodeSmile-000001...Assets/Plugins/CodeSmile/Netcode/FileTransfer

    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.
     
  5. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    639
    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:

    Code (CSharp):
    1.  public class BigDataSceneController : MonoBehaviour
    2.     {
    3.         [SerializeField] int dataSize;
    4.         byte[] data;
    5.  
    6.         void Start()
    7.         {
    8.             Application.targetFrameRate = 15;
    9.  
    10.             if (!ParrelSync.ClonesManager.IsClone())
    11.             {
    12.                 NetworkManager.Singleton.OnServerStarted += OnServerStarted;
    13.                 NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnected;
    14.                 NetworkManager.Singleton.StartHost();
    15.             }
    16.             else
    17.             {
    18.                 NetworkManager.Singleton.StartClient();
    19.                 NetworkManager.Singleton.CustomMessagingManager.OnUnnamedMessage += OnUnnamedMessage;
    20.             }
    21.         }
    22.  
    23.         private void OnUnnamedMessage(ulong clientId, FastBufferReader reader)
    24.         {
    25.             int dataLength = reader.Length;
    26.  
    27.             Debug.Log("BigDataSceneController OnUnnamedMessage reader length: " + dataLength);
    28.  
    29.             data = new byte[dataLength];
    30.             reader.ReadBytesSafe(ref data, dataLength);
    31.  
    32.             // can be a bit slow
    33.             for (int i = 0; i < data.Length; i++)
    34.             {
    35.                 Debug.Log($"Data byte[{i}] is {data[i]}");
    36.             }
    37.         }
    38.  
    39.         private void OnClientConnected(ulong clientId)
    40.         {
    41.             if(clientId != NetworkManager.ServerClientId)
    42.             {
    43.                 var writer = new FastBufferWriter(dataSize, Allocator.Temp);
    44.                 var customMessagingManager = NetworkManager.Singleton.CustomMessagingManager;
    45.  
    46.                 using (writer)
    47.                 {
    48.                     writer.WriteBytesSafe(data);
    49.  
    50.                     customMessagingManager.SendUnnamedMessage(clientId, writer, NetworkDelivery.ReliableFragmentedSequenced);
    51.                 }
    52.             }
    53.         }
    54.  
    55.         private void OnServerStarted()
    56.         {
    57.             data = new byte[dataSize];
    58.  
    59.             for (int i = 0; i < dataSize; i++)
    60.             {
    61.                 data[i] = (byte)i;
    62.             }
    63.         }
    64.     }
     
    Lev1athn and CodeSmile like this.
  6. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,014
    Thanks! That was the thing that lifted the transfer limit that I couldn‘t remember.
     
  7. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    639
    I couldn't remember either, I had to do a search as I knew it was Fragmented something or other. :D
     
    CodeSmile likes this.