import * as ByteBuffer from "../3rd/bytebuffer"; import SKPacket from "./SKPacket"; import SKTimeUtil from "../util/SKTimeUtil"; import GameModel from "../../core/GameModel"; import SocketUtil from "./SocketUtil"; export enum SocketCode { SUCCESS = 0, IP_ERROR = 1, PORT_ERROR = 2, TIMEOUT = 3, ERROR = 4, CLOSE = 5, PING = 6, } export default class SKSocket { static shared = new SKSocket(); static pbroot : any; static pbname : string = "c2s"; static stringBlock : (msg : string) => void; static disconnectBlock : () => void; ip : string; port : number; socket : WebSocket; sendCheckTimer : number; pingTimer : number; lastTime : number; lastRunTime : number; isReconnect : boolean = false; lastCode : SocketCode; /** * 是否輸出接口請求 */ isShowRequest : boolean = true; isShowPing : boolean = false; callList : { [key : string] : (data : any) => void }; constructor() { this.callList = {}; cc.game.on(cc.game.EVENT_SHOW, function () { console.log("SKSocket:重新返回游戲"); console.log(Date.now() - this.lastRunTime) if (Date.now() - this.lastRunTime > 30000) { // 超過30秒 // console.log("是否戰鬥", GameModel.isFight) if (GameModel.isFight) { GameModel.isFight = false GameModel.shared.relink1() } } this.lastTime = Date.now(); }, this); cc.game.on(cc.game.EVENT_HIDE, function () { console.log("SKSocket:遊戲進入後台"); this.lastRunTime = Date.now(); }, this); this.lastCode = SocketCode.SUCCESS; } static showRequest() { this.shared.isShowRequest = true; } static hideRequest() { this.shared.isShowRequest = false; } static showPing() { this.shared.isShowPing = true; } static hiddPing() { this.shared.isShowPing = false; } static register(prefix : string, call : (data : any) => void) { if (!prefix || prefix.length < 1) { cc.warn(`$警告:無效的註冊前綴!`); return; } if (this.shared.callList[prefix]) { cc.warn(`$警告:${prefix}已註冊!`); } this.shared.callList[prefix] = call; } static loadProto(url : string, complteBlock : () => void) { cc.loader.loadRes(url, cc.TextAsset, (error : Error, result : cc.TextAsset) => { if (error) { cc.log(`加載協議失敗${error.message}`); return; } let text = result.text; let temp = protobuf.parse(text); this.pbroot = temp.root; complteBlock(); }) } static connect(ip : string, port : number, failedBlock : (code : SocketCode) => void, successBlock : () => void) { cc.log("connect to .....", ip, port) // this.shared.connect(ip, port, failedBlock, successBlock); //192.168.200.28 117.187.233.158 this.shared.connect("117.187.233.158", port, failedBlock, successBlock); } // 重連 static reconnect(failedBlock : (code : SocketCode) => void, successBlock : () => void) { if (this.shared.isReconnect) { return; } this.shared.isReconnect = true; this.shared.connect(this.shared.ip, this.shared.port, failedBlock, successBlock); } // 發送 static send(prefix : string, properties : any) { var canSend = SocketUtil.getCanSend(prefix, properties); if (!canSend) return; if (this.shared.isShowRequest && prefix.indexOf("aoi") == -1 && prefix != "c2s_task_talk_npc") console.log("%c訪問接口:'" + prefix + "',攜帶數據:", 'color:green', properties); this.shared.send(prefix, properties); } // 關閉 static close() { this.shared.close(); } private connect(ip : string, port : number, failedBlock : (code : number) => void, successBlock : () => void) { if (!ip || ip.length < 1) { this.isReconnect = false; this.lastCode = SocketCode.IP_ERROR; failedBlock(this.lastCode); return; } if (!port) { this.isReconnect = false; this.lastCode = SocketCode.PORT_ERROR; failedBlock(this.lastCode); return; } this.ip = ip this.port = port; let link = `ws://${this.ip}:${this.port}`; console.log(link,'link') if (this.socket) { console.log("not get socket state, but return. maybe error!!!") this.isReconnect = false; successBlock(); return; } this.socket = new WebSocket(link); this.socket.binaryType = "arraybuffer"; let self = this; this.socket.onopen = (event : Event) => { console.log(`${self.isReconnect ? "重連" : "連接"}服務器:${link}成功`); self.onConnect(); if (successBlock) { successBlock(); } } this.socket.onmessage = (event : MessageEvent) => { self.lastTime = Date.now(); if (typeof event.data == 'string') { if (event.data == "ping") { if (this.isShowPing) { console.log("%cPING_RECIVE", 'color:cyan') console.timeEnd("Ping") } return; } if (SKSocket.stringBlock) { SKSocket.stringBlock(event.data); } return; } // try { let buffer = new ByteBuffer(); buffer.append(event.data); buffer.flip(); let len = buffer.readShort(); let prefix = buffer.readString(len); buffer.readString(2) let call = self.callList[prefix]; if (call) { let leftBuffer = new Uint8Array(buffer.buffer).subarray(buffer.offset, buffer.limit); let packet = SKPacket.create(prefix); let data = packet.decode(leftBuffer); if (this.isShowRequest && prefix.indexOf("aoi") == -1 && prefix.indexOf("s2c_screen_msg") == -1 && prefix.indexOf("s2c_game_chat") == -1) console.log("%c返回數據接口:", 'color:blue', data) call(data); if (GameModel.isDebug) { cc.log(`接收協議:${prefix}`); } } else { cc.warn(`未響應的協議:${prefix}`); } // } catch (error) { // cc.warn(error); // } } this.socket.onerror = (event : Event) => { cc.warn(`連接服務器錯誤!`); self.lastCode = SocketCode.ERROR; failedBlock(self.lastCode); self.close(); } this.socket.onclose = (event : CloseEvent) => { cc.warn(`服務器關閉!`); self.lastCode = SocketCode.CLOSE; failedBlock(self.lastCode); self.close(); } // cc.log(`開始連接服務器:${link}`); } onConnect() { this.isReconnect = false; this.lastCode = SocketCode.SUCCESS; this.lastTime = Date.now(); SKTimeUtil.cancelLoop(this.pingTimer); // 5秒心跳 this.pingTimer = SKTimeUtil.loop(() => { let current = Date.now(); // 20秒超時 if (current - this.lastTime > 20000) { console.log(`超時斷開鏈接`); this.lastCode = SocketCode.PING; this.close(); return; } if (this.socket) { if (this.isShowPing) { console.log("%cPING_SEND", 'color:red'); console.time("Ping") } this.socket.send("ping"); } }, 5000); this.sendCheckTimer = SKTimeUtil.loop(() => { SocketUtil.clearSendRecord(); }, SocketUtil.clearInterval) } close() { if (!this.socket) { return; } SKTimeUtil.cancelLoop(this.pingTimer); if (this.socket.readyState < 2) { this.socket.close(); } this.socket.onopen = null; this.socket.onmessage = null; this.socket.onerror = null; this.socket.onclose = null; this.socket = null; if (this.isReconnect) { this.isReconnect = false; return; } if (SKSocket.disconnectBlock) { SKSocket.disconnectBlock(); } } private send(prefix : string, properties : any) { if (!this.socket || this.socket.readyState != 1) { if (SKSocket.disconnectBlock) { SKSocket.disconnectBlock(); } return; } let packet = SKPacket.create(prefix); let buffer = packet.encode(properties); this.socket.send(buffer); if (GameModel.isDebug) { cc.log(`發送協議:${prefix}`); } } }