Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

LLAPI: how to send data to clients?

Discussion in 'Multiplayer' started by RavenTravelStudios, Jan 30, 2016.

  1. RavenTravelStudios

    RavenTravelStudios

    Joined:
    Oct 15, 2015
    Posts:
    100
    Hi there,
    i'm working on my custom networking system using the LLAPI, and so far i was able to open sockets between a server and a client so i can send messages from client which can be received on server.
    While the server log tells me that he's receiving messages (so i can control a character from client to server), the client log tells me that there is an incoming connection (which of course i presume is from server) but i can't find a way to transmit from server and receive what server sent on clients.

    Here's what i got:

    ----

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.Networking;
    4. using System.IO;
    5. using System.Collections;
    6. using System.Runtime.Serialization.Formatters.Binary;
    7. using System.Globalization;
    8.  
    9. public class PlayerNetworkSync : NetworkBehaviour
    10. {
    11.     HostTopology topology;
    12.     int hostId, connectionId;
    13.     int myReiliableChannelId;
    14.     int myUnreliableChannelId;
    15.  
    16.     //[SyncVar]
    17.     public Vector3 syncPos;
    18.  
    19.     //[SyncVar]
    20.     private Vector3 targetPosition;
    21.  
    22.     //[SerializeField]
    23.     Transform playerTransform;
    24.     //[SerializeField]
    25.     float syncRate = 10;
    26.  
    27.     // Use this for initialization
    28.     void Start()
    29.     {
    30.         this.tag = "DataSynchro";
    31.         playerTransform = transform;
    32.         // Initializing the Transport Layer with no arguments (default settings)
    33.         NetworkTransport.Init();
    34.  
    35.         ConnectionConfig config = new ConnectionConfig();
    36.         myReiliableChannelId = config.AddChannel(QosType.Reliable);       //lento ma verifica il dispatch
    37.         myUnreliableChannelId = config.AddChannel(QosType.Unreliable);    //veloce ma non verificaa il dispatch
    38.         topology = new HostTopology(config, 3);
    39.     }
    40.  
    41.     bool hostStarted;
    42.     bool hostConnected;
    43.     bool isClient = false;
    44.  
    45.     string buttonHostText = "Start Socket";
    46.     string buttonClientText = "Connect To Host";
    47.     void OnGUI()
    48.     {
    49.         //start socket
    50.         if (GUI.Button(new Rect(300, 150, 150, 50), buttonHostText))
    51.         {
    52.             if (!hostStarted)
    53.             {
    54.                 hostId = NetworkTransport.AddHost(topology, 7777);
    55.                 Debug.LogError("HostId is : " + hostId.ToString());
    56.                 hostStarted = true;
    57.                 buttonHostText = "HostID: " + hostId.ToString() + " - Stop Host";
    58.             }
    59.             else
    60.             {
    61.                 hostStarted = false;
    62.                 buttonHostText = "Start Socket";
    63.             }
    64.         }
    65.  
    66.         if (GUI.Button(new Rect(300, 250, 150, 50), buttonClientText))
    67.         {
    68.             byte error;
    69.            
    70.             if (!hostConnected)
    71.             {
    72.                 //opens a socket on client
    73.                 int chostId = NetworkTransport.AddHost(topology, (int)Random.Range(1111, 9999));
    74.                 //connect to server
    75.                 connectionId = NetworkTransport.Connect(0, "127.0.0.1", 7777, 0, out error);
    76.                 if (connectionId != 0)
    77.                 {
    78.                     Debug.LogError("HostID : " + chostId + " ConnID : " + connectionId.ToString());
    79.                     hostConnected = true;
    80.                     isClient = true;
    81.                     buttonHostText = "HostID : " + chostId + " ConnID : " + connectionId.ToString();
    82.                     buttonClientText = "Disconnect";
    83.                 }else
    84.                     Debug.LogError("Connection Failed");
    85.             }else
    86.             {
    87.                 bool result = NetworkTransport.Disconnect(0, connectionId, out error);
    88.                 Debug.LogError("Disconnection is : " + result.ToString());
    89.                 hostConnected = false;
    90.                 buttonClientText = "Connect To Host";
    91.             }
    92.         }
    93.     }
    94.  
    95.     // Update is called once per frame
    96.     public float speed = 200f;
    97.     float timeToUpdate = 0;
    98.  
    99.     void Update()
    100.     {
    101.         if (!hostConnected && !hostStarted)
    102.             return;
    103.  
    104.         TransmitPosition();
    105.         SyncPosition();
    106.     }
    107.    
    108.     void SyncPosition()
    109.     {
    110.         int recHostId;
    111.         int connectionId;
    112.         int channelId;
    113.         byte[] recBuffer = new byte[1024];
    114.         int bufferSize = 1024;
    115.         int dataSize;
    116.         byte error;
    117.      
    118.         //turns out that server is receiving messages from client, but client receives only an incoming connection
    119.         //from server, and no messages
    120.         NetworkEventType recNetworkEvent = NetworkTransport.ReceiveFromHost(hostId, out connectionId, out channelId, recBuffer, bufferSize, out dataSize, out error);
    121.  
    122.         switch (recNetworkEvent)
    123.         {
    124.             case NetworkEventType.Nothing:
    125.                 break;
    126.             case NetworkEventType.ConnectEvent:
    127.                 Debug.LogError("incoming connection event received");
    128.                 break;
    129.             case NetworkEventType.DataEvent:
    130.                 Stream stream = new MemoryStream(recBuffer);
    131.                 BinaryFormatter formatter = new BinaryFormatter();
    132.                 string message = formatter.Deserialize(stream) as string;
    133.                 Debug.LogError("incoming message event received: " + message);
    134.                 timeToUpdate += Time.deltaTime;
    135.                 transform.position = new Vector3((float.Parse(message, CultureInfo.InvariantCulture)), float.Parse(message, CultureInfo.InvariantCulture), 1);
    136.                 timeToUpdate = 0;
    137.                 break;
    138.             case NetworkEventType.DisconnectEvent:
    139.                 Debug.LogError("remote client event disconnected");
    140.                 break;
    141.         }
    142.     }
    143.  
    144.     void FixedUpdate()
    145.     {
    146.  
    147.     }
    148.  
    149.     void TransmitPosition()
    150.     {
    151.         byte error;
    152.         byte[] buffer = new byte[1024];
    153.         Stream stream = new MemoryStream(buffer);
    154.         BinaryFormatter formatter = new BinaryFormatter();
    155.         formatter.Serialize(stream, transform.position.x.ToString());
    156.  
    157.         int bufferSize = 1024;
    158.         //this for sure sends from client on server, but he's also sending from server to client?
    159.         NetworkTransport.Send(hostId, connectionId, myReiliableChannelId, buffer, bufferSize, out error);
    160.     }
    161. }
    162.  
    163.  

    ----

    My class is very simple so far. You can copy and paste on a GameObject script. You can build this and see there's two gui buttons (server/client behavior). I set a Debug.LogError to see what's happening on the build too.

    Run two instances of the build on the same machine, one as server and one as client.

    You will see that the server is getting right messages while the client don't.

    I suppose that if i want to use LLAPI to build my custom networking multiplayer system, i'd like to have:
    - Code for dedicated server
    - Code for local client connected to local server
    - Code for remote clients

    From that, i will able to build a room to host other clients, keep in mind that i need a simple two players networking system, nothing more. But i can't use HLAPI because i really need to build my own messages system, due to my game's nature.

    Thank you so much.
     
  2. RavenTravelStudios

    RavenTravelStudios

    Joined:
    Oct 15, 2015
    Posts:
    100
    Ok i had to pass the right connectionId in the Send function.
    Seems like you have to feed a connection pool as server and send to clients by each connectionId you got when a client is connected. Now i can update any client information on server and send it to other clients.
     
    Last edited: Jan 30, 2016
  3. Red_Kay

    Red_Kay

    Joined:
    Aug 14, 2015
    Posts:
    94
    hello :)

    I found your code very helpful. Even I am trying to learn UNET LLAPI can you help me learn it or maybe just gimme a link to start from. And please don't give unity manual I didn't understand it at all.

    Thanks :)
     
  4. ikefrc

    ikefrc

    Joined:
    Jul 3, 2012
    Posts:
    81
    Are you running your server as headless? You might run into issues if you do so as Update() is bound the the frame rate (and headless servers have no frame rate). Also as the frame rate can vary, your TransmitPosition will as well, so graphical lag would result in less position updates per second.
     
  5. GunLengend

    GunLengend

    Joined:
    Sep 24, 2014
    Posts:
    54
    Hello there,
    I have same ID with your NetworkSystem, but i using HLAPI instead..
    And yes, i build custom on my own, not using any NetworkManager from Unity.
    If you would like to discuss about this task, feel free to contact me :D
     
  6. RZer0S

    RZer0S

    Joined:
    Feb 28, 2015
    Posts:
    11
    Hi all, I know I'm a bit late here just posting it fro future reference maybe someone might need some help. I'll try my best to explain some concepts of LLAPI(Low Level API) using Transport Layer.

    So basically when you start a connection in LLAPI you're automatically listening for connections (meaning you're a server) even when you later connect to another server.

    Code (CSharp):
    1.  
    2.  
    3. //Defines the port which the socket will be using.
    4. int LocalPort = 8000;
    5.  
    6. //Always init, needed to be able to use LLAPI
    7. NetworkTransport.Init();
    8.  
    9. //Create a Connection Config for the Socket
    10. ConnectionConfig config = new ConnectionConfig();
    11.  
    12. //Add reliable channel to connection config
    13. config.AddChannel(QosType.Reliable);
    14.  
    15. //Create a host Topology, its just to specify the type of socket connection with config and number of connections allowed
    16. HostTopology topology = new HostTopology(config, 10);
    17.  
    18. //Open the actual SOCKET (dunno why its called Host)
    19. int SocketID = NetworkTransport.AddHost(topology, LocalPort);
    20.  
    21. //Now our socket is open for connections
    22.  
    Please note that while the function is called AddHost, it should be called OpenSocket, because we are actually enabling and opening a socket for network communications. By default AddHost uses UDP as the communications protocol and WebHost would be TCP.

    If you dont know what a socket is, you can simply think of it as a gate through which the communication can traverse, its just a door that allows data to come in and go out.
    While its not usual in games we can have multiple sockets (gates) opened for different communications. We will most likely only ever use one, specially in a game-play environment.

    Ok now that we understand what a socket is, the code above just opens up a socket which is enough to be able to host a server.

    If we wanted to create a client, we still need the same code plus a little extra:

    Code (CSharp):
    1.  
    2. //the IP in which the server is located
    3. ServerIP = "127.0.0.1";
    4.  
    5. //Defines the port to which server socket we want to connect.
    6. int serverPort = 8001;
    7.  
    8. //Just a placeholder for the error type, if something with the connection goes wrong
    9. byte error;
    10.  
    11. //Save the connection Number (usually 0, when its the first connection we start)
    12. ConnectionID = NetworkTransport.Connect(SocketID, ServerIP, serverPort, 0, out error);
    13. NetworkError networkError = (NetworkError)error;
    14. if (networkError != NetworkError.Ok) {
    15.     //If something went wrong, we can know what it was here
    16.     Debug.Log(networkError.ToString());
    17. }
    18.  
    This code will connect to a specific IP:pORT using the socket opened earlier.

    If you check this LLAPI, its Unity's official documentation, you can get a clear understanding of how most of the LLAPI works.

    Now how to talk to all of the clients connected to me?

    basically in the receive function when you're receiving a connection, you need to save all those ConnectionIDs into a List<int> and when you want to broadcast you can send a message to all the connections in the list or send a message to a specific client. You do NOT need to save the hostIDs, remember they're just gates and you will most likely only be using ONE.

    I Hope this helps understand a bit and clear some of the confusion on LLAPI, its specially confusing due to the names they use (Host??? Nah -> SOCKET!!!). A host is conceptually a very different thing.

    If any of you is interested in learning optimizations for Multiplayer games and go more in depth on this kind of knowledge feel free to check my blog Game-Savvy. You can also contact me through there. XD I might be uploading some sample projects either to github or the Unity Asset Store regarding LLAPI too.

    Cheers
     
  7. pjbaron

    pjbaron

    Joined:
    Jan 12, 2017
    Posts:
    53
    I'm just starting to look into this and it's 4 months late... but I think you can use:
    NetworkTransport.SendMulticast multiple times to add all the connections you want...
    NetworkTransport.StartSendMulticast to send your buffer to all of the added connections...
    NetworkTransport.FinishSendMulticast to finish the broadcast (which I think must reset the list of connections because of the comment: "Only one multicast message at a time is allowed per host" and because it would be dumb if it cut off the transmission)

    You'll still need to go through a loop to add the connections, but hopefully the multicast system will be more bandwidth efficient than sending the same message to each client individually.
     
  8. ZoidbergForPresident

    ZoidbergForPresident

    Joined:
    Dec 15, 2015
    Posts:
    157