diff --git a/Capy64/Assets/Lua/boot/02_http.lua b/Capy64/Assets/Lua/boot/02_http.lua index 9116dac..392bee5 100644 --- a/Capy64/Assets/Lua/boot/02_http.lua +++ b/Capy64/Assets/Lua/boot/02_http.lua @@ -2,23 +2,6 @@ local http = require("http") local event = require("event") local expect = require("expect").expect -local WebSocketHandle = {} -function WebSocketHandle:close() - self:closeAsync() - local _, id - repeat - _, id = event.pull("websocket_close") - until id == self.requestId -end - -function WebSocketHandle:receive() - local _, id, par - repeat - _, id, par = event.pull("websocket_message") - until id == self.requestId - - return par -end function http.request(url, body, headers, options) expect(1, url, "string") @@ -60,12 +43,27 @@ function http.post(url, body, headers, options) return http.request(url, body, headers, options) end -local function buildWebsocketHandle(requestId, handle) - handle.requestId = requestId - local metatable = getmetatable(handle) or {} - metatable.__index = WebSocketHandle +local WebSocketHandle +local function buildWebsocketHandle(handle) + if not WebSocketHandle then + WebSocketHandle = getmetatable(handle) or { __index = {} } + function WebSocketHandle.__index:close() + self:closeAsync() + local _, id + repeat + _, id = event.pull("websocket_close") + until id == self:getRequestID() + end - setmetatable(handle, metatable) + function WebSocketHandle.__index:receive() + local _, id, par + repeat + _, id, par = event.pull("websocket_message") + until id == self:getRequestID() + + return par + end + end return handle end @@ -88,5 +86,5 @@ function http.websocket(url, headers) return nil, par end - return buildWebsocketHandle(requestId, par) + return buildWebsocketHandle(par) end diff --git a/Capy64/Runtime/Libraries/HTTP.cs b/Capy64/Runtime/Libraries/HTTP.cs index 43308d2..45ac6ee 100644 --- a/Capy64/Runtime/Libraries/HTTP.cs +++ b/Capy64/Runtime/Libraries/HTTP.cs @@ -332,7 +332,7 @@ public class HTTP : IPlugin await task; - var handle = new WebSocketHandle(wsClient, requestId, _game); + var handle = new WebSocketHandle(wsClient, requestId); WebSocketConnections.Add(handle); _game.LuaRuntime.QueueEvent("websocket_connect", LK => @@ -368,10 +368,11 @@ public class HTTP : IPlugin if (result.EndOfMessage) { + var payload = builder.ToString(); _game.LuaRuntime.QueueEvent("websocket_message", LK => { LK.PushInteger(requestId); - LK.PushString(builder.ToString()); + LK.PushString(payload); return 2; }); diff --git a/Capy64/Runtime/Objects/Handlers/WebSocketHandle.cs b/Capy64/Runtime/Objects/Handlers/WebSocketHandle.cs index ffcdd07..d6a6a91 100644 --- a/Capy64/Runtime/Objects/Handlers/WebSocketHandle.cs +++ b/Capy64/Runtime/Objects/Handlers/WebSocketHandle.cs @@ -2,78 +2,80 @@ using KeraLua; using System; using System.Collections.Generic; -using System.Linq; using System.Net.WebSockets; -using System.Text; using System.Threading; -using System.Threading.Tasks; namespace Capy64.Runtime.Objects.Handlers; public class WebSocketHandle { - private ClientWebSocket _client; - private long _requestId; - private static IGame _game; - public WebSocketHandle(ClientWebSocket client, long requestId, IGame game) + public const string ObjectType = "WebSocketClient"; + private readonly ClientWebSocket _socket; + private readonly long _requestId; + public WebSocketHandle(ClientWebSocket socket, long requestId) { - _client = client; + _socket = socket; _requestId = requestId; - _game = game; } private static readonly Dictionary functions = new() { + ["getRequestID"] = L_GetRequestId, ["send"] = L_Send, ["closeAsync"] = L_CloseAsync, }; public void Push(Lua L) { - L.NewTable(); + L.PushObject(this); - // metatable - L.NewTable(); - L.PushString("__close"); - L.PushCFunction(L_CloseAsync); - L.SetTable(-3); - L.PushString("__gc"); - L.PushCFunction(L_CloseAsync); - L.SetTable(-3); - L.SetMetaTable(-2); - - foreach (var pair in functions) + if (L.NewMetaTable(ObjectType)) { - L.PushString(pair.Key); - L.PushCFunction(pair.Value); + L.PushString("__index"); + L.NewTable(); + foreach (var pair in functions) + { + L.PushString(pair.Key); + L.PushCFunction(pair.Value); + L.SetTable(-3); + } + L.SetTable(-3); + + L.PushString("__close"); + L.PushCFunction(L_CloseAsync); + L.SetTable(-3); + + L.PushString("__gc"); + L.PushCFunction(L_CloseAsync); L.SetTable(-3); } - L.PushString("_handle"); - L.PushObject(this); - L.SetTable(-3); + L.SetMetaTable(-2); } - private static WebSocketHandle GetHandle(Lua L, bool gc = true) + private static int L_GetRequestId(IntPtr state) { - L.CheckType(1, LuaType.Table); - L.PushString("_handle"); - L.GetTable(1); - return L.ToObject(-1, gc); + var L = Lua.FromIntPtr(state); + + var client = L.CheckObject(1, ObjectType, false); + + L.PushInteger(client._requestId); + + return 1; } private static int L_Send(IntPtr state) { var L = Lua.FromIntPtr(state); + var client = L.CheckObject(1, ObjectType, false); + var data = L.CheckBuffer(2); - var h = GetHandle(L, false); - - if (h is null || h._client.State == WebSocketState.Closed) + if (client is null || client._socket.State == WebSocketState.Closed) L.Error("connection is closed"); - h._client.SendAsync(data, WebSocketMessageType.Text, true, CancellationToken.None); + client._socket.SendAsync(data, WebSocketMessageType.Text, true, CancellationToken.None); return 0; } @@ -82,24 +84,24 @@ public class WebSocketHandle { var L = Lua.FromIntPtr(state); - var h = GetHandle(L, true); + var client = L.CheckObject(1, ObjectType, true); - if (h is null || h._client.State == WebSocketState.Closed) + if (client is null || client._socket.State == WebSocketState.Closed) return 0; - h._client.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None) + client._socket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None) .ContinueWith(async task => { await task; - _game.LuaRuntime.QueueEvent("websocket_close", LK => + Capy64.Instance.LuaRuntime.QueueEvent("websocket_close", LK => { - LK.PushInteger(h._requestId); + LK.PushInteger(client._requestId); return 1; }); }); - HTTP.WebSocketConnections.Remove(h); + HTTP.WebSocketConnections.Remove(client); return 0; }