2025-04-24 17:03:28 +08:00

278 lines
7.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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}`);
}
}
}