Search Unity

A relatively simple sample of base system of networking using the Unity LLAPI.

Discussion in 'Scripting' started by TheNoobieWaffle, May 26, 2016.

  1. TheNoobieWaffle

    TheNoobieWaffle

    Joined:
    Jul 12, 2014
    Posts:
    28
    I've been messing with the LLAPI because I feel like the HLAPI is somewhat lacking in documentation. Thus, has a lack in ease-of-use. So, instead using the HLAPI, I've been trying to understand the LLAPI so I could re-invent the wheel to suit my needs. I've viewed several articles and lessons on the LLAPI, and made a sample that may help some people getting started. I used this as a base: https://gist.github.com/LinaAdkins/a3bc0cee6f39bfd80110. If you wish to test the functionality of my script, add that script to a GameObject in the scene and build the game. Launch the build, and hit play in the editor. There are OnGUI buttons that allow you to make the running game a server or a client. Just make sure you change the IP and port it uses to suit your specific environment. You have to do it manually in the script.

    I hope this helps someone out!

    Here is the script:
    Code (CSharp):
    1.  
    2. //NetworkingMain.cs
    3. using UnityEngine;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6. using UnityEngine.Networking;
    7. using System.Runtime.Serialization.Formatters.Binary;
    8. using System.IO;
    9.  
    10. public class NetworkMain : MonoBehaviour {
    11.  
    12.    //Enum
    13.    enum NetworkType {
    14.      undefined,
    15.      server,
    16.      client
    17.    }
    18.  
    19.  
    20.  
    21.    //Preference Variables
    22.    public int maxConnections = 10;//Max connections allowed.
    23.    public bool autoStartServer = false;//Will this automatically become a server when the game is started?
    24.  
    25.  
    26.  
    27.    //Script Variables
    28.    int localSocket = -1;//The socket the server or client is connected to.
    29.    int connectionId = -1;//The ID of connection. Used to distinguish different clients on the network.
    30.    bool isInitialized = false;//Is the script currently networked, or offline?
    31.    NetworkType networkType;//State of this game instance. Is it a server, client, or undefined (neither a client or server, yet).
    32.    List<int> activeConnectionIds = new List<int>();//All active connections. Only manager on server.
    33.  
    34.  
    35.  
    36.    //Channels
    37.    byte channelUnreliable;//Unreliable channel.
    38.    byte channelReliable;//Reliable channel.
    39.  
    40.  
    41.  
    42.    //Configuration Variables
    43.    GlobalConfig gc = new GlobalConfig();
    44.    ConnectionConfig cc = new ConnectionConfig();
    45.    HostTopology ht;
    46.  
    47.  
    48.  
    49.  
    50.  
    51.    //--MONOBEHAVIOR METHODS------------------------------------------------------------------------------------------------------------------------------\\
    52.    void Start() {
    53.  
    54.      //Customize global config
    55.      gc.ReactorModel = ReactorModel.FixRateReactor;
    56.      gc.ThreadAwakeTimeout = 10;
    57.  
    58.      //Customize channel config
    59.      channelReliable = cc.AddChannel(QosType.ReliableSequenced);
    60.      channelUnreliable = cc.AddChannel(QosType.UnreliableSequenced);
    61.  
    62.      //Create host topology from config
    63.      ht = new HostTopology(cc, maxConnections);
    64.  
    65.      //Initialize network transport. Allows for the creation/connection of servers.
    66.      NetworkTransport.Init(gc);
    67.  
    68.      //If 'autoStartServer' is true, then start a server.
    69.      if ( autoStartServer )
    70.        StartServer();
    71.  
    72.      StartCoroutine( "TimedMessage" );
    73.    }
    74.  
    75.  
    76.  
    77.  
    78.  
    79.    void Update() {
    80.      //Recieve Network Messages
    81.      ReceiveMessages();
    82.    }
    83.  
    84.  
    85.  
    86.  
    87.  
    88.    void OnGUI() {
    89.      if ( GUI.Button(new Rect(Screen.width * .5f - 50, Screen.height * .5f - 50, 100, 40), "Create a Server") ) {
    90.        StartServer();
    91.      }
    92.      if ( GUI.Button(new Rect(Screen.width * .5f - 50, Screen.height * .5f + 50, 100, 40), "Join a Server") ) {
    93.        JoinServer();
    94.      }
    95.      if ( GUI.Button(new Rect(Screen.width * .5f - 50, Screen.height * .5f + 150, 100, 40), "Disconnect") ) {
    96.        Disconnect();
    97.      }
    98.    }
    99.  
    100.  
    101.  
    102.  
    103.  
    104.    //--START/CONNECT SERVER------------------------------------------------------------------------------------------------------------------------------\\
    105.    public void StartServer(int port = 25565) {
    106.      //Make sure this game is not already a server or client
    107.      if ( networkType != NetworkType.undefined ) {
    108.        Debug.LogError( "Error: This game is already a server or client. Cannot make it a server." );
    109.        GameManager.gm.Log("Error: This game is already a server or client. Cannot make it a server.");
    110.      }
    111.  
    112.      //Open socket for server
    113.      localSocket = NetworkTransport.AddHost(ht, port);
    114.  
    115.      //Make sure the socket formations were successful
    116.      if ( localSocket < 0 ) {
    117.        GameManager.gm.Log("Server socket creation failed!");
    118.      } else {
    119.        GameManager.gm.Log("Server socket creation successful! Socket: " + localSocket);
    120.        networkType = NetworkType.server;
    121.      }
    122.  
    123.      isInitialized = true;
    124.    }
    125.  
    126.  
    127.  
    128.  
    129.  
    130.    public void JoinServer(string ip = "127.0.0.1", int port = 25565) {
    131.      //Make sure this game is not already a server or client
    132.      if ( networkType != NetworkType.undefined ) {
    133.        Debug.LogError("Error: This game is already a server or client. Cannot make it a client.");
    134.        GameManager.gm.Log("Error: This game is already a server or client. Cannot make it a client.");
    135.      }
    136.  
    137.      //Open socket for client
    138.      localSocket = NetworkTransport.AddHost(ht);
    139.  
    140.      //Make sure the socket formations were successful
    141.      if ( localSocket < 0 ) {
    142.        GameManager.gm.Log("Client socket creation failed!");
    143.      } else {
    144.        GameManager.gm.Log("Client socket creation successful! Socket: " + localSocket);
    145.        networkType = NetworkType.client;
    146.      }
    147.  
    148.      isInitialized = true;
    149.  
    150.      //Connect to server
    151.      byte error;
    152.      connectionId = NetworkTransport.Connect(localSocket, ip, port, 0, out error);
    153.      LogNetworkError(error);
    154.    }
    155.  
    156.  
    157.  
    158.  
    159.  
    160.    //--SEND DATA------------------------------------------------------------------------------------------------------------------------------\\
    161.    public void ServerSendDataToSingle(string message, int recipient) {//message, (send to single) int id
    162.      List<int> recipients = new List<int>();
    163.      recipients.Add(recipient);
    164.      ServerSendDataInner(message, recipients);
    165.    }
    166.    public void ServerSendDataToMultiple(string message, List<int> recipients) {//message, (send to multiple) list<int> ids
    167.      ServerSendDataInner(message, recipients);
    168.    }
    169.    public void ServerSendDataToAll(string message) {//message, (send to all)
    170.      ServerSendDataInner(message, activeConnectionIds);
    171.    }
    172.    public void ServerSendDataInner(string message, List<int> recipients) {
    173.      //If not server, return
    174.      if ( networkType != NetworkType.server ) {
    175.        if ( networkType == NetworkType.undefined ) {
    176.          Debug.LogError("Error: Cannot sent message. No active connections");
    177.        } else {
    178.          Debug.LogError("Error: Client cannot send messages with this method. Use [ClientSendDataToHost] instead.");
    179.        }
    180.        return;
    181.      }
    182.  
    183.      // Send the client(s) a message
    184.      byte error;
    185.      byte[] buffer = new byte[1024];
    186.      Stream stream = new MemoryStream(buffer);
    187.      BinaryFormatter f = new BinaryFormatter();
    188.      f.Serialize(stream, message);
    189.  
    190.      foreach ( int id in recipients ) {
    191.        NetworkTransport.Send(localSocket, id, channelReliable, buffer, ( int ) stream.Position, out error);
    192.        LogNetworkError(error);
    193.      }
    194.    }
    195.  
    196.  
    197.  
    198.  
    199.  
    200.    public void ClientSendDataToHost(string message) {
    201.      //If not client, return
    202.      if ( networkType != NetworkType.client ) {
    203.        if ( networkType == NetworkType.undefined ) {
    204.          Debug.LogError("Error: Cannot sent message. No active connections");
    205.        } else {
    206.          Debug.LogError("Error: Server cannot send messages with this method. Use a variation of [ServerSendData___] instead.");
    207.        }
    208.        return;
    209.      }
    210.  
    211.      // Send the server a message
    212.      byte error;
    213.      byte[] buffer = new byte[1024];
    214.      Stream stream = new MemoryStream(buffer);
    215.      BinaryFormatter f = new BinaryFormatter();
    216.      f.Serialize(stream, message);
    217.  
    218.      NetworkTransport.Send(localSocket, connectionId, channelReliable, buffer, ( int ) stream.Position, out error);
    219.      LogNetworkError(error);
    220.    }
    221.  
    222.  
    223.  
    224.  
    225.  
    226.    //--RECEIVE DATA------------------------------------------------------------------------------------------------------------------------------\\
    227.    void ReceiveMessages() {
    228.      //Not a server or a connected client, return
    229.      if ( !isInitialized ) {
    230.        return;
    231.      }
    232.  
    233.      int recHostId;
    234.      int connId;
    235.      int channelId;
    236.      int dataSize;
    237.      byte[] buffer = new byte[1024];
    238.      byte error;
    239.  
    240.      NetworkEventType networkEvent = NetworkEventType.DataEvent;
    241.  
    242.      do {
    243.        networkEvent = NetworkTransport.Receive(out recHostId, out connId, out channelId, buffer, 1024, out dataSize, out error);
    244.  
    245.        switch ( networkEvent ) {
    246.          case NetworkEventType.Nothing:
    247.            break;
    248.  
    249.  
    250.  
    251.          case NetworkEventType.ConnectEvent:
    252.            if ( networkType == NetworkType.server ) {
    253.              GameManager.gm.Log("Server: Player " + connId.ToString() + " connected!");
    254.              activeConnectionIds.Add(connId);
    255.              ServerSendDataToSingle("You connected to me!", connId);
    256.              ServerSendDataToAll("A new player connected!");
    257.            } else if ( networkType == NetworkType.client ) {
    258.              GameManager.gm.Log("Client: Client connected to " + connId.ToString() + "!");
    259.  
    260.              ClientSendDataToHost("I connected to you!");
    261.            }
    262.            break;
    263.  
    264.  
    265.  
    266.          case NetworkEventType.DataEvent:
    267.            //Decode data
    268.            Stream stream = new MemoryStream(buffer);
    269.            BinaryFormatter f = new BinaryFormatter();
    270.            string msg = f.Deserialize(stream).ToString();
    271.  
    272.            //Received data
    273.            if ( networkType == NetworkType.server ) {
    274.  
    275.              GameManager.gm.Log("Server: Received Data from " + connId.ToString() + "! Message: " + msg);
    276.            } else if ( networkType == NetworkType.client ) {
    277.              GameManager.gm.Log("Client: Received Data from " + connId.ToString() + "! Message: " + msg);
    278.            }
    279.            break;
    280.  
    281.  
    282.  
    283.          case NetworkEventType.DisconnectEvent:
    284.            if ( networkType == NetworkType.server && connId != localSocket ) {
    285.              GameManager.gm.Log("Server: Received disconnect from " + connId.ToString());
    286.              int toRemove = -1;
    287.              for ( int i = 0; i < activeConnectionIds.Count; i++ ) {
    288.                if ( activeConnectionIds[i] == connId ) {
    289.                  toRemove = i;
    290.                  break;
    291.                }
    292.              }
    293.              activeConnectionIds.RemoveAt(toRemove);
    294.            } else if ( networkType == NetworkType.client ) {
    295.              GameManager.gm.Log("Client: Disconnected from server!");
    296.              Disconnect();
    297.            }
    298.            break;
    299.        }
    300.  
    301.      } while ( networkEvent != NetworkEventType.Nothing );
    302.    }
    303.  
    304.  
    305.  
    306.  
    307.  
    308.    //--DISCONNECT------------------------------------------------------------------------------------------------------------------------------\\
    309.    public void Disconnect() {
    310.      if ( networkType == NetworkType.client ) {
    311.        byte error;
    312.        NetworkTransport.Disconnect(localSocket, connectionId, out error);
    313.        LogNetworkError(error);
    314.      }
    315.  
    316.      //Close connection
    317.      NetworkTransport.Shutdown();
    318.  
    319.      //Reset Variables
    320.      localSocket = -1;
    321.      connectionId = -1;
    322.      isInitialized = false;
    323.      networkType = NetworkType.undefined;
    324.      List<int> activeConnectionIds = new List<int>();
    325.      channelUnreliable = 0;
    326.      channelReliable = 0;
    327.      StopCoroutine( "TimedMessage" );
    328.  
    329.      //Re-Open connection
    330.      NetworkTransport.Init(gc);
    331.      StartCoroutine( "TimedMessage" );
    332.    }
    333.  
    334.  
    335.  
    336.  
    337.  
    338.    //--OTHER------------------------------------------------------------------------------------------------------------------------------\\
    339.    void LogNetworkError(byte error) {
    340.      if ( error != ( byte ) NetworkError.Ok ) {
    341.        NetworkError nerror = ( NetworkError ) error;
    342.        Debug.LogError("Error: " + nerror.ToString());
    343.        GameManager.gm.Log("Error: " + nerror.ToString());
    344.      }
    345.    }
    346.  
    347.    IEnumerator TimedMessage() {
    348.      if ( networkType == NetworkType.server ) {
    349.        ServerSendDataToAll( "Server to all: connected!" );
    350.      } else if ( networkType == NetworkType.client ) {
    351.        ClientSendDataToHost( "Client to host: connected!" );
    352.      }
    353.      yield return new WaitForSeconds(1);
    354.      StartCoroutine( "TimedMessage" );
    355.    }
    356.  
    357. }
    358.  
    That script also references GameManager.cs. I have that in everything I make, and it simply has a OnGUI console so it can be viewed in play mode. Here is that:

    Code (CSharp):
    1. //GameManager.cs
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class GameManager : MonoBehaviour {
    6.  
    7.     public static GameManager gm;
    8.  
    9.     void Start() {
    10.         gm = this;
    11.  
    12.         logs = new string[debugLength];
    13.     }
    14.  
    15.     void OnGUI () {
    16.         //In-Game Debug Console
    17.         GUI.color = Color.black;
    18.         if (debug && logs.Length > 0) {
    19.             for (int i = 0; i <= debugLength - 1; i++) {
    20.                 GUI.Label(new Rect(10, Screen.height - 165 - (i * 15), 1000, 20), logs[i]);
    21.             }
    22.             if (GUI.Button(new Rect(Screen.width * .5f - 50, Screen.height - 30, 100, 20), "CLEAR LOG")) {
    23.                 for (int i = 0; i < debugLength; i++) {
    24.                     logs[i] = "";
    25.                 }
    26.                 Log("");
    27.             }
    28.         }
    29.     }
    30.  
    31.     //--[ Debug In-Game Console ]------------------------------------------------------------------------------------------------------------//
    32.     public bool debug = true;
    33.     public int debugLength = 20;
    34.     private string[] logs;
    35.     public void Log(string log) {
    36.         for (int i = debugLength - 1; i > 0; i--) {
    37.             logs[i] = logs[i - 1];
    38.         }
    39.         logs[0] = log;
    40.     }
    41. }
    42.  
     
    Last edited: May 26, 2016
    Samixon and ThaiCat like this.