import { BaseEnumerator } from "../CoroutineV2/Core/BaseEnumerator"; import { Action } from "../CSharp/System/Action"; import { Encoding } from "../CSharp/System/Text/Encoding"; import { INetRequest } from "./Core/INetRequest"; import { INetResponse } from "./Core/INetResponse"; import NetConfig from "./NetConfig"; export class NetConnector { readonly OnDataReceived: Action> = new Action>(); readonly OnDisconnected: Action = new Action(); readonly OnLoadUIMask: Action = new Action(); get IsConnected() { return this._ws && this._ws.readyState === WebSocket.OPEN; } private _host: string; private _ws: WebSocket; private _waitings: WsRequestEnumerator[] = []; constructor(host: string, port: number/*, ip: string*/) { let checkHttp: string = ""; let index: number = host.indexOf("https://"); if (index != -1) { checkHttp = "https"; host = host.replace("https://", ""); } else { checkHttp = window.location.href.substring(0, 5); host = host.replace("http://", ""); } if (CC_DEBUG) { cc.log("[事件]checkHttp=", checkHttp, host, port); } if (checkHttp != "https") { //this._host = `ws://${host}:${port}/?ip=${ip}`; this._host = `ws://${host}:${port}` } else { //this._host = `wss://${host}:${port}/?ip=${ip}`; this._host = `wss://${host}:${port}`; } } ConnectAsync() { if (this._ws) { throw new Error("請先執行CasinoNetManager.Disconnect()中斷連線"); } if (cc.sys.isNative && cc.sys.os == cc.sys.OS_ANDROID && this._host.indexOf("wss") !== -1) { let cacert = cc.url.raw('resources/cacert.cer'); if (cc.loader.md5Pipe) { cacert = cc.loader.md5Pipe.transformURL(cacert) } //@ts-ignore this._ws = new WebSocket(this._host, null, cacert) } else { //@ts-ignore this._ws = new WebSocket(this._host); } this._ws.binaryType = 'arraybuffer'; this._ws.onopen = this.OnWebSocketOpen.bind(this); this._ws.onmessage = this.OnWebSocketMessage.bind(this); this._ws.onclose = this.OnWebSocketClose.bind(this); return new WsConnectEnumerator(this._ws); } Send(req: INetRequest) { if (!this.IsConnected) return; let json = [req.Method]; if (req.Data != null && req.Data != undefined && req.Data != NaN) { json[1] = req.Data; } if (CC_DEBUG && NetConfig.ShowServerLog) { if (req.Data != null && req.Data != undefined && req.Data != NaN) { cc.log(`[RPC] 傳送server資料: ${req.Method}(${JSON.stringify(req.Data)})`); } else { cc.log(`[RPC] 傳送server資料: ${req.Method}()`); } } let str = JSON.stringify(json); if (str.length > 65535) { throw new Error('要傳的資料太大囉'); } let strary = Encoding.UTF8.GetBytes(str); let buffer = new Uint8Array(4 + strary.byteLength); let u16ary = new Uint16Array(buffer.buffer, 0, 3); u16ary[0] = strary.byteLength; buffer[3] = 0x01; buffer.set(strary, 4); this._ws.send(buffer); } SendAsync(req: INetRequest, mask: boolean) { let iterator = new WsRequestEnumerator(req); if (!this.IsConnected) { iterator.SetResponse(ErrorResponse); } else { this._waitings.push(iterator); if (mask) { this.OnLoadUIMask.DispatchCallback(true); } this.Send(req); } return iterator; }; Disconnect() { this.WebSocketEnded(); } private WebSocketEnded() { if (!this._ws) return; this._ws.close(); this._ws.onopen = null; this._ws.onmessage = null; this._ws.onclose = () => { }; this._ws = null; this.CleanWaitings(); this.OnDisconnected.DispatchCallback(); } private CleanWaitings() { for (let w of this._waitings) { w.SetResponse(ErrorResponse); this.OnLoadUIMask.DispatchCallback(false); } this._waitings.length = 0; } private OnWebSocketOpen(e: Event) { if (CC_DEBUG) { cc.log(`[RPC] ${this._host} Connected.`); } } private OnWebSocketMessage(e: MessageEvent) { if (e.data instanceof ArrayBuffer) { this.ParseRpcMessage(e.data); } else if (e.data instanceof Blob) { let reader = new FileReader(); reader.onload = (e) => { this.ParseRpcMessage(reader.result); reader.onload = null; } reader.readAsArrayBuffer(e.data); } else { throw new Error(`未知的OnWebSocketMessage(e.data)類型: ${e.data}`); } } private ParseRpcMessage(buffer: ArrayBuffer) { let startIndex = 0, byteLength = buffer.byteLength; while (startIndex + 4 < byteLength) { let strlen = new DataView(buffer, startIndex, 3).getUint16(0, true); let str = Encoding.UTF8.GetString(new Uint8Array(buffer, startIndex + 4, strlen)); startIndex += strlen + 4; try { let json = JSON.parse(str); let method = json[0]; let status = json[1][0]; let data = json[1][1]; let resp = >{ Method: method, Status: status, Data: data, IsValid: method && status === 0 }; if (CC_DEBUG && NetConfig.ShowServerLog) { if (data) { cc.log(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}(${JSON.stringify(resp.Data)})`); } else { cc.log(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}()`); } } let dispatch = true; for (let i = 0, len = this._waitings.length; i < len; i++) { let w = this._waitings[i]; if (w.MethodBack === resp.Method) { dispatch = false; this._waitings.splice(i, 1); w.SetResponse(resp); this.OnLoadUIMask.DispatchCallback(false); break; } } if (dispatch) { this.OnDataReceived.DispatchCallback(resp); } } catch { throw new Error(`[RPC] 無法解析Server回應: ${str}`); } } } private OnWebSocketClose(e: CloseEvent) { this.WebSocketEnded(); } } const ErrorResponse: INetResponse = { Status: -1, Method: "", Data: {}, IsValid: false, }; class WsConnectEnumerator extends BaseEnumerator { private _ws: WebSocket; constructor(ws: WebSocket) { super(); this._ws = ws; } next(value?: any): IteratorResult { return { done: this._ws.readyState === WebSocket.OPEN || this._ws.readyState === WebSocket.CLOSED, value: undefined }; } } class WsRequestEnumerator extends BaseEnumerator { readonly MethodBack: string; private _req: INetRequest; private _done: boolean = false; constructor(req: INetRequest) { super(); this._req = req; this.MethodBack = req.MethodBack; } SetResponse(resp: INetResponse) { this._req.Result = resp; this._done = true; } next(value?: any): IteratorResult { return { done: this._done, value: undefined }; } }