HJ_Server/src/setWs.ts
2024-01-03 21:57:12 +08:00

271 lines
9.7 KiB
TypeScript
Raw 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 {existsSync, readFileSync} from 'fs';
import {join, resolve} from 'path';
import {LogLevel, WsServer} from 'tsrpc';
import {ServiceType as ServiceTypeCross, serviceProto as serviceProtoCross} from './cross/protocols/serviceProto';
import {Encrypt} from './encrypt';
import {FightFun} from './public/fight';
import {PayFun} from './public/pay';
import {Rank} from './public/rank/rank';
import {createWsClient} from './setWsClient';
import {MsgPay} from './shared/protocols/msg_c2s/MsgPay';
import {ServiceType, serviceProto} from './shared/protocols/serviceProto';
import {player} from './shared/protocols/user/type';
import {unQueueByConn} from './api_s2c/user/ApiLogin';
import {clusterPublish, setUidProcessId} from './clusterUtils';
import {clearGud, getGud} from './public/gud';
import {mylogger} from './gameLog';
export async function createWs() {
if (G.argv.serverType == 'cross') {
G.serverCross = new WsServer(serviceProtoCross, {
//本地里考虑到一台服务器可能既启动game又启动cross
//会导致端口冲突所以本地特别配了一个crossPort
//外网环境的config.json里是没有crossPort的无论游服还是跨服都直接用msgPort
port: G.config.crossPort || G.config.msgPort,
json: true,
//API超时时间5分钟为登陆排队做准备
apiTimeout: 300000,
logLevel: G.argv.logModel as LogLevel,
logger: mylogger
});
setCrossWs(G.serverCross);
await G.serverCross.autoImplementApi(resolve(__dirname, 'api_cross'), true);
await G.serverCross.start();
} else {
G.server = new WsServer(serviceProto, {
port: G.config.msgPort,
logLevel: G.argv.logModel as LogLevel,
wss: getWssFile(),
//API超时时间5分钟为登陆排队做准备只针对游服
apiTimeout: 300000,
logger: mylogger
});
setWs(G.server);
await G.server.autoImplementApi(resolve(__dirname, 'api_s2c'), true);
await G.server.start();
G.argv.serverType == 'msg' && G.config.corssWsUrl && await createWsClient();
}
}
function getWssFile() {
if (G.config.wss.key && G.config.wss.cert && existsSync(join(__dirname, '../' + G.config.wss.key)) && existsSync(join(__dirname, '../' + G.config.wss.cert))) {
return {
key: readFileSync(join(__dirname, '../' + G.config.wss.key)),
cert: readFileSync(join(__dirname, '../' + G.config.wss.cert))
};
} else {
return null;
}
}
const writeList = ['hongdian/Get']
function setWs(server: WsServer<ServiceType>) {
encryptWs(server);
server.uid_connections = {};
//客户端连接后
server.flows.postConnectFlow.push(conn => {
conn.requstApiTime = {};
conn.apiLock = {};
return conn;
});
//客户端断开连接后
server.flows.postDisconnectFlow.push(node => {
unQueueByConn(node.conn);
//玩家断开后以玩家uid对socket连接进行解绑
if (node.conn.uid && server.uid_connections[node.conn.uid] && server.uid_connections[node.conn.uid] == node.conn) {
server.uid_connections[node.conn.uid] = null;
delete server.uid_connections[node.conn.uid];
}
if (node.conn.uid) {
clearGud(node.conn.uid);
G.emit('PLAYER_DISCONNECT', node.conn.uid);
}
//清理数据
delete node.conn.gud;
delete node.conn.requstApiTime
delete node.conn.apiLock;
delete node.conn.lshd;
delete node.conn.onlineTime;
return node;
});
//执行 API 接口实现之前
server.flows.preApiCallFlow.push(async call => {
// 临时停服维护方案
// let lng = {
// "zh-TW": "停服維護中,請等待!",
// "ko": "서버 점검 중, 잠시만 기다려 주세요.",
// "ja": "サーバーメンテナンス中、しばらくお待ちください。",
// "en": "Server under maintenance. Please wait.",
// }
// call.error("", {code: -1, message: lng[call.req.lng] || lng["ja"]})
// return null;
//是否短时间内重复请求某个api
// let timeIntervalLimit = call.service.conf?.timeIntervalLimit == undefined ? 500 : call.service.conf?.timeIntervalLimit;
// if (new Date().getTime() - call.conn.requstApiTime[call.service.name] < timeIntervalLimit) {
// call.error('', { code: -1, message: '', time: timeIntervalLimit });
// } else {
// call.conn.requstApiTime[call.service.name] = new Date().getTime();
// }
if (call.service.conf?.needGudKey?.length > 0) {
let gud: player;
gud = await getGud(call.uid);
for (let key of call.service.conf.needGudKey) {
if (!gud[key]) {
call.error(`you need has ${key}`);
return null;
}
}
}
//判断是否登录
if (!['user/Ping', 'user/Login'].includes(call.service.name) && !call.conn.uid) {
call.error("qingxiandenglu");
return null;
}
//处理API锁极限情况下只锁10s防止死锁
//在下方postApiReturnFlow里会解锁
if (call.conn.apiLock[call.service.name] && call.conn.apiLock[call.service.name] > new Date().getTime()) {
call.error('', {code: -100, message: '', apilock: 1});
return null;
}
//API锁定到什么时候
if (!writeList.includes(call.service.name)) {
call.conn.apiLock[call.service.name] = new Date().getTime() + 10000;
}
//API耗时统计
call.conn.requstApiTime[call.service.name] = new Date().getTime();
//为了维持原有的逻辑这里在每次请求时将gud数据写入conn中
if (call.conn.uid) {
call.conn.gud = await getGud(call.conn.uid);
}
return call;
});
server.flows.preApiReturnFlow.push(node => {
// if (!node.return.isSucc && node.return.err && node.return.err.type == "ApiError") {
// if (node.return.err?.message){
// node.return.err.message = ""
// node.call.return.err.message = ""
// }
// }
return node;
});
//API 接口返回结果call.succ、call.error之后
server.flows.postApiReturnFlow.push(async node => {
//接口响应速度统计
let startTime = node.call.conn.requstApiTime[node.call.service.name]
if (startTime && node.return.isSucc && G.mongodb) {
let needTime = new Date().getTime() - startTime;
G.mongodb.collection("apiCount").updateOne(
{api: node.call.service.name},
{$inc: {callnums: 1, needtimes: needTime}},
{upsert: true}
)
}
//玩家登陆后以玩家uid对socket连接进行绑定
if (node.call.service.name == 'user/Login' && node.return.isSucc) {
//玩家uid已经登陆在线 通知账号在其他地方登录
const uid = node.return.res.gud.uid;
server.uid_connections[uid] = node.call.conn;
setUidProcessId(uid);
}
try {
G.emit('API_CALL', node);
} catch (e) {
console.log('EMIT_API_CALL_ERROR', e);
}
//API解锁
let now = new Date().getTime();
if (node.return.isSucc) {
if (!writeList.includes(node.call.service.name)){
node.call.conn.apiLock[node.call.service.name] = now + 200;
}
} else {
if (!node.return.err.apilock) {
delete node.call.conn.apiLock[node.call.service.name];
}
}
return node;
});
server.listenMsg(/^msg_c2s\//, async msgHandler => {
switch (msgHandler.service.name as (keyof ServiceType['msg'] & `msg_c2s/${string}`)) {
case 'msg_c2s/Pay':
let msgPay = msgHandler.msg as MsgPay;
let gud = await getGud(msgHandler.conn.uid);
await PayFun.check(gud, msgPay.id, msgPay.args);
break;
}
});
}
function setCrossWs(server: WsServer<ServiceTypeCross>) {
// encryptWs(server);
server.listenMsg('msg_cross/CrossChat', msg => {
//跨服收到这条信息的时候,转发给连接到本跨服的所有客户端,即所有的服务端进程
server.connections.forEach(conn => conn.sendMsg('msg_cross/CrossChat', msg.msg));
});
// server.listenMsg('msg_cross/HbzbSendUser', sendData => {
// Rank.list.hbzbCross.addNew(sendData.msg.user);
// });
server.listenMsg('msg_cross/HbzbJfsLog', handle => {
FightFun.saveLog(handle.msg.uid, 'hbzbJfs', handle.msg.log);
FightFun.saveLog(handle.msg.toUid, 'hbzbJfs', handle.msg.log);
});
server.listenMsg('msg_cross/HbzbZbsLog', handle => {
FightFun.saveLog(handle.msg.uid, 'hbzbZbs', handle.msg.log);
FightFun.saveLog(handle.msg.toUid, 'hbzbZbs', handle.msg.log);
});
// server.listenMsg('msg_cross/HbzbChangeRank', handle => {
// //Rank.list.hbzbZbsCross.changeRank(handle.msg.uid, handle.msg.toUid);
// });
}
/**数据传输加解密 */
function encryptWs(server: WsServer) {
//处理收到的数据前
server.flows.preRecvDataFlow.push(node => {
// 接收前解密
node.data = Encrypt.decrypt(node.data);
return node;
});
//发送数据前
server.flows.preSendDataFlow.push(node => {
// 发送前加密
node.data = Encrypt.encrypt(node.data);
return node;
});
}