278 lines
7.3 KiB
TypeScript
Raw Permalink Normal View History

2025-04-24 17:03:28 +08:00
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}`);
}
}
}