278 lines
7.3 KiB
TypeScript
278 lines
7.3 KiB
TypeScript
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}`);
|
||
}
|
||
}
|
||
} |