Skip to content

Commit 3fd4955

Browse files
committed
Added automatically port forwarding using UPnP
1 parent 18da9a5 commit 3fd4955

File tree

6 files changed

+127
-9
lines changed

6 files changed

+127
-9
lines changed

MLAPI/Data/NetworkingConfiguration.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5+
using System.Net;
56
using System.Security.Cryptography;
67
using UnityEngine.Networking;
78

@@ -27,6 +28,8 @@ public class NetworkingConfiguration
2728
//Should only be used for dedicated servers and will require the servers RSA keypair being hard coded into clients in order to exchange a AES key
2829
//TODO
2930
public bool EncryptMessages = false;
31+
public bool UseUPnP = true;
32+
public Action<bool, IPAddress> UPnPCompleteCallback;
3033

3134
//Cached config hash
3235
private byte[] ConfigHash = null;

MLAPI/Helper/UPnPHelper.cs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using Open.Nat;
2+
using System;
3+
using System.Linq;
4+
using System.Net;
5+
using System.Text;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using UnityEngine;
9+
10+
namespace MLAPI.Helper
11+
{
12+
public class UPnPHelper
13+
{
14+
internal static void AttemptPortMap(int port, Action<bool, IPAddress> callback)
15+
{
16+
bool invoked = false;
17+
NatDiscoverer nat = new NatDiscoverer();
18+
CancellationTokenSource cts = new CancellationTokenSource();
19+
cts.CancelAfter(10000);
20+
21+
NatDevice device = null;
22+
StringBuilder sb = new StringBuilder();
23+
Task<NatDevice> natTask = nat.DiscoverDeviceAsync(PortMapper.Upnp, cts);
24+
Mapping tcpMapping = new Mapping(Protocol.Tcp, port, port, 0, Application.productName + " (TCP)");
25+
Mapping udpMapping = new Mapping(Protocol.Udp, port, port, 0, Application.productName + " (UDP)");
26+
IPAddress publicIPAddress = null;
27+
natTask.ContinueWith(tt =>
28+
{
29+
device = tt.Result;
30+
device.GetExternalIPAsync()
31+
.ContinueWith(task =>
32+
{
33+
publicIPAddress = task.Result;
34+
return device.CreatePortMapAsync(udpMapping);
35+
})
36+
.Unwrap()
37+
.ContinueWith(task =>
38+
{
39+
return device.CreatePortMapAsync(udpMapping);
40+
})
41+
.Unwrap()
42+
.ContinueWith(task =>
43+
{
44+
return device.GetAllMappingsAsync();
45+
})
46+
.Unwrap()
47+
.ContinueWith(task =>
48+
{
49+
Mapping[] mappings = task.Result.ToArray();
50+
if(mappings.Length == 0)
51+
{
52+
if (!invoked)
53+
callback(false, publicIPAddress);
54+
invoked = true;
55+
}
56+
else
57+
{
58+
for (int i = 0; i < mappings.Length; i++)
59+
{
60+
if(mappings[i].PrivatePort == port)
61+
{
62+
if (!invoked)
63+
callback(true, publicIPAddress);
64+
invoked = true;
65+
}
66+
}
67+
}
68+
});
69+
}, TaskContinuationOptions.OnlyOnRanToCompletion);
70+
71+
try
72+
{
73+
natTask.Wait();
74+
}
75+
catch (AggregateException e)
76+
{
77+
if (e.InnerException is NatDeviceNotFoundException)
78+
{
79+
if (!invoked)
80+
callback(false, publicIPAddress);
81+
invoked = true;
82+
}
83+
}
84+
}
85+
}
86+
}

MLAPI/MLAPI.csproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@
3232
<WarningLevel>4</WarningLevel>
3333
</PropertyGroup>
3434
<ItemGroup>
35+
<Reference Include="Open.Nat, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f22a6a4582336c76, processorArchitecture=MSIL">
36+
<HintPath>..\packages\Open.NAT.2.1.0.0\lib\net35\Open.Nat.dll</HintPath>
37+
</Reference>
3538
<Reference Include="System" />
3639
<Reference Include="System.Core" />
40+
<Reference Include="System.Threading, Version=1.0.2856.102, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
41+
<HintPath>..\packages\Open.NAT.2.1.0.0\lib\net35\System.Threading.dll</HintPath>
42+
</Reference>
3743
<Reference Include="System.Xml.Linq" />
3844
<Reference Include="System.Data.DataSetExtensions" />
3945
<Reference Include="System.Data" />
@@ -44,12 +50,16 @@
4450
</Reference>
4551
</ItemGroup>
4652
<ItemGroup>
53+
<Compile Include="Helper\UPnPHelper.cs" />
4754
<Compile Include="MonoBehaviours\Core\NetworkedBehaviour.cs" />
4855
<Compile Include="Data\NetworkedClient.cs" />
4956
<Compile Include="MonoBehaviours\Core\NetworkedObject.cs" />
5057
<Compile Include="Data\NetworkingConfiguration.cs" />
5158
<Compile Include="MonoBehaviours\Core\NetworkingManager.cs" />
5259
<Compile Include="Properties\AssemblyInfo.cs" />
5360
</ItemGroup>
61+
<ItemGroup>
62+
<None Include="packages.config" />
63+
</ItemGroup>
5464
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
5565
</Project>

MLAPI/MonoBehaviours/Core/NetworkingManager.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using MLAPI.Helper;
2+
using System;
23
using System.Collections;
34
using System.Collections.Generic;
45
using System.IO;
@@ -344,6 +345,10 @@ public void StartServer(NetworkingConfiguration netConfig)
344345
Debug.LogWarning("MLAPI: No ConnectionApproval callback defined. Connection approval will timeout");
345346
}
346347
}
348+
if (NetworkConfig.UseUPnP)
349+
{
350+
UPnPHelper.AttemptPortMap(NetworkConfig.Port, NetworkConfig.UPnPCompleteCallback);
351+
}
347352
HostTopology hostTopology = new HostTopology(cConfig, NetworkConfig.MaxConnections);
348353
hostId = NetworkTransport.AddHost(hostTopology, NetworkConfig.Port);
349354
isServer = true;
@@ -373,6 +378,10 @@ public void StartHost(NetworkingConfiguration netConfig)
373378
Debug.LogWarning("MLAPI: No ConnectionApproval callback defined. Connection approval will timeout");
374379
}
375380
}
381+
if(NetworkConfig.UseUPnP)
382+
{
383+
UPnPHelper.AttemptPortMap(NetworkConfig.Port, NetworkConfig.UPnPCompleteCallback);
384+
}
376385
HostTopology hostTopology = new HostTopology(cConfig, NetworkConfig.MaxConnections);
377386
hostId = NetworkTransport.AddHost(hostTopology, NetworkConfig.Port, null);
378387
isServer = true;

MLAPI/packages.config

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<packages>
3+
<package id="Open.NAT" version="2.1.0.0" targetFramework="net35" />
4+
</packages>

README.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,28 @@ The project is WIP.
55

66
It's licenced under the MIT licence :D
77

8-
## Features that are planned / done are:
9-
* Object and player spawning (done)
10-
* Connection approval (done)
11-
* Message names (done)
12-
* Replace the integer QOS with names. When you setup the networking you specify names that are associated with a channel. This makes it easier to manage. You can thus specify that a message should be sent on the "damage" channel which handles all damage related logic and is running on the AllCostDelivery channel. (done)
13-
* ProtocolVersion to allow making different versions not talk to each other. (done)
14-
* NetworkedBehaviours does not have to be on the root, it's simply just a class that implements the send methods etc. (done)
15-
* Multiple messages processed every frame with the ability to specify a maximum to prevent freezes in the normal game logic (done)
8+
9+
## Planned features
1610
* Built in lag compensation (going to be worked on when all base functionality is there)
1711
* Area of interest (not working on ATM but it's on the TODO)
1812
* Core gameplay components similar to what the HLAPI offers (but hopefully of better quality)
1913
* Encrypted messages / full encryption for all messages. This option will only be useful for dedicated servers. It will require all clients to have a hardcoded copy of the servers RSA keypair.
2014
* Serializer (both for the library to speed up and to allow structs to be sent easily)
2115
* SyncVars (allow variables to automatically be synced to new clients and current clients when it's changed)
2216
* Message compression
17+
18+
19+
## Done features
2320
* Host support (Client hosts the server) (done)
21+
* Port forwarding using Open.NAT using the UPnP protcol (done)
22+
* Object and player spawning (done)
23+
* Connection approval (done)
24+
* Message names (done)
25+
* Replace the integer QOS with names. When you setup the networking you specify names that are associated with a channel. This makes it easier to manage. You can thus specify that a message should be sent on the "damage" channel which handles all damage related logic and is running on the AllCostDelivery channel. (done)
26+
* ProtocolVersion to allow making different versions not talk to each other. (done)
27+
* NetworkedBehaviours does not have to be on the root, it's simply just a class that implements the send methods etc. (done)
28+
* Multiple messages processed every frame with the ability to specify a maximum to prevent freezes in the normal game logic (done)
29+
2430

2531
That's all I can think of right now. But there is more to come, especially if people show intrest in the project.
2632

0 commit comments

Comments
 (0)