Port back to Mono.Nat and make discovery async.

This commit is contained in:
Matthias Mailänder
2021-03-23 19:24:51 +01:00
committed by Paul Chote
parent 3f510b6d93
commit d15e7f76fc
13 changed files with 134 additions and 128 deletions

View File

@@ -173,9 +173,8 @@ under the MIT license.
Using FuzzyLogicLibrary (fuzzynet) by Dmitry Using FuzzyLogicLibrary (fuzzynet) by Dmitry
Kaluzhny and released under the GNU GPL terms. Kaluzhny and released under the GNU GPL terms.
Using Open.Nat by Lucas Ontivero, based on the work Using Mono.Nat by Alan McGovern, Ben Motmans,
of Alan McGovern and Ben Motmans and distributed Nicholas Terry distributed under the MIT license.
under the MIT license.
Using ICSharpCode.SharpZipLib initially by Mike Using ICSharpCode.SharpZipLib initially by Mike
Krueger and distributed under the GNU GPL terms. Krueger and distributed under the GNU GPL terms.

View File

@@ -56,7 +56,6 @@ namespace OpenRA
public static string EngineVersion { get; private set; } public static string EngineVersion { get; private set; }
public static LocalPlayerProfile LocalPlayerProfile; public static LocalPlayerProfile LocalPlayerProfile;
static Task discoverNat;
static bool takeScreenshot = false; static bool takeScreenshot = false;
static Benchmark benchmark = null; static Benchmark benchmark = null;
@@ -360,8 +359,7 @@ namespace OpenRA
} }
} }
if (Settings.Server.DiscoverNatDevices) Nat.Initialize();
discoverNat = UPnP.DiscoverNatDevices(Settings.Server.NatDiscoveryTimeout);
var modSearchArg = args.GetValue("Engine.ModSearchPaths", null); var modSearchArg = args.GetValue("Engine.ModSearchPaths", null);
var modSearchPaths = modSearchArg != null ? var modSearchPaths = modSearchArg != null ?
@@ -472,16 +470,6 @@ namespace OpenRA
JoinLocal(); JoinLocal();
try
{
discoverNat?.Wait();
}
catch (Exception e)
{
Console.WriteLine("NAT discovery failed: {0}", e.Message);
Log.Write("nat", e.ToString());
}
ChromeMetrics.TryGet("ChatMessageColor", out chatMessageColor); ChromeMetrics.TryGet("ChatMessageColor", out chatMessageColor);
ChromeMetrics.TryGet("SystemMessageColor", out systemMessageColor); ChromeMetrics.TryGet("SystemMessageColor", out systemMessageColor);
if (!ChromeMetrics.TryGet("SystemMessageLabel", out systemMessageLabel)) if (!ChromeMetrics.TryGet("SystemMessageLabel", out systemMessageLabel))

101
OpenRA.Game/Network/Nat.cs Normal file
View File

@@ -0,0 +1,101 @@
#region Copyright & License Information
/*
* Copyright 2007-2021 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Threading;
using Mono.Nat;
namespace OpenRA.Network
{
public enum NatStatus { Enabled, Disabled, NotSupported }
public class Nat
{
public static NatStatus Status => NatUtility.IsSearching ? natDevice != null ? NatStatus.Enabled : NatStatus.NotSupported : NatStatus.Disabled;
static Mapping mapping;
static INatDevice natDevice;
static bool initialized;
public static void Initialize()
{
if (initialized)
return;
if (Game.Settings.Server.DiscoverNatDevices)
{
NatUtility.DeviceFound += DeviceFound;
NatUtility.StartDiscovery();
}
initialized = true;
}
static readonly SemaphoreSlim Locker = new SemaphoreSlim(1, 1);
static async void DeviceFound(object sender, DeviceEventArgs args)
{
await Locker.WaitAsync();
try
{
// Only interact with one at a time. Some support both UPnP and NAT-PMP.
natDevice = args.Device;
Log.Write("nat", "Device found: {0}", natDevice.DeviceEndpoint);
Log.Write("nat", "Type: {0}", natDevice.NatProtocol);
}
finally
{
Locker.Release();
}
}
public static bool TryForwardPort(int listen, int external)
{
if (natDevice == null)
return false;
var lifetime = Game.Settings.Server.NatPortMappingLifetime;
mapping = new Mapping(Protocol.Tcp, listen, external, lifetime, "OpenRA");
try
{
natDevice.CreatePortMap(mapping);
}
catch (Exception e)
{
Console.WriteLine("Port forwarding failed: {0}", e.Message);
Log.Write("nat", e.StackTrace);
return false;
}
return true;
}
public static bool TryRemovePortForward()
{
if (natDevice == null)
return false;
try
{
natDevice.DeletePortMap(mapping);
}
catch (Exception e)
{
Console.WriteLine("Port removal failed: {0}", e.Message);
Log.Write("nat", e.StackTrace);
return false;
}
return true;
}
}
}

View File

@@ -1,84 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Diagnostics;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Open.Nat;
namespace OpenRA.Network
{
public enum UPnPStatus { Enabled, Disabled, NotSupported }
public class UPnP
{
static NatDevice natDevice;
static Mapping mapping;
static bool initialized;
public static IPAddress ExternalIP { get; private set; }
public static UPnPStatus Status =>
initialized ? natDevice != null ?
UPnPStatus.Enabled : UPnPStatus.NotSupported : UPnPStatus.Disabled;
public static async Task DiscoverNatDevices(int timeout)
{
initialized = true;
NatDiscoverer.TraceSource.Switch.Level = SourceLevels.Verbose;
var logChannel = Log.Channel("nat");
NatDiscoverer.TraceSource.Listeners.Add(new TextWriterTraceListener(logChannel.Writer));
var natDiscoverer = new NatDiscoverer();
var token = new CancellationTokenSource(timeout);
natDevice = await natDiscoverer.DiscoverDeviceAsync(PortMapper.Upnp, token);
try
{
ExternalIP = await natDevice.GetExternalIPAsync();
}
catch (Exception e)
{
Console.WriteLine("Getting the external IP from NAT device failed: {0}", e.Message);
Log.Write("nat", e.StackTrace);
}
}
public static async Task ForwardPort(int listen, int external)
{
mapping = new Mapping(Protocol.Tcp, listen, external, "OpenRA");
try
{
await natDevice.CreatePortMapAsync(mapping);
}
catch (Exception e)
{
Console.WriteLine("Port forwarding failed: {0}", e.Message);
Log.Write("nat", e.StackTrace);
}
}
public static async Task RemovePortForward()
{
try
{
await natDevice.DeletePortMapAsync(mapping);
}
catch (Exception e)
{
Console.WriteLine("Port removal failed: {0}", e.Message);
Log.Write("nat", e.StackTrace);
}
}
}
}

View File

@@ -37,7 +37,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="OpenRA-Eluant" Version="1.0.18" /> <PackageReference Include="OpenRA-Eluant" Version="1.0.18" />
<PackageReference Include="OpenRA-Open.NAT" Version="1.0.0" /> <PackageReference Include="Mono.NAT" Version="3.0.1" />
<PackageReference Include="SharpZipLib" Version="1.3.1" /> <PackageReference Include="SharpZipLib" Version="1.3.1" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" /> <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" /> <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />

View File

@@ -221,8 +221,8 @@ namespace OpenRA.Server
if (type != ServerType.Local && settings.EnableGeoIP) if (type != ServerType.Local && settings.EnableGeoIP)
GeoIP.Initialize(); GeoIP.Initialize();
if (type != ServerType.Local && UPnP.Status == UPnPStatus.Enabled) if (type != ServerType.Local)
UPnP.ForwardPort(Settings.ListenPort, Settings.ListenPort).Wait(); Nat.TryForwardPort(Settings.ListenPort, Settings.ListenPort);
foreach (var trait in modData.Manifest.ServerTraits) foreach (var trait in modData.Manifest.ServerTraits)
serverTraits.Add(modData.ObjectCreator.CreateObject<ServerTrait>(trait)); serverTraits.Add(modData.ObjectCreator.CreateObject<ServerTrait>(trait));
@@ -310,8 +310,8 @@ namespace OpenRA.Server
if (State == ServerState.ShuttingDown) if (State == ServerState.ShuttingDown)
{ {
EndGame(); EndGame();
if (type != ServerType.Local && UPnP.Status == UPnPStatus.Enabled) if (type != ServerType.Local)
UPnP.RemovePortForward().Wait(); Nat.TryRemovePortForward();
break; break;
} }
} }

View File

@@ -49,11 +49,11 @@ namespace OpenRA
[Desc("Locks the game with a password.")] [Desc("Locks the game with a password.")]
public string Password = ""; public string Password = "";
[Desc("Allow users to enable NAT discovery for external IP detection and automatic port forwarding.")] [Desc("Allow users to search UPnP/NAT-PMP enabled devices for automatic port forwarding.")]
public bool DiscoverNatDevices = false; public bool DiscoverNatDevices = false;
[Desc("Time in milliseconds to search for UPnP enabled NAT devices.")] [Desc("Time in seconds for UPnP/NAT-PMP mappings to last.")]
public int NatDiscoveryTimeout = 5000; public int NatPortMappingLifetime = 36000;
[Desc("Starts the game with a default map. Input as hash that can be obtained by the utility.")] [Desc("Starts the game with a default map. Input as hash that can be obtained by the utility.")]
public string Map = null; public string Map = null;

View File

@@ -116,20 +116,20 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (noticesNoUPnP != null) if (noticesNoUPnP != null)
{ {
noticesNoUPnP.IsVisible = () => advertiseOnline && noticesNoUPnP.IsVisible = () => advertiseOnline &&
(UPnP.Status == UPnPStatus.NotSupported || UPnP.Status == UPnPStatus.Disabled); (Nat.Status == NatStatus.NotSupported || Nat.Status == NatStatus.Disabled);
var settingsA = noticesNoUPnP.GetOrNull("SETTINGS_A"); var settingsA = noticesNoUPnP.GetOrNull("SETTINGS_A");
if (settingsA != null) if (settingsA != null)
settingsA.IsVisible = () => UPnP.Status == UPnPStatus.Disabled; settingsA.IsVisible = () => Nat.Status == NatStatus.Disabled;
var settingsB = noticesNoUPnP.GetOrNull("SETTINGS_B"); var settingsB = noticesNoUPnP.GetOrNull("SETTINGS_B");
if (settingsB != null) if (settingsB != null)
settingsB.IsVisible = () => UPnP.Status == UPnPStatus.Disabled; settingsB.IsVisible = () => Nat.Status == NatStatus.Disabled;
} }
var noticesUPnP = panel.GetOrNull("NOTICES_UPNP"); var noticesUPnP = panel.GetOrNull("NOTICES_UPNP");
if (noticesUPnP != null) if (noticesUPnP != null)
noticesUPnP.IsVisible = () => advertiseOnline && UPnP.Status == UPnPStatus.Enabled; noticesUPnP.IsVisible = () => advertiseOnline && Nat.Status == NatStatus.Enabled;
var noticesLAN = panel.GetOrNull("NOTICES_LAN"); var noticesLAN = panel.GetOrNull("NOTICES_LAN");
if (noticesLAN != null) if (noticesLAN != null)
@@ -145,16 +145,15 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (advertiseOnline) if (advertiseOnline)
{ {
noticesLabelA.Text = "Internet Server (UPnP "; noticesLabelA.Text = "Internet Server (UPnP/NAT-PMP ";
var aWidth = Game.Renderer.Fonts[noticesLabelA.Font].Measure(noticesLabelA.Text).X; var aWidth = Game.Renderer.Fonts[noticesLabelA.Font].Measure(noticesLabelA.Text).X;
noticesLabelA.Bounds.Width = aWidth; noticesLabelA.Bounds.Width = aWidth;
var status = UPnP.Status; noticesLabelB.Text = Nat.Status == NatStatus.Enabled ? "Enabled" :
noticesLabelB.Text = status == UPnPStatus.Enabled ? "Enabled" : Nat.Status == NatStatus.NotSupported ? "Not Supported" : "Disabled";
status == UPnPStatus.NotSupported ? "Not Supported" : "Disabled";
noticesLabelB.TextColor = status == UPnPStatus.Enabled ? ChromeMetrics.Get<Color>("NoticeSuccessColor") : noticesLabelB.TextColor = Nat.Status == NatStatus.Enabled ? ChromeMetrics.Get<Color>("NoticeSuccessColor") :
status == UPnPStatus.NotSupported ? ChromeMetrics.Get<Color>("NoticeErrorColor") : Nat.Status == NatStatus.NotSupported ? ChromeMetrics.Get<Color>("NoticeErrorColor") :
ChromeMetrics.Get<Color>("NoticeInfoColor"); ChromeMetrics.Get<Color>("NoticeInfoColor");
var bWidth = Game.Renderer.Fonts[noticesLabelB.Font].Measure(noticesLabelB.Text).X; var bWidth = Game.Renderer.Fonts[noticesLabelB.Font].Measure(noticesLabelB.Text).X;

View File

@@ -14,6 +14,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using OpenRA.Network;
using OpenRA.Support; using OpenRA.Support;
namespace OpenRA.Server namespace OpenRA.Server
@@ -56,6 +57,8 @@ namespace OpenRA.Server
Game.InitializeSettings(arguments); Game.InitializeSettings(arguments);
var settings = Game.Settings.Server; var settings = Game.Settings.Server;
Nat.Initialize();
var envModSearchPaths = Environment.GetEnvironmentVariable("MOD_SEARCH_PATHS"); var envModSearchPaths = Environment.GetEnvironmentVariable("MOD_SEARCH_PATHS");
var modSearchPaths = !string.IsNullOrWhiteSpace(envModSearchPaths) ? var modSearchPaths = !string.IsNullOrWhiteSpace(envModSearchPaths) ?
FieldLoader.GetValue<string[]>("MOD_SEARCH_PATHS", envModSearchPaths) : FieldLoader.GetValue<string[]>("MOD_SEARCH_PATHS", envModSearchPaths) :

View File

@@ -162,7 +162,7 @@ Container@MULTIPLAYER_CREATESERVER_PANEL:
Height: 25 Height: 25
Font: Tiny Font: Tiny
Align: Left Align: Left
Text: - You can enable UPnP (if supported by your router) in the Text: - You can enable UPnP/NAT-PMP (if supported by your router)
Label@SETTINGS_B: Label@SETTINGS_B:
X: 7 X: 7
Y: 60 Y: 60
@@ -170,7 +170,7 @@ Container@MULTIPLAYER_CREATESERVER_PANEL:
Height: 25 Height: 25
Font: Tiny Font: Tiny
Align: Left Align: Left
Text: Advanced tab of the OpenRA settings menu. Text: in the Advanced tab of the settings menu.
Container@NOTICES_UPNP: Container@NOTICES_UPNP:
X: 20 X: 20
Y: 145 Y: 145
@@ -196,14 +196,14 @@ Container@MULTIPLAYER_CREATESERVER_PANEL:
Height: 25 Height: 25
Font: Tiny Font: Tiny
Align: Left Align: Left
Text: - OpenRA will use UPnP to automaticaly configure port forwarding. Text: - Game will automatically configure port forwarding.
Label@SETTINGS_A: Label@SETTINGS_A:
Y: 36 Y: 36
Width: 305 Width: 305
Height: 25 Height: 25
Font: Tiny Font: Tiny
Align: Left Align: Left
Text: - You can disable UPnP in the OpenRA settings menu. Text: - You can disable UPnP/NAT-PMP in the settings menu.
Background@MAP_BG: Background@MAP_BG:
X: PARENT_RIGHT - 189 X: PARENT_RIGHT - 189
Y: 15 Y: 15

View File

@@ -15,7 +15,7 @@ Container@ADVANCED_PANEL:
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
Text: Enable Network Discovery (UPnP) Text: Enable UPnP/NAT-PMP Discovery
Checkbox@PERFTEXT_CHECKBOX: Checkbox@PERFTEXT_CHECKBOX:
X: 15 X: 15
Y: 73 Y: 73

View File

@@ -157,7 +157,7 @@ Background@MULTIPLAYER_CREATESERVER_PANEL:
Height: 25 Height: 25
Font: Tiny Font: Tiny
Align: Left Align: Left
Text: - You can enable UPnP (if supported by your router) in the Text: - You can enable UPnP/NAT-PMP (if supported by your router)
Label@SETTINGS_B: Label@SETTINGS_B:
X: 7 X: 7
Y: 60 Y: 60
@@ -165,7 +165,7 @@ Background@MULTIPLAYER_CREATESERVER_PANEL:
Height: 25 Height: 25
Font: Tiny Font: Tiny
Align: Left Align: Left
Text: Advanced tab of the OpenRA settings menu. Text: in the Advanced tab of the settings menu.
Container@NOTICES_UPNP: Container@NOTICES_UPNP:
X: 25 X: 25
Y: 176 Y: 176
@@ -191,14 +191,14 @@ Background@MULTIPLAYER_CREATESERVER_PANEL:
Height: 25 Height: 25
Font: Tiny Font: Tiny
Align: Left Align: Left
Text: - OpenRA will use UPnP to automaticaly configure port forwarding. Text: - Game will automatically configure port forwarding.
Label@SETTINGS_A: Label@SETTINGS_A:
Y: 36 Y: 36
Width: 305 Width: 305
Height: 25 Height: 25
Font: Tiny Font: Tiny
Align: Left Align: Left
Text: - You can disable UPnP in the OpenRA settings menu. Text: - You can disable UPnP/NAT-PMP in the settings menu.
Background@MAP_BG: Background@MAP_BG:
X: PARENT_RIGHT - 194 X: PARENT_RIGHT - 194
Y: 45 Y: 45

View File

@@ -9,7 +9,7 @@ Container@ADVANCED_PANEL:
Width: 200 Width: 200
Height: 20 Height: 20
Font: Regular Font: Regular
Text: Enable Network Discovery (UPnP) Text: Enable UPnP/NAT-PMP Discovery
Checkbox@PERFTEXT_CHECKBOX: Checkbox@PERFTEXT_CHECKBOX:
X: 15 X: 15
Y: 73 Y: 73