Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Unity Multiplayer Question about serialization

Discussion in 'Connected Games' started by karmaowner13, Aug 8, 2019.

  1. karmaowner13

    karmaowner13

    Joined:
    Aug 8, 2019
    Posts:
    2
    Hello I've decided to open a thread about serialization because I really have problem and I can't progress my game I would like to know why binary serialization take so much space example I send only a object with size of 12 bytes (2 string for a totally of 12 characters) 1 character is 1 byte and according to unity console the size of sended object is 210 bytes the difference is so high ...


    Code (CSharp):
    1. 210bytes sended.
    2. UnityEngine.Debug:Log(Object)
    3. Client:sendToServer(NMSG, Int32) (at Assets/Scripts/Network/Client.cs:153)
    4. Main:connectAccount() (at Assets/Scripts/Client/Main.cs:220)
    5. UnityEngine.EventSystems.EventSystem:Update()
    6.  
    7.  
    sended object :


    Code (CSharp):
    1. [System.Serializable]
    2. public class NMSG_ConnectAccount : NMSG
    3. {
    4.  
    5.     public string username;
    6.  
    7.     public string password;
    8.  
    9.     public NMSG_ConnectAccount()
    10.     {
    11.  
    12.     }
    13.  
    14.     public NMSG_ConnectAccount(string username, string password) : base((byte)PacketHandler.packets.IndexOf(typeof(NMSG_ConnectAccount)))
    15.     {
    16.         this.username = username;
    17.         this.password = password;
    18.     }
    19.  
    20.  
    21.     public override void HandleClient(NMSG msg)
    22.     {
    23.  
    24.     }
    25.  
    26.     public override void HandleServer(NMSG msg, int connectionId)
    27.     {
    28.         NMSG_ConnectAccount cmsg = (NMSG_ConnectAccount)msg;
    29.         Server server = Server.getServer();
    30.         Mysql mysql = server.mysql;
    31.         password = EncryptionUtils.MD5Hash(password);
    32.  
    33.         mysql.openMysqlConnection();
    34.  
    35.         MySqlCommand commandsql = new MySqlCommand("SELECT * FROM users WHERE username = '" + cmsg.username + "'", mysql.con);
    36.         MySqlDataReader MyReader = commandsql.ExecuteReader();
    37.  
    38.         string activated = "";
    39.         string mpassword = "";
    40.         string muser = "";
    41.  
    42.         if (MyReader.Read())
    43.         {
    44.             activated = MyReader["confirmed"].ToString();
    45.             mpassword = MyReader["password"].ToString();
    46.             muser = MyReader["username"].ToString();
    47.         }
    48.         MyReader.Close();
    49.  
    50.         if (mpassword != cmsg.password || cmsg.username != muser)
    51.         {
    52.             server.sendToPlayer(new NMSG_ConnectionMessage(cmsg.username,false,false), server.reliableChannel, connectionId);
    53.             return;
    54.         }
    55.  
    56.         if (server.users.ContainsKey(connectionId))
    57.             server.users[connectionId].setName(muser);
    58.         else
    59.             return;
    60.  
    61.         if (activated == "False")
    62.         {
    63.             server.sendToPlayer(new NMSG_ConnectionMessage(cmsg.username,false,true), server.reliableChannel, connectionId);
    64.             return;
    65.         }
    66.  
    67.         server.users[connectionId].pData = new PlayerData(cmsg.username);
    68.         server.users[connectionId].isAuth = true;
    69.         server.sendToPlayer(new NMSG_ConnectionMessage(cmsg.username,true,false), server.reliableChannel, connectionId);
    70.     }
    71.  
    72. }
    which is child of NMSG class :

    Code (CSharp):
    1. [System.Serializable]
    2. public abstract class NMSG
    3. {
    4.  
    5.     private byte? discriminator = null;
    6.  
    7.     public NMSG()
    8.     {
    9.  
    10.     }
    11.  
    12.     public NMSG(byte discriminator)
    13.     {
    14.         this.discriminator = discriminator;
    15.  
    16.     }
    17.  
    18.     public byte? getPacketId()
    19.     {
    20.         return this.discriminator;
    21.     }
    22.  
    23.     public abstract void HandleServer(NMSG msg, int connectionId);
    24.  
    25.     public abstract void HandleClient(NMSG msg);
    26.  
    27. }
    I serialize like this :

    Code (CSharp):
    1. public void sendToServer(NMSG msg, int channelId)
    2.     {
    3.  
    4.         if (!PacketHandler.packets.Contains(msg.GetType()))
    5.         {
    6.             Debug.Log("packet not registered");
    7.             return;
    8.         }
    9.  
    10.         byte error;
    11.         byte[] buffer = new byte[BYTE_SIZE];
    12.  
    13.         MemoryStream stream = new MemoryStream(buffer);
    14.         BinaryFormatter formatter = new BinaryFormatter();
    15.         try
    16.         {
    17.             formatter.Serialize(stream, msg);
    18.         }
    19.         catch (SerializationException e)
    20.         {
    21.             Debug.Log("Serialization Failed : " + e.Message);
    22.         }
    23.         Debug.Log(stream.Position + "bytes sended.");
    24.         stream.Close();
    25.         int bufferSize = buffer.Length;
    26.  
    27.         NetworkTransport.Send(hostId, connectionId, channelId, buffer, bufferSize, out error);
    28.     }
    and I deserialize :

    Code (CSharp):
    1.  case NetworkEventType.DataEvent:       //3
    2.                 MemoryStream stream = new MemoryStream();
    3.                 stream.Write(recBuffer, 0, recBuffer.Length);
    4.                 stream.Seek(0, SeekOrigin.Begin);
    5.                 BinaryFormatter formatter = new BinaryFormatter();
    6.                 NMSG msg = null;
    7.                 try
    8.                 {
    9.                     msg = (NMSG)formatter.Deserialize(stream);
    10.                 }
    11.                 catch (SerializationException e)
    12.                 {
    13.                     Debug.Log("Deserialization Failed : " + e.Message);
    14.                 }
    15.                 stream.Close();
    16.  
    17.  
    18.                 onData(connectionId, channelId, recHostId, msg);
    19.                 break;
    the message of connection that I send is not a problem but I have another object with which I create player instance with items and contains 6 int property 2 float property only this is a totally of 1100 bytes and the limit of bytes that I can send fixed in NetworkTransport is 1400 .

    Code (CSharp):
    1. [System.Serializable]
    2. public class NMSG_CreatePlayer : NMSG
    3. {
    4.  
    5.     public string playerName;
    6.  
    7.     public float? posX;
    8.     public float? posY;
    9.  
    10.     public int? shipSlot;
    11.     public int? skinSlot;
    12.     public int? particleSlot;
    13.     public int? trailSlot1;
    14.     public int? trailSlot2;
    15.  
    16.     public int? connectionId;
    17.  
    18.  
    19.     public NMSG_CreatePlayer(string playerName) : base((byte)PacketHandler.packets.IndexOf(typeof(NMSG_CreatePlayer)))
    20.     {
    21.         this.playerName = playerName;
    22.     }
    23.  
    24.     public NMSG_CreatePlayer(string playerName, int connectionId, float posX, float posY, int shipSlot, int skinSlot, int particleSlot, int trailSlot1, int trailSlot2) : base((byte)PacketHandler.packets.IndexOf(typeof(NMSG_CreatePlayer)))
    25.     {
    26.         this.playerName = playerName;
    27.         this.posX = posX;
    28.         this.posY = posY;
    29.         this.shipSlot = shipSlot;
    30.         this.skinSlot = skinSlot;
    31.         this.particleSlot = particleSlot;
    32.         this.trailSlot1 = trailSlot1;
    33.         this.trailSlot2 = trailSlot2;
    34.         this.connectionId = connectionId;
    35.     }
    36.  
    37.     //Here packet data send from server
    38.     public override void HandleClient(NMSG msg)
    39.     {
    40.         NMSG_CreatePlayer cmsg = (NMSG_CreatePlayer)msg;
    41.         Player p = new Player(cmsg.playerName,0);
    42.         Ship ship = (Ship)Item.getItemById((int)cmsg.shipSlot);
    43.         Skin skin = (Skin)Item.getItemById((int)cmsg.skinSlot);
    44.         Particle particle = (Particle)Item.getItemById((int) this.particleSlot);
    45.         Trail trail1 = (Trail)Item.getItemById((int) this.trailSlot1);
    46.         Trail trail2 = (Trail)Item.getItemById((int) this.trailSlot2);
    47.  
    48.         GameObject instance = GameObject.Instantiate(trail2 != null ? Main.instance.modelInitializator.playerModel_2Trail : Main.instance.modelInitializator.playerModel_1Trail, GameObject.Find("World").transform);
    49.  
    50.         p.setPosition((float)cmsg.posX, (float)cmsg.posY);
    51.         instance.transform.localPosition = new Vector3(p.getPositionX(),p.getPositionY(), 0);
    52.         instance.name = playerName;
    53.  
    54.         p.setObject(instance);
    55.         p.applyEquipment(ship, skin, particle, trail1, trail2);
    56.         p.setName(playerName);
    57.         p.setConnectionId((int)connectionId);
    58.  
    59.         instance.transform.localEulerAngles = new Vector3(0, 0, 0);
    60.         Client.getClient().players.Add((int)connectionId, p);
    61.  
    62.         if ((int) connectionId == Client.getClient().getConnectionId())
    63.         {
    64.             instance.AddComponent<PlayerController>();
    65.             Main.player = p;
    66.             Client.getClient().gm = GameManager.createGameManager(p);
    67.             p.isInGame = true;
    68.         }
    69.     }
    70.  
    71.     //Here packet data send from client
    72.     public override void HandleServer(NMSG msg, int connectionId)
    73.     {
    74.         NMSG_CreatePlayer cmsg = (NMSG_CreatePlayer)msg;
    75.         Player p = Server.getPlayer(connectionId);
    76.         p.setName(cmsg.playerName);
    77.         float posx = World.generateRandomPosX();
    78.         float posy = World.generateRandomPosY();
    79.         p.setPosition(posx, posy);
    80.  
    81.         GameObject instance = GameObject.Instantiate(Main.instance.modelInitializator.player, GameObject.Find("World").transform);
    82.         instance.transform.localPosition = new Vector3(posx, posy, 0);
    83.  
    84.         p.playerGameObject = instance;
    85.  
    86.         int shipSlot = p.pData.shipSlot == null ? 0 : p.pData.shipSlot.getItemId();
    87.         int skinSlot = p.pData.skinSlot == null ? 0 : p.pData.skinSlot.getItemId();
    88.         int particleSlot = p.pData.particleSlot == null ? 0 : p.pData.particleSlot.getItemId();
    89.         int trailSlot1 = p.pData.trailSlot1 == null ? 0 : p.pData.trailSlot1.getItemId();
    90.         int trailSlot2 = p.pData.trailSlot2 == null ? 0 : p.pData.trailSlot2.getItemId();
    91.         Server.getServer().sendToAll(new NMSG_CreatePlayer(p.getName(), p.getConnectionId(), p.getPositionX(), p.getPositionY(), shipSlot, skinSlot, particleSlot, trailSlot1, trailSlot2),Server.getServer().reliableFragmentedChannel);
    92.         p.isInGame = true;
    93.     }
    94.  
    95.  
    96. }
    Is the problem coming from me?
    the manipulation for serialize that I use is wrong?

    I look forward to your advice, thank you in advance.
     
  2. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    5,954
    BinaryFormatter is not particularly space efficient. It is going to serialize a lot of info describing the class and its members in addition to the data you want to send, so that can be reconstructed on the other side. If you want to maximize space efficiency you probably want to manually serialize your data.

    When I manually serialize strings, I serialize using UTF8 because it requires only 1 byte (edit: oops put 8 bytes instead of 1 byte originally) per character for common characters which appear in the ASCII set (which is most of what I end up sending). Otherwise I'd expect that they serialize using UTF16 since that is what .net uses internally - which means most common strings will use double the space than UTF8. Then I prepend the string with a serialized integer saying how many bytes are in the string, since strings can be variable length of course so I know where one string stops and the next string or other unrelated data starts.

    So 2 strings which are 6 characters each typically end up being 20 bytes total payload for the message, not counting any network API header overhead.
     
    Last edited: Aug 12, 2019 at 11:03 PM
    karmaowner13 likes this.
  3. karmaowner13

    karmaowner13

    Joined:
    Aug 8, 2019
    Posts:
    2
    Oh my god! thanks problem solved thank to you I did not know we could manually serialize I save a lot of space.
    since 1 week I did not advance, I will be able to finalize my first multiplayer game, good evening to you.
     
    Joe-Censored likes this.