Hush

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ HEAD  →  ?path2? @ 1
/trunk/Hush/Hush.cs
@@ -8,13 +8,11 @@
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Hush.Chat;
using Hush.Communication;
using Hush.Discovery;
using Hush.Lobby;
using Hush.Properties;
using Hush.Utilities;
using MQTTnet.Extensions.ManagedClient;
using MQTTnet.Server;
using WingMan.Communication;
 
namespace Hush
@@ -21,12 +19,6 @@
{
public partial class Hush : Form
{
private static TaskScheduler FormTaskScheduler { get; set; }
private static CancellationTokenSource FormCancellationTokenSource { get; set; }
private static MqttCommunication MqttCommunication { get; set; }
private static ChatMessageSynchronizer ChatMessageSynchronizer { get; set; }
private static Discovery.Discovery Discovery { get; set; }
 
public Hush()
{
InitializeComponent();
@@ -40,27 +32,194 @@
Settings.Default.PropertyChanged += DefaultOnPropertyChanged;
 
// Set up discovery.
Discovery = new Discovery.Discovery(Constants.AssemblyName, FormCancellationTokenSource.Token,
Discovery = new Discovery.Discovery(Constants.ASSEMBLY_NAME, FormCancellationTokenSource,
FormTaskScheduler);
Discovery.OnPortMapFailed += OnDiscoveryPortMapFailed;
 
// Bind to MQTT events.
MqttCommunication = new MqttCommunication(FormTaskScheduler, FormCancellationTokenSource.Token);
MqttCommunication.OnClientConnectionFailed += MqttOnClientConnectionFailed;
MqttCommunication.OnClientAuthenticationFailed += MqttOnClientAuthenticationFailed;
MqttCommunication.OnClientConnected += MqttOnClientConnected;
MqttCommunication.OnClientDisconnected += MqttOnClientDisconnected;
MqttCommunication.OnServerClientConnected += MqttCommunicationOnOnServerClientConnected;
MqttCommunication.OnServerClientDisconnected += MqttOnServerClientDisconnected;
//mqttCommunication.
 
// Start message synchronizer.
ChatMessageSynchronizer = new ChatMessageSynchronizer(Constants.MqttTopic, MqttCommunication,
FormTaskScheduler,
// Start lobby message synchronizer.
LobbyMessageSynchronizer = new LobbyMessageSynchronizer(MqttCommunication, FormTaskScheduler,
FormCancellationTokenSource.Token);
ChatMessageSynchronizer.OnMessageReceived += OnMessageReceived;
LobbyMessageSynchronizer.OnLobbyMessageReceived += OnLobbyMessageReceived;
}
 
private static TaskScheduler FormTaskScheduler { get; set; }
private static CancellationTokenSource FormCancellationTokenSource { get; set; }
private static MqttCommunication MqttCommunication { get; set; }
private static LobbyMessageSynchronizer LobbyMessageSynchronizer { get; set; }
private static Discovery.Discovery Discovery { get; set; }
 
private void OnDiscoveryPortMapFailed(object sender, DiscoveryFailedEventArgs args)
{
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
notifyIcon1.BalloonTipTitle = Strings.Network_warning;
notifyIcon1.BalloonTipText = Strings.Failed_to_create_automatic_NAT_port_mapping;
notifyIcon1.ShowBalloonTip(1000);
}
 
private void OnLobbyMessageReceived(object sender, LobbyMessageReceivedEventArgs e)
{
LobbyTextBox.AppendText($"{e.Nick} : {e.Message}{Environment.NewLine}");
}
 
private async void DefaultOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "LaunchOnBoot")
LaunchOnBoot.Set(Settings.Default.LaunchOnBoot);
 
if (e.PropertyName == "Start")
await ToggleStart();
}
 
private async Task ToggleStart()
{
switch (Settings.Default.Mode)
{
case "Server":
await StopServer();
 
if (!Settings.Default.Start)
break;
 
try
{
await StartServer();
}
catch (ToolTippedException ex)
{
notifyIcon1.BalloonTipIcon = ex.Icon;
notifyIcon1.BalloonTipTitle = ex.Title;
notifyIcon1.BalloonTipText = ex.Body;
notifyIcon1.ShowBalloonTip(1000);
}
 
break;
case "Client":
await StopClient();
 
if (!Settings.Default.Start)
break;
 
try
{
await StartClient();
}
catch (ToolTippedException ex)
{
notifyIcon1.BalloonTipIcon = ex.Icon;
notifyIcon1.BalloonTipTitle = ex.Title;
notifyIcon1.BalloonTipText = ex.Body;
notifyIcon1.ShowBalloonTip(1000);
}
 
break;
}
 
startToolStripMenuItem.Checked = Settings.Default.Start;
}
 
private async Task StopClient()
{
// Stop the client if it is already started.
await MqttCommunication.Stop();
}
 
private async Task StopServer()
{
// Remove UPnP and Pmp mappings.
await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ =>
{
await Discovery.DeleteMapping(DiscoveryType.Upnp, MqttCommunication.Port);
await Discovery.DeleteMapping(DiscoveryType.Pmp, MqttCommunication.Port);
},
FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler);
 
// Stop the MQTT server if it is running.
await MqttCommunication.Stop();
}
 
private async Task StartClient()
{
if (!IPAddress.TryParse(Settings.Default.Address, out var address))
try
{
address = Dns.GetHostAddresses(Settings.Default.Address).FirstOrDefault();
}
catch
{
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error,
$"{Strings.Unable_to_determine_address} {Settings.Default.Address}");
}
 
if (!uint.TryParse(Settings.Default.Port, out var port))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error,
$"{Strings.Unable_to_determine_port} {Settings.Default.Port}");
 
 
if (string.IsNullOrEmpty(Settings.Default.Nick))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_nickname_set);
 
if (string.IsNullOrEmpty(Settings.Default.Password))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_password_set);
 
if (!await MqttCommunication
.Start(MqttCommunicationType.Client, address, (int) port, Settings.Default.Nick,
Settings.Default.Password,
new[] {"lobby"}))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.Unable_to_start_client);
}
 
private async Task StartServer()
{
if (!IPAddress.TryParse(Settings.Default.Address, out var address))
try
{
address = Dns.GetHostAddresses(Settings.Default.Address).FirstOrDefault();
}
catch
{
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error,
$"{Strings.Unable_to_determine_address} {Settings.Default.Address}");
}
 
if (!uint.TryParse(Settings.Default.Port, out var port))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error,
$"{Strings.Unable_to_determine_port} {Settings.Default.Port}");
 
 
if (string.IsNullOrEmpty(Settings.Default.Nick))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_nickname_set);
 
if (string.IsNullOrEmpty(Settings.Default.Password))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_password_set);
 
// Try to reserve port: try UPnP followed by PMP.
await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ =>
{
if (!await Discovery.CreateMapping(DiscoveryType.Upnp, (int) port))
await Discovery.CreateMapping(DiscoveryType.Pmp, (int) port);
},
FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler);
 
// Start the MQTT server.
if (!await MqttCommunication
.Start(MqttCommunicationType.Server, address, (int) port, Settings.Default.Nick,
Settings.Default.Password, new[] {"lobby"}))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.Unable_to_start_server);
}
 
private void DefaultOnSettingsSaving(object sender, CancelEventArgs e)
{
}
 
private void DefaultOnSettingsLoaded(object sender, SettingsLoadedEventArgs e)
{
LaunchOnBoot.Set(Settings.Default.LaunchOnBoot);
}
 
/// <summary>
/// Clean up any resources being used.
/// </summary>
@@ -67,9 +226,6 @@
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
// Unbind message synchronizer.
ChatMessageSynchronizer.OnMessageReceived -= OnMessageReceived;
 
// Unbind settings handlers.
Settings.Default.SettingsLoaded -= DefaultOnSettingsLoaded;
Settings.Default.SettingsSaving -= DefaultOnSettingsSaving;
@@ -79,7 +235,14 @@
base.Dispose(disposing);
}
 
#region Overrides
private void OnMouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Natives.ReleaseCapture();
Natives.SendMessage(Handle, Natives.WM_NCLBUTTONDOWN, Natives.HT_CAPTION, 0);
}
}
 
protected override void OnPaintBackground(PaintEventArgs e)
{
@@ -164,113 +327,12 @@
base.WndProc(ref m);
}
 
#endregion
 
#region Event Handlers
 
private void MqttOnServerClientDisconnected(object sender,
MqttClientDisconnectedEventArgs mqttClientDisconnectedEventArgs)
private void OnMouseEnter(object sender, EventArgs e)
{
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
notifyIcon1.BalloonTipTitle = Strings.Network_warning;
notifyIcon1.BalloonTipText = $"{Strings.Client_disconnected}";
notifyIcon1.ShowBalloonTip(1000);
}
 
 
private void MqttCommunicationOnOnServerClientConnected(object sender,
MqttClientConnectedEventArgs mqttClientConnectedEventArgs)
{
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
notifyIcon1.BalloonTipTitle = Strings.Network_warning;
notifyIcon1.BalloonTipText = $"{Strings.Client_connected}";
notifyIcon1.ShowBalloonTip(1000);
}
 
private void MqttOnClientDisconnected(object sender,
MQTTnet.Client.MqttClientDisconnectedEventArgs mqttClientDisconnectedEventArgs)
{
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
notifyIcon1.BalloonTipTitle = Strings.Network_warning;
notifyIcon1.BalloonTipText = $"{Strings.Client_disconnected}";
notifyIcon1.ShowBalloonTip(1000);
}
 
private void MqttOnClientConnected(object sender,
MQTTnet.Client.MqttClientConnectedEventArgs mqttClientConnectedEventArgs)
{
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
notifyIcon1.BalloonTipTitle = Strings.Network_warning;
notifyIcon1.BalloonTipText = $"{Strings.Client_connected}";
notifyIcon1.ShowBalloonTip(1000);
}
 
private void MqttOnClientAuthenticationFailed(object sender,
MqttAuthenticationFailureEventArgs mqttAuthenticationFailureEventArgs)
{
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
notifyIcon1.BalloonTipTitle = Strings.Network_warning;
notifyIcon1.BalloonTipText = $"{Strings.Failed_to_authenticate_to_server}";
notifyIcon1.ShowBalloonTip(1000);
}
 
private void MqttOnClientConnectionFailed(object sender,
MqttManagedProcessFailedEventArgs mqttManagedProcessFailedEventArgs)
{
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
notifyIcon1.BalloonTipTitle = Strings.Network_warning;
notifyIcon1.BalloonTipText =
$"{Strings.Failed_to_connect_to_server} {mqttManagedProcessFailedEventArgs.Exception.Message}";
notifyIcon1.ShowBalloonTip(1000);
}
 
private void OnDiscoveryPortMapFailed(object sender, DiscoveryFailedEventArgs args)
{
notifyIcon1.BalloonTipIcon = ToolTipIcon.Warning;
notifyIcon1.BalloonTipTitle = Strings.Network_warning;
notifyIcon1.BalloonTipText = Strings.Failed_to_create_automatic_NAT_port_mapping;
notifyIcon1.ShowBalloonTip(1000);
}
 
private void OnMessageReceived(object sender, ChatMessageReceivedEventArgs e)
{
var message = $"{e.Nick} : {e.Message}{Environment.NewLine}";
chatTextBox.AppendText(message);
}
 
private async void DefaultOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (string.Equals(e.PropertyName, "LaunchOnBoot"))
LaunchOnBoot.Set(Settings.Default.LaunchOnBoot);
 
if (string.Equals(e.PropertyName, "Start"))
await ToggleStart();
}
 
private void DefaultOnSettingsSaving(object sender, CancelEventArgs e)
{
}
 
private void DefaultOnSettingsLoaded(object sender, SettingsLoadedEventArgs e)
{
LaunchOnBoot.Set(Settings.Default.LaunchOnBoot);
}
 
private void OnMouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
DllImports.ReleaseCapture();
DllImports.SendMessage(Handle, DllImports.WM_NCLBUTTONDOWN, DllImports.HT_CAPTION, 0);
}
}
 
private void HushFocus(object sender, EventArgs e)
{
Opacity = .75;
}
 
private void HushUnfocus(object sender, EventArgs e)
private void OnMouseLeave(object sender, EventArgs e)
{
Opacity = .33;
}
@@ -287,22 +349,18 @@
 
private async void MessageTextBoxOnKeyDown(object sender, KeyEventArgs e)
{
// Prevent the enter key to be passed to the text box or we get a nasty ding.
if (e.KeyCode == Keys.Enter)
e.SuppressKeyPress = true;
// Do not send messages if the communication is not running.
if (!MqttCommunication.Running)
return;
 
if (e.KeyCode != Keys.Enter)
return;
 
// Do not send messages if the communication is not running.
if (!MqttCommunication.Running)
return;
 
if (string.IsNullOrEmpty(messageTextBox.Text))
return;
 
// Send the message
await ChatMessageSynchronizer.Broadcast($"{messageTextBox.Text}");
await LobbyMessageSynchronizer.Broadcast($"{messageTextBox.Text}\n");
 
messageTextBox.Text = string.Empty;
}
@@ -350,148 +408,5 @@
 
Settings.Default.Port = toolStripMenuItem.Text;
}
 
#endregion
 
private async Task ToggleStart()
{
switch (Settings.Default.Mode)
{
case "Server":
await StopServer();
 
if (!Settings.Default.Start)
break;
 
try
{
await StartServer();
}
catch (ToolTippedException ex)
{
notifyIcon1.BalloonTipIcon = ex.Icon;
notifyIcon1.BalloonTipTitle = ex.Title;
notifyIcon1.BalloonTipText = ex.Body;
notifyIcon1.ShowBalloonTip(1000);
}
 
break;
case "Client":
await StopClient();
 
if (!Settings.Default.Start)
break;
 
try
{
await StartClient();
}
catch (ToolTippedException ex)
{
notifyIcon1.BalloonTipIcon = ex.Icon;
notifyIcon1.BalloonTipTitle = ex.Title;
notifyIcon1.BalloonTipText = ex.Body;
notifyIcon1.ShowBalloonTip(1000);
}
 
break;
}
 
startToolStripMenuItem.Checked = Settings.Default.Start;
}
 
private async Task StopClient()
{
// Stop the client if it is already started.
await MqttCommunication.Stop();
}
 
private async Task StopServer()
{
// Remove UPnP and Pmp mappings.
await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ =>
{
await Discovery.DeleteMapping(DiscoveryType.Upnp, MqttCommunication.Port);
await Discovery.DeleteMapping(DiscoveryType.Pmp, MqttCommunication.Port);
},
FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler);
 
// Stop the MQTT server if it is running.
await MqttCommunication.Stop();
}
 
private async Task StartClient()
{
if (!IPAddress.TryParse(Settings.Default.Address, out var address))
try
{
var getHostAddresses = await Dns.GetHostAddressesAsync(Settings.Default.Address);
if (!getHostAddresses.Any())
throw new Exception();
 
address = getHostAddresses.FirstOrDefault();
}
catch
{
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error,
$"{Strings.Unable_to_determine_address} {Settings.Default.Address}");
}
 
if (!uint.TryParse(Settings.Default.Port, out var port))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error,
$"{Strings.Unable_to_determine_port} {Settings.Default.Port}");
 
 
if (string.IsNullOrEmpty(Settings.Default.Nick))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_nickname_set);
 
if (string.IsNullOrEmpty(Settings.Default.Password))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_password_set);
 
if (!await MqttCommunication
.Start(MqttCommunicationType.Client, address, (int) port, Settings.Default.Nick,
Settings.Default.Password,
new[] {Constants.MqttTopic}))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.Unable_to_start_client);
}
 
private async Task StartServer()
{
if (!IPAddress.TryParse(Settings.Default.Address, out var address))
try
{
address = Dns.GetHostAddresses(Settings.Default.Address).FirstOrDefault();
}
catch
{
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error,
$"{Strings.Unable_to_determine_address} {Settings.Default.Address}");
}
 
if (!uint.TryParse(Settings.Default.Port, out var port))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error,
$"{Strings.Unable_to_determine_port} {Settings.Default.Port}");
 
 
if (string.IsNullOrEmpty(Settings.Default.Nick))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_nickname_set);
 
if (string.IsNullOrEmpty(Settings.Default.Password))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.No_password_set);
 
// Try to reserve port: try UPnP followed by PMP.
await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ =>
{
if (!await Discovery.CreateMapping(DiscoveryType.Upnp, (int) port))
await Discovery.CreateMapping(DiscoveryType.Pmp, (int) port);
},
FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler);
 
// Start the MQTT server.
if (!await MqttCommunication
.Start(MqttCommunicationType.Server, address, (int) port, Settings.Default.Nick,
Settings.Default.Password, new[] {Constants.MqttTopic}))
throw new ToolTippedException(ToolTipIcon.Error, Strings.Network_error, Strings.Unable_to_start_server);
}
}
}