diff --git a/EtumrepMMO.Server/HostSettings.cs b/EtumrepMMO.Server/HostSettings.cs index e350e53..04fcdac 100644 --- a/EtumrepMMO.Server/HostSettings.cs +++ b/EtumrepMMO.Server/HostSettings.cs @@ -2,21 +2,36 @@ namespace EtumrepMMO.Server { - public class HostSettings + public class ServerSettings { - public override string ToString() => "Host Settings"; + public override string ToString() => "Server Settings"; private const string Startup = nameof(Startup); - [Category(Startup), Description("Host port.")] + [Category(Startup), Description("Port.")] public int Port { get; set; } = 80; [Category(Startup), Description("Token for client authorization.")] public string Token { get; set; } = string.Empty; - [Category(Startup), Description("Whitelisted clients' numerical Discord user IDs.")] - public List HostWhitelist { get; set; } = new(); + [Category(Startup), Description("Whitelisted clients (bot hosts).")] + public List HostWhitelist { get; set; } = new(); - [Category(Startup), Description("Blacklisted users' numerical Discord IDs.")] - public List UserBlacklist { get; set; } = new(); + [Category(Startup), Description("Blacklisted users.")] + public List UserBlacklist { get; set; } = new(); + + [Category(Startup), Description("Connections accepted.")] + public int ConnectionsAccepted { get; set; } + + [Category(Startup), Description("Users authenticated.")] + public int UsersAuthenticated { get; set; } + + [Category(Startup), Description("EtumrepMMOs successfully run.")] + public int EtumrepsRun { get; set; } + + public class DiscordUser + { + public string UserName { get; set; } = string.Empty; + public ulong ID { get; set; } + } } } diff --git a/EtumrepMMO.Server/ServerConnection.cs b/EtumrepMMO.Server/ServerConnection.cs index 57b5046..b18ed1d 100644 --- a/EtumrepMMO.Server/ServerConnection.cs +++ b/EtumrepMMO.Server/ServerConnection.cs @@ -10,26 +10,23 @@ namespace EtumrepMMO.Server { public class ServerConnection { - private int Port { get; } - private string Token { get; } + private ServerSettings Settings { get; } private TcpListener Listener { get; } - private bool IsStopped { get; set; } - private List HostWhitelist { get; } - private List UserBlacklist { get; } + private ConcurrentQueue UserQueue { get; set; } = new(); + private static IProgress<(string, int, bool)> UserProgress { get; set; } = default!; + private static IProgress<(int, int, int)> Labels { get; set; } = default!; + private string CurrentSeedChecker { get; set; } = "Waiting for users..."; + private bool IsStopped { get; set; } private int Progress { get; set; } private bool InQueue { get; set; } - private static IProgress<(string, int, bool)> UserProgress { get; set; } = default!; - private ConcurrentQueue UserQueue { get; set; } = new(); - public ServerConnection(HostSettings settings, IProgress<(string, int, bool)> progress) + public ServerConnection(ServerSettings settings, IProgress<(string, int, bool)> progress, IProgress<(int, int, int)> labels) { - Port = settings.Port; - Token = settings.Token; - HostWhitelist = settings.HostWhitelist; - UserBlacklist = settings.UserBlacklist; + Settings = settings; UserProgress = progress; - Listener = new(IPAddress.Any, Port); + Labels = labels; + Listener = new(IPAddress.Any, settings.Port); } internal class RemoteUser @@ -44,8 +41,7 @@ internal RemoteUser(TcpClient client, AuthenticatedStream stream) public TcpClient Client { get; } public AuthenticatedStream Stream { get; } - public string Name { get; set; } = string.Empty; - public string SeedCheckerName { get; set; } = string.Empty; + public UserAuth? UserAuth { get; set; } = new(); public bool IsAuthenticated { get; set; } public byte[] Buffer { get; } = new byte[1504]; @@ -55,10 +51,10 @@ internal RemoteUser(TcpClient client, AuthenticatedStream stream) internal class UserAuth { public string HostName { get; set; } = string.Empty; - public string HostID { get; set; } = string.Empty; + public ulong HostID { get; set; } public string Token { get; set; } = string.Empty; public string SeedCheckerName { get; set; } = string.Empty; - public string SeedCheckerID { get; set; } = string.Empty; + public ulong SeedCheckerID { get; set; } } public async Task Stop() @@ -79,11 +75,12 @@ public async Task MainAsync(CancellationToken token) _ = Task.Run(async () => await RemoteUserQueue(token).ConfigureAwait(false), token); _ = Task.Run(async () => await ReportUserProgress(token).ConfigureAwait(false), token); + _ = Task.Run(async () => await UpdateLabels(token).ConfigureAwait(false), token); LogUtil.Log("Server initialized, waiting for connections...", "[TCP Listener]"); while (!token.IsCancellationRequested) { - if (!Listener.Pending() || UserQueue.Count >= 100) + if (!Listener.Pending()) { await Task.Delay(0_250, token).ConfigureAwait(false); continue; @@ -91,23 +88,25 @@ public async Task MainAsync(CancellationToken token) LogUtil.Log("A user is attempting to connect, authenticating connection...", "[TCP Listener]"); TcpClient remoteClient = await Listener.AcceptTcpClientAsync(token).ConfigureAwait(false); + Settings.ConnectionsAccepted += 1; LogUtil.Log("A user has connected, authenticating the user...", "[TCP Listener]"); - RemoteUser user = await AuthenticateConnection(remoteClient).ConfigureAwait(false); - if (!user.IsAuthenticated) + RemoteUser? user = await AuthenticateConnection(remoteClient).ConfigureAwait(false); + if (user is null || !user.IsAuthenticated) { DisposeStream(user); continue; } - var userAuth = await AuthenticateUser(user, token).ConfigureAwait(false); - if (userAuth is null) + user.UserAuth = await AuthenticateUser(user, token).ConfigureAwait(false); + if (user.UserAuth is null) { DisposeStream(user); continue; } + Settings.UsersAuthenticated += 1; - LogUtil.Log($"{user.Name} was successfully authenticated, enqueueing...", "[TCP Listener]"); + LogUtil.Log($"{user.UserAuth.HostName} {(user.UserAuth.HostID)} was successfully authenticated, enqueueing...", "[TCP Listener]"); UserQueue.Enqueue(user); } } @@ -117,16 +116,16 @@ private async Task RemoteUserQueue(CancellationToken token) while (!token.IsCancellationRequested) { UserQueue.TryDequeue(out var user); - if (user is not null) + if (user is not null && user.UserAuth is not null) { - CurrentSeedChecker = user.SeedCheckerName; + CurrentSeedChecker = user.UserAuth.SeedCheckerName; Progress = 10; InQueue = true; - LogUtil.Log($"{user.Name}: Attempting to read PKM data from {user.SeedCheckerName}.", "[UserQueue]"); + LogUtil.Log($"{user.UserAuth.HostName}: Attempting to read PKM data from {user.UserAuth.SeedCheckerName}.", "[User Queue]"); if (!user.Stream.CanRead) { - LogUtil.Log($"{user.Name}: Unable to read stream.", "[UserQueue]"); + LogUtil.Log($"{user.UserAuth.HostName}: Unable to read stream.", "[User Queue]"); DisposeStream(user); continue; } @@ -136,13 +135,13 @@ private async Task RemoteUserQueue(CancellationToken token) if (read is 0 || count is < 2) { - LogUtil.Log($"{user.Name}: Received an incorrect amount of data from {user.SeedCheckerName}.", "[UserQueue]"); + LogUtil.Log($"{user.UserAuth.HostName}: Received an incorrect amount of data from {user.UserAuth.SeedCheckerName}.", "[User Queue]"); DisposeStream(user); continue; } Progress = 30; - LogUtil.Log($"{user.Name}: Beginning seed calculation for {user.SeedCheckerName}...", "[UserQueue]"); + LogUtil.Log($"{user.UserAuth.HostName}: Beginning seed calculation for {user.UserAuth.SeedCheckerName}...", "[User Queue]"); var sw = new Stopwatch(); sw.Start(); @@ -150,19 +149,19 @@ private async Task RemoteUserQueue(CancellationToken token) sw.Stop(); Progress = 80; - LogUtil.Log($"{user.Name}: Seed ({seed}) calculation for {user.SeedCheckerName} complete ({sw.Elapsed}). Attempting to send the result...", "[UserQueue]"); + LogUtil.Log($"{user.UserAuth.HostName}: Seed ({seed}) calculation for {user.UserAuth.SeedCheckerName} complete ({sw.Elapsed}). Attempting to send the result...", "[User Queue]"); if (!user.Stream.CanWrite) { - LogUtil.Log($"{user.Name}: Unable to write to stream.", "[UserQueue]"); + LogUtil.Log($"{user.UserAuth.HostName}: Unable to write to stream.", "[User Queue]"); DisposeStream(user); continue; } var bytes = BitConverter.GetBytes(seed); await user.Stream.WriteAsync(bytes, token).ConfigureAwait(false); - - LogUtil.Log($"{user.Name}: Sent results to {user.Name}, removing from queue.", "[UserQueue]"); + Settings.EtumrepsRun += 1; + LogUtil.Log($"{user.UserAuth.HostName}: Results were sent, removing from queue.", "[User Queue]"); DisposeStream(user); Progress = 100; @@ -176,14 +175,14 @@ private async Task RemoteUserQueue(CancellationToken token) } } - private static async Task AuthenticateConnection(TcpClient client) + private static async Task AuthenticateConnection(TcpClient client) { - var stream = client.GetStream(); - var authStream = new NegotiateStream(stream, false); - var user = new RemoteUser(client, authStream); - try { + var stream = client.GetStream(); + var authStream = new NegotiateStream(stream, false); + var user = new RemoteUser(client, authStream); + await authStream.AuthenticateAsServerAsync().ConfigureAwait(false); user.IsAuthenticated = true; LogUtil.Log("Initial authentication complete.", "[Connection Authentication]"); @@ -192,7 +191,7 @@ private static async Task AuthenticateConnection(TcpClient client) catch (Exception ex) { LogUtil.Log($"Failed to authenticate user.\n{ex.Message}", "[Connection Authentication]"); - return user; + return null; } } @@ -214,25 +213,22 @@ private static async Task AuthenticateConnection(TcpClient client) LogUtil.Log("User did not send an authentication packet.", "[User Authentication]"); return null; } - else if (!HostWhitelist.Contains(authObj.HostID)) + else if (!Settings.HostWhitelist.Exists(x => x.ID == authObj.HostID)) { LogUtil.Log($"{authObj.HostName} ({authObj.HostID}) is not a whitelisted bot host.", "[User Authentication]"); return null; } - else if (UserBlacklist.Contains(authObj.SeedCheckerID)) + else if (Settings.UserBlacklist.Exists(x => x.ID == authObj.SeedCheckerID)) { LogUtil.Log($"{authObj.SeedCheckerName} ({authObj.SeedCheckerID}) is a blacklisted user.", "[User Authentication]"); return null; } - else if (authObj.Token != Token) + else if (Settings.Token != authObj.Token) { LogUtil.Log($"The provided token ({authObj.Token}) does not match the token defined by us.", "[User Authentication]"); return null; } - user.Name = authObj.HostName; - user.SeedCheckerName = authObj.SeedCheckerName; - // Send confirmation to client. var bytes = BitConverter.GetBytes(true); await user.Stream.WriteAsync(bytes, token).ConfigureAwait(false); @@ -254,10 +250,22 @@ private async Task ReportUserProgress(CancellationToken token) } } - private static void DisposeStream(RemoteUser user) + private async Task UpdateLabels(CancellationToken token) { - user.Client.Dispose(); - user.Stream.Dispose(); + while (!token.IsCancellationRequested) + { + await Task.Delay(0_100, token).ConfigureAwait(false); + Labels.Report((Settings.ConnectionsAccepted, Settings.UsersAuthenticated, Settings.EtumrepsRun)); + } + } + + private static void DisposeStream(RemoteUser? user) + { + if (user is not null) + { + user.Client.Dispose(); + user.Stream.Dispose(); + } } } } diff --git a/EtumrepMMO.WinForms/Main.Designer.cs b/EtumrepMMO.WinForms/Main.Designer.cs index 0bd490d..7c36cce 100644 --- a/EtumrepMMO.WinForms/Main.Designer.cs +++ b/EtumrepMMO.WinForms/Main.Designer.cs @@ -37,6 +37,9 @@ private void InitializeComponent() this.Button_Stop = new System.Windows.Forms.Button(); this.Bar_Load = new System.Windows.Forms.ProgressBar(); this.ActiveConnection = new System.Windows.Forms.TextBox(); + this.Connections = new System.Windows.Forms.Label(); + this.Etumreps = new System.Windows.Forms.Label(); + this.Authenticated = new System.Windows.Forms.Label(); this.Tab_Main.SuspendLayout(); this.Tab_Settings.SuspendLayout(); this.Tab_Logs.SuspendLayout(); @@ -46,11 +49,11 @@ private void InitializeComponent() // this.Tab_Main.Controls.Add(this.Tab_Settings); this.Tab_Main.Controls.Add(this.Tab_Logs); - this.Tab_Main.Location = new System.Drawing.Point(13, 39); + this.Tab_Main.Location = new System.Drawing.Point(14, 87); this.Tab_Main.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.Tab_Main.Name = "Tab_Main"; this.Tab_Main.SelectedIndex = 0; - this.Tab_Main.Size = new System.Drawing.Size(907, 467); + this.Tab_Main.Size = new System.Drawing.Size(907, 419); this.Tab_Main.TabIndex = 0; this.FormClosing += Main_Closing; // @@ -61,7 +64,7 @@ private void InitializeComponent() this.Tab_Settings.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.Tab_Settings.Name = "Tab_Settings"; this.Tab_Settings.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.Tab_Settings.Size = new System.Drawing.Size(899, 439); + this.Tab_Settings.Size = new System.Drawing.Size(899, 391); this.Tab_Settings.TabIndex = 0; this.Tab_Settings.Text = "Settings"; this.Tab_Settings.UseVisualStyleBackColor = true; @@ -95,7 +98,7 @@ private void InitializeComponent() this.Tab_Logs.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.Tab_Logs.Name = "Tab_Logs"; this.Tab_Logs.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.Tab_Logs.Size = new System.Drawing.Size(899, 439); + this.Tab_Logs.Size = new System.Drawing.Size(899, 391); this.Tab_Logs.TabIndex = 1; this.Tab_Logs.Text = "Logs"; this.Tab_Logs.UseVisualStyleBackColor = true; @@ -109,7 +112,7 @@ private void InitializeComponent() this.RTB_Logs.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.RTB_Logs.Name = "RTB_Logs"; this.RTB_Logs.ReadOnly = true; - this.RTB_Logs.Size = new System.Drawing.Size(891, 433); + this.RTB_Logs.Size = new System.Drawing.Size(891, 385); this.RTB_Logs.TabIndex = 0; this.RTB_Logs.Text = ""; // @@ -137,9 +140,9 @@ private void InitializeComponent() // // Bar_Load // - this.Bar_Load.Location = new System.Drawing.Point(533, 21); + this.Bar_Load.Location = new System.Drawing.Point(13, 54); this.Bar_Load.Name = "Bar_Load"; - this.Bar_Load.Size = new System.Drawing.Size(156, 15); + this.Bar_Load.Size = new System.Drawing.Size(687, 10); this.Bar_Load.Style = System.Windows.Forms.ProgressBarStyle.Continuous; this.Bar_Load.TabIndex = 3; this.Bar_Load.Visible = false; @@ -150,20 +153,47 @@ private void InitializeComponent() this.ActiveConnection.Enabled = false; this.ActiveConnection.Font = new System.Drawing.Font("Cambria", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point); this.ActiveConnection.ForeColor = System.Drawing.SystemColors.ControlText; - this.ActiveConnection.Location = new System.Drawing.Point(141, 14); + this.ActiveConnection.Location = new System.Drawing.Point(12, 23); this.ActiveConnection.Name = "ActiveConnection"; this.ActiveConnection.ReadOnly = true; - this.ActiveConnection.Size = new System.Drawing.Size(386, 25); + this.ActiveConnection.Size = new System.Drawing.Size(688, 25); this.ActiveConnection.TabIndex = 4; this.ActiveConnection.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; this.ActiveConnection.Visible = false; // + // Connections + // + this.Connections.AutoSize = true; + this.Connections.Location = new System.Drawing.Point(720, 54); + this.Connections.Name = "Connections"; + this.Connections.Size = new System.Drawing.Size(0, 15); + this.Connections.TabIndex = 5; + // + // Etumreps + // + this.Etumreps.AutoSize = true; + this.Etumreps.Location = new System.Drawing.Point(720, 84); + this.Etumreps.Name = "Etumreps"; + this.Etumreps.Size = new System.Drawing.Size(0, 15); + this.Etumreps.TabIndex = 6; + // + // Authenticated + // + this.Authenticated.AutoSize = true; + this.Authenticated.Location = new System.Drawing.Point(720, 69); + this.Authenticated.Name = "Authenticated"; + this.Authenticated.Size = new System.Drawing.Size(0, 15); + this.Authenticated.TabIndex = 7; + // // Main // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.SystemColors.ControlDark; this.ClientSize = new System.Drawing.Size(933, 519); + this.Controls.Add(this.Authenticated); + this.Controls.Add(this.Etumreps); + this.Controls.Add(this.Connections); this.Controls.Add(this.ActiveConnection); this.Controls.Add(this.Bar_Load); this.Controls.Add(this.Button_Stop); @@ -193,5 +223,8 @@ private void InitializeComponent() private System.Windows.Forms.PropertyGrid Grid_Settings; private ProgressBar Bar_Load; private TextBox ActiveConnection; + private Label Connections; + private Label Etumreps; + private Label Authenticated; } } diff --git a/EtumrepMMO.WinForms/Main.cs b/EtumrepMMO.WinForms/Main.cs index 8f8d1dc..e6ba161 100644 --- a/EtumrepMMO.WinForms/Main.cs +++ b/EtumrepMMO.WinForms/Main.cs @@ -5,12 +5,13 @@ namespace EtumrepMMO.WinForms { public sealed partial class Main : Form { - private static CancellationTokenSource Source = new(); - private readonly HostSettings Settings; private readonly ServerConnection Connection; - private static bool WasStarted = false; private readonly string ConfigPath = GetConfigPath(); + private static CancellationTokenSource Source { get; set; } = new(); + private ServerSettings Settings { get; set; } + private static bool WasStarted { get; set; } + public Main() { InitializeComponent(); @@ -25,11 +26,17 @@ public Main() if (File.Exists(ConfigPath)) { var text = File.ReadAllText(ConfigPath); - Settings = JsonConvert.DeserializeObject(text, GetSettings()) ?? new HostSettings(); + Settings = JsonConvert.DeserializeObject(text, GetSettings()) ?? new ServerSettings(); + UpdateLabels(Settings.ConnectionsAccepted, Settings.UsersAuthenticated, Settings.EtumrepsRun); } else Settings = new(); - Connection = new(Settings, prg); + var labels = new Progress<(int, int, int)>(x => + { + UpdateLabels(x.Item1, x.Item2, x.Item3); + }); + + Connection = new(Settings, prg, labels); Grid_Settings.SelectedObject = Settings; LogUtil.Forwarders.Add(PostLog); } @@ -108,5 +115,12 @@ private void SaveSettings() var lines = JsonConvert.SerializeObject(Settings, GetSettings()); File.WriteAllText(ConfigPath, lines); } + + private void UpdateLabels(int connections, int authentications, int etumreps) + { + Connections.Text = connections > 0 ? $"Connections accepted: {connections}" : string.Empty; + Authenticated.Text = authentications > 0 ? $"Users authenticated: {authentications}" : string.Empty; + Etumreps.Text = etumreps > 0 ? $"EtumrepMMOs run: {etumreps}" : string.Empty; + } } } diff --git a/EtumrepMMO.WinForms/Main.resx b/EtumrepMMO.WinForms/Main.resx index 03e4cd1..f298a7b 100644 --- a/EtumrepMMO.WinForms/Main.resx +++ b/EtumrepMMO.WinForms/Main.resx @@ -57,7 +57,4 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - True - \ No newline at end of file diff --git a/README.md b/README.md index 6326300..8562d12 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Requirements: Usage: - Compile the EtumrepMMO.WinForms project using the x64 build configuration. -- Configure settings as desired (clients have to be whitelisted). Restart the executable in order to save your configuration. +- Configure settings as desired (clients have to be whitelisted). - Configure your router and/or firewall to allow connections to your specified port (may also need to set up port forwarding). - Click 'Start' to begin listening for connections.