Search Unity

How to make connecting clients receive big Texture files before ticking Ready

Discussion in 'Multiplayer' started by Zukas, Apr 28, 2016.

  1. Zukas

    Zukas

    Joined:
    Dec 17, 2013
    Posts:
    40
    I'm trying to send clients big texture files (200 Kb, definitely not for one packet) on when they connect to server. How could I possibly achieve this? I've tried using NetworkWriter, but couldn't get it working with limited amount of documentation
     
  2. virror

    virror

    Joined:
    Feb 3, 2012
    Posts:
    2,963
    Here you go!

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Networking;
    3. using System.Collections;
    4. using System.IO;
    5. using Ionic.Zip;
    6. using UnityEngine.UI;
    7.  
    8. public class FileTranfer : NetworkBehaviour
    9. {
    10.     //Server only stuff
    11.     public int packageSize = 1024;
    12.  
    13.     //Client only stuff
    14.     private int recievedSize = 0;
    15.     private int fileSize = 0;
    16.     private string filePath = "";
    17.     private byte[] clientData;
    18.     private string chunkCallback;
    19.     private string finishedCallback;
    20.  
    21.     //Set up messages
    22.     public class MyMsgType
    23.     {
    24.         public static short FileInformation = MsgType.Highest + 1;
    25.         public static short FileChunk = MsgType.Highest + 2;
    26.     };
    27.  
    28.     public class FileInformation : MessageBase
    29.     {
    30.         public string fileName;
    31.         public int fileSize;
    32.     }
    33.  
    34.     public class FileChunk : MessageBase
    35.     {
    36.         public byte[] fileData;
    37.     }
    38.  
    39.     //Functions
    40.     public void ClientRequestMap(string name, string myChunkCallback, string myFinishedCallback)
    41.     {
    42.         finishedCallback = myFinishedCallback;
    43.         chunkCallback = myChunkCallback;
    44.  
    45.         NetworkClient netClient = NetworkLobbyManager.singleton.client;
    46.         int clientId = netClient.connection.connectionId;
    47.  
    48.         netClient.RegisterHandler (MyMsgType.FileInformation, OnFileInformation);
    49.         netClient.RegisterHandler (MyMsgType.FileChunk, OnFileChunk);
    50.  
    51.         CmdRequestMap (name, clientId);
    52.     }
    53.  
    54.     [Server]
    55.     private void SendMap(string fileName, int clientId)
    56.     {
    57.         string shortName = fileName;
    58.         fileName = Application.dataPath + "/Maps/" + fileName;
    59.         string zipName = fileName + ".zip";
    60.         using (ZipFile zip = new ZipFile())
    61.         {
    62.             zip.AddDirectory(fileName);
    63.             zip.Save(zipName);
    64.         }
    65.  
    66.         byte[] data = File.ReadAllBytes (zipName);
    67.         File.Delete (zipName);
    68.  
    69.         Debug.Log ("Server: Sending File: " + fileName + " with size: " + data.Length);
    70.  
    71.         FileInformation msg = new FileInformation();
    72.         msg.fileName = shortName;
    73.         msg.fileSize = data.Length;
    74.         NetworkServer.SendToClient(clientId, MyMsgType.FileInformation, msg);
    75.         StartCoroutine (SendFilesRoutine(data, clientId));
    76.     }
    77.  
    78.     [Server]
    79.     IEnumerator SendFilesRoutine(byte[] data, int clientId)
    80.     {
    81.         int bufferSize = packageSize;
    82.         int dataLeft = data.Length;
    83.         while (0 < dataLeft)
    84.         {
    85.             //determine the remaining amount of bytes, still need to be sent.
    86.             int remaining = dataLeft - bufferSize;
    87.             if (remaining < 0)
    88.                 bufferSize = dataLeft;
    89.  
    90.             byte[] buffer = new byte[bufferSize];
    91.             System.Array.Copy(data, data.Length - dataLeft, buffer, 0, bufferSize);
    92.  
    93.             //send the chunk
    94.             FileChunk msg = new FileChunk();
    95.             msg.fileData = buffer;
    96.             NetworkServer.SendToClient(clientId, MyMsgType.FileChunk, msg);
    97.             dataLeft -= bufferSize;
    98.  
    99.             yield return null;
    100.         }
    101.     }
    102.  
    103.     [Command]
    104.     public void CmdRequestMap(string fileName, int clientId)
    105.     {
    106.         SendMap(fileName, clientId);
    107.     }
    108.  
    109.     public void OnFileInformation(NetworkMessage netMsg)
    110.     {
    111.         FileInformation msg = netMsg.ReadMessage<FileInformation>();
    112.         fileSize = msg.fileSize;
    113.         filePath = Application.dataPath + "/Maps/" + msg.fileName;
    114.         clientData = new byte[msg.fileSize];
    115.     }
    116.  
    117.     public void OnFileChunk(NetworkMessage netMsg)
    118.     {
    119.         FileChunk msg = netMsg.ReadMessage<FileChunk>();
    120.         byte[] data = msg.fileData;
    121.  
    122.         System.Array.Copy (data, 0, clientData, recievedSize, data.Length);
    123.         recievedSize += data.Length;
    124.         gameObject.SendMessage (chunkCallback, recievedSize / fileSize);
    125.  
    126.         if (fileSize == recievedSize)
    127.         {
    128.             string zipFile = filePath + ".zip";
    129.             File.WriteAllBytes (zipFile, clientData);
    130.  
    131.             using (ZipFile zip = ZipFile.Read(zipFile))
    132.             {
    133.                 zip.ExtractAll(filePath);
    134.             }
    135.  
    136.             File.Delete (zipFile);
    137.             gameObject.SendMessage (finishedCallback);
    138.         }
    139.     }
    140. }
    141.  
    142.  
    In this case, its the client that requests a file from the server, but can be changed to just client sending a file with ease.
    Client requests a file using the "ClientRequestMap" function, and the strings are callback functions for updating progress bar or something and doing whatever when the file transfer is complete. This script also compresses whatever file thats being sent, but that can be removed as well, it requires an external dll.

    Use:
    Code (csharp):
    1. //Request file
    2. fileTranfer.ClientRequestMap(StaticSettings.project, "OnMapChunk", "OnMapFinished");
    3.  
    4. //For every chunk recieved, update progressbar
    5. public void OnMapChunk(float progress)
    6. {
    7.     progressImg.fillAmount = progress;
    8. }
    9.  
    10. //When file is done, do stuff
    11. public void OnMapFinished()
    12. {
    13.     toggle.interactable = true;
    14.     string mapPath = Application.dataPath + "/Maps/" + StaticSettings.project;
    15.     ReadMap(mapPath);
    16.     Debug.Log("Client:File complete!");
    17. }
     
    Last edited: Apr 29, 2016
    Zukas likes this.
  3. Zukas

    Zukas

    Joined:
    Dec 17, 2013
    Posts:
    40
    Seems way simplier than I have thought! I'll try it this evening after I'll get home and let you know how it went! Thanks!

    Edit: Okay, actually not that easy with my situation. Thing is, I want to send couple of byte arrays and I don't know how to put it all together in clean loop without messing it all up. Is there any way to have a coroutine that waits for a certain message before going on to another cycle of the loop?
     
    Last edited: Apr 29, 2016
  4. virror

    virror

    Joined:
    Feb 3, 2012
    Posts:
    2,963
    If you use reliable channel the packages should always arrive in the same order as you sent them, so you should not have to wait for confirmation, thats handled by the lower layers already.
     
    Zukas likes this.
  5. Zukas

    Zukas

    Joined:
    Dec 17, 2013
    Posts:
    40
    Yeah, you're right. Managed to make it work in one Coroutine using additional loop, thanks!:D