From fd097ba808b2ad7f782e9aa7c36c63c4e71207ca Mon Sep 17 00:00:00 2001 From: ninjafriend Date: Tue, 27 Jun 2023 09:32:48 -0500 Subject: [PATCH 1/3] Websocket enabling - Fixing websocket implementation --- .../Api/ClientWebSocketWrapper.cs | 56 ++++++++++++++----- .../Api/Interfaces/ICommApi.cs | 2 - .../Api/WebSocketServer.cs | 2 +- .../lua/CommonLibs/CommLuaLibrary.cs | 29 +++++----- 4 files changed, 59 insertions(+), 30 deletions(-) diff --git a/src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs b/src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs index 9f754573936..5ba8c17aaad 100644 --- a/src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs +++ b/src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs @@ -5,20 +5,33 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Collections.Generic; namespace BizHawk.Client.Common { - public struct ClientWebSocketWrapper + public class ClientWebSocketWrapper { private ClientWebSocket? _w; + //private Task _receiveTask; + + private List _receivedMessages; + + Uri _uri; /// calls getter (unless closed/disposed, then is always returned) public WebSocketState State => _w?.State ?? WebSocketState.Closed; - public ClientWebSocketWrapper(Uri uri, CancellationToken? cancellationToken = null) + public ClientWebSocketWrapper(Uri uri, int bufferSize) { + _uri = uri; _w = new ClientWebSocket(); - _w.ConnectAsync(uri, cancellationToken ?? CancellationToken.None).Wait(); + _receivedMessages = new List(); + try{ + Connect(bufferSize).Wait(); + } + catch(Exception ex) + { + } } /// calls @@ -26,24 +39,31 @@ public ClientWebSocketWrapper(Uri uri, CancellationToken? cancellationToken = nu public Task Close(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken? cancellationToken = null) { if (_w == null) throw new ObjectDisposedException(nameof(_w)); - var task = _w.CloseAsync(closeStatus, statusDescription, cancellationToken ?? CancellationToken.None); + var task = _w.CloseOutputAsync(closeStatus, statusDescription, cancellationToken ?? CancellationToken.None); _w.Dispose(); _w = null; return task; } /// calls - public Task Receive(ArraySegment buffer, CancellationToken? cancellationToken = null) - => _w?.ReceiveAsync(buffer, cancellationToken ?? CancellationToken.None) - ?? throw new ObjectDisposedException(nameof(_w)); + public async Task Receive(int bufferSize){ + var buffer = new ArraySegment(new byte[bufferSize]); + while (_w != null && _w.State == WebSocketState.Open) + { + WebSocketReceiveResult result; + result = await _w.ReceiveAsync(buffer, CancellationToken.None); + _receivedMessages.Add(Encoding.UTF8.GetString(buffer.Array,0,result.Count)); + } + } - /// calls - public string Receive(int bufferCap, CancellationToken? cancellationToken = null) - { - if (_w == null) throw new ObjectDisposedException(nameof(_w)); - var buffer = new byte[bufferCap]; - var result = Receive(new ArraySegment(buffer), cancellationToken ?? CancellationToken.None).Result; - return Encoding.UTF8.GetString(buffer, 0, result.Count); + public async Task Connect(int bufferSize){ + if (_w == null){ + _w = new ClientWebSocket(); + } + if(_w != null && _w.State != WebSocketState.Open){ + _w.ConnectAsync(_uri, CancellationToken.None).Wait(); + Receive(bufferSize); + } } /// calls @@ -62,5 +82,13 @@ public Task Send(string message, bool endOfMessage, CancellationToken? cancellat cancellationToken ?? CancellationToken.None ); } + + public string GetMessage() + { + if (_receivedMessages == null || _receivedMessages.Count == 0) return ""; + string returnThis = _receivedMessages[0]; + _receivedMessages.RemoveAt(0); + return returnThis; + } } } diff --git a/src/BizHawk.Client.Common/Api/Interfaces/ICommApi.cs b/src/BizHawk.Client.Common/Api/Interfaces/ICommApi.cs index 3d40a598f75..38eb1f02641 100644 --- a/src/BizHawk.Client.Common/Api/Interfaces/ICommApi.cs +++ b/src/BizHawk.Client.Common/Api/Interfaces/ICommApi.cs @@ -10,9 +10,7 @@ public interface ICommApi : IExternalApi SocketServer? Sockets { get; } -#if ENABLE_WEBSOCKETS WebSocketServer WebSockets { get; } -#endif string? HttpTest(); diff --git a/src/BizHawk.Client.Common/Api/WebSocketServer.cs b/src/BizHawk.Client.Common/Api/WebSocketServer.cs index da6a34a5100..f98f5eede64 100644 --- a/src/BizHawk.Client.Common/Api/WebSocketServer.cs +++ b/src/BizHawk.Client.Common/Api/WebSocketServer.cs @@ -7,6 +7,6 @@ namespace BizHawk.Client.Common { public sealed class WebSocketServer { - public ClientWebSocketWrapper Open(Uri uri, CancellationToken? cancellationToken = null) => new ClientWebSocketWrapper(uri, cancellationToken); + public ClientWebSocketWrapper Open(Uri uri, int bufferSize = 1024) => new ClientWebSocketWrapper(uri, bufferSize); } } diff --git a/src/BizHawk.Client.Common/lua/CommonLibs/CommLuaLibrary.cs b/src/BizHawk.Client.Common/lua/CommonLibs/CommLuaLibrary.cs index bc44d08d1cb..2e633ed6e4a 100644 --- a/src/BizHawk.Client.Common/lua/CommonLibs/CommLuaLibrary.cs +++ b/src/BizHawk.Client.Common/lua/CommonLibs/CommLuaLibrary.cs @@ -3,8 +3,10 @@ using System.ComponentModel; using System.Linq; using System.Text; +using System.Net.WebSockets; using NLua; +using System.Threading.Tasks; namespace BizHawk.Client.Common { @@ -254,20 +256,22 @@ private void CheckHttp() } } -#if ENABLE_WEBSOCKETS - [LuaMethod("ws_open", "Opens a websocket and returns the id so that it can be retrieved later.")] + [LuaMethod("ws_open", "Opens a websocket and returns the id so that it can be retrieved later. If an id is provided, reconnects to the ")] [LuaMethodExample("local ws_id = comm.ws_open(\"wss://echo.websocket.org\");")] - public string WebSocketOpen(string uri) + public string WebSocketOpen(string uri, string guid = null, int bufferSize = 1024) { var wsServer = APIs.Comm.WebSockets; + var localGuid = guid == null ? new Guid() : Guid.Parse(guid); if (wsServer == null) { Log("WebSocket server is somehow not available"); return null; } - var guid = new Guid(); - _websockets[guid] = wsServer.Open(new Uri(uri)); - return guid.ToString(); + if (guid == null) + _websockets[localGuid] = wsServer.Open(new Uri(uri),bufferSize); + else + _websockets[localGuid].Connect(bufferSize); + return localGuid.ToString(); } [LuaMethod("ws_send", "Send a message to a certain websocket id (boolean flag endOfMessage)")] @@ -280,11 +284,11 @@ public void WebSocketSend( if (_websockets.TryGetValue(Guid.Parse(guid), out var wrapper)) wrapper.Send(content, endOfMessage); } - [LuaMethod("ws_receive", "Receive a message from a certain websocket id and a maximum number of bytes to read")] - [LuaMethodExample("local ws = comm.ws_receive(ws_id, str_len);")] - public string WebSocketReceive(string guid, int bufferCap) + [LuaMethod("ws_receive", "Get a receive message from a certain websocket id")] + [LuaMethodExample("local ws = comm.ws_receive(ws_id);")] + public string WebSocketReceive(string guid) => _websockets.TryGetValue(Guid.Parse(guid), out var wrapper) - ? wrapper.Receive(bufferCap) + ? wrapper.GetMessage() : null; [LuaMethod("ws_get_status", "Get a websocket's status")] @@ -298,11 +302,10 @@ public string WebSocketReceive(string guid, int bufferCap) [LuaMethodExample("local ws_status = comm.ws_close(ws_id, close_status);")] public void WebSocketClose( string guid, - WebSocketCloseStatus status, + int status, string closeMessage) { - if (_websockets.TryGetValue(Guid.Parse(guid), out var wrapper)) wrapper.Close(status, closeMessage); + if (_websockets.TryGetValue(Guid.Parse(guid), out var wrapper)) wrapper.Close((WebSocketCloseStatus)status, closeMessage); } -#endif } } \ No newline at end of file From 85a3ba01d6947e15926cbb3e21450a47b955f1d2 Mon Sep 17 00:00:00 2001 From: ninjafriend Date: Tue, 12 Sep 2023 21:08:14 -0500 Subject: [PATCH 2/3] Max Messages Adding configurable max message size for storage. --- .../Api/ClientWebSocketWrapper.cs | 19 +++++++++---------- .../Api/WebSocketServer.cs | 2 +- .../lua/CommonLibs/CommLuaLibrary.cs | 8 ++++---- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs b/src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs index 5ba8c17aaad..e0913264d88 100644 --- a/src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs +++ b/src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs @@ -12,7 +12,6 @@ namespace BizHawk.Client.Common public class ClientWebSocketWrapper { private ClientWebSocket? _w; - //private Task _receiveTask; private List _receivedMessages; @@ -21,17 +20,15 @@ public class ClientWebSocketWrapper /// calls getter (unless closed/disposed, then is always returned) public WebSocketState State => _w?.State ?? WebSocketState.Closed; - public ClientWebSocketWrapper(Uri uri, int bufferSize) + public ClientWebSocketWrapper(Uri uri, int bufferSize, int maxMessages) { _uri = uri; _w = new ClientWebSocket(); _receivedMessages = new List(); try{ - Connect(bufferSize).Wait(); - } - catch(Exception ex) - { + Connect(bufferSize, maxMessages).Wait(); } + catch(Exception ex){} } /// calls @@ -46,23 +43,25 @@ public Task Close(WebSocketCloseStatus closeStatus, string statusDescription, Ca } /// calls - public async Task Receive(int bufferSize){ + public async Task Receive(int bufferSize, int maxMessages){ var buffer = new ArraySegment(new byte[bufferSize]); while (_w != null && _w.State == WebSocketState.Open) { WebSocketReceiveResult result; result = await _w.ReceiveAsync(buffer, CancellationToken.None); - _receivedMessages.Add(Encoding.UTF8.GetString(buffer.Array,0,result.Count)); + if (maxMessages == 0 || _receivedMessages.length < maxMessages) + _receivedMessages.Add(Encoding.UTF8.GetString(buffer.Array,0,result.Count)); } } - public async Task Connect(int bufferSize){ + public async Task Connect(int bufferSize, int maxMessages){ if (_w == null){ _w = new ClientWebSocket(); } + Console.WriteLine(_w.State); if(_w != null && _w.State != WebSocketState.Open){ _w.ConnectAsync(_uri, CancellationToken.None).Wait(); - Receive(bufferSize); + Receive(bufferSize, maxMessages); } } diff --git a/src/BizHawk.Client.Common/Api/WebSocketServer.cs b/src/BizHawk.Client.Common/Api/WebSocketServer.cs index f98f5eede64..457f9006ffe 100644 --- a/src/BizHawk.Client.Common/Api/WebSocketServer.cs +++ b/src/BizHawk.Client.Common/Api/WebSocketServer.cs @@ -7,6 +7,6 @@ namespace BizHawk.Client.Common { public sealed class WebSocketServer { - public ClientWebSocketWrapper Open(Uri uri, int bufferSize = 1024) => new ClientWebSocketWrapper(uri, bufferSize); + public ClientWebSocketWrapper Open(Uri uri, int bufferSize, int maxMessages) => new ClientWebSocketWrapper(uri, bufferSize, maxMessages); } } diff --git a/src/BizHawk.Client.Common/lua/CommonLibs/CommLuaLibrary.cs b/src/BizHawk.Client.Common/lua/CommonLibs/CommLuaLibrary.cs index 2e633ed6e4a..333163a33b8 100644 --- a/src/BizHawk.Client.Common/lua/CommonLibs/CommLuaLibrary.cs +++ b/src/BizHawk.Client.Common/lua/CommonLibs/CommLuaLibrary.cs @@ -258,19 +258,19 @@ private void CheckHttp() [LuaMethod("ws_open", "Opens a websocket and returns the id so that it can be retrieved later. If an id is provided, reconnects to the ")] [LuaMethodExample("local ws_id = comm.ws_open(\"wss://echo.websocket.org\");")] - public string WebSocketOpen(string uri, string guid = null, int bufferSize = 1024) + public string WebSocketOpen(string uri, string guid = null, int bufferSize = 1024, int maxMessages = 20) { var wsServer = APIs.Comm.WebSockets; var localGuid = guid == null ? new Guid() : Guid.Parse(guid); if (wsServer == null) { - Log("WebSocket server is somehow not available"); + Log("WebSocket server is not available"); return null; } if (guid == null) - _websockets[localGuid] = wsServer.Open(new Uri(uri),bufferSize); + _websockets[localGuid] = wsServer.Open(new Uri(uri),bufferSize, maxMessages); else - _websockets[localGuid].Connect(bufferSize); + _websockets[localGuid].Connect(bufferSize, maxMessages); return localGuid.ToString(); } From 395ef90c8b8d95354bfed4ea111a7654a6ef3c20 Mon Sep 17 00:00:00 2001 From: ninjafriend Date: Wed, 13 Sep 2023 00:44:28 -0500 Subject: [PATCH 3/3] Bugfix --- src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs b/src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs index e0913264d88..467aea200a4 100644 --- a/src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs +++ b/src/BizHawk.Client.Common/Api/ClientWebSocketWrapper.cs @@ -49,7 +49,7 @@ public async Task Receive(int bufferSize, int maxMessages){ { WebSocketReceiveResult result; result = await _w.ReceiveAsync(buffer, CancellationToken.None); - if (maxMessages == 0 || _receivedMessages.length < maxMessages) + if (maxMessages == 0 || _receivedMessages.Count < maxMessages) _receivedMessages.Add(Encoding.UTF8.GetString(buffer.Array,0,result.Count)); } } @@ -58,7 +58,6 @@ public async Task Connect(int bufferSize, int maxMessages){ if (_w == null){ _w = new ClientWebSocket(); } - Console.WriteLine(_w.State); if(_w != null && _w.State != WebSocketState.Open){ _w.ConnectAsync(_uri, CancellationToken.None).Wait(); Receive(bufferSize, maxMessages);