diff --git a/Assets/OxGFrame/CHANGELOG.md b/Assets/OxGFrame/CHANGELOG.md index 706aaee9..19274add 100644 --- a/Assets/OxGFrame/CHANGELOG.md +++ b/Assets/OxGFrame/CHANGELOG.md @@ -1,5 +1,17 @@ # CHANGELOG +## [2.9.11] - 2024-01-09 +- Optimized NetFrame. +- Added TcpNetOption. +- Added WebsocketNetOption. +- Modified NetOption. +- Modified SetResponseHandler to SetResponseBinaryHandler and SetResponseMessageHandler. +- Modified typo SetOutReciveAction to SetOutReceiveAction. +- Renamed TcpSock to TcpNetProvider. +- Renamed WebSock to WebsocketNetProvider. +- Renamed method CloseSocket to Close. +- Renamed ISocket to INetProvider. + ## [2.9.10] - 2023-12-28 - Updated YooAsset to v2.1.0 ([CHANGELOG](https://github.com/tuyoogame/YooAsset/releases/tag/2.1.0)). - Organized coding style ([Wiki](https://github.com/michael811125/OxGFrame/wiki/Coding-Style)). diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Concrete.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetNode.meta similarity index 77% rename from Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Concrete.meta rename to Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetNode.meta index 53b8d27d..0c17dbd7 100644 --- a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Concrete.meta +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetNode.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 50417cf26854a7149877cf173febce03 +guid: 0e5cce3f7dd8e61438eb08b31078caad folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Concrete/NetNode.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetNode/NetNode.cs similarity index 55% rename from Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Concrete/NetNode.cs rename to Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetNode/NetNode.cs index b61b4ec0..fb6f2a00 100644 --- a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Concrete/NetNode.cs +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetNode/NetNode.cs @@ -4,40 +4,8 @@ namespace OxGFrame.NetFrame { - public delegate void ResponseHandler(byte[] data); - public delegate void FirstSendHandler(); - - public class NetOption - { - public string url { get; set; } - public string host { get; set; } - public int port { get; set; } - public int autoReconnectCount { get; set; } - - /// - /// TCP/IP 初始連線位置參數 - /// - /// - /// - /// - public NetOption(string host, int port, int autoReconnectCount = -1) - { - this.host = host; - this.port = port; - this.autoReconnectCount = autoReconnectCount; - } - - /// - /// Websocket 初始連線位置參數 - /// - /// - /// - public NetOption(string url, int autoReconnectCount = -1) - { - this.url = url; - this.autoReconnectCount = autoReconnectCount; - } - } + public delegate void ResponseHandler(T data); + public delegate void ConnectingHandler(); public enum NetStatus { @@ -50,111 +18,109 @@ public enum NetStatus public class NetNode : IDisposable { - protected NetStatus _netStatus; // Net 狀態 - protected NetOption _netOption = null; // 網路設置選項 - protected ISocket _socket = null; // Socket 介面 - protected INetTips _netTips = null; // 網路狀態提示介面 + protected NetStatus _netStatus; // Net 狀態 + protected NetOption _netOption = null; // 網路設置選項 + protected INetProvider _netProvider = null; // 網路供應者 (TCP/IP, websocket or other) + protected INetTips _netTips = null; // 網路狀態提示介面 - private bool _isCloseForce = false; // 是否強制斷線 + private bool _isCloseForce = false; // 是否強制斷線 - protected RealTimer _hearBeatTicker = null; // 心跳檢測計循環計時器 - private float _heartBeatTick = 10f; // 心跳檢測時間, 預設 = 10秒 - protected Action _heartBeatAction = null; // 心跳檢測 Callback + protected RealTimer _hearBeatTicker = null; // 心跳檢測計循環計時器 + private float _heartBeatTick = 10f; // 心跳檢測時間, 預設 = 10秒 + protected Action _heartBeatAction = null; // 心跳檢測 Callback - protected RealTimer _outReceiveTicker = null; // 超時檢測循環計時器 - private float _outReceiveTick = 60f; // 超時檢測時間, 預設 = 60秒 - protected Action _outReceiveAction = null; // 超時檢測 Callback + protected RealTimer _outReceiveTicker = null; // 超時檢測循環計時器 + private float _outReceiveTick = 60f; // 超時檢測時間, 預設 = 60秒 + protected Action _outReceiveAction = null; // 超時檢測 Callback - protected RealTimer _reconnectTicker = null; // 斷線重連循環計時器 - private float _reconnectTick = 5f; // 斷線重連時間, 預設 = 5秒 - private int _autoReconnectCount = 0; // 自動連線次數 (由 NetOption 帶入) - protected Action _reconnectAction = null; // 斷線重連 Callback + protected RealTimer _reconnectTicker = null; // 斷線重連循環計時器 + private float _reconnectTick = 5f; // 斷線重連時間, 預設 = 5秒 + private int _autoReconnectCount = 0; // 自動連線次數 + protected Action _reconnectAction = null; // 斷線重連 Callback - protected ResponseHandler _responseHandler = null; // 接收的回調 - protected FirstSendHandler _firstSendHandler = null; // 第一次封包的回調 + protected ResponseHandler _responseBinaryHandler = null; // 接收的回調 (Binary) + protected ResponseHandler _responseMessageHandler; // 接收的回調 (Text) + protected ConnectingHandler _connectingHandler = null; // 連線中的回調 - public NetNode() + protected NetNode() { - this._hearBeatTicker = new RealTimer(); - this._outReceiveTicker = new RealTimer(); - this._reconnectTicker = new RealTimer(); } - public NetNode(ISocket socket, INetTips netTips = null) + public NetNode(INetProvider socket, INetTips netTips = null) { this._hearBeatTicker = new RealTimer(); this._outReceiveTicker = new RealTimer(); this._reconnectTicker = new RealTimer(); - this.Init(socket, netTips); + this._Initialize(socket, netTips); } - public T GetSocket() where T : ISocket + public T GetNetProvider() where T : INetProvider { - T socket = (T)this._socket; + T socket = (T)this._netProvider; return socket; } - public void Init(ISocket socket, INetTips netTips = null) + private void _Initialize(INetProvider socket, INetTips netTips = null) { - this._socket = socket; - this._InitSocketEvents(); + this._netProvider = socket; + this._InitNetEvents(); this._netTips = netTips; this._netStatus = NetStatus.DISCONNECTED; } - private void _InitSocketEvents() + private void _InitNetEvents() { - this._socket.OnOpen += (sender, e) => + this._netProvider.OnOpen += (sender, status) => { - this._OnOpen(e); + this._OnOpen(status); }; - this._socket.OnMessage += (sender, data) => + this._netProvider.OnBinary += (sender, binary) => { - this._OnMessage(data); + this._OnBinary(binary); }; - this._socket.OnError += (sender, e) => + this._netProvider.OnMessage += (sender, text) => { - this._OnError(e); + this._OnMessage(text); }; - this._socket.OnClose += (sender, e) => + this._netProvider.OnError += (sender, error) => { - this._OnClose(e); + this._OnError(error); + }; + + this._netProvider.OnClose += (sender, status) => + { + this._OnClose(status); }; } public void Connect(NetOption netOption) { - if (this._socket == null) + if (this._netProvider == null) { Logging.PrintError("The socket cannot be null, Please init first."); return; } - if (this._netStatus == NetStatus.DISCONNECTED || this._netStatus == NetStatus.RECONNECTING) + if (this._netStatus == NetStatus.DISCONNECTED || + this._netStatus == NetStatus.RECONNECTING) { - this._netStatus = NetStatus.CONNECTING; // 目前處於 CONNECTING 狀態 - this._NetStatusHandler(); - - this._firstSendHandler?.Invoke(); // 重連時重新初始第一次封包 - - this._netOption = netOption; // 設置 NetOption (連線配置) - this._socket.CreateConnect(netOption); // 最後再建立 Socket 連線 (TCP/IP => 需先 InitNetSocket 註冊後才能 Handle, Websocket => 透過原先 EventHandler 再進行註冊) + this._netStatus = NetStatus.CONNECTING; + this._NetStatusHandler(null); + this._netOption = netOption; + this._connectingHandler?.Invoke(); + this._netProvider.CreateConnect(netOption); } } - /// - /// 設置 NetTips (設置其他實作的 NetTips) - /// - /// public void SetNetTips(INetTips netTips) { this._netTips = netTips; } - private void _NetStatusHandler(object args = null) + private void _NetStatusHandler(object args) { switch (this._netStatus) { @@ -162,13 +128,13 @@ private void _NetStatusHandler(object args = null) this._netTips?.OnConnecting(); break; case NetStatus.CONNECTED: - this._netTips?.OnConnected(args as EventArgs); + this._netTips?.OnConnected(args); break; case NetStatus.CONNECTION_ERROR: this._netTips?.OnConnectionError(Convert.ToString(args)); break; case NetStatus.DISCONNECTED: - this._netTips?.OnDisconnected(Convert.ToUInt16(args)); + this._netTips?.OnDisconnected(args); break; case NetStatus.RECONNECTING: this._netTips?.OnReconnecting(); @@ -176,17 +142,17 @@ private void _NetStatusHandler(object args = null) } } - public void OnUpdate() + public void OnUpdate(float dt) { this._ProcessOutReceive(); this._ProcessHeartBeat(); this._ProcessAutoReconnect(); } - private void _OnOpen(EventArgs e) + private void _OnOpen(object status) { this._netStatus = NetStatus.CONNECTED; - this._NetStatusHandler(e); + this._NetStatusHandler(status); this._isCloseForce = false; this._ResetAutoReconnect(); @@ -194,11 +160,16 @@ private void _OnOpen(EventArgs e) this._ResetHeartBeatTicker(); } - private void _OnMessage(byte[] data) + private void _OnBinary(byte[] binary) { this._ResetOutReceiveTicker(); + this._responseBinaryHandler?.Invoke(binary); + } - this._responseHandler?.Invoke(data); + private void _OnMessage(string text) + { + this._ResetOutReceiveTicker(); + this._responseMessageHandler?.Invoke(text); } private void _OnError(string msg) @@ -207,33 +178,31 @@ private void _OnError(string msg) this._NetStatusHandler(msg); } - private void _OnClose(ushort code) + private void _OnClose(object status) { this._netStatus = NetStatus.DISCONNECTED; - this._NetStatusHandler(code); + this._NetStatusHandler(status); this._StopTicker(); if (this._isCloseForce) return; this._StartAutoReconnect(); } - /// - /// 傳送 Binary Data 至 Server - /// - /// - /// public bool Send(byte[] buffer) { - return this._socket.Send(buffer); + return this._netProvider.SendBinary(buffer); } - /// - /// 關閉 Socket - /// - public void CloseSocket() + public bool Send(string text) + { + return this._netProvider.SendMessage(text); + } + + public void Close() { this._isCloseForce = true; - if (this._socket != null) this._socket.Close(); + if (this._netProvider != null) + this._netProvider.Close(); } /// @@ -242,32 +211,41 @@ public void CloseSocket() /// public bool IsConnected() { - if (this._socket == null) return false; - return this._socket.IsConnected(); + if (this._netProvider == null) return false; + return this._netProvider.IsConnected(); + } + + /// + /// 設置接收的 Handler (Binary) + /// + /// + public void SetResponseBinaryHandler(ResponseHandler handler) + { + this._responseBinaryHandler = handler; } /// - /// 設置接收的 Handler + /// 設置接收的 Handler (Text) /// - /// - public void SetResponseHandler(ResponseHandler rh) + /// + public void SetResponseMessageHandler(ResponseHandler handler) { - this._responseHandler = rh; + this._responseMessageHandler = handler; } /// - /// 設置第一次初始寄送封包的 Handler + /// 設置每次連線中的 Handler /// - /// - public void SetFirstSendHandler(FirstSendHandler fsh) + /// + public void SetConnectingHandler(ConnectingHandler handler) { - this._firstSendHandler = fsh; + this._connectingHandler = handler; } #region 超時 Ticker 處理 - public void SetOutReciveAction(Action outReciveAction) + public void SetOutReceiveAction(Action outReceiveAction) { - this._outReceiveAction = outReciveAction; + this._outReceiveAction = outReceiveAction; } public void SetOutReceiveTickerTime(float time) @@ -350,7 +328,7 @@ private void _ResetAutoReconnect() private void _StartAutoReconnect() { this._netStatus = NetStatus.RECONNECTING; - this._NetStatusHandler(); + this._NetStatusHandler(null); this._reconnectTicker.Play(); this._reconnectTicker.SetTick(this._reconnectTick); @@ -369,7 +347,7 @@ private void _ProcessAutoReconnect() { if (this._reconnectTicker.IsTickTimeout()) { - this._socket.Close(); + this._netProvider.Close(); this.Connect(this._netOption); if (this._autoReconnectCount > 0) this._autoReconnectCount -= 1; @@ -382,7 +360,7 @@ private void _ProcessAutoReconnect() else { this._netStatus = NetStatus.DISCONNECTED; - this._NetStatusHandler(); + this._NetStatusHandler(null); this._reconnectTicker.Stop(); } } @@ -400,9 +378,9 @@ private void _StopTicker() public void Dispose() { - if (this._socket != null) - this._socket.Close(); - this._socket = null; + if (this._netProvider != null) + this._netProvider.Close(); + this._netProvider = null; this._netTips = null; this._netOption = null; this._hearBeatTicker = null; @@ -411,7 +389,7 @@ public void Dispose() this._outReceiveAction = null; this._reconnectTicker = null; this._reconnectAction = null; - this._responseHandler = null; + this._responseBinaryHandler = null; } ~NetNode() @@ -419,5 +397,4 @@ public void Dispose() this.Dispose(); } } -} - +} \ No newline at end of file diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Concrete/NetNode.cs.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetNode/NetNode.cs.meta similarity index 100% rename from Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Concrete/NetNode.cs.meta rename to Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetNode/NetNode.cs.meta diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption.meta new file mode 100644 index 00000000..a38ba231 --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 00a41bb16b92ee84ba58e93f7671a4f4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption/TcpNetOption.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption/TcpNetOption.cs new file mode 100644 index 00000000..25794add --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption/TcpNetOption.cs @@ -0,0 +1,14 @@ +namespace OxGFrame.NetFrame +{ + public class TcpNetOption : NetOption + { + public string host { get; set; } + public int port { get; set; } + + public TcpNetOption(string host, int port, int autoReconnectCount = -1) : base(autoReconnectCount) + { + this.host = host; + this.port = port; + } + } +} diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption/TcpNetOption.cs.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption/TcpNetOption.cs.meta new file mode 100644 index 00000000..0878bfd5 --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption/TcpNetOption.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c5099630645b1cf43a28e887372a2bce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption/WebsocketNetOption.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption/WebsocketNetOption.cs new file mode 100644 index 00000000..75b6826b --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption/WebsocketNetOption.cs @@ -0,0 +1,12 @@ +namespace OxGFrame.NetFrame +{ + public class WebsocketNetOption : NetOption + { + public string url { get; set; } + + public WebsocketNetOption(string url, int autoReconnectCount = -1) : base(autoReconnectCount) + { + this.url = url; + } + } +} \ No newline at end of file diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption/WebsocketNetOption.cs.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption/WebsocketNetOption.cs.meta new file mode 100644 index 00000000..15680669 --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetOption/WebsocketNetOption.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f97631c1af44be4c8d2df64cf85da9c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider.meta new file mode 100644 index 00000000..68593eba --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 81ed76e9e53b0114496135c64f45f642 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/TcpSock.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider/TcpNetProvider.cs similarity index 79% rename from Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/TcpSock.cs rename to Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider/TcpNetProvider.cs index ead64c83..c4a58cbc 100644 --- a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/TcpSock.cs +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider/TcpNetProvider.cs @@ -8,30 +8,42 @@ namespace OxGFrame.NetFrame { public delegate void WaitReadNetPacket(); - public class TcpSock : ISocket + public class TcpNetProvider : INetProvider { private const int _CONNECTING_TIMEOUT_MSEC = 10000; private const int _MAX_BUFFER_SIZE = 65536; private const int _MAX_FAILED_CONNECTION_COUNT = 3; private TcpClient _tcp = null; - private NetOption _netOption = null; private int _failedConnectionCount; private int _readBufferOffset = 0; private byte[] _readBuffer = null; - public event EventHandler OnOpen; - public event EventHandler OnMessage; + public event EventHandler OnOpen; + public event EventHandler OnBinary; + public event EventHandler OnMessage; public event EventHandler OnError; - public event EventHandler OnClose; + public event EventHandler OnClose; private WaitReadNetPacket _waitReadNetPacket = null; public void CreateConnect(NetOption netOption) { - this._netOption = netOption; - if (string.IsNullOrEmpty(netOption.host) || netOption.port == 0) return; + if (netOption == null) + { + Logging.Print("ERROR: Connect failed, NetOption cannot be null."); + return; + } + + string host = (netOption as TcpNetOption).host; + int port = (netOption as TcpNetOption).port; + + if (string.IsNullOrEmpty(host)) + { + Logging.Print("ERROR: TCP/IP Connect failed, NetOption Host cannot be null or empty."); + return; + } this._tcp = new TcpClient(); this._tcp.ReceiveBufferSize = _MAX_BUFFER_SIZE; @@ -41,16 +53,16 @@ public void CreateConnect(NetOption netOption) try { - Logging.Print($"Connecting... Host {netOption.host}:{netOption.port}"); - IPAddress ipa = IPAddress.Parse(netOption.host); - IAsyncResult result = this._tcp.BeginConnect(ipa, netOption.port, this._ConnectedAction, null); + Logging.Print($"Connecting... Host {host}:{port}"); + IPAddress ipa = IPAddress.Parse(host); + IAsyncResult result = this._tcp.BeginConnect(ipa, port, this._ConnectedAction, null); result.AsyncWaitHandle.WaitOne(_CONNECTING_TIMEOUT_MSEC); if (!result.IsCompleted) { this._tcp.Close(); - this.OnClose(this, 0); - Logging.PrintError($"Begin Connect Failed!!! Host {netOption.host}:{netOption.port}"); + this.OnClose(this, -1); + Logging.PrintError($"Begin Connect Failed!!! Host {host}:{port}"); } } catch (Exception ex) @@ -66,7 +78,7 @@ private void _ConnectedAction(IAsyncResult result) try { this._tcp?.EndConnect(result); - Logging.Print($"TcpSock Connected."); + Logging.Print($"TCP/IP Connected."); } catch (Exception ex) { @@ -81,8 +93,7 @@ private void _ConnectedAction(IAsyncResult result) } } - this.OnOpen(this, EventArgs.Empty); - + this.OnOpen(this, 0); NetworkStream ns = this._tcp.GetStream(); this._readBufferOffset = 0; ns.BeginRead(this._readBuffer, this._readBufferOffset, this._readBuffer.Length, new AsyncCallback(this._ReadAction), ns); @@ -134,7 +145,7 @@ private void _ReadAction(IAsyncResult result) { if (this._readBufferOffset > 0) { - this.OnMessage(this, this._readBuffer); + this.OnBinary(this, this._readBuffer); this._readBufferOffset = 0; } @@ -168,7 +179,7 @@ private void _WriteAction(IAsyncResult result) Logging.Print($"Sending Buffer End Time: {DateTime.Now}"); } - public bool Send(byte[] buffer) + public bool SendBinary(byte[] buffer) { if (this.IsConnected()) { @@ -176,7 +187,7 @@ public bool Send(byte[] buffer) { NetworkStream ns = this._tcp?.GetStream(); ns.BeginWrite(buffer, 0, buffer.Length, new AsyncCallback(this._WriteAction), ns); - Logging.Print($"TcpSock - SentSize: {buffer.Length} bytes"); + Logging.Print($"[Binary] - Send Size: {buffer.Length} bytes."); return true; } catch (Exception ex) @@ -190,6 +201,11 @@ public bool Send(byte[] buffer) return false; } + public bool SendMessage(string text) + { + throw new Exception("[Text] TCP/IP not supports SendMessge!!! Please convert string to binary and send by binary."); + } + public bool IsConnected() { if (this._tcp == null) return false; @@ -199,16 +215,12 @@ public bool IsConnected() public void Close() { this._tcp?.Close(); - this.OnClose(this, 0); + this.OnClose(this, -1); } - /// - /// 設置等待讀取封包 Callback - /// - /// public void SetWaitReadNetPacket(WaitReadNetPacket wrnp) { this._waitReadNetPacket = wrnp; } } -} +} \ No newline at end of file diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/TcpSock.cs.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider/TcpNetProvider.cs.meta similarity index 100% rename from Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/TcpSock.cs.meta rename to Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider/TcpNetProvider.cs.meta diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider/WebsocketNetProvider.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider/WebsocketNetProvider.cs new file mode 100644 index 00000000..6f38f590 --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider/WebsocketNetProvider.cs @@ -0,0 +1,93 @@ +using OxGKit.LoggingSystem; +using System; +using UnityWebSocket; + +namespace OxGFrame.NetFrame +{ + public class WebsocketNetProvider : INetProvider + { + private WebSocket _ws = null; + + public event EventHandler OnOpen; + public event EventHandler OnBinary; + public event EventHandler OnMessage; + public event EventHandler OnError; + public event EventHandler OnClose; + + public void CreateConnect(NetOption netOption) + { + if (netOption == null) + { + Logging.Print("ERROR: Connect failed, NetOption cannot be null."); + return; + } + + string url = (netOption as WebsocketNetOption).url; + if (string.IsNullOrEmpty(url)) + { + Logging.Print("ERROR: Websocket Connect failed, NetOption URL cannot be null or empty."); + return; + } + + this._ws = new WebSocket(url); + + this._ws.OnOpen += (sender, e) => + { + this.OnOpen(sender, e); + }; + this._ws.OnMessage += (sender, e) => + { + if (e.IsBinary) this.OnBinary(sender, e.RawData); + else this.OnMessage(sender, e.Data); + }; + this._ws.OnError += (sender, e) => + { + this.OnError(sender, e.Message); + }; + this._ws.OnClose += (sender, e) => + { + this.OnClose(sender, e.Code); + }; + + this._ws.ConnectAsync(); + + Logging.Print($"URL: {url} => Websocket is Connected."); + } + + public bool IsConnected() + { + if (this._ws == null) return false; + if (this._ws.ReadyState == WebSocketState.Open) return true; + return false; + } + + public bool SendBinary(byte[] buffer) + { + if (this.IsConnected()) + { + this._ws?.SendAsync(buffer); + Logging.Print($"[Binary] Websocket - Send Size: {buffer.Length} bytes."); + return true; + } + + return false; + } + + public bool SendMessage(string text) + { + if (this.IsConnected()) + { + this._ws?.SendAsync(text); + Logging.Print($"[Text] Websocket - Try Send: {text}."); + return true; + } + + return false; + } + + public void Close() + { + this._ws?.CloseAsync(); + } + } +} diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/WebSock.cs.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider/WebsocketNetProvider.cs.meta similarity index 100% rename from Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/WebSock.cs.meta rename to Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/NetProvider/WebsocketNetProvider.cs.meta diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/WebSock.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/WebSock.cs deleted file mode 100644 index a9464b87..00000000 --- a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Implement/WebSock.cs +++ /dev/null @@ -1,74 +0,0 @@ -using OxGKit.LoggingSystem; -using System; -using UnityWebSocket; - -namespace OxGFrame.NetFrame -{ - public class WebSock : ISocket - { - private WebSocket _ws = null; - private NetOption _netOption = null; - - public event EventHandler OnOpen; - public event EventHandler OnMessage; - public event EventHandler OnError; - public event EventHandler OnClose; - - public void CreateConnect(NetOption netOption) - { - this._netOption = netOption; - if (string.IsNullOrEmpty(netOption.url)) - { - Logging.Print("ERROR: Websocket Connect failed, net option URL cannot be null."); - return; - } - - this._ws = new WebSocket(netOption.url); - - this._ws.OnOpen += (sender, e) => - { - this.OnOpen(sender, e); - }; - this._ws.OnMessage += (sender, e) => - { - this.OnMessage(sender, e.RawData); - }; - this._ws.OnError += (sender, e) => - { - this.OnError(sender, e.Message); - }; - this._ws.OnClose += (sender, e) => - { - this.OnClose(sender, e.Code); - }; - - this._ws.ConnectAsync(); - - Logging.Print(string.Format("URL: {0} => Websocket is Connected.", this._netOption.url)); - } - - public bool IsConnected() - { - if (this._ws == null) return false; - if (this._ws.ReadyState == WebSocketState.Open) return true; - return false; - } - - public bool Send(byte[] buffer) - { - if (this.IsConnected()) - { - this._ws?.SendAsync(buffer); - Logging.Print(string.Format("Websocket - SentSize: {0} bytes", buffer.Length)); - return true; - } - - return false; - } - - public void Close() - { - this._ws?.CloseAsync(); - } - } -} diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/INetTips.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/INetTips.cs deleted file mode 100644 index cf406f89..00000000 --- a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/INetTips.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; - -namespace OxGFrame.NetFrame -{ - public interface INetTips - { - /// - /// 連線中 - /// - void OnConnecting(); - - /// - /// 已連線 - /// - /// - void OnConnected(EventArgs e); - - /// - /// 連線錯誤 - /// - /// - void OnConnectionError(string msg); - - /// - /// 關閉連線 - /// - /// - void OnDisconnected(ushort code); - - /// - /// 自動重連 - /// - void OnReconnecting(); - } -} diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/ISocket.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/ISocket.cs deleted file mode 100644 index 6edb8646..00000000 --- a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/ISocket.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; - -namespace OxGFrame.NetFrame -{ - public interface ISocket - { - event EventHandler OnOpen; - event EventHandler OnMessage; - event EventHandler OnError; - event EventHandler OnClose; - - /// - /// 建立連線 - /// - /// - void CreateConnect(NetOption netOption); - - /// - /// 是否已連線 - /// - /// - bool IsConnected(); - - /// - /// 傳送 BinaryData - /// - /// - /// - bool Send(byte[] buffer); - - /// - /// 關閉連線 - /// - void Close(); - } - -} diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetOption.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetOption.meta new file mode 100644 index 00000000..32767df5 --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetOption.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1b77f50c60836c640bfa35f3f7f377c0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetOption/NetOption.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetOption/NetOption.cs new file mode 100644 index 00000000..61b0122e --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetOption/NetOption.cs @@ -0,0 +1,12 @@ +namespace OxGFrame.NetFrame +{ + public abstract class NetOption + { + public int autoReconnectCount { get; set; } + + public NetOption(int autoReconnectCount = -1) + { + this.autoReconnectCount = autoReconnectCount; + } + } +} \ No newline at end of file diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetOption/NetOption.cs.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetOption/NetOption.cs.meta new file mode 100644 index 00000000..2235aa69 --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetOption/NetOption.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 24d094f1e0d6ac947adea9969342ca5b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetProvider.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetProvider.meta new file mode 100644 index 00000000..eef91c41 --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetProvider.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bd2a280c6e4c99344a68c9d9492d3358 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetProvider/INetProvider.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetProvider/INetProvider.cs new file mode 100644 index 00000000..f107ad33 --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetProvider/INetProvider.cs @@ -0,0 +1,48 @@ +using System; + +namespace OxGFrame.NetFrame +{ + public interface INetProvider + { + event EventHandler OnOpen; + + event EventHandler OnBinary; + + event EventHandler OnMessage; + + event EventHandler OnError; + + event EventHandler OnClose; + + /// + /// Open connection + /// + /// + void CreateConnect(NetOption netOption); + + /// + /// Connection state + /// + /// + bool IsConnected(); + + /// + /// Send binary data + /// + /// + /// + bool SendBinary(byte[] buffer); + + /// + /// Send string data + /// + /// + /// + bool SendMessage(string text); + + /// + /// Close connection + /// + void Close(); + } +} diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/ISocket.cs.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetProvider/INetProvider.cs.meta similarity index 100% rename from Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/ISocket.cs.meta rename to Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetProvider/INetProvider.cs.meta diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetTip.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetTip.meta new file mode 100644 index 00000000..4cbcac73 --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetTip.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8625d024add37e34ca9ad9fa0c85a203 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetTip/INetTips.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetTip/INetTips.cs new file mode 100644 index 00000000..d0c380ee --- /dev/null +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetTip/INetTips.cs @@ -0,0 +1,17 @@ +using System; + +namespace OxGFrame.NetFrame +{ + public interface INetTips + { + void OnConnecting(); + + void OnConnected(object status); + + void OnConnectionError(string msg); + + void OnDisconnected(object status); + + void OnReconnecting(); + } +} \ No newline at end of file diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/INetTips.cs.meta b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetTip/INetTips.cs.meta similarity index 100% rename from Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/INetTips.cs.meta rename to Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/Interface/NetTip/INetTips.cs.meta diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/NetManager.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/NetManager.cs index 23ccdc7a..0390cbc5 100644 --- a/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/NetManager.cs +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/Core/NetManager.cs @@ -1,11 +1,14 @@ using OxGKit.LoggingSystem; +using OxGKit.Utilities.Timer; using System.Collections.Generic; +using System.Linq; namespace OxGFrame.NetFrame { internal class NetManager { private Dictionary _netNodes = null; // NetNode 緩存 + private RTUpdater _rtUpdater = null; private static readonly object _locker = new object(); private static NetManager _instance = null; @@ -25,56 +28,74 @@ public static NetManager GetInstance() public NetManager() { this._netNodes = new Dictionary(); + this._rtUpdater = new RTUpdater(); + this._rtUpdater.onUpdate = this._OnUpdate; + this._rtUpdater.Start(); } - public void OnUpdate() + private void _OnUpdate(float dt) { - if (this._netNodes.Count == 0) return; - - foreach (NetNode netNode in this._netNodes.Values) + if (this._netNodes.Count > 0) { - if (netNode == null) continue; - netNode.OnUpdate(); + var nodes = this._netNodes.Values.ToArray(); + foreach (var node in nodes) + { + if (this._netNodes.Count != nodes.Length) break; + else if (node == null) continue; + node.OnUpdate(dt); + } } } /// - /// 透過 NetNodeID 方式註冊新增 NetNode + /// Add net node /// /// - /// 預設 = 0 - public void AddNetNode(NetNode netNode, byte nnId = 0) + /// + public void AddNetNode(NetNode netNode, byte nnId) { - if (!this._netNodes.ContainsKey(nnId)) this._netNodes.Add(nnId, netNode); - else this._netNodes[nnId] = netNode; + if (!this._netNodes.ContainsKey(nnId)) + { + this._netNodes.Add(nnId, netNode); + } + else + { + this._netNodes[nnId].Dispose(); + this._netNodes[nnId] = netNode; + } } /// - /// 透過 NetNodeID 移除 NetNode + /// Remove net node /// - /// 預設 = 0 - public void RemoveNetNode(byte nnId = 0) + /// + public void RemoveNetNode(byte nnId) { - if (this._netNodes.ContainsKey(nnId)) this._netNodes.Remove(nnId); + if (this._netNodes.ContainsKey(nnId)) + { + this._netNodes[nnId].Dispose(); + this._netNodes.Remove(nnId); + } } /// - /// 透過 NetNodeID 取得 NetNode + /// Get net node /// - /// 預設 = 0 + /// /// - public NetNode GetNetNode(byte nnId = 0) + public NetNode GetNetNode(byte nnId) { - if (this._netNodes.ContainsKey(nnId)) return this._netNodes[nnId]; + if (this._netNodes.ContainsKey(nnId)) + return this._netNodes[nnId]; return null; } /// - /// 設置 NetOption 並且透過 NetNodeID 指定 NetNode 進行連接 + /// Open connection /// /// - /// 預設 = 0 - public void Connect(NetOption netOption, byte nnId = 0) + /// + public void Connect(NetOption netOption, byte nnId) { if (this._netNodes.ContainsKey(nnId)) { @@ -84,27 +105,27 @@ public void Connect(NetOption netOption, byte nnId = 0) } /// - /// 透過 NetNodeID 取得 NetNode 的連線狀態 + /// Connection state /// - /// 預設 = 0 + /// /// - public bool IsConnected(byte nnId = 0) + public bool IsConnected(byte nnId) { if (this._netNodes.ContainsKey(nnId)) { if (this._netNodes[nnId] == null) return false; return this._netNodes[nnId].IsConnected(); } - return false; } + /// - /// 透過 NetNodeID 指定 NetNode 傳送資料至 Server + /// Send binary data /// /// - /// 預設 = 0 + /// /// - public bool Send(byte[] buffer, byte nnId = 0) + public bool Send(byte[] buffer, byte nnId) { if (this._netNodes.ContainsKey(nnId)) { @@ -118,15 +139,35 @@ public bool Send(byte[] buffer, byte nnId = 0) } /// - /// 透過 NetNodeID 選擇要關閉的 NetNode + /// Send text data /// - /// 預設 = 0 - public void CloseSocket(byte nnId = 0) + /// + /// + /// + public bool Send(string text, byte nnId) { if (this._netNodes.ContainsKey(nnId)) { - this._netNodes[nnId].CloseSocket(); - this._netNodes.Remove(nnId); + return this._netNodes[nnId].Send(text); + } + else + { + Logging.PrintWarning(string.Format("The NodeId: {0} Can't Found !!! Send Failed.", nnId)); + return false; + } + } + + /// + /// Close network + /// + /// + /// + public void Close(byte nnId, bool remove) + { + if (this._netNodes.ContainsKey(nnId)) + { + this._netNodes[nnId].Close(); + if (remove) this.RemoveNetNode(nnId); } } } diff --git a/Assets/OxGFrame/NetFrame/Scripts/Runtime/NetFrames.cs b/Assets/OxGFrame/NetFrame/Scripts/Runtime/NetFrames.cs index 110d50f5..ebb5869d 100644 --- a/Assets/OxGFrame/NetFrame/Scripts/Runtime/NetFrames.cs +++ b/Assets/OxGFrame/NetFrame/Scripts/Runtime/NetFrames.cs @@ -32,9 +32,14 @@ public static bool Send(byte[] buffer, byte nnId = 0) return NetManager.GetInstance().Send(buffer, nnId); } - public static void CloseSocket(byte nnId = 0) + public static bool Send(string text, byte nnId = 0) { - NetManager.GetInstance().CloseSocket(nnId); + return NetManager.GetInstance().Send(text, nnId); + } + + public static void Close(byte nnId = 0, bool remove = false) + { + NetManager.GetInstance().Close(nnId, remove); } } -} +} \ No newline at end of file diff --git a/Assets/OxGFrame/Samples~/NetFrameDemo/Scripts/NetTipsExample.cs b/Assets/OxGFrame/Samples~/NetFrameDemo/Scripts/NetTipsExample.cs index 5505ec03..297ba975 100644 --- a/Assets/OxGFrame/Samples~/NetFrameDemo/Scripts/NetTipsExample.cs +++ b/Assets/OxGFrame/Samples~/NetFrameDemo/Scripts/NetTipsExample.cs @@ -1,10 +1,9 @@ using OxGFrame.NetFrame; -using System; using UnityEngine; public class NetTipsExample : INetTips { - public void OnConnected(EventArgs e) + public void OnConnected(object status) { Debug.Log("OnConnected Message"); } @@ -19,7 +18,7 @@ public void OnConnectionError(string msg) Debug.Log("OnConnectionError"); } - public void OnDisconnected(ushort code) + public void OnDisconnected(object status) { Debug.Log("OnDisconnected"); } @@ -28,4 +27,4 @@ public void OnReconnecting() { Debug.Log("OnReconnecting"); } -} +} \ No newline at end of file diff --git a/Assets/OxGFrame/Samples~/NetFrameDemo/Scripts/NetworkExample.cs b/Assets/OxGFrame/Samples~/NetFrameDemo/Scripts/NetworkExample.cs index 11a87195..128a74e1 100644 --- a/Assets/OxGFrame/Samples~/NetFrameDemo/Scripts/NetworkExample.cs +++ b/Assets/OxGFrame/Samples~/NetFrameDemo/Scripts/NetworkExample.cs @@ -11,20 +11,20 @@ public static void InitNetNode() var netTips = new NetTipsExample(); #region Websocket Example - NetNode wsNetNode = new NetNode(new WebSock(), netTips); + NetNode wsNetNode = new NetNode(new WebsocketNetProvider(), netTips); // Set data receive callback - wsNetNode.SetResponseHandler(ProcessRecvData); - // Set first send callback (verification) - wsNetNode.SetFirstSendHandler(ProcessFirstSend); + wsNetNode.SetResponseBinaryHandler(ProcessRecvData); + // Set connecting callback + wsNetNode.SetConnectingHandler(ProcessConnectingEvent); // Set heart beat callback wsNetNode.SetHeartBeatAction(() => { /* Process Heart Beat */ }); // Set out receive callback - wsNetNode.SetOutReciveAction(() => + wsNetNode.SetOutReceiveAction(() => { - /* Process Out Of Recive */ + /* Process Out Of Receive */ }); // Set reconnect callback wsNetNode.SetReconnectAction(() => @@ -37,20 +37,20 @@ public static void InitNetNode() #endregion #region TCP/IP Example - NetNode tcpNetNode = new NetNode(new TcpSock(), netTips); + NetNode tcpNetNode = new NetNode(new TcpNetProvider(), netTips); // Set data receive callback - tcpNetNode.SetResponseHandler(ProcessRecvData); - // Set first send callback (verification) - tcpNetNode.SetFirstSendHandler(ProcessFirstSend); + tcpNetNode.SetResponseBinaryHandler(ProcessRecvData); + // Set connecting callback + tcpNetNode.SetConnectingHandler(ProcessConnectingEvent); // Set heart beat callback tcpNetNode.SetHeartBeatAction(() => { /* Process Heart Beat */ }); // Set out receive callback - tcpNetNode.SetOutReciveAction(() => + tcpNetNode.SetOutReceiveAction(() => { - /* Process Out Of Recive */ + /* Process Out Of Receive */ }); // Set reconnect callback tcpNetNode.SetReconnectAction(() => @@ -73,11 +73,15 @@ public static void ProcessRecvData(byte[] recvData) } /// - /// Protocol first send verification + /// Connecting handler /// - public static void ProcessFirstSend() + public static void ProcessConnectingEvent() { - Debug.Log("Init First Send"); + /** + * If there is first verification can do somethings in here + */ + + Debug.Log("Process Connecting Event"); } /// @@ -95,7 +99,7 @@ public static void OpenConnection(NetOption netOption, byte nnid = 0) /// public static void CloseConnection(byte nnid = 0) { - NetFrames.CloseSocket(nnid); + NetFrames.Close(nnid, true); } /// @@ -116,4 +120,4 @@ public static bool SendData(byte[] buffer, byte nnid = 0) { return NetFrames.Send(buffer, nnid); } -} +} \ No newline at end of file diff --git a/Assets/OxGFrame/package.json b/Assets/OxGFrame/package.json index c7d38f01..3695a4fc 100644 --- a/Assets/OxGFrame/package.json +++ b/Assets/OxGFrame/package.json @@ -2,7 +2,7 @@ "name": "com.michaelo.oxgframe", "displayName": "OxGFrame", "description": "The OxGFrame is a framework based on Unity for accelerating game development. Supports multi-platform Win, OSX, Android, iOS, WebGL.", - "version": "2.9.10", + "version": "2.9.11", "unity": "2021.3", "license": "MIT", "samples": [