Search Unity

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.