parent
dbcd6b5b68
commit
166c50b6cf
|
@ -0,0 +1,66 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public struct ClientWebSocketWrapper
|
||||
{
|
||||
private ClientWebSocket? _w;
|
||||
|
||||
/// <summary>calls <see cref="ClientWebSocket.State"/> getter (unless closed/disposed, then <see cref="WebSocketState.Closed"/> is alwars returned)</summary>
|
||||
public WebSocketState State => _w?.State ?? WebSocketState.Closed;
|
||||
|
||||
public ClientWebSocketWrapper(Uri uri, CancellationToken? cancellationToken = null)
|
||||
{
|
||||
_w = new ClientWebSocket();
|
||||
_w.ConnectAsync(uri, cancellationToken ?? CancellationToken.None).Wait();
|
||||
}
|
||||
|
||||
/// <summary>calls <see cref="ClientWebSocket.CloseAsync"/></summary>
|
||||
/// <remarks>also calls <see cref="ClientWebSocket.Dispose"/> (wrapper property <see cref="State"/> will continue to work, method calls will throw <see cref="ObjectDisposedException"/>)</remarks>
|
||||
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);
|
||||
_w?.Dispose();
|
||||
_w = null;
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>calls <see cref="ClientWebSocket.ReceiveAsync"/></summary>
|
||||
public Task<WebSocketReceiveResult> Receive(ArraySegment<byte> buffer, CancellationToken? cancellationToken = null)
|
||||
=> _w?.ReceiveAsync(buffer, cancellationToken ?? CancellationToken.None)
|
||||
?? throw new ObjectDisposedException(nameof(_w));
|
||||
|
||||
/// <summary>calls <see cref="ClientWebSocket.ReceiveAsync"/></summary>
|
||||
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<byte>(buffer), cancellationToken ?? CancellationToken.None).Result;
|
||||
return Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||
}
|
||||
|
||||
/// <summary>calls <see cref="ClientWebSocket.SendAsync"/></summary>
|
||||
public Task Send(ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken? cancellationToken = null)
|
||||
=> _w?.SendAsync(buffer, messageType, endOfMessage, cancellationToken ?? CancellationToken.None)
|
||||
?? throw new ObjectDisposedException(nameof(_w));
|
||||
|
||||
/// <summary>calls <see cref="ClientWebSocket.SendAsync"/></summary>
|
||||
public Task Send(string message, bool endOfMessage, CancellationToken? cancellationToken = null)
|
||||
{
|
||||
if (_w == null) throw new ObjectDisposedException(nameof(_w));
|
||||
return Send(
|
||||
new ArraySegment<byte>(Encoding.UTF8.GetBytes(message)),
|
||||
WebSocketMessageType.Text,
|
||||
endOfMessage,
|
||||
cancellationToken ?? CancellationToken.None
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ namespace BizHawk.Client.Common
|
|||
|
||||
SocketServer? Sockets { get; }
|
||||
|
||||
WebSocketServer? WebSockets { get; }
|
||||
|
||||
string? HttpTest();
|
||||
|
||||
string? HttpTestGet();
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public class WebSocketClient
|
||||
{
|
||||
private Dictionary<string, ClientWebSocket> activeSockets;
|
||||
|
||||
public WebSocketClient()
|
||||
{
|
||||
activeSockets = new Dictionary<string, ClientWebSocket>();
|
||||
}
|
||||
|
||||
public string OpenSocket(string url)
|
||||
{
|
||||
var ws = new ClientWebSocket();
|
||||
var id = new Guid().ToString();
|
||||
|
||||
ws.ConnectAsync(new Uri(url), CancellationToken.None).Wait();
|
||||
|
||||
activeSockets[id] = ws;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public void SendToSocket(string id, string content, bool endOfMessage)
|
||||
{
|
||||
var ws = activeSockets[id];
|
||||
var msg = new ArraySegment<byte>(Encoding.UTF8.GetBytes(content));
|
||||
|
||||
ws.SendAsync(msg, WebSocketMessageType.Text, endOfMessage, CancellationToken.None);
|
||||
}
|
||||
|
||||
public string ReceiveFromSocket(string id, int maxRead)
|
||||
{
|
||||
var ws = activeSockets[id];
|
||||
|
||||
var rcvBytes = new byte[maxRead];
|
||||
var rcvBuffer = new ArraySegment<byte>(rcvBytes);
|
||||
|
||||
var result = ws.ReceiveAsync(rcvBuffer, CancellationToken.None).Result;
|
||||
string rcvMsg = Encoding.UTF8.GetString(rcvBuffer.Take(result.Count).ToArray());
|
||||
|
||||
return rcvMsg;
|
||||
}
|
||||
|
||||
public int GetSocketStatus(string id)
|
||||
{
|
||||
var ws = activeSockets[id];
|
||||
|
||||
return (int)ws.State;
|
||||
}
|
||||
|
||||
public void CloseSocket(string id, int status, string closeMessage)
|
||||
{
|
||||
var ws = activeSockets[id];
|
||||
|
||||
ws.CloseAsync((WebSocketCloseStatus)status, closeMessage, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
public sealed class WebSocketServer
|
||||
{
|
||||
public ClientWebSocketWrapper Open(Uri uri, CancellationToken? cancellationToken = null) => new ClientWebSocketWrapper(uri, cancellationToken);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
|
||||
using NLua;
|
||||
|
@ -9,6 +12,8 @@ namespace BizHawk.Client.Common
|
|||
[Description("A library for communicating with other programs")]
|
||||
public sealed class CommLuaLibrary : DelegatingLuaLibrary
|
||||
{
|
||||
private readonly IDictionary<Guid, ClientWebSocketWrapper> _websockets = new Dictionary<Guid, ClientWebSocketWrapper>();
|
||||
|
||||
public CommLuaLibrary(Lua lua)
|
||||
: base(lua) { }
|
||||
|
||||
|
@ -237,41 +242,6 @@ namespace BizHawk.Client.Common
|
|||
return APIs.Comm.HTTP?.GetUrl;
|
||||
}
|
||||
|
||||
//[LuaMethodExample("local ws = bizsocket.open('wss://echo.websocket.org');")]
|
||||
//[LuaMethod("websocket_open", "Opens a websocket and returns the id so that it can be retrieved later.")]
|
||||
//public string OpenSocket(string url)
|
||||
//{
|
||||
// return APIs.Comm.WebSocketClient?.OpenSocket(url);
|
||||
//}
|
||||
|
||||
//[LuaMethodExample("local ws = bizsocket.send(ws_id, 'some message', true);")]
|
||||
//[LuaMethod("websocket_send", "Send a message to a certain websocket id (boolean flag endOfMessage)")]
|
||||
//public void SendToSocket(string id, string content, bool endOfMessage)
|
||||
//{
|
||||
// APIs.Comm.WebSocketClient?.SendToSocket(id, content, endOfMessage);
|
||||
//}
|
||||
|
||||
//[LuaMethodExample("local ws = bizsocket.receive(ws_id, max_read);")]
|
||||
//[LuaMethod("websocket_receive", "Receive a message from a certain websocket id and a maximum number of bytes to read.")]
|
||||
//public string ReceiveFromSocket(string id, int maxRead)
|
||||
//{
|
||||
// return APIs.Comm.WebSocketClient?.ReceiveFromSocket(id, maxRead);
|
||||
//}
|
||||
|
||||
//[LuaMethodExample("local ws_status = bizsocket.getstatus(ws_id);")]
|
||||
//[LuaMethod("websocket_getStatus", "Get a websocket's status")]
|
||||
//public int? GetSocketStatus(string id)
|
||||
//{
|
||||
// return APIs.Comm.WebSocketClient?.GetSocketStatus(id);
|
||||
//}
|
||||
|
||||
//[LuaMethodExample("local ws_status = bizsocket.close(ws_id, close_status);")]
|
||||
//[LuaMethod("websocket_close", "Close a websocket connection with a close status, defined in section 11.7 of the web sockets protocol spec.")]
|
||||
//public void CloseSocket(string id, int status, string closeMessage)
|
||||
//{
|
||||
// APIs.Comm.WebSocketClient?.CloseSocket(id, status, closeMessage);
|
||||
//}
|
||||
|
||||
private void CheckHttp()
|
||||
{
|
||||
if (APIs.Comm.HTTP == null)
|
||||
|
@ -280,12 +250,45 @@ namespace BizHawk.Client.Common
|
|||
}
|
||||
}
|
||||
|
||||
//private void CheckWebSocket()
|
||||
//{
|
||||
// if (APIs.Comm.WebSocketClient == null)
|
||||
// {
|
||||
// Log("WebSocketClient was not initialized");
|
||||
// }
|
||||
//}
|
||||
[LuaMethod("ws_open", "Opens a websocket and returns the id so that it can be retrieved later.")]
|
||||
[LuaMethodExample("local ws_id = comm.ws_open(\"wss://echo.websocket.org\");")]
|
||||
public string WebSocketOpen(string uri)
|
||||
{
|
||||
var wsServer = APIs.Comm.WebSockets;
|
||||
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();
|
||||
}
|
||||
|
||||
[LuaMethod("ws_send", "Send a message to a certain websocket id (boolean flag endOfMessage)")]
|
||||
[LuaMethodExample("local ws = comm.ws_send(ws_id, \"some message\", true);")]
|
||||
public void WebSocketSend(string guid, string content, bool endOfMessage)
|
||||
{
|
||||
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) => _websockets.TryGetValue(Guid.Parse(guid), out var wrapper)
|
||||
? wrapper.Receive(bufferCap)
|
||||
: null;
|
||||
|
||||
[LuaMethod("ws_get_status", "Get a websocket's status")]
|
||||
[LuaMethodExample("local ws_status = comm.ws_get_status(ws_id);")]
|
||||
public int? WebSocketGetStatus(string guid) => _websockets.TryGetValue(Guid.Parse(guid), out var wrapper)
|
||||
? (int) wrapper.State
|
||||
: (int?) null;
|
||||
|
||||
[LuaMethod("ws_close", "Close a websocket connection with a close status")]
|
||||
[LuaMethodExample("local ws_status = comm.ws_close(ws_id, close_status);")]
|
||||
public void WebSocketClose(string guid, WebSocketCloseStatus status, string closeMessage)
|
||||
{
|
||||
if (_websockets.TryGetValue(Guid.Parse(guid), out var wrapper)) wrapper.Close(status, closeMessage);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using NLua;
|
||||
|
||||
|
||||
namespace BizHawk.Client.Common.lua.LuaHelperLibs
|
||||
{
|
||||
[Description("A library exposing standard .NET string methods")]
|
||||
public sealed class WebSocketLuaLibrary : LuaLibraryBase
|
||||
{
|
||||
private Dictionary<string, ClientWebSocket> activeSockets;
|
||||
|
||||
public WebSocketLuaLibrary(Lua lua) : base(lua)
|
||||
{
|
||||
activeSockets = new Dictionary<string, ClientWebSocket>();
|
||||
}
|
||||
|
||||
public WebSocketLuaLibrary(Lua lua, Action<string> logOutputCallback) : base(lua, logOutputCallback)
|
||||
{
|
||||
activeSockets = new Dictionary<string, ClientWebSocket>();
|
||||
}
|
||||
|
||||
public override string Name => "bizsocket";
|
||||
|
||||
[LuaMethodExample("local ws = bizsocket.open('wss://echo.websocket.org');")]
|
||||
[LuaMethod("open", "Opens a websocket and returns the id so that it can be retrieved later.")]
|
||||
public string OpenSocket(string url)
|
||||
{
|
||||
var ws = new ClientWebSocket();
|
||||
var id = new Guid().ToString();
|
||||
|
||||
ws.ConnectAsync(new Uri(url), CancellationToken.None).Wait();
|
||||
|
||||
activeSockets[id] = ws;
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
[LuaMethodExample("local ws = bizsocket.send(ws_id, 'some message', true);")]
|
||||
[LuaMethod("send", "Send a message to a certain websocket id (boolean flag endOfMessage)")]
|
||||
public void SendToSocket(string id, string content, bool endOfMessage)
|
||||
{
|
||||
var ws = activeSockets[id];
|
||||
var msg = new ArraySegment<byte>(Encoding.UTF8.GetBytes(content));
|
||||
|
||||
ws.SendAsync(msg, WebSocketMessageType.Text, endOfMessage, CancellationToken.None);
|
||||
}
|
||||
|
||||
[LuaMethodExample("local ws = bizsocket.receive(ws_id, max_read);")]
|
||||
[LuaMethod("receive", "Receive a message from a certain websocket id and a maximum number of bytes to read.")]
|
||||
public string ReceiveFromSocket(string id, int maxRead)
|
||||
{
|
||||
var ws = activeSockets[id];
|
||||
|
||||
var rcvBytes = new byte[maxRead];
|
||||
var rcvBuffer = new ArraySegment<byte>(rcvBytes);
|
||||
|
||||
var result = ws.ReceiveAsync(rcvBuffer, CancellationToken.None).Result;
|
||||
string rcvMsg = Encoding.UTF8.GetString(rcvBuffer.Take(result.Count).ToArray());
|
||||
|
||||
return rcvMsg;
|
||||
}
|
||||
|
||||
[LuaMethodExample("local ws_status = bizsocket.getstatus(ws_id);")]
|
||||
[LuaMethod("getStatus", "Get a websocket's status")]
|
||||
public int GetSocketStatus(string id)
|
||||
{
|
||||
var ws = activeSockets[id];
|
||||
|
||||
return (int)ws.State;
|
||||
}
|
||||
|
||||
[LuaMethodExample("local ws_status = bizsocket.close(ws_id, close_status);")]
|
||||
[LuaMethod("close", "Close a websocket connection with a close status, defined in section 11.7 of the web sockets protocol spec.")]
|
||||
public void CloseSocket(string id, int status, string closeMessage)
|
||||
{
|
||||
var ws = activeSockets[id];
|
||||
|
||||
ws.CloseAsync((WebSocketCloseStatus) status, closeMessage, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
public sealed class CommApi : ICommApi
|
||||
{
|
||||
private static readonly WebSocketServer _wsServer = new WebSocketServer();
|
||||
|
||||
private readonly (HttpCommunication HTTP, MemoryMappedFiles MMF, SocketServer Sockets) _networkingHelpers;
|
||||
|
||||
public HttpCommunication? HTTP => _networkingHelpers.HTTP;
|
||||
|
@ -16,6 +18,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
public SocketServer? Sockets => _networkingHelpers.Sockets;
|
||||
|
||||
public WebSocketServer? WebSockets => _wsServer;
|
||||
|
||||
public CommApi(Action<string> logCallback, DisplayManager displayManager, InputManager inputManager, IMainFormForApi mainForm)
|
||||
{
|
||||
_networkingHelpers = mainForm.NetworkingHelpers;
|
||||
|
|
Loading…
Reference in New Issue