WingMan – Rev 9
?pathlinks?
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Gma.System.MouseKeyHook;
using MQTTnet.Extensions.ManagedClient;
using MQTTnet.Server;
using WingMan.Communication;
using WingMan.Lobby;
using WingMan.MouseKey;
using WingMan.Properties;
using WingMan.Utilities;
namespace WingMan
{
public partial class WingManForm : Form
{
public WingManForm()
{
InitializeComponent();
FormTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
FormCancellationTokenSource = new CancellationTokenSource();
MqttCommunication = new MqttCommunication(FormTaskScheduler, FormCancellationTokenSource.Token);
MqttCommunication.OnClientAuthenticationFailed += OnMqttClientAuthenticationFailed;
MqttCommunication.OnClientConnectionFailed += OnMqttClientConnectionFailed;
MqttCommunication.OnClientDisconnected += OnMqttClientDisconnected;
MqttCommunication.OnClientConnected += OnMqttClientConnected;
MqttCommunication.OnServerAuthenticationFailed += OnMqttServerAuthenticationFailed;
MqttCommunication.OnServerStopped += OnMqttServerStopped;
MqttCommunication.OnServerStarted += OnMqttServerStarted;
MqttCommunication.OnServerClientConnected += OnMqttServerClientConnected;
MqttCommunication.OnServerClientDisconnected += OnMqttServerClientDisconnected;
LocalMouseKeyBindings = new MouseKeyBindings(new List<MouseKeyBinding>());
RemoteMouseKeyBindings = new RemoteMouseKeyBindings(new List<RemoteMouseKeyBinding>());
LocalListBoxBindingSource = new BindingSource
{
DataSource = LocalMouseKeyBindings.Bindings
};
LocalBindingsListBox.DisplayMember = "DisplayName";
LocalBindingsListBox.ValueMember = "Keys";
LocalBindingsListBox.DataSource = LocalListBoxBindingSource;
MouseKeyBindingsExchange = new MouseKeyBindingsExchange
{
ExchangeBindings = new List<MouseKeyBindingExchange>()
};
RemoteBindingsComboBoxSource = new BindingSource
{
DataSource = MouseKeyBindingsExchange.ExchangeBindings
};
RemoteBindingsComboBox.DisplayMember = "Nick";
RemoteBindingsComboBox.ValueMember = "MouseKeyBindings";
RemoteBindingsComboBox.DataSource = RemoteBindingsComboBoxSource;
// Start lobby message synchronizer.
LobbyMessageSynchronizer = new LobbyMessageSynchronizer(MqttCommunication, FormTaskScheduler,
FormCancellationTokenSource.Token);
LobbyMessageSynchronizer.OnLobbyMessageReceived += OnLobbyMessageReceived;
// Start mouse key bindings synchronizer.
MouseKeyBindingsSynchronizer = new MouseKeyBindingsSynchronizer(LocalMouseKeyBindings, MqttCommunication,
FormTaskScheduler, FormCancellationTokenSource.Token);
MouseKeyBindingsSynchronizer.OnMouseKeyBindingsSynchronized += OnMouseKeyBindingsSynchronized;
// Start key binding simulator.
}
private static CancellationTokenSource FormCancellationTokenSource { get; set; }
private static TaskScheduler FormTaskScheduler { get; set; }
private static IKeyboardMouseEvents MouseKeyApplicationHook { get; set; }
private List<string> MouseKeyCombo { get; set; }
private MouseKeyBindings LocalMouseKeyBindings { get; }
private RemoteMouseKeyBindings RemoteMouseKeyBindings { get; }
private BindingSource LocalListBoxBindingSource { get; }
private BindingSource RemoteBindingsComboBoxSource { get; }
private MouseKeyBindingsExchange MouseKeyBindingsExchange { get; }
public MqttCommunication MqttCommunication { get; set; }
public LobbyMessageSynchronizer LobbyMessageSynchronizer { get; set; }
public MouseKeyBindingsSynchronizer MouseKeyBindingsSynchronizer { get; set; }
private async Task SaveLocalMouseKeyBindings()
{
try
{
using (var memoryStream = new MemoryStream())
{
MouseKeyBindings.XmlSerializer.Serialize(memoryStream, LocalMouseKeyBindings);
memoryStream.Position = 0L;
using (var fileStream = new FileStream("LocalMouseKeyBindings.xml", FileMode.Create))
{
await memoryStream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
}
catch (Exception)
{
ActivityTextBox.AppendText(
$"{Strings.Failed_saving_local_bindings}{Environment.NewLine}");
}
}
private void OnMqttServerClientDisconnected(object sender, MqttClientDisconnectedEventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Client_disconnected}{Environment.NewLine}");
}
private void OnMqttServerClientConnected(object sender, MqttClientConnectedEventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Client_connected}{Environment.NewLine}");
}
private void OnMqttServerStarted(object sender, EventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Server_started}{Environment.NewLine}");
}
private void OnMqttServerStopped(object sender, EventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Server_stopped}{Environment.NewLine}");
}
private void OnMqttClientConnected(object sender, MQTTnet.Client.MqttClientConnectedEventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Client_connected}{Environment.NewLine}");
}
private void OnMqttClientDisconnected(object sender, MQTTnet.Client.MqttClientDisconnectedEventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Client_disconnected}{Environment.NewLine}");
}
private void OnMqttClientConnectionFailed(object sender, MqttManagedProcessFailedEventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Client_connection_failed}{Environment.NewLine}");
}
private void OnMqttServerAuthenticationFailed(object sender, EventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Failed_to_authenticate_client}{Environment.NewLine}");
}
private void OnMqttClientAuthenticationFailed(object sender, EventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Server_authentication_failed}{Environment.NewLine}");
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && components != null)
{
FormCancellationTokenSource.Dispose();
FormCancellationTokenSource = null;
components.Dispose();
}
base.Dispose(disposing);
}
private void OnMouseKeyBindingsSynchronized(object sender, MouseKeyBindingsSynchronizedEventArgs e)
{
ActivityTextBox.AppendText(
$"{Strings.Synchronized_bindings_with_client} : {e.Bindings.Nick} : {e.Bindings.MouseKeyBindings.Count}{Environment.NewLine}");
var exchangeBindings = MouseKeyBindingsExchange.ExchangeBindings.FirstOrDefault(exchangeBinding =>
string.Equals(exchangeBinding.Nick, e.Bindings.Nick, StringComparison.Ordinal));
// If the nick does not exist then add it.
if (exchangeBindings == null)
{
MouseKeyBindingsExchange.ExchangeBindings.Add(e.Bindings);
RemoteBindingsComboBoxSource.ResetBindings(false);
UpdateRemoteListBoxItems();
return;
}
// If the bindings for the nick have not changed then do not update.
if (exchangeBindings.MouseKeyBindings.SequenceEqual(e.Bindings.MouseKeyBindings))
{
RemoteBindingsComboBoxSource.ResetBindings(false);
UpdateRemoteListBoxItems();
return;
}
// Update the bindings.
exchangeBindings.MouseKeyBindings = e.Bindings.MouseKeyBindings;
RemoteBindingsComboBoxSource.ResetBindings(false);
UpdateRemoteListBoxItems();
}
private void UpdateRemoteListBoxItems()
{
var exchangeBinding = (List<MouseKeyBinding>) RemoteBindingsComboBox.SelectedValue;
if (exchangeBinding == null)
return;
RemoteBindingsListBox.Items.Clear();
RemoteBindingsListBox.DisplayMember = "Name";
RemoteBindingsListBox.ValueMember = "Name";
var i = exchangeBinding.Select(binding => (object) binding.Name).ToArray();
if (i.Length == 0)
return;
RemoteBindingsListBox.Items.AddRange(i);
}
private void OnLobbyMessageReceived(object sender, LobbyMessageReceivedEventArgs e)
{
LobbyTextBox.AppendText($"{e.Nick} : {e.Message}{Environment.NewLine}");
}
private void AddressTextBoxClick(object sender, EventArgs e)
{
Address.BackColor = Color.Empty;
}
private void PortTextBoxClick(object sender, EventArgs e)
{
Port.BackColor = Color.Empty;
}
private async void HostButtonClickAsync(object sender, EventArgs e)
{
// Stop the MQTT server if it is running.
if (MqttCommunication.Running)
{
await MqttCommunication.Stop().ConfigureAwait(false);
HostButton.BackColor = Color.Empty;
// Enable controls.
ConnectButton.Enabled = true;
Address.Enabled = true;
Port.Enabled = true;
Nick.Enabled = true;
return;
}
if (!ValidateAddressPort(out var ipAddress, out var port, out var nick, out var password))
return;
// Start the MQTT server.
if (!await MqttCommunication.Start(MqttCommunicationType.Server, ipAddress, port, nick, password)
.ConfigureAwait(false))
{
ActivityTextBox.AppendText(
$"{Strings.Failed_starting_server}{Environment.NewLine}");
return;
}
HostButton.BackColor = Color.Aquamarine;
// Disable controls
ConnectButton.Enabled = false;
Address.Enabled = false;
Port.Enabled = false;
Nick.Enabled = false;
}
private bool ValidateAddressPort(out IPAddress address, out int port, out string nick, out string password)
{
address = IPAddress.Any;
port = 0;
nick = string.Empty;
password = string.Empty;
if (string.IsNullOrEmpty(Address.Text) &&
string.IsNullOrEmpty(Port.Text) &&
string.IsNullOrEmpty(Nick.Text) &&
string.IsNullOrEmpty(Password.Text))
{
Address.BackColor = Color.LightPink;
Port.BackColor = Color.LightPink;
Nick.BackColor = Color.LightPink;
Password.BackColor = Color.LightPink;
return false;
}
if (!IPAddress.TryParse(Address.Text, out address))
{
Address.BackColor = Color.LightPink;
return false;
}
if (!uint.TryParse(Port.Text, out var uPort))
{
Port.BackColor = Color.LightPink;
return false;
}
port = (int) uPort;
if (string.IsNullOrEmpty(Nick.Text))
{
Nick.BackColor = Color.LightPink;
return false;
}
nick = Nick.Text;
if (string.IsNullOrEmpty(Password.Text))
{
Password.BackColor = Color.LightPink;
return false;
}
password = AES.LinearFeedbackShiftPassword(Password.Text);
Address.BackColor = Color.Empty;
Port.BackColor = Color.Empty;
Nick.BackColor = Color.Empty;
Password.BackColor = Color.Empty;
return true;
}
private async void ConnectButtonClickAsync(object sender, EventArgs e)
{
if (MqttCommunication.Running)
{
await MqttCommunication.Stop().ConfigureAwait(false);
ConnectButton.Text = Strings.Connect;
ConnectButton.BackColor = Color.Empty;
Address.Enabled = true;
Port.Enabled = true;
Nick.Enabled = true;
HostButton.Enabled = true;
return;
}
if (!ValidateAddressPort(out var ipAddress, out var port, out var nick, out var password))
return;
if (!await MqttCommunication.Start(MqttCommunicationType.Client, ipAddress, port, nick, password)
.ConfigureAwait(false))
{
ActivityTextBox.AppendText(
$"{Strings.Failed_starting_client}{Environment.NewLine}");
return;
}
ConnectButton.Text = Strings.Disconnect;
ConnectButton.BackColor = Color.Aquamarine;
HostButton.Enabled = false;
Address.Enabled = false;
Port.Enabled = false;
Nick.Enabled = false;
}
private async void LobbySayTextBoxKeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode != Keys.Enter)
return;
await LobbyMessageSynchronizer.Broadcast(LobbySayTextBox.Text).ConfigureAwait(false);
LobbySayTextBox.Text = string.Empty;
}
private void HelmAddButtonClick(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(LocalNameTextBox.Text))
{
LocalNameTextBox.BackColor = Color.LightPink;
return;
}
ShowOverlayPanel();
MouseKeyCombo = new List<string>();
MouseKeyApplicationHook = Hook.AppEvents();
MouseKeyApplicationHook.MouseDown += LocalMouseKeyHookOnMouseDown;
MouseKeyApplicationHook.KeyUp += LocalMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown += LocalMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.MouseUp += LocalMouseKeyHookOnMouseUp;
}
private void ShowOverlayPanel()
{
OverlayPanel.BringToFront();
OverlayPanel.Visible = true;
OverlayPanel.Invalidate();
}
private async void LocalMouseKeyHookOnKeyUp(object sender, KeyEventArgs e)
{
LocalMouseKeyBindings.Bindings.Add(new MouseKeyBinding(LocalNameTextBox.Text, MouseKeyCombo));
LocalListBoxBindingSource.ResetBindings(false);
MouseKeyApplicationHook.KeyDown -= LocalMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= LocalMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown -= LocalMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= LocalMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.Dispose();
LocalNameTextBox.Text = string.Empty;
HideOverlayPanel();
await SaveLocalMouseKeyBindings().ConfigureAwait(false);
}
private void HideOverlayPanel()
{
OverlayPanel.SendToBack();
OverlayPanel.Visible = false;
OverlayPanel.Invalidate();
}
private async void LocalMouseKeyHookOnMouseUp(object sender, MouseEventArgs e)
{
LocalMouseKeyBindings.Bindings.Add(new MouseKeyBinding(LocalNameTextBox.Text, MouseKeyCombo));
LocalListBoxBindingSource.ResetBindings(false);
MouseKeyApplicationHook.KeyDown -= LocalMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= LocalMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown -= LocalMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= LocalMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.Dispose();
LocalNameTextBox.Text = string.Empty;
HideOverlayPanel();
await SaveLocalMouseKeyBindings().ConfigureAwait(false);
}
private void LocalMouseKeyHookOnMouseDown(object sender, MouseEventArgs e)
{
MouseKeyCombo.Add(e.Button.ToDisplayName());
}
private void LocalMouseKeyHookOnKeyDown(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = true;
MouseKeyCombo.Add(e.KeyCode.ToDisplayName());
}
private void HelmNameTextBoxClick(object sender, EventArgs e)
{
LocalNameTextBox.BackColor = Color.Empty;
}
private async void HelmRemoveButtonClick(object sender, EventArgs e)
{
var helmBinding = (MouseKeyBinding) LocalBindingsListBox.SelectedItem;
if (helmBinding == null)
return;
LocalMouseKeyBindings.Bindings.Remove(helmBinding);
LocalListBoxBindingSource.ResetBindings(false);
await SaveLocalMouseKeyBindings().ConfigureAwait(false);
}
private async void LobbySayButtonClick(object sender, EventArgs e)
{
await LobbyMessageSynchronizer.Broadcast(LobbySayTextBox.Text).ConfigureAwait(false);
LobbySayTextBox.Text = string.Empty;
}
private void RemoteBindingsComboBoxSelectionChangeCompleted(object sender, EventArgs e)
{
UpdateRemoteListBoxItems();
}
private async void WingManFormOnLoad(object sender, EventArgs e)
{
await LoadLocalMouseKeyBindings();
await LoadRemoteMouseKeyBindings();
}
private async Task LoadLocalMouseKeyBindings()
{
try
{
using (var fileStream = new FileStream("LocalMouseKeyBindings.xml", FileMode.Open))
{
using (var memoryStream = new MemoryStream())
{
await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0L;
var loadedBindings =
(MouseKeyBindings) MouseKeyBindings.XmlSerializer.Deserialize(memoryStream);
foreach (var binding in loadedBindings.Bindings) LocalMouseKeyBindings.Bindings.Add(binding);
LocalListBoxBindingSource.ResetBindings(false);
}
}
}
catch (Exception)
{
ActivityTextBox.AppendText(
$"{Strings.Failed_loading_local_bindings}{Environment.NewLine}");
}
}
private void RemoteBindingsBindButtonClicked(object sender, EventArgs e)
{
ShowOverlayPanel();
MouseKeyCombo = new List<string>();
MouseKeyApplicationHook = Hook.AppEvents();
MouseKeyApplicationHook.MouseDown += RemoteMouseKeyHookOnMouseDown;
MouseKeyApplicationHook.KeyUp += RemoteMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown += RemoteMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.MouseUp += RemoteMouseKeyHookOnMouseUp;
}
private void RemoteMouseKeyHookOnKeyDown(object sender, KeyEventArgs e)
{
e.SuppressKeyPress = true;
MouseKeyCombo.Add(e.KeyCode.ToDisplayName());
}
private void RemoteMouseKeyHookOnMouseDown(object sender, MouseEventArgs e)
{
MouseKeyCombo.Add(e.Button.ToDisplayName());
}
private async void RemoteMouseKeyHookOnMouseUp(object sender, MouseEventArgs e)
{
RemoteMouseKeyBindings.Bindings.Add(new RemoteMouseKeyBinding(RemoteBindingsComboBox.Text,
(string) RemoteBindingsListBox.SelectedItem, MouseKeyCombo));
MouseKeyApplicationHook.KeyDown -= RemoteMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= RemoteMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown -= RemoteMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= RemoteMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.Dispose();
RemoteBindingsBindToBox.Text = string.Join(" + ", MouseKeyCombo);
HideOverlayPanel();
await SaveRemoteMouseKeyBindings().ConfigureAwait(false);
}
private async void RemoteMouseKeyHookOnKeyUp(object sender, KeyEventArgs e)
{
RemoteMouseKeyBindings.Bindings.Add(new RemoteMouseKeyBinding(RemoteBindingsComboBox.Text,
(string) RemoteBindingsListBox.SelectedItem, MouseKeyCombo));
MouseKeyApplicationHook.KeyDown -= RemoteMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= RemoteMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.KeyDown -= RemoteMouseKeyHookOnKeyDown;
MouseKeyApplicationHook.KeyUp -= RemoteMouseKeyHookOnKeyUp;
MouseKeyApplicationHook.Dispose();
RemoteBindingsBindToBox.Text = string.Join(" + ", MouseKeyCombo);
HideOverlayPanel();
await SaveRemoteMouseKeyBindings().ConfigureAwait(false);
}
private async Task SaveRemoteMouseKeyBindings()
{
try
{
using (var memoryStream = new MemoryStream())
{
RemoteMouseKeyBindings.XmlSerializer.Serialize(memoryStream, RemoteMouseKeyBindings);
memoryStream.Position = 0L;
using (var fileStream = new FileStream("RemoteMouseKeyBindings.xml", FileMode.Create))
{
await memoryStream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
}
catch (Exception)
{
ActivityTextBox.AppendText(
$"{Strings.Failed_saving_remote_bindings}{Environment.NewLine}");
}
}
private async Task LoadRemoteMouseKeyBindings()
{
try
{
using (var fileStream = new FileStream("RemoteMouseKeyBindings.xml", FileMode.Open))
{
using (var memoryStream = new MemoryStream())
{
await fileStream.CopyToAsync(memoryStream).ConfigureAwait(false);
memoryStream.Position = 0L;
var loadedBindings =
(RemoteMouseKeyBindings) RemoteMouseKeyBindings.XmlSerializer.Deserialize(memoryStream);
foreach (var binding in loadedBindings.Bindings) RemoteMouseKeyBindings.Bindings.Add(binding);
}
}
}
catch (Exception)
{
ActivityTextBox.AppendText(
$"{Strings.Failed_loading_remote_bindings}{Environment.NewLine}");
}
}
}
}