Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

C# Photon Serialize Object Over network

Discussion in 'Scripting' started by Kamil-Says, Dec 25, 2015.

  1. Kamil-Says

    Kamil-Says

    Joined:
    Jun 30, 2014
    Posts:
    154
    Hey, let's say i have a damage info class which has the damage info values etc.
    Now i want to serialize the class object to byte[] via Memory stream and BinaryFormatter.
    Ok, now i'm going to send the byte[] data over network to any player in the room.
    My question is, would the other client which got the byte[] data be able to deserialize also convert the byte[] data back to object class?
     
  2. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    The best way to do this is to take advantage of Phton's Protocol and PhotonPeer here is a read about how you can serialize your custom classes.

    Below is an example of how to do this. Hope this helps.

    Code (CSharp):
    1. // Name spaces required.
    2. using System.IO;
    3. using ExitGames.Client.Photon;
    4.  
    5. public static class CustomStream : System.Object {
    6.     /// <summary>
    7.     /// CALL THIS IN START OR AWAKE IN YOUR NETWORK MANAGER CLASS ONCE
    8.     /// BEFORE YOU TRY TO SEND YOUR OBJECT DATA. YOU WILL GET AN
    9.     /// EXCEPTION!
    10.     /// </summary>
    11.     public static void Register() {
    12.         // Register with photon your custom data type to be serialized and deserialized.
    13.         PhotonPeer.RegisterType(typeof(DamageInfo), (byte)'Z', CustomStream.SerializeDamageInfo, CustomStream.DeserializeDamageInfo);
    14.     }
    15.  
    16.     /// <summary>
    17.     /// byte memory stream for damage info. The size of the array must be equal to
    18.     /// the size of the sum of all the data type byte sizes.
    19.     ///
    20.     ///
    21.     /// </summary>
    22.     public static readonly byte[] memDamageInfo = new byte[FLOAT_FLOAT + INTEGER_INT + FLOAT_DOUBLE];
    23.  
    24.     static short SerializeDamageInfo(MemoryStream outStream, object customObject) {
    25.         DamageInfo dInfo = (DamageInfo)customObject; // Cast to object type.
    26.  
    27.         // Lock the memory byte stream for damage info
    28.         // this prevents the byte[] from being changed while we are
    29.         // changing it.
    30.         lock(CustomStream.memDamageInfo) {
    31.             int index = 0; // byte stream starting index.
    32.  
    33.             byte[] bytes = CustomStream.memDamageInfo;
    34.  
    35.             // Serialize each value in damage info
    36.             Protocol.Serialize(dInfo.DamageDealt, bytes, ref index);
    37.             Protocol.Serialize(dInfo.PlayerID, bytes, ref index);
    38.             Protocol.Serialize(dInfo.TimeStamp, bytes, ref index);
    39.  
    40.             outStream.Write(bytes, 0, CustomStream.memDamageInfo.Length);
    41.         }
    42.  
    43.         return (short)CustomStream.memDamageInfo.Length;
    44.     }
    45.  
    46.     static object DeserializeDamageInfo(MemoryStream inStream, short length) {
    47.         // Temperary holders for each member in DamageInfo
    48.         float damageDealt = 0.0F;
    49.         int playerID = 0;
    50.         double timeStamp = 0.0;
    51.  
    52.         lock(CustomStream.memDamageInfo) {
    53.             int index = 0;
    54.  
    55.             // Deserailize in the same order the object was serialized!
    56.             Protocol.Deserialize(out damageDealt, CustomStream.memDamageInfo, ref index);
    57.             Protocol.Deserialize(out playerID, CustomStream.memDamageInfo, ref index);
    58.             Protocol.Deserialize(out timeStamp, CustomStream.memDamageInfo, ref index);
    59.         }
    60.  
    61.         // Return a new instance of DamageInfo with all the data.
    62.         return new DamageInfo(damageDealt, playerID, timeStamp);
    63.     }
    64.  
    65.     const int INTEGER_BYTE = 1; // Unsigned (0 to 255)
    66.     const int INTEGER_SBYTE = 2; // Signed (-128 to 127)
    67.     const int INTEGER_SHORT = 2; // Signed (-32,768 to 32,767)
    68.     const int INTEGER_USHORT = 2; // Unsigned (0 to 65,535)
    69.     const int INTEGER_INT = 4; // Singed (-2,147,483,648 to 2,147,483,647)
    70.     const int INTEGER_UINT = 4; // Unsigned (0 to 4,294,967,295)
    71.     const int INTEGER_LONG = 8; //  Signed (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807)
    72.     const int INTEGER_ULONG = 8; // Unsinged (0 to 18,446,744,073,709,551,615)
    73.     const int FLOAT_FLOAT = 4; // ±1.5e−45 to ±3.4e38  (Precision:7 digits)
    74.     const int FLOAT_DOUBLE = 8; // ±5.0e−324 to ±1.7e308 (Precision:15-16 digits)
    75.     const int FLOAT_DECIMAL = 16; // (-7.9 x 1028 to 7.9 x 1028) / (100 to 28) (Precision:28-29 digits)
    76.     const int CHARACTER_CHAR = 2;
    77.     const int OTHER_DATETIME = 8;
    78.     const int OTHER_BOOL = 1;
    79. }
    80.  
    81. /// <summary>
    82. /// Example layout, yours will be different.
    83. /// </summary>
    84. [System.Serializable]
    85. public struct DamageInfo : System.Object {
    86.     [SerializeField] protected float m_damageDealt;
    87.     [SerializeField] protected int m_playerID; // Photon ID
    88.     [SerializeField] protected double m_timeStamp;
    89.  
    90.     public DamageInfo(float damageDealt, int playerID, double timeStamp) {
    91.         this.m_damageDealt = damageDealt;
    92.         this.m_playerID = playerID;
    93.         this.m_timeStamp = timeStamp;
    94.     }
    95.  
    96.     public float DamageDealt {
    97.         get {
    98.             return this.m_playerID;
    99.         }
    100.     }
    101.  
    102.     public int PlayerID {
    103.         get {
    104.             return this.m_playerID;
    105.         }
    106.     }
    107.  
    108.     public double TimeStamp {
    109.         get {
    110.             return this.m_timeStamp;
    111.         }
    112.     }
    113. }
     
  3. fum1h1ro

    fum1h1ro

    Joined:
    Apr 22, 2014
    Posts:
    6
    Hi Polymorphik.

    Your reply is so helpful for me.
    But I couldn't understand what (byte)'Z' is. I know, it is a RegisterType's argument.
    Why did you choose Z?

    https://doc-api.photonengine.com/en...n_peer.html#a9f69cbbb71d6d3f50fe232e51fb44025

    I could find this reference. But there were no describing about it.

    I think, code should be unique.
    And PhotonSDK use some other codes.

    Do you know about the code?

    Regards.
     
  4. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    So a char can be represented by an int. You use a letter and then cast it to a byte to get a number. 0-255 or something like that. Photon's doc do not go into this...so just try to not reuse any letters if possible.
     
  5. fum1h1ro

    fum1h1ro

    Joined:
    Apr 22, 2014
    Posts:
    6
    Really thanks.
    I'll try it.
     
  6. Kamil-Says

    Kamil-Says

    Joined:
    Jun 30, 2014
    Posts:
    154
    Why do I have to register it? Can't I just do it like that:
    Code (CSharp):
    1. private byte[] ObjectToByteArray(Object obj)
    2. {
    3.     if(obj == null)
    4.         return null;
    5.  
    6.     BinaryFormatter bf = new BinaryFormatter();
    7.     MemoryStream ms = new MemoryStream();
    8.     bf.Serialize(ms, obj);
    9.  
    10.     return ms.ToArray();
    11. }
    12.  
    13. // Convert a byte array to an Object
    14. private Object ByteArrayToObject(byte[] arrBytes)
    15. {
    16.     MemoryStream memStream = new MemoryStream();
    17.     BinaryFormatter binForm = new BinaryFormatter();
    18.     memStream.Write(arrBytes, 0, arrBytes.Length);
    19.     memStream.Seek(0, SeekOrigin.Begin);
    20.     Object obj = (Object) binForm.Deserialize(memStream);
    21.  
    22.     return obj;
    23. }
    And send the byte data via RPC to other players? Will that work becuase i was little confused with the memory stuff & sending over network.
     
  7. Polymorphik

    Polymorphik

    Joined:
    Jul 25, 2014
    Posts:
    599
    Yes you can but its much easier if you can pass a custom object as a parameter much like you would a primitive type or a Vector3. If you do it your way, you would always need to write code to serialize and deserializeat whenever you send that object.

    bar would be registered
    Photon.RPC("Foo", bar, PhotonTargets.AllBuffered);

    [PunRPC]
    void Foo(Bar bar) {
    // Do stuff
    }

    byte[] data = Serialze(bar);
    Photon.RPC("Foo", data, PhotonTargets.AllBuffered);

    [PunRPC]
    void Foo(byte[] data) {
    Bar bar = Deserialize(data);
    // Do stuff
    }

    see the difference? So yeah you can but its just more elegant too hook into Photon.
     
    julienbil likes this.