zero-remote
Advanced tools
| import WebSocket from "ws"; | ||
| import ZeroDispatcher from "./ZeroDispatcher"; | ||
| import { IPack, jsonPack, PromiseSender, Receiver, ISCMessage, ReportCode } from "./IZeroRomeote"; | ||
| /** | ||
| * 错误错误会弹窗 | ||
| * 重连多次后会弹窗 | ||
| */ | ||
| /** | ||
| * 与后端协议 | ||
| * 所有数据以IMessage接收和发送 | ||
| * 只支持一级属性是个回调函数 | ||
| */ | ||
| export class ZeroRemoteClient extends ZeroDispatcher<{ | ||
| /** | ||
| * 消息阻塞 | ||
| */ | ||
| showMask: []; | ||
| /** | ||
| * 阻塞后收到消息 | ||
| */ | ||
| hideMask: []; | ||
| /** | ||
| * 主动错误 | ||
| * 用于debug | ||
| */ | ||
| error: [string?]; | ||
| /** | ||
| * 重连提示 | ||
| */ | ||
| popup: []; | ||
| /** | ||
| * 心跳 | ||
| */ | ||
| heartbeat: []; | ||
| /** | ||
| * 开始连接 | ||
| */ | ||
| linking: []; | ||
| /** | ||
| * 连接完成 | ||
| */ | ||
| linked: []; | ||
| /** | ||
| * 连接中断 | ||
| */ | ||
| suspend: []; | ||
| /** | ||
| * 连接结束 | ||
| */ | ||
| stop: []; | ||
| }> { | ||
| relinkMaxCount: number = 3; | ||
| relinkTime: number = 2000; | ||
| /** | ||
| * 回调函数缓存个数 | ||
| */ | ||
| callBackMaxIndex: number = 1000; | ||
| outTime: number = 1; //整秒 | ||
| heartbeatTime: number = 5; //整秒=5 | ||
| public pack: IPack = jsonPack; | ||
| isOpen = false; | ||
| isBusy = false; | ||
| private isRelink: boolean = true; | ||
| private relinkCount: number = 0; | ||
| protected ws!: WebSocket; | ||
| protected router: ZeroDispatcher<any> = new ZeroDispatcher(); | ||
| protected callbackPool: { [key: number]: Function; } = {}; | ||
| private ci: number = 0; | ||
| private pingIndex: number = 0; | ||
| relinkTimeOut?: any; | ||
| private pingTimeStart!: Date; | ||
| pingTime: number = 0; | ||
| private _mask: number = 0; | ||
| private isMask: boolean = false; | ||
| public get mask(): number { | ||
| return this._mask; | ||
| } | ||
| public set mask(value: number) { | ||
| let isMask = value > 0 | ||
| if (this.isMask != isMask) { | ||
| this.isMask = isMask; | ||
| if (isMask) { | ||
| this.emit("showMask"); | ||
| } else { | ||
| this.emit("hideMask"); | ||
| } | ||
| } | ||
| } | ||
| constructor(public url: string) { | ||
| super(); | ||
| setInterval(() => { | ||
| if (this.isOpen) { | ||
| if (this.pingIndex == this.heartbeatTime + this.outTime) { | ||
| this.mask++ | ||
| } else if (this.pingIndex == this.heartbeatTime) { | ||
| this.pingTimeStart = new Date(); | ||
| this.ws.send(this.pack.stringify({ data: this.pingTime })); | ||
| } | ||
| this.pingIndex++; | ||
| } | ||
| }, 1000); | ||
| } | ||
| /** | ||
| * 本条结构协议 | ||
| * @param route 要发送的路由名 | ||
| * @returns | ||
| */ | ||
| getSender<T>(route: string): PromiseSender<T> { | ||
| return new Proxy<PromiseSender<T>>({} as any, { | ||
| get: (target: any, p: string, receiver: any) => { | ||
| return (data: any) => { | ||
| return new Promise((resolve: (value: any) => void, reject: (reason?: any) => void) => { | ||
| this.send(route, p, data, (isError: boolean, value: any) => { | ||
| if (isError) { | ||
| resolve(value); | ||
| } else { | ||
| reject(value); | ||
| this.emit("error", value); | ||
| } | ||
| }); | ||
| }); | ||
| }; | ||
| } | ||
| }); | ||
| } | ||
| getReceiver<T>(r: string): Receiver<T> { | ||
| return new Proxy<Receiver<T>>({} as any, { | ||
| get: (target: any, p: string, receiver: any) => { | ||
| return (data: any) => { | ||
| this.receive(r, p, data); | ||
| }; | ||
| } | ||
| }); | ||
| } | ||
| send(route: string, key: string, data: any, callback: (isOK: boolean, value: any) => void) { | ||
| if (this.isOpen) { | ||
| if (!this.mask) { | ||
| let cbs: { [key: string]: number; } | undefined = undefined; | ||
| for (const key in data) { | ||
| if (data.hasOwnProperty(key)) { | ||
| const element = data[key]; | ||
| if (typeof element == "function") { | ||
| this.ci++; | ||
| delete this.callbackPool[this.ci - this.callBackMaxIndex]; | ||
| this.callbackPool[this.ci] = element; | ||
| if (cbs == null) { | ||
| cbs = {}; | ||
| } | ||
| cbs[key] = this.ci; | ||
| } | ||
| } | ||
| } | ||
| let index: number | undefined = undefined; | ||
| if (callback != null) { | ||
| this.ci++; | ||
| delete this.callbackPool[this.ci - this.callBackMaxIndex]; | ||
| index = this.ci; | ||
| this.callbackPool[this.ci] = callback; | ||
| } | ||
| this.ws.send(this.pack.stringify( | ||
| { route: route, key: key, data: data, cbs: cbs, index: index } | ||
| )); | ||
| } else { | ||
| // console.log("弱网环境,send信息被忽略" + route) | ||
| callback(false, "弱网环境,send信息被忽略"); | ||
| } | ||
| } else { | ||
| // console.log("游戏服务器未连接,send信息被忽略" + route) | ||
| callback(false, "服务器未连接,send信息被忽略"); | ||
| } | ||
| } | ||
| receive(route: string, key: string, callback: (value: any) => void) { | ||
| this.router.on(route + "." + key, callback); | ||
| } | ||
| protected reLink() { | ||
| if (this.relinkCount < this.relinkMaxCount) { | ||
| this.relinkTimeOut = setTimeout(() => { | ||
| this.relinkTimeOut == null; | ||
| this.relinkCount++; | ||
| this.link(); | ||
| }, this.relinkTime); | ||
| } else { | ||
| this.relinkCount = 0; | ||
| this.emit("popup"); | ||
| } | ||
| } | ||
| message(value: string) { | ||
| let obj: ISCMessage; | ||
| try { | ||
| obj = this.pack.parse(value); | ||
| } catch { | ||
| throw new Error("服务器数据无法序列化"); | ||
| } | ||
| this.pong(); | ||
| if (obj.code == null) { | ||
| if (obj.route != null) { | ||
| if (obj.key == null) { | ||
| throw new Error("服务器数据出错"); | ||
| } else { | ||
| this.router.emit(obj.route + "." + obj.key, obj.data); | ||
| } | ||
| } else { | ||
| // pingTimeStart() | ||
| // this.pingTime = new Date() | ||
| this.pingTime = new Date().getTime() - this.pingTimeStart.getTime(); | ||
| } | ||
| } else if (obj.code == ReportCode.CALLBACK) { | ||
| if (obj.ci != null) { | ||
| let method = this.callbackPool[obj.ci]; | ||
| if (method) { | ||
| method.apply(this, obj.args); | ||
| delete this.callbackPool[obj.ci]; | ||
| } | ||
| } | ||
| } else if (obj.code == ReportCode.KICK) { | ||
| this.close(); | ||
| } else { | ||
| this.emit("error", obj.error); | ||
| } | ||
| } | ||
| protected pong() { | ||
| this.emit("heartbeat"); | ||
| this.pingIndex = 0; | ||
| this.mask-- | ||
| } | ||
| /** | ||
| * 这个callback 在重连里不会触发 | ||
| * 如需要触发 请使用 on("linked") | ||
| * @param callback | ||
| */ | ||
| link(callback?: () => void) { | ||
| this.isRelink = true; | ||
| if (this.url == "" || this.url == null) { | ||
| throw new Error("服务器地址为空"); | ||
| } else { | ||
| if (this.isBusy) { | ||
| throw new Error("重复连接服务"); | ||
| } else { | ||
| this.ws = new WebSocket(this.url); | ||
| this.isBusy = true; | ||
| this.emit("linking"); | ||
| this.ws.onerror = () => { | ||
| this.emit("error", "网络出错"); | ||
| }; | ||
| this.ws.onopen = () => { | ||
| this.mask = 0 | ||
| this.relinkCount = 0; | ||
| this.isOpen = true; | ||
| this.emit("linked"); | ||
| if (callback) { | ||
| callback(); | ||
| } | ||
| }; | ||
| this.ws.onclose = () => { | ||
| this.mask = 0; | ||
| this.isOpen = false; | ||
| this.isBusy = false; | ||
| this.emit("suspend"); | ||
| for (const key in this.callbackPool) { | ||
| if (Object.prototype.hasOwnProperty.call(this.callbackPool, key)) { | ||
| const element = this.callbackPool[key]; | ||
| element(false, "网络断开"); | ||
| delete this.callbackPool[key]; | ||
| } | ||
| } | ||
| if (this.isRelink) { | ||
| this.reLink(); | ||
| } else { | ||
| this.emit("stop"); | ||
| } | ||
| }; | ||
| this.ws.onmessage = (evt) => { | ||
| this.message(evt.data.toString()); | ||
| }; | ||
| } | ||
| } | ||
| } | ||
| close() { | ||
| if (this.relinkTimeOut) { | ||
| clearTimeout(this.relinkTimeOut); | ||
| this.relinkTimeOut == null; | ||
| } | ||
| this.isRelink = false; | ||
| this.isOpen = false; | ||
| this.ws.close(); | ||
| } | ||
| } |
+1
-1
| { | ||
| "name": "zero-remote", | ||
| "version": "1.2.0", | ||
| "version": "1.2.1", | ||
| "description": "常连接网络", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
34484
37.15%9
12.5%850
50.98%0
-100%