import Event from "@/modules/event"; import * as Define from "@/modules/player/define"; 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 { /** Event */ public readonly event: Event = new Event(); 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; } public get ws(): WebSocket { return this._ws; } private _host: string; private _ws: WebSocket; private _waitings: WsRequestEnumerator[] = []; constructor(host: string, port: number) { 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) { console.debug("[事件]checkHttp=", checkHttp, host, port); // } if (checkHttp != "https") { this._host = `ws://${host}:${port}`; } else { this._host = `wss://${host}:${port}`; } } ConnectAsync() { if (this._ws) { throw new Error("請先執行CasinoNetManager.Disconnect()中斷連線"); } 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); this._ws.onerror = this.OnWebSocketError.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 && !Number.isNaN(req.Data)) { json[1] = req.Data; } // if (CC_DEBUG && NetConfig.ShowServerLog) { if (NetConfig.ShowServerLog) { if (req.Data != null && req.Data != undefined && !Number.isNaN(req.Data)) { console.debug(`[RPC] 傳送client資料: ${req.Method}(${JSON.stringify(req.Data)})`); } else { console.debug(`[RPC] 傳送client資料: ${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) { console.debug(`[RPC] ${this._host} Connected.`); // } this.event.emit(Define.Event.Socket.Connect); } private OnWebSocketMessage(e: MessageEvent) { if (e.data instanceof ArrayBuffer) { this.ParseRpcMessage(e.data, e); } else if (e.data instanceof Blob) { let reader = new FileReader(); reader.onload = (e) => { this.ParseRpcMessage(reader.result, e); reader.onload = null; }; reader.readAsArrayBuffer(e.data); } else { throw new Error(`未知的OnWebSocketMessage(e.data)類型: ${e.data}`); } } private ParseRpcMessage(buffer: ArrayBuffer, e: any) { 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 (NetConfig.ShowServerLog) { if (data) { console.debug(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}(${JSON.stringify(resp.Data)})`); } else { console.debug(`[RPC] 收到server呼叫:(${resp.Status}): ${resp.Method}()`); } } let dispatch = true; let isCocos = false; 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(); this.event.emit(Define.Event.Socket.Disconnect); } private OnWebSocketError(e: CloseEvent) { this.event.emit(Define.Event.Socket.Error); } } 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 }; } }