2022-08-26 16:48:17 +08:00

259 lines
6.7 KiB
TypeScript

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<INetResponse<any>> = new Action<INetResponse<any>>();
readonly OnDisconnected: Action<void> = new Action<void>();
readonly OnLoadUIMask: Action<boolean> = new Action<boolean>();
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<any, any>) {
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<any, any>, 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(<ArrayBuffer>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 = <string>json[0];
let status = <number>json[1][0];
let data = json[1][1];
let resp = <INetResponse<any>>{
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<any> = {
Status: -1,
Method: "",
Data: {},
IsValid: false,
};
class WsConnectEnumerator extends BaseEnumerator {
private _ws: WebSocket;
constructor(ws: WebSocket) {
super();
this._ws = ws;
}
next(value?: any): IteratorResult<any> {
return {
done: this._ws.readyState === WebSocket.OPEN || this._ws.readyState === WebSocket.CLOSED,
value: undefined
};
}
}
class WsRequestEnumerator extends BaseEnumerator {
readonly MethodBack: string;
private _req: INetRequest<any, any>;
private _done: boolean = false;
constructor(req: INetRequest<any, any>) {
super();
this._req = req;
this.MethodBack = req.MethodBack;
}
SetResponse(resp: INetResponse<any>) {
this._req.Result = resp;
this._done = true;
}
next(value?: any): IteratorResult<any> {
return {
done: this._done,
value: undefined
};
}
}