From 7fca7f30122692807091d5352d9fbc1c139ad40a Mon Sep 17 00:00:00 2001 From: "Ronald M. Clifford" Date: Fri, 9 Aug 2024 18:45:45 -0700 Subject: [PATCH 1/2] Add connection status bar to UI. --- InsightLogParser.Client/Spider.cs | 2 + .../Websockets/ISocketUiCommands.cs | 10 ++ InsightLogParser.Client/Websockets/Server.cs | 45 +++++++- InsightLogParser.UI/Main.Designer.cs | 104 +++++++++++------- InsightLogParser.UI/Main.cs | 10 ++ InsightLogParser.UI/Main.resx | 6 + InsightLogParser.UI/Program.cs | 2 +- InsightLogParser.UI/Websockets/Client.cs | 2 +- 8 files changed, 137 insertions(+), 44 deletions(-) diff --git a/InsightLogParser.Client/Spider.cs b/InsightLogParser.Client/Spider.cs index 3a6fdad..5215c1f 100644 --- a/InsightLogParser.Client/Spider.cs +++ b/InsightLogParser.Client/Spider.cs @@ -66,6 +66,7 @@ public void SetServer(string serverAddress, DateTimeOffset timestamp) _sessionStart = timestamp; _serverTracker.Connected(serverAddress); _messageWriter.ConnectedToServer(serverAddress, timestamp); + _uiCommands.SetConnection(true, serverAddress); } public void ConnectingToServer(string serverAddress) @@ -78,6 +79,7 @@ public void EndSession(DateTimeOffset timestamp) _messageWriter.SessionEnded(timestamp, _serverAddress); _sessionStart = null; _serverAddress = null; + _uiCommands.SetConnection(false, string.Empty); } public async Task SessionBeingDisconnectedAsync() diff --git a/InsightLogParser.Client/Websockets/ISocketUiCommands.cs b/InsightLogParser.Client/Websockets/ISocketUiCommands.cs index 9eea1fd..70af030 100644 --- a/InsightLogParser.Client/Websockets/ISocketUiCommands.cs +++ b/InsightLogParser.Client/Websockets/ISocketUiCommands.cs @@ -19,6 +19,13 @@ public interface ISocketUiCommands /// /// The coordinate of the last known player position void MovePlayer(Coordinate coordinate); + + /// + /// Updates the connection state + /// + /// Whether the user is connected to the game + /// The IP address of the server the user is connected to + void SetConnection(bool isConnected, string ipAddress) { } } internal class DummySocketUiCommands : ISocketUiCommands @@ -28,4 +35,7 @@ public void SetTarget(Coordinate coordinate, InsightPuzzle puzzle, int routeNumb //No-op public void MovePlayer(Coordinate coordinate) { } + + //No-op + public void SetConnection(bool isConnected, string ipAddress) { } } \ No newline at end of file diff --git a/InsightLogParser.Client/Websockets/Server.cs b/InsightLogParser.Client/Websockets/Server.cs index 742d164..ddf1e48 100644 --- a/InsightLogParser.Client/Websockets/Server.cs +++ b/InsightLogParser.Client/Websockets/Server.cs @@ -17,6 +17,9 @@ internal class Server : ISocketUiCommands private ISocketParserCommands _parserCommands; private Task _listenerTask; + private bool _isConnected = false; + private string _ipAddress = string.Empty; + public Server(CancellationToken forcedCancellationToken) { _stopTokenSource = CancellationTokenSource.CreateLinkedTokenSource(forcedCancellationToken); @@ -106,6 +109,16 @@ private async Task HandleWebSocketConnection(WebSocket webSocket, CancellationTo // Add the connection. _clients.Add(webSocket); + // Initialize the data for the UI. + if (webSocket.State == WebSocketState.Open) + { + _ = SendAsync(new { + type = "setConnection", + isConnected = _isConnected, + ipAddress = _ipAddress + }, webSocket); + } + while (webSocket.State == WebSocketState.Open && !cancellationToken.IsCancellationRequested) { var buffer = new byte[1024]; @@ -190,17 +203,28 @@ private async Task HandleWebSocketConnection(WebSocket webSocket, CancellationTo _clients.TryTake(out _); } - private async Task SendAsync(object message) + private async Task SendAsync(object message, WebSocket? webSocket = null) { // Encode the message. var messageBuffer = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(message)); var messageSegment = new ArraySegment(messageBuffer); - // Send the message to all clients. - foreach (var client in _clients) + if (webSocket == null) + { + // Send the message to all clients. + foreach (var client in _clients) + { + if (client.State != WebSocketState.Open) continue; + await client.SendAsync(messageSegment, WebSocketMessageType.Text, true, CancellationToken.None); + } + } + else { - if (client.State != WebSocketState.Open) continue; - await client.SendAsync(messageSegment, WebSocketMessageType.Text, true, CancellationToken.None); + // Send the message to a specific client. + if (webSocket.State == WebSocketState.Open) + { + await webSocket.SendAsync(messageSegment, WebSocketMessageType.Text, true, CancellationToken.None); + } } } @@ -234,6 +258,17 @@ public void MovePlayer(Coordinate coordinate) }); } + public void SetConnection(bool isConnected, string ipAddress) + { + _ = SendAsync(new { + type = "setConnection", + isConnected = isConnected, + ipAddress = ipAddress + }); + + _ipAddress = ipAddress; + _isConnected = isConnected; + } #endregion } \ No newline at end of file diff --git a/InsightLogParser.UI/Main.Designer.cs b/InsightLogParser.UI/Main.Designer.cs index 8fa3706..2650ec0 100644 --- a/InsightLogParser.UI/Main.Designer.cs +++ b/InsightLogParser.UI/Main.Designer.cs @@ -32,17 +32,20 @@ private void InitializeComponent() { picArrow = new PictureBox(); lblPuzzleType = new Label(); lblID = new Label(); - tableLayoutPanel1 = new TableLayoutPanel(); - picMap = new PictureBox(); + pnlRoute = new TableLayoutPanel(); picScreenshot = new PictureBox(); + picMap = new PictureBox(); + strStatus = new StatusStrip(); + lblIP = new ToolStripStatusLabel(); pnlMain.SuspendLayout(); pnlTarget.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)picPuzzleType).BeginInit(); ((System.ComponentModel.ISupportInitialize)picCompass).BeginInit(); ((System.ComponentModel.ISupportInitialize)picArrow).BeginInit(); - tableLayoutPanel1.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)picMap).BeginInit(); + pnlRoute.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)picScreenshot).BeginInit(); + ((System.ComponentModel.ISupportInitialize)picMap).BeginInit(); + strStatus.SuspendLayout(); SuspendLayout(); // // pnlMain @@ -50,7 +53,7 @@ private void InitializeComponent() { pnlMain.ColumnCount = 1; pnlMain.ColumnStyles.Add(new ColumnStyle()); pnlMain.Controls.Add(pnlTarget, 0, 0); - pnlMain.Controls.Add(tableLayoutPanel1, 0, 1); + pnlMain.Controls.Add(pnlRoute, 0, 1); pnlMain.Dock = DockStyle.Fill; pnlMain.Location = new Point(0, 0); pnlMain.Margin = new Padding(0); @@ -58,7 +61,7 @@ private void InitializeComponent() { pnlMain.RowCount = 2; pnlMain.RowStyles.Add(new RowStyle(SizeType.Absolute, 64F)); pnlMain.RowStyles.Add(new RowStyle()); - pnlMain.Size = new Size(800, 450); + pnlMain.Size = new Size(814, 447); pnlMain.TabIndex = 0; // // pnlTarget @@ -84,7 +87,7 @@ private void InitializeComponent() { pnlTarget.Name = "pnlTarget"; pnlTarget.RowCount = 1; pnlTarget.RowStyles.Add(new RowStyle()); - pnlTarget.Size = new Size(800, 64); + pnlTarget.Size = new Size(814, 64); pnlTarget.TabIndex = 0; // // lbl2DDistance @@ -166,25 +169,37 @@ private void InitializeComponent() { lblID.Location = new Point(192, 0); lblID.Margin = new Padding(0); lblID.Name = "lblID"; - lblID.Size = new Size(608, 65); + lblID.Size = new Size(622, 65); lblID.TabIndex = 6; lblID.TextAlign = ContentAlignment.TopRight; // - // tableLayoutPanel1 - // - tableLayoutPanel1.ColumnCount = 2; - tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33F)); - tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 67F)); - tableLayoutPanel1.Controls.Add(picScreenshot, 0, 0); - tableLayoutPanel1.Controls.Add(picMap, 0, 0); - tableLayoutPanel1.Dock = DockStyle.Fill; - tableLayoutPanel1.Location = new Point(0, 64); - tableLayoutPanel1.Margin = new Padding(0); - tableLayoutPanel1.Name = "tableLayoutPanel1"; - tableLayoutPanel1.RowCount = 1; - tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); - tableLayoutPanel1.Size = new Size(800, 386); - tableLayoutPanel1.TabIndex = 1; + // pnlRoute + // + pnlRoute.ColumnCount = 2; + pnlRoute.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 33F)); + pnlRoute.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 67F)); + pnlRoute.Controls.Add(picScreenshot, 0, 0); + pnlRoute.Controls.Add(picMap, 0, 0); + pnlRoute.Dock = DockStyle.Fill; + pnlRoute.Location = new Point(0, 64); + pnlRoute.Margin = new Padding(0); + pnlRoute.Name = "pnlRoute"; + pnlRoute.RowCount = 2; + pnlRoute.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); + pnlRoute.RowStyles.Add(new RowStyle(SizeType.Absolute, 20F)); + pnlRoute.Size = new Size(814, 386); + pnlRoute.TabIndex = 1; + // + // picScreenshot + // + picScreenshot.Dock = DockStyle.Fill; + picScreenshot.Location = new Point(268, 0); + picScreenshot.Margin = new Padding(0); + picScreenshot.Name = "picScreenshot"; + picScreenshot.Size = new Size(546, 366); + picScreenshot.SizeMode = PictureBoxSizeMode.Zoom; + picScreenshot.TabIndex = 1; + picScreenshot.TabStop = false; // // picMap // @@ -193,29 +208,39 @@ private void InitializeComponent() { picMap.Location = new Point(0, 0); picMap.Margin = new Padding(0); picMap.Name = "picMap"; - picMap.Size = new Size(264, 386); + pnlRoute.SetRowSpan(picMap, 2); + picMap.Size = new Size(268, 386); picMap.SizeMode = PictureBoxSizeMode.Zoom; picMap.TabIndex = 0; picMap.TabStop = false; // - // picScreenshot + // strStatus // - picScreenshot.Dock = DockStyle.Fill; - picScreenshot.Location = new Point(264, 0); - picScreenshot.Margin = new Padding(0); - picScreenshot.Name = "picScreenshot"; - picScreenshot.Size = new Size(536, 386); - picScreenshot.SizeMode = PictureBoxSizeMode.Zoom; - picScreenshot.TabIndex = 1; - picScreenshot.TabStop = false; + strStatus.BackColor = Color.Black; + strStatus.Items.AddRange(new ToolStripItem[] { lblIP }); + strStatus.LayoutStyle = ToolStripLayoutStyle.Flow; + strStatus.Location = new Point(0, 447); + strStatus.Name = "strStatus"; + strStatus.Size = new Size(814, 20); + strStatus.SizingGrip = false; + strStatus.TabIndex = 1; + // + // lblIP + // + lblIP.BackColor = Color.Black; + lblIP.ForeColor = Color.White; + lblIP.Name = "lblIP"; + lblIP.Size = new Size(79, 15); + lblIP.Text = "Disconnected"; // // Main // AutoScaleDimensions = new SizeF(7F, 15F); AutoScaleMode = AutoScaleMode.Font; BackColor = Color.Black; - ClientSize = new Size(800, 450); + ClientSize = new Size(814, 467); Controls.Add(pnlMain); + Controls.Add(strStatus); Name = "Main"; Text = "InsightLogParser.Client"; pnlMain.ResumeLayout(false); @@ -224,10 +249,13 @@ private void InitializeComponent() { ((System.ComponentModel.ISupportInitialize)picPuzzleType).EndInit(); ((System.ComponentModel.ISupportInitialize)picCompass).EndInit(); ((System.ComponentModel.ISupportInitialize)picArrow).EndInit(); - tableLayoutPanel1.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)picMap).EndInit(); + pnlRoute.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)picScreenshot).EndInit(); + ((System.ComponentModel.ISupportInitialize)picMap).EndInit(); + strStatus.ResumeLayout(false); + strStatus.PerformLayout(); ResumeLayout(false); + PerformLayout(); } #endregion @@ -241,8 +269,10 @@ private void InitializeComponent() { private PictureBox picArrow; private Label lblPuzzleType; private Label lblID; - private TableLayoutPanel tableLayoutPanel1; + private TableLayoutPanel pnlRoute; private PictureBox picMap; private PictureBox picScreenshot; + private StatusStrip strStatus; + private ToolStripStatusLabel lblIP; } } \ No newline at end of file diff --git a/InsightLogParser.UI/Main.cs b/InsightLogParser.UI/Main.cs index 5cb98b9..07a56b5 100644 --- a/InsightLogParser.UI/Main.cs +++ b/InsightLogParser.UI/Main.cs @@ -52,6 +52,12 @@ private async void WebSocketClient_OnMessageReceived(object sender, MessageRecei await SetTarget(location, (PuzzleType)puzzleType, puzzleId, routeNumber, routeLength); break; } + case "setConnection": { + var isConnected = data.RootElement.GetProperty("isConnected").GetBoolean(); + var ipAddress = data.RootElement.TryGetProperty("ipAddress", out var ipAddressElement) ? ipAddressElement.GetString() : string.Empty; + SetConnection(isConnected, ipAddress); + break; + } case "shutdown": Application.Exit(); break; @@ -123,6 +129,10 @@ await _webSocketClient.SendAsync(new { } } + private void SetConnection(bool isConnected, string ipAddress) { + lblIP.Text = isConnected ? $"Connected to {ipAddress}" : "Disconnected"; + } + private void UpdateTarget() { if (_target.X == 0 && _target.Y == 0 && _target.Z == 0) { return; diff --git a/InsightLogParser.UI/Main.resx b/InsightLogParser.UI/Main.resx index af32865..443fba1 100644 --- a/InsightLogParser.UI/Main.resx +++ b/InsightLogParser.UI/Main.resx @@ -117,4 +117,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + + + True + \ No newline at end of file diff --git a/InsightLogParser.UI/Program.cs b/InsightLogParser.UI/Program.cs index 1bbbd8e..6a7b8e2 100644 --- a/InsightLogParser.UI/Program.cs +++ b/InsightLogParser.UI/Program.cs @@ -32,7 +32,7 @@ static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEv // Show exception message static void ShowException(Exception ex) { if (ex != null) { - MessageBox.Show(ex.Message, "Unhandled Exception", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(ex.Message + " " + ex.StackTrace, "Unhandled Exception", MessageBoxButtons.OK, MessageBoxIcon.Error); } } diff --git a/InsightLogParser.UI/Websockets/Client.cs b/InsightLogParser.UI/Websockets/Client.cs index 18412fd..bc2cbbd 100644 --- a/InsightLogParser.UI/Websockets/Client.cs +++ b/InsightLogParser.UI/Websockets/Client.cs @@ -12,7 +12,7 @@ internal class Client { public async Task ConnectAsync(int port) { try { await _clientWebSocket.ConnectAsync(new Uri($"ws://localhost:{port}/ws/"), _cancellationTokenSource.Token); - } catch (Exception ex) { + } catch (Exception) { _cancellationTokenSource.Cancel(); } _ = ReceiveMessagesAsync(); From c03464fc883f216a246df1fb7eceae73d106dee3 Mon Sep 17 00:00:00 2001 From: "Ronald M. Clifford" Date: Sat, 10 Aug 2024 01:26:59 -0700 Subject: [PATCH 2/2] Websocket update. Refactors websocket commands to be sent to one websocket, if specified, or all websockets if not. Also stores the most recent target, position, and connection status, and sends that data to newly connecting UIs, so if you open the UI after you've been out and about in the game, the UI will be up to date. --- .../Websockets/ISocketUiCommands.cs | 22 +++++--- InsightLogParser.Client/Websockets/Server.cs | 55 +++++++++++++------ 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/InsightLogParser.Client/Websockets/ISocketUiCommands.cs b/InsightLogParser.Client/Websockets/ISocketUiCommands.cs index 70af030..871f5de 100644 --- a/InsightLogParser.Client/Websockets/ISocketUiCommands.cs +++ b/InsightLogParser.Client/Websockets/ISocketUiCommands.cs @@ -1,4 +1,5 @@ -using InsightLogParser.Common.PuzzleParser; +using System.Net.WebSockets; +using InsightLogParser.Common.PuzzleParser; using InsightLogParser.Common.World; namespace InsightLogParser.Client.Websockets; @@ -8,34 +9,37 @@ public interface ISocketUiCommands /// /// Sets the current target /// - /// The coordinate of the target + /// The coordinate of the target /// The target puzzle /// The number of the current route node or 0 if not on route /// The length of the route or 0 if not on route - void SetTarget(Coordinate coordinate, InsightPuzzle puzzle, int routeNumber, int routeLength); + /// The websocket to send to, or everyone if not specified + void SetTarget(Coordinate target, InsightPuzzle puzzle, int routeNumber, int routeLength, WebSocket? webSocket = null); /// /// Updates the player position /// - /// The coordinate of the last known player position - void MovePlayer(Coordinate coordinate); + /// The coordinate of the last known player position + /// The websocket to send to, or everyone if not specified + void MovePlayer(Coordinate destination, WebSocket? webSocket = null); /// /// Updates the connection state /// /// Whether the user is connected to the game /// The IP address of the server the user is connected to - void SetConnection(bool isConnected, string ipAddress) { } + /// The websocket to send to, or everyone if not specified + void SetConnection(bool isConnected, string ipAddress, WebSocket? webSocket = null ) { } } internal class DummySocketUiCommands : ISocketUiCommands { //No-op - public void SetTarget(Coordinate coordinate, InsightPuzzle puzzle, int routeNumber, int routeLength) { } + public void SetTarget(Coordinate target, InsightPuzzle puzzle, int routeNumber, int routeLength, WebSocket? webSocket) { } //No-op - public void MovePlayer(Coordinate coordinate) { } + public void MovePlayer(Coordinate destination, WebSocket? webSocket) { } //No-op - public void SetConnection(bool isConnected, string ipAddress) { } + public void SetConnection(bool isConnected, string ipAddress, WebSocket? webSocket) { } } \ No newline at end of file diff --git a/InsightLogParser.Client/Websockets/Server.cs b/InsightLogParser.Client/Websockets/Server.cs index ddf1e48..a868558 100644 --- a/InsightLogParser.Client/Websockets/Server.cs +++ b/InsightLogParser.Client/Websockets/Server.cs @@ -17,6 +17,17 @@ internal class Server : ISocketUiCommands private ISocketParserCommands _parserCommands; private Task _listenerTask; + // setTarget data + private Coordinate _target = new Coordinate(0, 0, 0); + private PuzzleType _puzzleType = PuzzleType.Unknown; + private int _puzzleId = 0; + private int _routeNumber = 0; + private int _routeLength = 0; + + // movePlayer data + private Coordinate _destination = new Coordinate(0, 0, 0); + + // setConnected data private bool _isConnected = false; private string _ipAddress = string.Empty; @@ -112,11 +123,9 @@ private async Task HandleWebSocketConnection(WebSocket webSocket, CancellationTo // Initialize the data for the UI. if (webSocket.State == WebSocketState.Open) { - _ = SendAsync(new { - type = "setConnection", - isConnected = _isConnected, - ipAddress = _ipAddress - }, webSocket); + SetTarget(_target, new InsightPuzzle { Type = _puzzleType, KrakenId = _puzzleId }, _routeNumber, _routeLength, webSocket); + MovePlayer(_destination, webSocket); + SetConnection(_isConnected, _ipAddress, webSocket); } while (webSocket.State == WebSocketState.Open && !cancellationToken.IsCancellationRequested) @@ -230,41 +239,53 @@ private async Task SendAsync(object message, WebSocket? webSocket = null) #region ISocketUiCommands - public void SetTarget(Coordinate coordinate, InsightPuzzle puzzle, int routeNumber, int routeLength) + public void SetTarget(Coordinate target, InsightPuzzle puzzle, int routeNumber, int routeLength, WebSocket? websocket) { + if (target.X == 0 && target.Y == 0 && target.Z == 0) return; + _ = SendAsync(new { type = "setTarget", target = new { - coordinate.X, - coordinate.Y, - coordinate.Z + target.X, + target.Y, + target.Z }, puzzleType = (int)puzzle.Type, puzzleId = puzzle.KrakenId, routeNumber = routeNumber, routeLength = routeLength, - }); + }, websocket); + + _target = target; + _puzzleType = puzzle.Type; + _puzzleId = puzzle.KrakenId; + _routeNumber = routeNumber; + _routeLength = routeLength; } - public void MovePlayer(Coordinate coordinate) + public void MovePlayer(Coordinate destination, WebSocket? websocket) { + if (destination.X == 0 && destination.Y == 0 && destination.Z == 0) return; + _ = SendAsync(new { type = "movePlayer", destination = new { - coordinate.X, - coordinate.Y, - coordinate.Z + destination.X, + destination.Y, + destination.Z } - }); + }, websocket); + + _destination = destination; } - public void SetConnection(bool isConnected, string ipAddress) + public void SetConnection(bool isConnected, string ipAddress, WebSocket? websocket) { _ = SendAsync(new { type = "setConnection", isConnected = isConnected, ipAddress = ipAddress - }); + }, websocket); _ipAddress = ipAddress; _isConnected = isConnected;