266 lines
8.5 KiB
TypeScript
266 lines
8.5 KiB
TypeScript
import EventEmitter from 'events';
|
||
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'fs';
|
||
import { parse } from 'json5';
|
||
import * as mathjs from 'mathjs';
|
||
import { join, resolve } from 'path';
|
||
import { argv, env } from 'process';
|
||
import { HttpServer, WsClient, WsServer } from 'tsrpc';
|
||
import { ServiceType as ServiceTypeCross } from './cross/protocols/serviceProto';
|
||
import { MyEvent } from './event';
|
||
import { addListener, gEventType } from './globalListener';
|
||
import localConfig from './localConfig';
|
||
import { ServiceType as ServiceTypeHttp } from './monopoly/protocols/serviceProto';
|
||
import { SchedulerManage } from './public/scheduler/scheduler';
|
||
import { _mongodb } from './setMongodb';
|
||
import { redisJsonFun } from './setRedis';
|
||
import { ResGetList } from './shared/protocols/pay/PtlGetList';
|
||
import { ServiceType as ServiceTypeWs } from './shared/protocols/serviceProto';
|
||
import { PublicShared } from './shared/public/public';
|
||
import { clusterRunOnce } from './clusterUtils';
|
||
import * as ramda from 'ramda'
|
||
import Redis from 'ioredis';
|
||
|
||
declare global {
|
||
var G: _G & Partial<{
|
||
/**仅类型映射 */
|
||
otherBuff: any;
|
||
}>;
|
||
var mathJs: typeof mathjs;
|
||
|
||
var R
|
||
|
||
type k_v<T> = { [k: string]: T; };
|
||
type atn = { a: string, t: string | number | any, n: number; colour?: number; shiwuBuff?: any; };
|
||
|
||
/**类型过滤 */
|
||
type FilterConditionally<Source, Condition> = Pick<
|
||
Source,
|
||
{
|
||
[K in keyof Source]: Source[K] extends Condition ? K : never
|
||
}[keyof Source]
|
||
>;
|
||
|
||
interface Array<T> {
|
||
/**数组随机取值 */
|
||
random(): T;
|
||
/**取一个数组在当前数组中的交集 */
|
||
intersection(other: T[]): T[];
|
||
/**取一个数组在当前数组中的差集 */
|
||
difference(other: T[]): T[];
|
||
/**数组是否存在重复元素 */
|
||
isDuplication(): boolean;
|
||
/**打乱数组 */
|
||
shuffle(): this;
|
||
}
|
||
|
||
interface Uint8Array {
|
||
/**是否已经加密 */
|
||
encrypt: boolean;
|
||
}
|
||
}
|
||
|
||
class _G {
|
||
/**游戏配置 */
|
||
gc: Partial<_gcType> = {};
|
||
/**服务启动时命令行参数解析 */
|
||
argv: {
|
||
/**服务器类型 */
|
||
serverType: 'msg' | 'cross',
|
||
/**服务器日志模式 error | debug*/
|
||
logModel: string;
|
||
} = {
|
||
serverType: 'msg',
|
||
logModel: 'error'
|
||
};
|
||
/**当前时间对象 */
|
||
date: Date;
|
||
/**当前时间戳 */
|
||
time: number;
|
||
// /**serverType==msg时记录所有玩家的uid, 剔除全局变量,转移到redis */
|
||
// uids?: string[];
|
||
/**服务器配置 */
|
||
config: Partial<typeof localConfig> & k_v<any> = {};
|
||
/**当serverType==msg时才会启动的http */
|
||
http?: HttpServer<ServiceTypeHttp>;
|
||
/**当serverType!=cross时才会启动的ws */
|
||
server?: WsServer<ServiceTypeWs>;
|
||
/**当serverType==cross时才会启动的ws */
|
||
serverCross?: WsServer<ServiceTypeCross>;
|
||
/**当serverType==msg时才会启动的跨服连接 */
|
||
clientCross?: WsClient<ServiceTypeCross>;
|
||
/**redis连接对象 */
|
||
redis: redisJsonFun;
|
||
/**ioredis连接对象 */
|
||
ioredis: Redis;
|
||
/**mongodb连接对象 */
|
||
mongodb: _mongodb;
|
||
/**crossmongodb连接对象 */
|
||
crossmongodb: _mongodb;
|
||
// mongodbClient: any;
|
||
/**所有玩家的充值记录 */
|
||
allPlayerPayLog: k_v<ResGetList['list']> = {};
|
||
|
||
private event = new EventEmitter();
|
||
|
||
/**映射开服时间 */
|
||
get openTime() {
|
||
return this.config.openTime;
|
||
}
|
||
|
||
get getEvent(): new (...args: any) => {
|
||
on(type: any, callback: any, caller?: any): any;
|
||
once(type: any, callback: any): any;
|
||
off(type: any, callback: any, caller?: any): void;
|
||
emit(type: any, ...args: any[]): void;
|
||
debug(): any;
|
||
removeAllListeners(type?:any):void;
|
||
} {
|
||
return MyEvent as any;
|
||
}
|
||
|
||
constructor(argArr: string[]) {
|
||
let configJson = existsSync(resolve(__dirname, 'config.json')) ? JSON.parse(readFileSync(resolve(__dirname, 'config.json'), 'utf-8')) : {};
|
||
|
||
for (let index = 0; index < argArr.length; index++) {
|
||
let arg = argArr[index];
|
||
if (arg == '-serverType') {
|
||
this.argv.serverType = argArr[index + 1] as any;
|
||
} else if (arg == '-logModel') {
|
||
this.argv.logModel = argArr[index + 1];
|
||
}
|
||
}
|
||
|
||
Object.assign(localConfig, configJson);
|
||
this.config = localConfig;
|
||
if (env.SERVER_ID) {
|
||
this.config.serverId = ~~env.SERVER_ID;
|
||
}
|
||
|
||
this.initGc();
|
||
}
|
||
|
||
on<T extends keyof gEventType>(event: T, callback: gEventType[T]) {
|
||
return this.event.on(event, (...args)=>{
|
||
try{
|
||
callback.call(this, ...args);
|
||
}catch(e){
|
||
console.error(e)
|
||
}
|
||
});
|
||
}
|
||
|
||
once<T extends keyof gEventType>(event: T, callback: gEventType[T]) {
|
||
return this.event.once(event, callback);
|
||
}
|
||
|
||
emit<T extends keyof gEventType>(event: T, ...args: Parameters<gEventType[T]>) {
|
||
return this.event.emit(event, ...args);
|
||
}
|
||
|
||
/**加载json */
|
||
initGc() {
|
||
let jsonPath = join(__dirname, 'json');
|
||
readdirSync(jsonPath).forEach(file => {
|
||
if (file.endsWith('.json5')) {
|
||
let json = parse(readFileSync(join(jsonPath, file), 'utf-8'));
|
||
this.gc[file.split('.')[0]] = json;
|
||
} else if(file.endsWith('.json')) {
|
||
let json = JSON.parse(readFileSync(join(jsonPath, file), 'utf-8'));
|
||
this.gc[file.split('.')[0]] = json;
|
||
}
|
||
});
|
||
}
|
||
|
||
/**更新时间 每秒刷新 */
|
||
updateTime() {
|
||
if (G.config.debug && G.config.time) {
|
||
try {
|
||
let conf = JSON.parse(readFileSync(resolve(__dirname, 'config.json'), 'utf-8'));
|
||
this.date = new Date(conf.time);
|
||
this.date = new Date(this.date.getTime() + 1000);
|
||
this.time = Math.round(this.date.getTime() / 1000);
|
||
conf.time = this.date.format('YYYY-MM-DD hh:mm:ss');
|
||
// writeFileSync(resolve(__dirname, 'config.json'), JSON.stringify(conf, null, 2));
|
||
} catch (error) {
|
||
|
||
}
|
||
} else {
|
||
// let date = new Date();
|
||
// let time = date.getTime();
|
||
// let offset = date.getTimezoneOffset() / 60 + this.config.utc;
|
||
// this.date = new Date(time + offset * 3600 * 1000);
|
||
this.date = new Date();
|
||
this.time = Math.round(this.date.getTime() / 1000);
|
||
}
|
||
}
|
||
|
||
/**检查redis路径是否首字符为数字 */
|
||
formatRedisKey(key: string) {
|
||
let firstStr = key.toString().substring(0, 1);
|
||
if (!isNaN(Number(firstStr))) return '_' + key;
|
||
return key;
|
||
}
|
||
|
||
/**改写开区时间(外部请求 games/open 改写)*/
|
||
set openTime(_opentine) {
|
||
try {
|
||
let conf = JSON.parse(readFileSync(resolve(__dirname, 'config.json'), 'utf-8'));
|
||
G.date = new Date(_opentine);
|
||
G.date = new Date(this.date.getTime() + 1000);
|
||
conf.openTime = this.date.format('YYYY-MM-DD hh:mm:ss');
|
||
// this.openTime = this.date.format('YYYY-MM-DD hh:mm:ss'); // 外部调用时直接改写了,不然会无限重复调用
|
||
this.config.openTime = this.date.format('YYYY-MM-DD hh:mm:ss')
|
||
// writeFileSync(resolve(__dirname, 'config.json'), JSON.stringify(conf, null, 2));
|
||
} catch (error) {
|
||
|
||
}
|
||
}
|
||
|
||
sleep(ms) {
|
||
return new Promise(resolve => setTimeout(resolve, ms))
|
||
}
|
||
|
||
}
|
||
|
||
|
||
export function ctor() {
|
||
globalThis.mathJs = mathjs;
|
||
globalThis.G = new _G(argv);
|
||
G.updateTime();
|
||
globalThis.R = ramda
|
||
}
|
||
|
||
Array.prototype.random = function (this: Array<any>) {
|
||
return this[PublicShared.randomNum(0, this.length)];
|
||
};
|
||
|
||
Array.prototype.intersection = function (this: Array<any>, other: Array<any>) {
|
||
let a = new Set(this);
|
||
let b = new Set(other);
|
||
|
||
return Array.from(new Set([...a].filter(x => b.has(x))));
|
||
};
|
||
|
||
Array.prototype.difference = function (this: Array<any>, other: Array<any>) {
|
||
let a = new Set(this);
|
||
let b = new Set(other);
|
||
|
||
return Array.from(new Set([...a].filter(x => !b.has(x))));
|
||
};
|
||
|
||
Array.prototype.isDuplication = function (this: Array<any>) {
|
||
return this.length != [...new Set(this)].length;
|
||
};
|
||
|
||
Array.prototype.shuffle = function (this: Array<any>) {
|
||
this.forEach((v, k) => {
|
||
let _k = 0 | (Math.random() * (k + 1));
|
||
var tmp = this[k];
|
||
this[k] = this[_k];
|
||
this[_k] = tmp;
|
||
});
|
||
return this;
|
||
}
|
||
|
||
|