271 lines
9.7 KiB
TypeScript
271 lines
9.7 KiB
TypeScript
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;
|
||
});
|
||
}
|
||
|