Search Unity

  1. Are you interested in providing feedback directly to Unity teams? Sign up to become a member of Unity Pulse, our new product feedback and research community.
    Dismiss Notice

Networking How should I use local discovery with NetworkTransport?

Discussion in '5.1 Beta' started by Indiefreaks, May 4, 2015.

  1. Indiefreaks

    Indiefreaks

    Joined:
    Nov 12, 2012
    Posts:
    89
    Hi,

    I'm trying to evaluate UNet for my upcoming game and the local discovery is my first step towards making my decision.

    I've been trying to use NetworkTransport and its Start/Stop/Get/Set Broadcast methods without success so far.
    How should I use it in a context where I'd most likely would like to see this working with the current available HLAPI classes such as NetworkManager or NetworkLobbyManager?

    Am I overestimating the HLAPI features so far and would therefore have to make my own NetworkManager class from scratch on top of the LLAPI?

    Thanks,
    Regards,

    Philippe
     
  2. aabramychev

    aabramychev

    Unity Technologies

    Joined:
    Jul 17, 2012
    Posts:
    574
    Hi Philippe
    We are creating HLAPI adapter for local discovery feature right now. If by some reason it won't be included in 5.1. It is not very difficult to use HLAPI with local discovery. I will update documentation on this week (I hope :)) So with RC you will receive as minimum documents and possible HLAPI solution.
     
    Pr0n likes this.
  3. Indiefreaks

    Indiefreaks

    Joined:
    Nov 12, 2012
    Posts:
    89
    Great to see you're working on it. I guess I'll just have to wait for RC unless you could share the major principles on how I should use these Broadcast methods?
     
  4. aabramychev

    aabramychev

    Unity Technologies

    Joined:
    Jul 17, 2012
    Posts:
    574
    Small bad news, discussed with my colleagues yesterday. RC will be open for bug fix only... (it is expected). so there won't direct support of this feature from hlapi. Hence, llapi for this feature is quite straightforward and should be clean.

    There is a two roles here: sender and receiver(s).

    Sender send Local Discovery (LD) messages to local network (actually to all local subnets which it is connected).
    Receiver(s) are waiting on corresponding ports and when they receive LD they will inform about this.

    Send message looks like "I'm a peer and I'm able to receive connections on the ip: port"
    To do this Sender should call the following function:
    bool StartBroadcastDiscovery ( int hostId, int broadcastPort, int key, int version, int subversion, byte[] buffer, int size, int timeout, out byte error )
    where
    hostId is a id of the host which you want to receive connections: (NetwokConnection.hostId from HLAPI stuff)
    broadcastPort it is a port which Sender will send LD messages.
    key, version and subversion are 3 values which will be checked by LD receiver, if they are not the same, LD message will be dropped.
    buffer, size define message which will be delivered to receiver and will be accessible for user
    timeout defines how often these LD messages will send.
    So after all information from LD message delivered to end user will contain: ip: port of hostId and buffer[], size message.

    To stop spamming network with LD messages sender should call
    StopBroadcastDiscovery().

    Receivers:
    It should call SetBroadcastCredentials() function to set exactly the same key, version, subversion;
    It should call AddHost LLAPI function directly and register it on broadcasting port:
    int localDiscoveryReceiverId = AddHost(..., broadcastPort ); (llapi func)
    It should run ReceiveFromHost() (llapi func) inside unity Update() waiting for BroacastEvent:
    And when it receive this event, it can grab connection information
    evnt = ReceiveFromHost(localDiscoveryReceiverId, ...)
    if(evnt ==BroadcastEvent)
    {
    GetBroadcastConnectionInfo(.. out ip, out port );
    GetBroadcastConnectionMessage( .... byte[] message, out messageLen);
    }

    It was short description.
     
  5. Indiefreaks

    Indiefreaks

    Joined:
    Nov 12, 2012
    Posts:
    89
    Perfectly what I just needed to get started. I ended up developing something similar. I just missed small parts.

    Thanks
     
  6. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    Here is a script that does local discovery and auto-joins game if it finds a NetworkManager. The IP and port of the host are included in the broadcasted message as data. You can change the function public virtual void OnReceivedBroadcast(string fromAddress, string data) to do something more friendly when a broadcast is received on a client.

    Maybe there should be a component like this in HLAPI?

    using System;
    using UnityEngine;
    using UnityEngine.Networking;
    using System.Collections;

    public class NetworkDiscovery : MonoBehaviour
    {
    const int kMaxBroadcastMsgSize = 1024;

    // config data
    [SerializeField]
    public int broadcastPort = 47777;

    [SerializeField]
    public int broadcastKey = 1000;

    [SerializeField]
    public int broadcastVersion = 1;

    [SerializeField]
    public int broadcastSubVersion = 1;

    [SerializeField]
    public string broadcastData = "HELLO";

    [SerializeField]
    public int offsetX;

    [SerializeField]
    public int offsetY;

    // runtime data
    public int hostId = -1;
    public bool running = false;
    public bool isServer = false;
    public bool isClient = false;

    byte[] msgOutBuffer = null;
    byte[] msgInBuffer = null;

    static byte[] GetBytes(string str)
    {
    byte[] bytes = new byte[str.Length * sizeof(char)];
    System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
    return bytes;
    }

    static string GetString(byte[] bytes)
    {
    char[] chars = new char[bytes.Length / sizeof(char)];
    System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
    return new string(chars);
    }

    public bool Initialize()
    {
    if (broadcastData.Length >= kMaxBroadcastMsgSize)
    {
    Debug.LogError("NetworkDiscovery Initialize - data too large. max is " + kMaxBroadcastMsgSize);
    return false;
    }

    if (!NetworkTransport.IsStarted)
    {
    NetworkTransport.Init();
    }


    if (NetworkManager.singleton != null)
    {
    broadcastData = "NetworkManager:"+NetworkManager.singleton.networkAddress + ":" + NetworkManager.singleton.networkPort;
    }

    DontDestroyOnLoad(gameObject);
    msgOutBuffer = GetBytes(broadcastData);
    msgInBuffer = new byte[kMaxBroadcastMsgSize];
    return true;
    }

    // listen for broadcasts
    public bool StartAsClient()
    {
    if (hostId != -1 || running)
    {
    Debug.LogWarning("NetworkDiscovery StartAsClient already started");
    return false;
    }

    ConnectionConfig cc = new ConnectionConfig();
    cc.AddChannel(QosType.Unreliable);
    HostTopology defaultTopology = new HostTopology(cc, 1);

    hostId = NetworkTransport.AddHost(defaultTopology, broadcastPort);
    if (hostId == -1)
    {
    Debug.LogError("NetworkDiscovery StartAsClient - addHost failed");
    return false;
    }

    byte error;
    NetworkTransport.SetBroadcastCredentials(hostId, broadcastKey, broadcastVersion, broadcastSubVersion, out error);

    running = true;
    isClient = true;
    Debug.Log("StartAsClient Discovery listening");
    return true;
    }

    // perform actual broadcasts
    public bool StartAsServer()
    {
    if (hostId != -1 ||running)
    {
    Debug.LogWarning("NetworkDiscovery StartAsServer already started");
    return false;
    }

    ConnectionConfig cc = new ConnectionConfig();
    cc.AddChannel(QosType.Unreliable);
    HostTopology defaultTopology = new HostTopology(cc, 1);

    hostId = NetworkTransport.AddHost(defaultTopology, 0);
    if (hostId == -1)
    {
    Debug.LogError("NetworkDiscovery StartAsServer - addHost failed");
    return false;
    }

    byte err;
    if (!NetworkTransport.StartBroadcastDiscovery(hostId, broadcastPort, broadcastKey, broadcastVersion, broadcastSubVersion, msgOutBuffer, msgOutBuffer.Length, 1000, out err))
    {
    Debug.LogError("NetworkDiscovery StartBroadcast failed err: " + err);
    return false;
    }

    running = true;
    isServer = true;
    Debug.Log("StartAsServer Discovery broadcasting");
    return true;
    }

    public void StopBroadcast()
    {
    if (hostId == -1)
    {
    Debug.LogError("NetworkDiscovery StopBroadcast not initialized");
    return;
    }

    if (!running)
    {
    Debug.LogWarning("NetworkDiscovery StopBroadcast not started");
    return;
    }
    if (isServer)
    {
    NetworkTransport.StopBroadcastDiscovery();
    }

    NetworkTransport.RemoveHost(hostId);
    hostId = -1;
    running = false;
    isServer = false;
    isClient = false;
    Debug.Log("Stopped Discovery broadcasting");
    }

    void Update()
    {
    if (hostId == -1)
    return;

    if (isServer)
    return;

    int connectionId;
    int channelId;
    int receivedSize;
    byte error;
    NetworkEventType networkEvent = NetworkEventType.DataEvent;

    do
    {
    networkEvent = NetworkTransport.ReceiveFromHost(hostId, out connectionId, out channelId, msgInBuffer, kMaxBroadcastMsgSize, out receivedSize, out error);

    if (networkEvent == NetworkEventType.BroadcastEvent)
    {
    NetworkTransport.GetBroadcastConnectionMessage(hostId, msgInBuffer, kMaxBroadcastMsgSize, out receivedSize, out error);

    string senderAddr;
    int senderPort;
    NetworkTransport.GetBroadcastConnectionInfo(hostId, out senderAddr, out senderPort, out error);

    OnReceivedBroadcast(senderAddr, GetString(msgInBuffer));
    }
    } while (networkEvent != NetworkEventType.Nothing);

    }

    public virtual void OnReceivedBroadcast(string fromAddress, string data)
    {
    Debug.Log("Got broadcast from [" + fromAddress + "] " + data);
    var items = data.Split(':');
    if (items.Length == 3 && items[0] == "NetworkManager")
    {
    if (NetworkManager.singleton != null && NetworkManager.singleton.client == null)
    {
    NetworkManager.singleton.networkAddress = items[1];
    NetworkManager.singleton.networkPort = Convert.ToInt32(items[2]);
    NetworkManager.singleton.StartClient();
    }
    }

    }

    void OnGUI()
    {
    int xpos = 10 + offsetX;
    int ypos = 40 + offsetY;
    int spacing = 24;

    if (msgInBuffer == null)
    {
    if (GUI.Button(new Rect(xpos, ypos, 200, 20), "Initialize Broadcast"))
    {
    Initialize();
    }
    return;
    }
    else
    {
    GUI.Label(new Rect(xpos, ypos, 200, 20), "initialized");
    }
    ypos += spacing;

    if (running)
    {
    if (GUI.Button(new Rect(xpos, ypos, 200, 20), "Stop"))
    {
    StopBroadcast();
    }
    ypos += spacing;
    }
    else
    {
    if (GUI.Button(new Rect(xpos, ypos, 200, 20), "Start Broadcasting"))
    {
    StartAsServer();
    }
    ypos += spacing;

    if (GUI.Button(new Rect(xpos, ypos, 200, 20), "Listen for Broadcast"))
    {
    StartAsClient();
    }
    ypos += spacing;
    }
    }
    }
     
  7. Inclusive

    Inclusive

    Joined:
    May 14, 2014
    Posts:
    7
    I'm getting an error message saying "By some reason host doesn't have broadcast addreses" on the NetworkTransport.StartBroadcastDiscovery call in the StartAsServer method in your example, seanr. Am I missing something?
     
  8. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    Maybe it is a configuration issue. Here is a simple project with the components setup
     

    Attached Files:

    Salazar, efge, HenryJV and 3 others like this.
  9. Inclusive

    Inclusive

    Joined:
    May 14, 2014
    Posts:
    7
    Your project works in a standalone build on a Windows machine but I can't get it work on either my Macbook or on iOS. I'll investigate further to see if I can get it to work somehow. Thanks for the help!

    EDIT: Seems like it works on Android as well.
     
    Last edited: May 11, 2015
  10. aabramychev

    aabramychev

    Unity Technologies

    Joined:
    Jul 17, 2012
    Posts:
    574
    Hi, could you paste ifconfig information here? Was it localdiscovery between two different computers? If yes, paste please ifconfig info from both machines.
     
  11. Inclusive

    Inclusive

    Joined:
    May 14, 2014
    Posts:
    7
    I've tested both between different devices as well as on the same device. The problem is that it never begins broadcasting, it just gives me the following two error messages:


    By some reason host doesn't have broadcast addreses
    UnityEngine.Networking.NetworkTransport:StartBroadcastDiscovery(Int32, Int32, Int32, Int32, Int32, Byte[], Int32, Int32, Byte&)
    NetworkDiscovery:StartAsServer() (at Assets/NetworkDiscovery.cs:139)
    NetworkDiscovery:OnGUI() (at Assets/NetworkDiscovery.cs:268)

    and

    NetworkDiscovery StartBroadcast failed err: 8
    UnityEngine.Debug:LogError(Object)
    NetworkDiscovery:StartAsServer() (at Assets/NetworkDiscovery.cs:141)
    NetworkDiscovery:OnGUI() (at Assets/NetworkDiscovery.cs:268)


    This is the ifconfig info about my active connection. Is this enough?

    en1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    ether a8:bb:cf:05:42:64
    inet6 fe80::aabb:cfff:fe05:4264%en1 prefixlen 64 scopeid 0x5
    inet 192.168.1.114 netmask 0xffffff00 broadcast 192.168.1.255
    nd6 options=1<PERFORMNUD>
    media: autoselect
    status: active
     
    alteredgene-london and Pr0n like this.
unityunity