WingMan

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 31  →  ?path2? @ 32
/trunk/WingMan/Discovery/Discovery.cs
@@ -0,0 +1,99 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Open.Nat;
 
namespace WingMan.Discovery
{
public class Discovery : IDisposable
{
public delegate void PortMapFailed(object sender, DiscoveryFailedEventArgs args);
 
public Discovery()
{
NatDiscoverer = new NatDiscoverer();
}
 
public Discovery(CancellationTokenSource cancellationTokenSource, TaskScheduler taskScheduler) : this()
{
CancellationTokenSource = cancellationTokenSource;
TaskScheduler = taskScheduler;
}
 
private static CancellationTokenSource CancellationTokenSource { get; set; }
private static NatDiscoverer NatDiscoverer { get; set; }
private static TaskScheduler TaskScheduler { get; set; }
 
private static CancellationTokenSource UPnPCancellationTokenSource { get; set; }
private static CancellationTokenSource PMPCancellationTokenSource { get; set; }
 
public void Dispose()
{
PMPCancellationTokenSource?.Dispose();
PMPCancellationTokenSource = null;
 
PMPCancellationTokenSource?.Dispose();
PMPCancellationTokenSource = null;
}
 
public event PortMapFailed OnPortMapFailed;
 
public async Task<bool> CreateMapping(DiscoveryType type, int port)
{
switch (type)
{
case DiscoveryType.UPnP:
return await CreateUPnPMapping(port);
case DiscoveryType.PMP:
return await CreatePMPPortMapping(port);
default:
throw new ArgumentException("Unknown disocvery type");
}
}
 
private async Task<bool> CreateUPnPMapping(int port)
{
try
{
UPnPCancellationTokenSource = new CancellationTokenSource();
var linkedCancellationTokenSource =
CancellationTokenSource.CreateLinkedTokenSource(UPnPCancellationTokenSource.Token,
CancellationTokenSource.Token);
var device = await NatDiscoverer.DiscoverDeviceAsync(PortMapper.Upnp, linkedCancellationTokenSource);
await device.CreatePortMapAsync(new Mapping(Protocol.Tcp, port, port, "WingMan Host"));
 
return true;
}
catch (Exception ex)
{
await Task.Delay(0, CancellationTokenSource.Token).ContinueWith(_ =>
OnPortMapFailed?.Invoke(this, new DiscoveryFailedEventArgs(DiscoveryType.UPnP, ex)),
CancellationTokenSource.Token, TaskContinuationOptions.None, TaskScheduler);
 
return false;
}
}
 
private async Task<bool> CreatePMPPortMapping(int port)
{
try
{
PMPCancellationTokenSource = new CancellationTokenSource();
var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(
PMPCancellationTokenSource.Token,
CancellationTokenSource.Token);
var device = await NatDiscoverer.DiscoverDeviceAsync(PortMapper.Pmp, linkedCancellationTokenSource);
await device.CreatePortMapAsync(new Mapping(Protocol.Tcp, port, port, "WingMan Host"));
return true;
}
catch (Exception ex)
{
await Task.Delay(0, CancellationTokenSource.Token).ContinueWith(_ =>
OnPortMapFailed?.Invoke(this, new DiscoveryFailedEventArgs(DiscoveryType.PMP, ex)),
CancellationTokenSource.Token, TaskContinuationOptions.None, TaskScheduler);
 
return false;
}
}
}
}
/trunk/WingMan/Discovery/DiscoveryFailedEventArgs.cs
@@ -0,0 +1,16 @@
using System;
 
namespace WingMan.Discovery
{
public class DiscoveryFailedEventArgs : EventArgs
{
public DiscoveryFailedEventArgs(DiscoveryType type, Exception ex)
{
Type = type;
Exception = ex;
}
 
public DiscoveryType Type { get; set; }
public Exception Exception { get; set; }
}
}
/trunk/WingMan/Discovery/DiscoveryType.cs
@@ -0,0 +1,8 @@
namespace WingMan.Discovery
{
public enum DiscoveryType
{
UPnP,
PMP
}
}
/trunk/WingMan/Properties/Strings.Designer.cs
@@ -151,6 +151,16 @@
}
/// <summary>
/// Looks up a localized string similar to Failed creating automatic port mapping, please ensure that the port is routed through the firewall properly.
/// </summary>
internal static string Failed_creating_automatic_port_mapping_please_make_sure_the_port_is_routed_properly {
get {
return ResourceManager.GetString("Failed_creating_automatic_port_mapping_please_make_sure_the_port_is_routed_proper" +
"ly", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed loading autocomplete source.
/// </summary>
internal static string Failed_loading_autocomplete_source {
@@ -241,6 +251,24 @@
}
/// <summary>
/// Looks up a localized string similar to Failed to create PMP port mapping.
/// </summary>
internal static string Failed_to_create_PMP_port_mapping {
get {
return ResourceManager.GetString("Failed_to_create_PMP_port_mapping", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed to create UPnp port mapping.
/// </summary>
internal static string Failed_to_create_UPnP_port_mapping {
get {
return ResourceManager.GetString("Failed_to_create_UPnP_port_mapping", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Lobby message.
/// </summary>
internal static string Lobby_message {
/trunk/WingMan/Properties/Strings.resx
@@ -147,6 +147,9 @@
<data name="Executing_binding_from_remote_client" xml:space="preserve">
<value>Executing binding from remote client</value>
</data>
<data name="Failed_creating_automatic_port_mapping_please_make_sure_the_port_is_routed_properly" xml:space="preserve">
<value>Failed creating automatic port mapping, please ensure that the port is routed through the firewall properly</value>
</data>
<data name="Failed_loading_autocomplete_source" xml:space="preserve">
<value>Failed loading autocomplete source</value>
</data>
@@ -177,6 +180,12 @@
<data name="Failed_to_authenticate_with_server" xml:space="preserve">
<value>Failed to authenticate with server</value>
</data>
<data name="Failed_to_create_PMP_port_mapping" xml:space="preserve">
<value>Failed to create PMP port mapping</value>
</data>
<data name="Failed_to_create_UPnP_port_mapping" xml:space="preserve">
<value>Failed to create UPnp port mapping</value>
</data>
<data name="Lobby_message" xml:space="preserve">
<value>Lobby message</value>
</data>
/trunk/WingMan/WingMan.csproj
@@ -84,6 +84,9 @@
<Compile Include="AutoCompletion\AutoCompletionFailedEventArgs.cs" />
<Compile Include="AutoCompletion\AutoCompletionFailedType.cs" />
<Compile Include="Communication\MqttAuthenticationFailureEventArgs.cs" />
<Compile Include="Discovery\Discovery.cs" />
<Compile Include="Discovery\DiscoveryFailedEventArgs.cs" />
<Compile Include="Discovery\DiscoveryType.cs" />
<Compile Include="Lobby\LobbyMessageReceivedEventArgs.cs" />
<Compile Include="Lobby\LobbyMessageSynchronizer.cs" />
<Compile Include="Bindings\ExecuteKeyBinding.cs" />
/trunk/WingMan/WingManForm.cs
@@ -15,6 +15,7 @@
using WingMan.AutoCompletion;
using WingMan.Bindings;
using WingMan.Communication;
using WingMan.Discovery;
using WingMan.Lobby;
using WingMan.Properties;
using WingMan.Utilities;
@@ -30,6 +31,10 @@
FormTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
FormCancellationTokenSource = new CancellationTokenSource();
 
// Set up discovery.
Discovery = new Discovery.Discovery(FormCancellationTokenSource, FormTaskScheduler);
Discovery.OnPortMapFailed += OnDiscoveryPortMapFailed;
 
// Set up autocompletion.
AutoCompletion = new AutoCompletion.AutoCompletion(FormTaskScheduler, FormCancellationTokenSource.Token);
AutoCompletion.OnSaveFailed += AutoCompletionOnSaveFailed;
@@ -96,6 +101,7 @@
KeySimulator.OnMouseKeyBindingExecuting += OnMouseKeyBindingExecuting;
}
 
private static Discovery.Discovery Discovery { get; set; }
private static AutoCompletion.AutoCompletion AutoCompletion { get; set; }
private static CancellationTokenSource FormCancellationTokenSource { get; set; }
 
@@ -125,6 +131,21 @@
 
public KeySimulator KeySimulator { get; set; }
 
public void OnDiscoveryPortMapFailed(object sender, DiscoveryFailedEventArgs args)
{
switch (args.Type)
{
case DiscoveryType.UPnP:
ActivityTextBox.AppendText(
$"{Strings.Failed_to_create_UPnP_port_mapping}{Environment.NewLine}");
break;
case DiscoveryType.PMP:
ActivityTextBox.AppendText(
$"{Strings.Failed_to_create_PMP_port_mapping}{Environment.NewLine}");
break;
}
}
 
private void LocalCheckedListBoxBindingSourceOnListChanged(object sender, ListChangedEventArgs e)
{
// Check items
@@ -342,6 +363,12 @@
 
StoreConnectionAutocomplete();
 
// Try to reserve port: try UPnP followed by PMP.
if (!await Discovery.CreateMapping(DiscoveryType.UPnP, port) &&
!await Discovery.CreateMapping(DiscoveryType.PMP, port))
ActivityTextBox.AppendText(
$"{Strings.Failed_creating_automatic_port_mapping_please_make_sure_the_port_is_routed_properly}{Environment.NewLine}");
 
// Start the MQTT server.
if (!await MqttCommunication
.Start(MqttCommunicationType.Server, ipAddress, port, nick, password))