How should I use local discovery with NetworkTransport?

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

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.

1 Like

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?

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.

3 Likes

Perfectly what I just needed to get started. I ended up developing something similar. I just missed small parts.

Thanks

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;
}
}
}

5 Likes

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?

Maybe it is a configuration issue. Here is a simple project with the components setup

2104650–137926–broadcast (2).zip (25.1 KB)

6 Likes

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.

Hi, could you paste ifconfig information here? Was it localdiscovery between two different computers? If yes, paste please ifconfig info from both machines.

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
media: autoselect
status: active

2 Likes