From 3fd4955fafc3d28fd06d8493c697554c33a3f374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albin=20Cor=C3=A9n?= <2108U9@gmail.com> Date: Sun, 7 Jan 2018 08:29:01 +0100 Subject: [PATCH] Added automatically port forwarding using UPnP --- MLAPI/Data/NetworkingConfiguration.cs | 3 + MLAPI/Helper/UPnPHelper.cs | 86 +++++++++++++++++++ MLAPI/MLAPI.csproj | 10 +++ .../MonoBehaviours/Core/NetworkingManager.cs | 11 ++- MLAPI/packages.config | 4 + README.md | 22 +++-- 6 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 MLAPI/Helper/UPnPHelper.cs create mode 100644 MLAPI/packages.config diff --git a/MLAPI/Data/NetworkingConfiguration.cs b/MLAPI/Data/NetworkingConfiguration.cs index 3dab9c5..d2027f8 100644 --- a/MLAPI/Data/NetworkingConfiguration.cs +++ b/MLAPI/Data/NetworkingConfiguration.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Security.Cryptography; using UnityEngine.Networking; @@ -27,6 +28,8 @@ namespace MLAPI //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 //TODO public bool EncryptMessages = false; + public bool UseUPnP = true; + public Action UPnPCompleteCallback; //Cached config hash private byte[] ConfigHash = null; diff --git a/MLAPI/Helper/UPnPHelper.cs b/MLAPI/Helper/UPnPHelper.cs new file mode 100644 index 0000000..b617cc2 --- /dev/null +++ b/MLAPI/Helper/UPnPHelper.cs @@ -0,0 +1,86 @@ +using Open.Nat; +using System; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; + +namespace MLAPI.Helper +{ + public class UPnPHelper + { + internal static void AttemptPortMap(int port, Action callback) + { + bool invoked = false; + NatDiscoverer nat = new NatDiscoverer(); + CancellationTokenSource cts = new CancellationTokenSource(); + cts.CancelAfter(10000); + + NatDevice device = null; + StringBuilder sb = new StringBuilder(); + Task natTask = nat.DiscoverDeviceAsync(PortMapper.Upnp, cts); + Mapping tcpMapping = new Mapping(Protocol.Tcp, port, port, 0, Application.productName + " (TCP)"); + Mapping udpMapping = new Mapping(Protocol.Udp, port, port, 0, Application.productName + " (UDP)"); + IPAddress publicIPAddress = null; + natTask.ContinueWith(tt => + { + device = tt.Result; + device.GetExternalIPAsync() + .ContinueWith(task => + { + publicIPAddress = task.Result; + return device.CreatePortMapAsync(udpMapping); + }) + .Unwrap() + .ContinueWith(task => + { + return device.CreatePortMapAsync(udpMapping); + }) + .Unwrap() + .ContinueWith(task => + { + return device.GetAllMappingsAsync(); + }) + .Unwrap() + .ContinueWith(task => + { + Mapping[] mappings = task.Result.ToArray(); + if(mappings.Length == 0) + { + if (!invoked) + callback(false, publicIPAddress); + invoked = true; + } + else + { + for (int i = 0; i < mappings.Length; i++) + { + if(mappings[i].PrivatePort == port) + { + if (!invoked) + callback(true, publicIPAddress); + invoked = true; + } + } + } + }); + }, TaskContinuationOptions.OnlyOnRanToCompletion); + + try + { + natTask.Wait(); + } + catch (AggregateException e) + { + if (e.InnerException is NatDeviceNotFoundException) + { + if (!invoked) + callback(false, publicIPAddress); + invoked = true; + } + } + } + } +} diff --git a/MLAPI/MLAPI.csproj b/MLAPI/MLAPI.csproj index 66102ae..d9cd4d2 100644 --- a/MLAPI/MLAPI.csproj +++ b/MLAPI/MLAPI.csproj @@ -32,8 +32,14 @@ 4 + + ..\packages\Open.NAT.2.1.0.0\lib\net35\Open.Nat.dll + + + ..\packages\Open.NAT.2.1.0.0\lib\net35\System.Threading.dll + @@ -44,6 +50,7 @@ + @@ -51,5 +58,8 @@ + + + \ No newline at end of file diff --git a/MLAPI/MonoBehaviours/Core/NetworkingManager.cs b/MLAPI/MonoBehaviours/Core/NetworkingManager.cs index c8b11df..6f3ad04 100644 --- a/MLAPI/MonoBehaviours/Core/NetworkingManager.cs +++ b/MLAPI/MonoBehaviours/Core/NetworkingManager.cs @@ -1,4 +1,5 @@ -using System; +using MLAPI.Helper; +using System; using System.Collections; using System.Collections.Generic; using System.IO; @@ -344,6 +345,10 @@ namespace MLAPI Debug.LogWarning("MLAPI: No ConnectionApproval callback defined. Connection approval will timeout"); } } + if (NetworkConfig.UseUPnP) + { + UPnPHelper.AttemptPortMap(NetworkConfig.Port, NetworkConfig.UPnPCompleteCallback); + } HostTopology hostTopology = new HostTopology(cConfig, NetworkConfig.MaxConnections); hostId = NetworkTransport.AddHost(hostTopology, NetworkConfig.Port); isServer = true; @@ -373,6 +378,10 @@ namespace MLAPI Debug.LogWarning("MLAPI: No ConnectionApproval callback defined. Connection approval will timeout"); } } + if(NetworkConfig.UseUPnP) + { + UPnPHelper.AttemptPortMap(NetworkConfig.Port, NetworkConfig.UPnPCompleteCallback); + } HostTopology hostTopology = new HostTopology(cConfig, NetworkConfig.MaxConnections); hostId = NetworkTransport.AddHost(hostTopology, NetworkConfig.Port, null); isServer = true; diff --git a/MLAPI/packages.config b/MLAPI/packages.config new file mode 100644 index 0000000..409883c --- /dev/null +++ b/MLAPI/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/README.md b/README.md index dd26002..d698531 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,8 @@ The project is WIP. It's licenced under the MIT licence :D -## Features that are planned / done are: -* Object and player spawning (done) -* Connection approval (done) -* Message names (done) -* 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) -* ProtocolVersion to allow making different versions not talk to each other. (done) -* NetworkedBehaviours does not have to be on the root, it's simply just a class that implements the send methods etc. (done) -* Multiple messages processed every frame with the ability to specify a maximum to prevent freezes in the normal game logic (done) + +## Planned features * Built in lag compensation (going to be worked on when all base functionality is there) * Area of interest (not working on ATM but it's on the TODO) * Core gameplay components similar to what the HLAPI offers (but hopefully of better quality) @@ -20,7 +14,19 @@ It's licenced under the MIT licence :D * Serializer (both for the library to speed up and to allow structs to be sent easily) * SyncVars (allow variables to automatically be synced to new clients and current clients when it's changed) * Message compression + + +## Done features * Host support (Client hosts the server) (done) +* Port forwarding using Open.NAT using the UPnP protcol (done) +* Object and player spawning (done) +* Connection approval (done) +* Message names (done) +* 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) +* ProtocolVersion to allow making different versions not talk to each other. (done) +* NetworkedBehaviours does not have to be on the root, it's simply just a class that implements the send methods etc. (done) +* Multiple messages processed every frame with the ability to specify a maximum to prevent freezes in the normal game logic (done) + That's all I can think of right now. But there is more to come, especially if people show intrest in the project.