TwoTen
TwoTen

Reputation: 35

Unity .Net 2.0 UPNP Port Mapping

I have recently been trying to setup port mapping for my game. This is to make one of the players be the host. For that I need the player to open port 7777.

If there is a match already on it will simply join it. If it doesn't find empty matches it will simply make one. And If it fails to make one it will wait until someone makes one and join it.

Therefor I need to be able to use something like UPnP to portmap my port. I also need to be able to catch errors to see wheter to proceed with creation of a match or to simply wait for one and join.

I am currently working in the Unity game engine which uses .NET 2.0. This makes me very limited as Open.NAT isn't compatible. I tried Mono.NAT but I can't get it working.

Does anyone have any suggestions of how I should approach this? What libraries to use and maybe even provide me with code snippets to get me started.

Thanks, TwoTen.

Edit1: The current Mono.NAT code looks like this:

private void DeviceFound(object sender, DeviceEventArgs args)
{
    Debug.Log("1");
    INatDevice device = args.Device;
    Debug.Log("2");
    Mapping map = new Mapping(Protocol.Tcp, 6699, 6699);
    Debug.Log("3");
    device.CreatePortMap(map);
    Debug.Log("4");
    int test = device.GetAllMappings().Length;
    Debug.Log(test);
    foreach (Mapping portMap in device.GetAllMappings())
    {
        Debug.Log("5");
        Debug.Log(portMap.ToString());
    }
}
private void DeviceLost(object sender, DeviceEventArgs args)
{
    INatDevice device = args.Device;
    Mapping map = new Mapping(Protocol.Tcp, 6699, 6699);
    device.DeletePortMap(map);
}

I The last debug statement that is called is number 4. I the port does not get opened and no excetion is thrown either. What am I doing wrong?

Also, In my start function I call this:

NatUtility.DeviceFound += DeviceFound;
NatUtility.DeviceLost += DeviceLost;
NatUtility.StartDiscovery();

Upvotes: 2

Views: 2457

Answers (1)

TwoTen
TwoTen

Reputation: 35

I have found an answer on how to successfully. I dropped Mono.NAT and instead went with Open.NAT which has been ported to .NET 3.5 which is compatible with Unity!

The final code I ended up using looks like this:

  private static Task Test()
{
    var nat = new NatDiscoverer();
    var cts = new CancellationTokenSource();
    cts.CancelAfter(5000);

    NatDevice device = null;
    var sb = new StringBuilder();
    IPAddress ip = null;

    return nat.DiscoverDeviceAsync(PortMapper.Upnp, cts)
        .ContinueWith(task =>
        {
            device = task.Result;
            return device.GetExternalIPAsync();

        })
        .Unwrap()
        .ContinueWith(task =>
        {
            ip = task.Result;
            sb.AppendFormat("\nYour IP: {0}", ip);
            return device.CreatePortMapAsync(new Mapping(Protocol.Tcp, 7777, 7777, 0, "myGame Server (TCP)"));
        })
        .Unwrap()
        .ContinueWith(task =>
        {
            return device.CreatePortMapAsync(
                new Mapping(Protocol.Udp, 7777, 7777, 0, "myGame Server (UDP)"));
        })
        .Unwrap()
        .ContinueWith(task =>
        {
            sb.AppendFormat("\nAdded mapping: {0}:1700 -> 127.0.0.1:1600\n", ip);
            sb.AppendFormat("\n+------+-------------------------------+--------------------------------+------------------------------------+-------------------------+");
            sb.AppendFormat("\n| PORT | PUBLIC (Reacheable)           | PRIVATE (Your computer)        | Description                        |                         |");
            sb.AppendFormat("\n+------+----------------------+--------+-----------------------+--------+------------------------------------+-------------------------+");
            sb.AppendFormat("\n|      | IP Address           | Port   | IP Address            | Port   |                                    | Expires                 |");
            sb.AppendFormat("\n+------+----------------------+--------+-----------------------+--------+------------------------------------+-------------------------+");
            return device.GetAllMappingsAsync();
        })
        .Unwrap()
        .ContinueWith(task =>
        {
            foreach (var mapping in task.Result)
            {
                sb.AppendFormat("\n|  {5} | {0,-20} | {1,6} | {2,-21} | {3,6} | {4,-35}|{6,25}|",
                    ip, mapping.PublicPort, mapping.PrivateIP, mapping.PrivatePort, mapping.Description,
                    mapping.Protocol == Protocol.Tcp ? "TCP" : "UDP", mapping.Expiration.ToLocalTime());
            }
            sb.AppendFormat("\n+------+----------------------+--------+-----------------------+--------+------------------------------------+-------------------------+");
            sb.AppendFormat("\n[Removing TCP mapping] {0}:1700 -> 127.0.0.1:1600", ip);
            return device.DeletePortMapAsync(new Mapping(Protocol.Tcp, 1600, 1700));
        })
        .Unwrap()
        .ContinueWith(task =>
        {
            sb.AppendFormat("\n[Done]");
            Debug.Log(sb.ToString());
        });
}

All I had to do was to do this in my start function:

Test().Wait();

The router registers the port and opens it. HOWEVER. The website canyouseeme.org FAILED to see the port as open for what ever reason. To verify that it was registered I went into my router settings and looked for UPnP. There was then a list of applications including mine. I am yet to do real world tests but I will update the post once those have been made.

The Open.NAT version compatible with Unity was found here:

Edit: I have now performed real world tests. Everything is working and users can connect without relay and or unitys matchmaking servers

Upvotes: 1

Related Questions