832 lines
36 KiB
TypeScript
832 lines
36 KiB
TypeScript
|
||
import { scheduleJob, Job, scheduledJobs, } from 'node-schedule';
|
||
import { PVPConfigModel, PVPConfigType } from '../db/SystemConfig';
|
||
import { nowSeconds, getTimeFun, getSeconds } from '../pubUtils/timeUtil';
|
||
import { getTodayGuildActivity, gameData } from '../pubUtils/data';
|
||
import { pvpSeasonEnd } from './pvpService';
|
||
import { getAllOnlineRoles, getAllServers, delGuildActivityRank, getServerCreateTime } from './redisService';
|
||
import { GUILD_ACTIVITY_TYPE, REFRESH_TIME, COUNTER, AUCTION_TIME, GM_MAIL_TYPE, SERVER_TIMER, ACTIVITY_TYPE, PUSH_ROUTE, STATUS, LADDER_STATUS, LADDER_SERVER_GAP_TIME } from '../consts';
|
||
import { pinus } from 'pinus';
|
||
import { settleGuildWeekly } from './guildService';
|
||
import { SendMailFun, sendMailsByGmMail, } from './mailService';
|
||
import { sendEndMsgToAllServer, autoDeclare, sendGuildActivityStatus, setPreDayActiveData, incCurGuildActivityIndex } from './guildActivity/guildActivityService';
|
||
import { sendUngotDividendJob, startGuildAuction, startWorldAuction, stopAuction } from './auctionService';
|
||
import { DicGuildActivity } from '../pubUtils/dictionary/DicGuildActivity';
|
||
import { dispatch } from '../pubUtils/dispatcher';
|
||
import { createNewServer, initMarquee, setServerMainten } from './gmService';
|
||
import moment = require('moment');
|
||
import { CounterModel } from '../db/Counter';
|
||
import { reportOneOnline } from './authenticateService';
|
||
import { LADDER, PVP } from '../pubUtils/dicParam';
|
||
import { fetch37Words } from './sdkService';
|
||
import { GMMailModel, GMMailType } from '../db/GMMail';
|
||
import { Maintenance, ServerlistModel, ServerlistType } from '../db/Serverlist';
|
||
import { createMarqueeMsg, pushMarqueeMsg } from './sysChatService';
|
||
import { RegionModel, RegionType } from '../db/Region';
|
||
import { CreateServerParam } from '../domain/backEndField/params';
|
||
import { infologger } from '../util/logger';
|
||
import { MailModel, MailType } from '../db/Mail';
|
||
import { GroupMailModel, GroupMailType } from '../db/GroupMail';
|
||
import { ServerMailModel, ServerMailType } from '../db/ServerMail';
|
||
import { ActivityModel, ActivityModelType } from '../db/Activity';
|
||
import { TimeLimitRankData } from '../domain/activityField/timeLimitRankField';
|
||
import { sendRankMail, takeSnapshot } from './activity/timeLimitRankService';
|
||
import { ActivityGroupModel } from '../db/ActivityGroup';
|
||
import { sendMessageToGroupShopWithSuc, sendMessageToServer } from './pushService';
|
||
import { resResult } from '../pubUtils/util';
|
||
import { checkPopUpConditionWhenGuildActivityEnd } from './activity/popUpShopService';
|
||
import { pushRefreshTime } from './connectorService';
|
||
import { sendUnReceivedActivityDailyCoin } from './activity/dailyCoinService';
|
||
import { ladderTimeout, ladderTimeWillout, sendLadderDailyReward } from './ladderService';
|
||
import { LadderMatchRecModel } from '../db/LadderMatchRec';
|
||
import { LadderMatchModel } from '../db/LadderMatch';
|
||
import { getGroupShopTimers, refundGroupShop, setGroupShopToSetSum } from './activity/groupShopService';
|
||
|
||
const PER_SECOND = 1 * 1000;
|
||
const PER_DAY = 24 * 60 * 60;
|
||
const PER_HOUR = 1 * 60 * 60;
|
||
const PER_MINUTE = 1 * 60;
|
||
var seasonMakeRewardTimJobId: Job;
|
||
var seasonRefreshTimeJobId: Job;
|
||
let guildWeeklyJobId: Job;
|
||
let guildActStartJobId: Job; // 军团活动开启后每10(or 1)秒循环的定时任务,到结束活动清除
|
||
let guildActSecondsJobId: Job; // 军团活动开启后每10(or 1)秒循环的定时任务,到结束活动清除
|
||
let guildActEndJobId: Job; // 军团活动开启后每10(or 1)秒循环的定时任务,到结束活动清除
|
||
|
||
/**
|
||
* 初始定时器
|
||
*/
|
||
export async function init() {
|
||
|
||
console.log('******* init systimer *******')
|
||
|
||
// pvp赛季
|
||
await setPvpSeason(true);
|
||
|
||
// 周功勋结算任务
|
||
guildWeeklyJobId = scheduleJob('settleGuildWeekly', '0 0 0 * * 1', settleGuildWeekly);
|
||
|
||
// 每5分钟汇报在线玩家在线情况
|
||
scheduleJob('reportOnline', '0 0/5 * * * *', reportOnlineSchedule);
|
||
|
||
// 每天拉取37词库
|
||
scheduleJob('fetchWord', '0 0 4 * * ?', fetch37Words);
|
||
|
||
// 每天邮件
|
||
checkCircleMail();
|
||
// 每小时查询邮件推送
|
||
checkMailByHour();
|
||
|
||
// 军团活动排行榜
|
||
guildActivitySchedule();
|
||
|
||
// 拍卖行刷新:拍卖阶段刷新,分红发放
|
||
auctionSchedule();
|
||
|
||
// 每天5点推送刷新时间消息
|
||
// 顺便每天0点计算前一天活跃玩家中位数战力
|
||
scheduleJob('everyDayRefresh', `0 0 ${REFRESH_TIME} * * ?`, everydayRefresh);
|
||
|
||
// 跑马灯
|
||
await initMarquee();
|
||
|
||
// 自动开服
|
||
await initAutoCreateServer();
|
||
|
||
// 限时排行榜
|
||
await initTimeLimitRank();
|
||
|
||
// 名将擂台每日奖励
|
||
await ladderDailyReward();
|
||
|
||
// 团购定时器
|
||
initGroupShopSchedule();
|
||
}
|
||
|
||
// 每日刷新
|
||
export async function everydayRefresh() {
|
||
let servers = await ServerlistModel.findByEnv(pinus.app.get('env'));
|
||
pushRefreshTime();
|
||
setPreDayActiveData(servers);
|
||
sendUngotDividendJob();
|
||
sendUnReceivedActivityDailyCoin(servers);
|
||
pinus.app.rpc.guild.guildActivityRemote.clearActivityObj.broadcast();
|
||
}
|
||
|
||
|
||
// —————————————— PVP 及赛季相关 —————————————— //
|
||
function getSeasonContinueDay(seasonNum: number) {
|
||
const pvpSeasonDuring = PVP.PVP_SEASON_DAYS.split('|').map(cur => {
|
||
let arr = cur.split('&');
|
||
let seasonNum = parseInt(arr[0]);
|
||
let day = parseInt(arr[1]);
|
||
if(isNaN(seasonNum) || isNaN(day)) return null;
|
||
return { seasonNum, day }
|
||
}).filter(cur => !!cur);
|
||
|
||
let maxDay = 0;
|
||
for(let {seasonNum: dicSeasonNum, day } of pvpSeasonDuring) {
|
||
if(seasonNum == dicSeasonNum) return day;
|
||
if(maxDay < day) maxDay = day;
|
||
}
|
||
return maxDay;
|
||
}
|
||
|
||
async function setPvpSeasonJob() {
|
||
await setPvpSeason(false);
|
||
}
|
||
|
||
async function setPvpSeason(isFirst: boolean, isForce?: boolean, minute?: number) {
|
||
console.log(`******** setPvpSeason1: isForce-${isForce}, minute-${minute}`)
|
||
|
||
let during = minute? minute * PER_MINUTE: null; // 下一次重置赛季天数
|
||
let oldPvpConfig = await PVPConfigModel.findCurPVPConfig();
|
||
let pvpConfig = oldPvpConfig;
|
||
console.log(`******** setPvpSeason2: during-${during}, seasonEndTime-${pvpConfig?.seasonEndTime}, now-${nowSeconds()}`)
|
||
if(!pvpConfig || pvpConfig.seasonEndTime - PER_MINUTE <= nowSeconds() || isForce) {
|
||
if(pvpConfig && !pvpConfig.hasSettleReward) {
|
||
await pvpSeasonEnd(pvpConfig.seasonNum);
|
||
}
|
||
|
||
let lastSeasonNum = pvpConfig? pvpConfig.seasonNum: 0;
|
||
let lastSeasonEndTime = pvpConfig? pvpConfig.seasonEndTime: 0;
|
||
console.log(`******** setPvpSeason3: lastSeasonNum-${lastSeasonNum}, lastSeasonEndTime-${lastSeasonEndTime}`)
|
||
|
||
let newSeasonStartTime = lastSeasonEndTime;
|
||
if(!during) during = getSeasonContinueDay(lastSeasonNum + 1) * PER_DAY;
|
||
let rewardTime = PVP.PVP_SEASON_REWARD_TIME_BEFORE * PER_MINUTE;
|
||
if(nowSeconds() - newSeasonStartTime > during) {
|
||
newSeasonStartTime = <number>getTimeFun().getDayZeroPoint(0);
|
||
}
|
||
console.log(`******** setPvpSeason4: newSeasonStartTime-${newSeasonStartTime}, during-${during}`)
|
||
|
||
if(isForce) { // debug使用,如果seasonEndTime是未来的,强行结束掉,新赛季从现在开始
|
||
newSeasonStartTime = nowSeconds();
|
||
} else { // 不是用debug的情况,如果(因为debug)newSeasonStartTime不是每天0点结算,那么改成lastSeasonEndTime之后的0点开始
|
||
let d = new Date(newSeasonStartTime * 1000);
|
||
if(d.getHours() != 0) {
|
||
d.setHours(0, 0, 0, 0);
|
||
newSeasonStartTime = getSeconds(d);
|
||
}
|
||
}
|
||
console.log(`******** setPvpSeason5: isForce-${isForce}, newSeasonStartTime-${newSeasonStartTime}`)
|
||
let newSeasonNum = await CounterModel.getNewCounter(COUNTER.PVP_SEASON_NUM);
|
||
pvpConfig = await PVPConfigModel.createPVPConfig(newSeasonNum, newSeasonStartTime, newSeasonStartTime + during - rewardTime, newSeasonStartTime + during);
|
||
}
|
||
await setPvpSeasonMakeRewardJob(pvpConfig);
|
||
await setNextSeasonJob(pvpConfig);
|
||
setPvpSeasonNum(pvpConfig, isFirst);
|
||
return pvpConfig;
|
||
}
|
||
|
||
function setPvpSeasonNum(pvpConfig: PVPConfigType, isFirst = false) {
|
||
if(pvpConfig) {
|
||
pinus.app.set('pvpSeasonNum', pvpConfig.seasonNum);
|
||
pinus.app.set('pvpSeasonEndTime', pvpConfig.seasonEndTime);
|
||
if(!isFirst) {
|
||
pinus.app.rpc.battle.battleRemote.setPvpSeasonNum.broadcast(pvpConfig);
|
||
pinus.app.rpc.role.roleRemote.setPvpSeasonNum.broadcast(pvpConfig);
|
||
pinus.app.rpc.connector.connectorRemote.setPvpSeasonNum.broadcast(pvpConfig);
|
||
}
|
||
}
|
||
}
|
||
|
||
async function setPvpSeasonMakeRewardJob(pvpConfig: PVPConfigType) {
|
||
if (!!seasonMakeRewardTimJobId) {
|
||
seasonMakeRewardTimJobId.cancel();
|
||
}
|
||
if(!pvpConfig) return;
|
||
if(pvpConfig.seasonRewardTime < nowSeconds() && !pvpConfig.hasSettleReward) { // 未发奖励
|
||
await pvpSeasonEnd(pvpConfig.seasonNum);
|
||
} else {
|
||
seasonMakeRewardTimJobId = scheduleJob('seasonMakeRewardTimJobId', pvpConfig.seasonRewardTime * 1000, async () => {
|
||
console.log('************ seasonMakeRewardTimJobId *********');
|
||
await pvpSeasonEnd(pvpConfig.seasonNum);
|
||
});
|
||
}
|
||
}
|
||
|
||
async function setNextSeasonJob(pvpConfig: PVPConfigType) {
|
||
if (!!seasonRefreshTimeJobId) {
|
||
seasonRefreshTimeJobId.cancel();
|
||
}
|
||
//定时开启新赛季,比seasonEndTime多定一分钟,保证定时器时间没错
|
||
seasonRefreshTimeJobId = scheduleJob('seasonRefreshTimeJobId', (pvpConfig.seasonEndTime) * 1000, setPvpSeasonJob);
|
||
}
|
||
|
||
/**
|
||
* debug接口
|
||
* @param hour
|
||
*/
|
||
export async function resetPvpSeasonTime(minute: number) {
|
||
return await setPvpSeason(false, true, minute);
|
||
}
|
||
|
||
export async function reportOnlineSchedule() {
|
||
let allRoles = await getAllOnlineRoles();
|
||
infologger.info('reportOnlineSchedule all roles count: ', allRoles.length);
|
||
console.log('reportOnlineSchedule all roles count: ', allRoles.length)
|
||
for (let { roleId, userCode, sid } of allRoles) {
|
||
let result = reportOneOnline(roleId, userCode, sid, false);
|
||
if (!result) continue;
|
||
}
|
||
}
|
||
|
||
// —————————————— PVP 及赛季相关 end —————————————— //
|
||
|
||
// —————————————— 军团活动 —————————————— //
|
||
/**
|
||
* 军团活动,每晚8点开启
|
||
*/
|
||
export async function guildActivitySchedule() {
|
||
/***********guildActivitySchedule***********/
|
||
if (guildActStartJobId) {
|
||
guildActStartJobId.cancel();
|
||
}
|
||
|
||
let dicGuildActivity = getTodayGuildActivity();
|
||
// console.log(dicGuildActivity)
|
||
|
||
guildActStartJobId = scheduleJob('guildActivityStart', `${dicGuildActivity.startSeconds} ${dicGuildActivity.startMinute} ${dicGuildActivity.startTime} * * ?`, guildActivityStartSchedule);
|
||
}
|
||
|
||
// 军团晚间活动,每天8点开始
|
||
async function guildActivityStartSchedule() {
|
||
await guildActivityStart();
|
||
}
|
||
|
||
export async function guildActivityStart(dicGuildActivity?: DicGuildActivity) {
|
||
console.log('*******开始军团晚间活动****')
|
||
if (!dicGuildActivity) dicGuildActivity = getTodayGuildActivity();
|
||
if (!dicGuildActivity) return;
|
||
|
||
console.log('********', dicGuildActivity.id, Date.now() + dicGuildActivity.duringTime * 1000)
|
||
let servers = await getAllServers(); // 玩家serverId列表
|
||
console.log('****** guildActSecondsJobId', guildActSecondsJobId)
|
||
if (guildActSecondsJobId) {
|
||
guildActSecondsJobId.cancel();
|
||
guildActSecondsJobId = undefined;
|
||
}
|
||
if (guildActEndJobId) {
|
||
guildActEndJobId.cancel();
|
||
guildActEndJobId = undefined;
|
||
}
|
||
|
||
let aid = dicGuildActivity.id;
|
||
await incCurGuildActivityIndex(aid);
|
||
await pinus.app.rpc.guild.guildActivityRemote.guildActivityStart.broadcast(aid);
|
||
|
||
if (aid == GUILD_ACTIVITY_TYPE.GATE_ACTIVITY) {
|
||
guildActSecondsJobId = scheduleJob('guildActivitySeconds', '*/10 * * * * *', gateActivitySeconds);
|
||
// 结束时间
|
||
guildActEndJobId = scheduleJob('guildActivityEnd', Date.now() + dicGuildActivity.duringTime * 1000, gateActivityEnd);
|
||
} else if (aid == GUILD_ACTIVITY_TYPE.CITY_ACTIVITY) {
|
||
guildActSecondsJobId = scheduleJob('guildActivitySeconds', '*/10 * * * * *', cityActivitySeconds);
|
||
// 结束时间
|
||
guildActEndJobId = scheduleJob('guildActivityEnd', Date.now() + dicGuildActivity.duringTime * 1000, cityActivityEnd);
|
||
} else if (aid == GUILD_ACTIVITY_TYPE.RACE_ACTIVITY) {
|
||
|
||
// 开始活动
|
||
let guildServers = pinus.app.getServersByType('guild');
|
||
for (let serverId of servers) {
|
||
let sid = dispatch(serverId.toString(), guildServers, 'guild');
|
||
await pinus.app.rpc.guild.guildActivityRemote.raceActivityStart.toServer(sid.id, serverId);
|
||
}
|
||
|
||
guildActSecondsJobId = scheduleJob('guildActivitySeconds', '*/2 * * * * *', raceActivitySeconds);
|
||
// 结束时间
|
||
guildActEndJobId = scheduleJob('guildActivityEnd', Date.now() + dicGuildActivity.duringTime * 1000, raceActivityEnd);
|
||
}
|
||
|
||
for (let serverId of servers) {
|
||
await sendGuildActivityStatus(serverId);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// 蛮夷入侵
|
||
// 结束军团活动
|
||
export async function gateActivityEnd() {
|
||
|
||
console.log('*****gateActivityEnd');
|
||
await sendEndMsgToAllServer();
|
||
await pinus.app.rpc.guild.guildActivityRemote.guildActivityEnd.broadcast(GUILD_ACTIVITY_TYPE.GATE_ACTIVITY);
|
||
|
||
if (guildActSecondsJobId) {
|
||
guildActSecondsJobId.cancel();
|
||
guildActSecondsJobId = undefined;
|
||
}
|
||
if (guildActEndJobId) {
|
||
guildActEndJobId.cancel();
|
||
guildActEndJobId = undefined;
|
||
}
|
||
checkPopUpConditionWhenGuildActivityEnd();
|
||
}
|
||
|
||
// 每10秒下发一次的任务
|
||
export async function gateActivitySeconds() {
|
||
console.log('*****gateActivitySeconds')
|
||
await pinus.app.rpc.guild.guildActivityRemote.sendRankToGuilds.broadcast(GUILD_ACTIVITY_TYPE.GATE_ACTIVITY);
|
||
}
|
||
|
||
// 诸侯混战
|
||
// 结束军团活动
|
||
export async function cityActivityEnd() {
|
||
console.log('*****cityActivityEnd');
|
||
await sendEndMsgToAllServer();
|
||
await pinus.app.rpc.guild.guildActivityRemote.guildActivityEnd.broadcast(GUILD_ACTIVITY_TYPE.CITY_ACTIVITY);
|
||
|
||
// 发完之后再做下周自动宣战
|
||
await autoDeclare();
|
||
if (guildActSecondsJobId) {
|
||
guildActSecondsJobId.cancel();
|
||
guildActSecondsJobId = undefined;
|
||
}
|
||
if (guildActEndJobId) {
|
||
guildActEndJobId.cancel();
|
||
guildActEndJobId = undefined;
|
||
}
|
||
checkPopUpConditionWhenGuildActivityEnd();
|
||
}
|
||
|
||
// 每10秒下发一次的任务
|
||
export async function cityActivitySeconds() {
|
||
console.log('*****cityActivitySeconds')
|
||
await pinus.app.rpc.guild.guildActivityRemote.sendRankToGuilds.broadcast(GUILD_ACTIVITY_TYPE.CITY_ACTIVITY);
|
||
}
|
||
|
||
// 粮草先行
|
||
// 结束军团活动
|
||
export async function raceActivityEnd() {
|
||
console.log('*****raceActivityEnd');
|
||
await sendEndMsgToAllServer();
|
||
await pinus.app.rpc.guild.guildActivityRemote.guildActivityEnd.broadcast(GUILD_ACTIVITY_TYPE.RACE_ACTIVITY);
|
||
|
||
if (guildActSecondsJobId) {
|
||
guildActSecondsJobId.cancel();
|
||
guildActSecondsJobId = undefined;
|
||
}
|
||
if (guildActEndJobId) {
|
||
guildActEndJobId.cancel();
|
||
guildActEndJobId = undefined;
|
||
}
|
||
checkPopUpConditionWhenGuildActivityEnd();
|
||
}
|
||
|
||
// 每10秒下发一次的任务
|
||
export async function raceActivitySeconds() {
|
||
console.log('*****raveActivitySeconds')
|
||
let servers = await getAllServers(); // 玩家serverId列表
|
||
let guildServers = pinus.app.getServersByType('guild');
|
||
for (let serverId of servers) {
|
||
let sid = dispatch(serverId.toString(), guildServers, 'guild');
|
||
await pinus.app.rpc.guild.guildActivityRemote.calWoodenHorseAndSend.toServer(sid.id, serverId);
|
||
}
|
||
}
|
||
|
||
// —————————————— 军团活动 end —————————————— //
|
||
|
||
// —————————————— 拍卖行 —————————————— //
|
||
|
||
let startGuildAuctionJobId: Job;
|
||
let startWorldAuctionJobId: Job;
|
||
let stopAuctionJobId: Job;
|
||
|
||
export async function auctionSchedule() {
|
||
clearAuctionSchedule();
|
||
let guildOpen = gameData.auctionTime.get(AUCTION_TIME.GUILD_OPEN);
|
||
let worldOpen = gameData.auctionTime.get(AUCTION_TIME.WORLD_OPEN);
|
||
let worldClose = gameData.auctionTime.get(AUCTION_TIME.WORLD_CLOSE);
|
||
|
||
// console.log('***** auctionSchedule', guildOpen.hour, guildOpen.minute, guildOpen.seconds);
|
||
// console.log('***** auctionSchedule', worldOpen.hour, worldOpen.minute, worldOpen.seconds);
|
||
// console.log('***** auctionSchedule', worldClose.hour, worldClose.minute, worldClose.seconds);
|
||
startGuildAuctionJobId = scheduleJob('startGuildAuction', `${guildOpen.seconds} ${guildOpen.minute} ${guildOpen.hour} * * ?`, startGuildAuction);
|
||
startWorldAuctionJobId = scheduleJob('startWorldAuction', `${worldOpen.seconds} ${worldOpen.minute} ${worldOpen.hour} * * ?`, startWorldAuction);
|
||
stopAuctionJobId = scheduleJob('stopAuction', `${worldClose.seconds} ${worldClose.minute} ${worldClose.hour} * * ?`, stopAuction);
|
||
|
||
}
|
||
|
||
function clearAuctionSchedule() {
|
||
if (startGuildAuctionJobId) {
|
||
startGuildAuctionJobId.cancel();
|
||
startGuildAuctionJobId = undefined;
|
||
}
|
||
if (startWorldAuctionJobId) {
|
||
startWorldAuctionJobId.cancel();
|
||
startWorldAuctionJobId = undefined;
|
||
}
|
||
if (stopAuctionJobId) {
|
||
stopAuctionJobId.cancel();
|
||
stopAuctionJobId = undefined;
|
||
}
|
||
}
|
||
|
||
// —————————————— 拍卖行 end —————————————— //
|
||
|
||
// —————————————— 邮件 —————————————— //
|
||
let circleMailJob: Job;
|
||
async function checkCircleMail() {
|
||
if(circleMailJob) {
|
||
circleMailJob.cancel();
|
||
}
|
||
circleMailJob = scheduleJob('circleMail', '0 0 20 * * ?', scheduleSendCircleMail);
|
||
// scheduleSendCircleMail();
|
||
}
|
||
|
||
async function scheduleSendCircleMail() {
|
||
let gmmails = await GMMailModel.findCircleMails(pinus.app.get('env'));
|
||
await sendMailsByGmMail(gmmails);
|
||
}
|
||
|
||
|
||
let mailByHourJob: Job;
|
||
async function checkMailByHour() {
|
||
if(mailByHourJob) {
|
||
mailByHourJob.cancel();
|
||
}
|
||
// 每小时一次,为了和每天循环邮件任务不重复,设在每小时05分
|
||
mailByHourJob = scheduleJob('setMailTimerByHour', '0 5 0/1 * * ?', async () => {
|
||
await setMailSchedule(false)
|
||
});
|
||
setMailSchedule(true); // 初始时候启动一次
|
||
}
|
||
|
||
async function setMailSchedule(isInit: boolean) {
|
||
let minuteNow = moment().minute();
|
||
let beforeTime = minuteNow >= 5? moment().minute(5).unix(): moment().minute(5).add(-1, 'h').unix();
|
||
let time = minuteNow >= 5? moment().minute(5).add(1, 'h').unix(): moment().minute(5).unix();
|
||
|
||
let mails = await MailModel.findByTimeGap(beforeTime, time);
|
||
let groupMails = await GroupMailModel.findByTimeGap(beforeTime, time);
|
||
let serverMails = await ServerMailModel.findByTimeGap(beforeTime, time);
|
||
|
||
let times: number[] = [];
|
||
for(let { sendTime } of [...mails, ...groupMails, ...serverMails]) {
|
||
if(times.indexOf(sendTime) == -1) times.push(sendTime);
|
||
}
|
||
|
||
for(let time of times) {
|
||
if(Date.now() > time * 1000) {
|
||
if(!isInit) await pushMailSchedule(time);
|
||
} else {
|
||
scheduleJob(`mailPush${time}`, time * 1000, async () => {
|
||
await pushMailSchedule(time);
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// 当前时间到下一次定时器之间的定时器
|
||
export async function addMailsToSchedule(mails: MailType[], groupMails: GroupMailType[], serverMails: ServerMailType[]) {
|
||
let minuteNow = moment().minute();
|
||
let time = minuteNow >= 5? moment().minute(5).add(1, 'h').unix(): moment().minute(5).unix();
|
||
|
||
let times: number[] = [];
|
||
for(let { sendTime } of [...mails, ...groupMails, ...serverMails]) {
|
||
if(sendTime * 1000 > Date.now() && sendTime < time && times.indexOf(sendTime) == -1) times.push(sendTime);
|
||
}
|
||
|
||
for(let time of times) {
|
||
if(!scheduledJobs[`mailPush${time}`]) {
|
||
scheduleJob(`mailPush${time}`, time * 1000, async () => {
|
||
await pushMailSchedule(time);
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
async function pushMailSchedule(time: number) {
|
||
if(scheduledJobs[`mailPush${time}`]) scheduledJobs[`mailPush${time}`].cancel();
|
||
|
||
let mails = await MailModel.findBySendTime(time);
|
||
let groupMails = await GroupMailModel.findBySendTime(time);
|
||
let serverMails = await ServerMailModel.findBySendTime(time);
|
||
|
||
let f = new SendMailFun();
|
||
f.setMails(mails, groupMails, serverMails);
|
||
await f.pushToUsers();
|
||
}
|
||
|
||
// —————————————— 邮件 end —————————————— //
|
||
|
||
// —————————————— 维护 —————————————— //
|
||
let maintenInfos = new Map<string, { servers: ServerlistType[], maintenance: Maintenance }>(); // batchCode => {servers, maintenance}
|
||
export async function initMaintenance(servers?: ServerlistType[], fromApp = false) {
|
||
if(!servers) servers = await ServerlistModel.findByEnv(pinus.app.get('env'));
|
||
|
||
for(let server of servers) {
|
||
let { maintenance } = server;
|
||
if(maintenance && maintenance.isOpen) {
|
||
if(!maintenInfos.has(maintenance.batchCode)) {
|
||
maintenInfos.set(maintenance.batchCode, { servers: [], maintenance });
|
||
}
|
||
maintenInfos.get(maintenance.batchCode).servers.push(server);
|
||
}
|
||
}
|
||
for(let [batchCode] of maintenInfos) {
|
||
await setMaintenance(batchCode, fromApp);
|
||
}
|
||
}
|
||
|
||
export async function initMaintenanaceInOtherServers() {
|
||
const servers = await ServerlistModel.findByEnv(pinus.app.get('env'));
|
||
|
||
for(let server of servers) {
|
||
let { id, maintenance } = server;
|
||
if(maintenance && maintenance.isOpen) {
|
||
setServerMainten([id], maintenance.startTime, maintenance.endTime)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 设置维护
|
||
async function setMaintenance(batchCode: string, fromApp: boolean) {
|
||
let { maintenance } = maintenInfos.get(batchCode);
|
||
|
||
console.log('***** maintenance', maintenance);
|
||
|
||
let now = nowSeconds();
|
||
// 发送消息
|
||
if(maintenance.hasNotify && now < maintenance.startTime) { // 提前5分钟发通知
|
||
if(now > maintenance.startTime - 5 * 60) {
|
||
maintenanceNotifySchedule(batchCode);
|
||
} else {
|
||
if(scheduledJobs[`maintenNotify${batchCode}`]) scheduledJobs[`maintenNotify${batchCode}`].cancel();
|
||
scheduleJob(`maintenNotify${batchCode}`, (maintenance.startTime - 5 * 60) * 1000, () => {
|
||
maintenanceNotifySchedule(batchCode);
|
||
})
|
||
}
|
||
} else {
|
||
if(scheduledJobs[`maintenNotify${batchCode}`]) scheduledJobs[`maintenNotify${batchCode}`].cancel();
|
||
}
|
||
// 开始维护
|
||
console.log('******* setMaintenance', now, now < maintenance.endTime, now > maintenance.startTime)
|
||
if(now < maintenance.endTime) {
|
||
if(now > maintenance.startTime) {
|
||
if(!fromApp) await startMaintenanceSchedule(batchCode);
|
||
} else {
|
||
scheduleJob(`startMainten${batchCode}`, maintenance.startTime * 1000, async () => {
|
||
await startMaintenanceSchedule(batchCode);
|
||
});
|
||
}
|
||
} else {
|
||
if(scheduledJobs[`startMainten${batchCode}`]) scheduledJobs[`startMainten${batchCode}`].cancel();
|
||
}
|
||
|
||
}
|
||
|
||
// 维护前通知
|
||
async function maintenanceNotifySchedule(batchCode: string) {
|
||
let { servers, maintenance } = maintenInfos.get(batchCode);
|
||
if(scheduledJobs[`maintenNotify${batchCode}`]) scheduledJobs[`maintenNotify${batchCode}`].cancel();
|
||
if(scheduledJobs[`maintenSec${batchCode}`]) scheduledJobs[`maintenSec${batchCode}`].cancel();
|
||
|
||
scheduleJob(`maintenSec${batchCode}`, `0 */1 * * * *`, async () => {
|
||
for(let { id: serverId } of servers) {
|
||
let msgData = await createMarqueeMsg('', '系统', serverId, '服务器即将维护,请玩家提前退出游戏');
|
||
await pushMarqueeMsg(msgData);
|
||
}
|
||
})
|
||
}
|
||
|
||
// 开始维护
|
||
async function startMaintenanceSchedule(batchCode: string) {
|
||
let { servers, maintenance } = maintenInfos.get(batchCode);
|
||
let serverIds = servers.map(cur => cur.id);
|
||
if(scheduledJobs[`maintenNotify${batchCode}`]) scheduledJobs[`maintenNotify${batchCode}`].cancel();
|
||
if(scheduledJobs[`maintenSec${batchCode}`]) scheduledJobs[`maintenSec${batchCode}`].cancel();
|
||
if(scheduledJobs[`startMainten${batchCode}`]) scheduledJobs[`startMainten${batchCode}`].cancel();
|
||
|
||
// 向全服发送
|
||
for(let { id: serverId } of servers) {
|
||
await sendMessageToServer(serverId, PUSH_ROUTE.SERVER_MAINTENANCE, resResult(STATUS.SERVER_MAINTENANCE));
|
||
}
|
||
|
||
// 更新connectorRemote里面的维护服务器
|
||
console.log('******** startMaintenanceSchedule', batchCode, serverIds, maintenance.startTime, maintenance.endTime)
|
||
pinus.app.rpc.connector.connectorRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
|
||
pinus.app.rpc.activity.activityRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
|
||
pinus.app.rpc.battle.battleRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
|
||
pinus.app.rpc.chat.chatRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
|
||
pinus.app.rpc.guild.guildRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
|
||
pinus.app.rpc.order.orderRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
|
||
pinus.app.rpc.role.roleRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
|
||
// 数数flush
|
||
pinus.app.rpc.activity.activityRemote.taflush.broadcast();
|
||
pinus.app.rpc.battle.battleRemote.taflush.broadcast();
|
||
pinus.app.rpc.chat.chatRemote.taflush.broadcast();
|
||
pinus.app.rpc.connector.connectorRemote.taflush.broadcast();
|
||
pinus.app.rpc.guild.guildRemote.taflush.broadcast();
|
||
pinus.app.rpc.order.orderRemote.taflush.broadcast();
|
||
pinus.app.rpc.role.roleRemote.taflush.broadcast();
|
||
pinus.app.rpc.systimer.systimerRemote.taflush.broadcast();
|
||
}
|
||
|
||
// 提前结束维护
|
||
export async function stopMaintenance(batchCode: string, serverIds: number[]) {
|
||
// console.log('***********', serverIds)
|
||
let { servers = [], maintenance = {} as Maintenance } = maintenInfos.get(batchCode)||{};
|
||
for(let id of serverIds) {
|
||
let index = servers.findIndex(cur => cur.id == id);
|
||
if(index != -1) servers.splice(index);
|
||
}
|
||
if(servers.length == 0) {
|
||
if(scheduledJobs[`maintenNotify${batchCode}`]) scheduledJobs[`maintenNotify${batchCode}`].cancel();
|
||
if(scheduledJobs[`maintenSec${batchCode}`]) scheduledJobs[`maintenSec${batchCode}`].cancel();
|
||
if(scheduledJobs[`startMainten${batchCode}`]) scheduledJobs[`startMainten${batchCode}`].cancel();
|
||
maintenInfos.delete(batchCode);
|
||
}
|
||
|
||
// 更新connectorRemote里面的维护服务器
|
||
pinus.app.rpc.connector.connectorRemote.stopServerMainten.broadcast(serverIds);
|
||
pinus.app.rpc.activity.activityRemote.stopServerMainten.broadcast(serverIds);
|
||
pinus.app.rpc.battle.battleRemote.stopServerMainten.broadcast(serverIds);
|
||
pinus.app.rpc.chat.chatRemote.stopServerMainten.broadcast(serverIds);
|
||
pinus.app.rpc.guild.guildRemote.stopServerMainten.broadcast(serverIds);
|
||
pinus.app.rpc.order.orderRemote.stopServerMainten.broadcast(serverIds);
|
||
pinus.app.rpc.role.roleRemote.stopServerMainten.broadcast(serverIds);
|
||
}
|
||
|
||
// —————————————— 维护 end —————————————— //
|
||
// —————————————— 自动开服 —————————————— //
|
||
export async function initAutoCreateServer(region?: RegionType) {
|
||
if(!region) region = await RegionModel.findRegionByEnv(pinus.app.get('env'));
|
||
if(region && region.stategy && region.stategy.isOpen) {
|
||
for(let timer of region.stategy.timers) {
|
||
setStategyTimer(timer, region)
|
||
}
|
||
} else {
|
||
if(scheduledJobs[`autoServer${SERVER_TIMER.FIVE_HALF}`]) scheduledJobs[`autoServer${SERVER_TIMER.FIVE_HALF}`].cancel();
|
||
if(scheduledJobs[`autoServer${SERVER_TIMER.TEN_HALF}`]) scheduledJobs[`autoServer${SERVER_TIMER.FIVE_HALF}`].cancel();
|
||
if(scheduledJobs[`autoServer${SERVER_TIMER.FIFTEEN_HALF}`]) scheduledJobs[`autoServer${SERVER_TIMER.FIVE_HALF}`].cancel();
|
||
if(scheduledJobs[`autoServer${SERVER_TIMER.NINETEEN_HALF}`]) scheduledJobs[`autoServer${SERVER_TIMER.FIVE_HALF}`].cancel();
|
||
}
|
||
}
|
||
|
||
function setStategyTimer(timerId: number, region: RegionType) {
|
||
if(scheduledJobs[`autoServer${timerId}`]) scheduledJobs[`autoServer${timerId}`].cancel();
|
||
let cron = '';
|
||
switch(timerId) {
|
||
case SERVER_TIMER.FIVE_HALF:
|
||
cron = '0 30 5 * * *'; break;
|
||
case SERVER_TIMER.TEN_HALF:
|
||
cron = '0 30 10 * * *'; break;
|
||
case SERVER_TIMER.FIFTEEN_HALF:
|
||
cron = '0 30 15 * * *'; break;
|
||
case SERVER_TIMER.NINETEEN_HALF:
|
||
cron = '0 30 19 * * *'; break;
|
||
}
|
||
if(cron) {
|
||
scheduleJob(`autoServer${timerId}`, cron, () => {
|
||
autoCreateServerSchedule(region);
|
||
})
|
||
}
|
||
}
|
||
|
||
async function autoCreateServerSchedule(region: RegionType) {
|
||
console.log('******* createNewServer *******')
|
||
let latestServer = await ServerlistModel.findByServerId(region.latestServerUniqId);
|
||
if(!latestServer || latestServer.playerCnt >= region.stategy.maxPlayerCnt) {
|
||
let params = new CreateServerParam();
|
||
params.setByRegionStategy(region, nowSeconds())
|
||
await createNewServer(region, (latestServer?.serverId||0) + 1, params);
|
||
}
|
||
}
|
||
// —————————————— 自动开服 end —————————————— //
|
||
|
||
// —————————————— 活动 start —————————————— //
|
||
async function initTimeLimitRank() {
|
||
let activities = await ActivityModel.findActivityByType(ACTIVITY_TYPE.TIME_LIMIT_RANK);
|
||
await updateTimeLimitRank(activities);
|
||
}
|
||
|
||
export async function updateTimeLimitRank(activities: ActivityModelType[]) {
|
||
for(let activity of activities) {
|
||
let { activityId } = activity
|
||
let group = await ActivityGroupModel.findByActivity(activityId);
|
||
for(let { serverIds } of group) {
|
||
let servers = await ServerlistModel.findByServerIds(serverIds);
|
||
for(let { env, openTime, id: serverId } of servers) {
|
||
if(env == pinus.app.get('env')) {
|
||
let data = new TimeLimitRankData(activity, 0, openTime);
|
||
if(data.sendMailTime > Date.now()) {
|
||
await setSendRankMailSchedule(activityId, serverId, data);
|
||
}
|
||
if(data.rankEndTime > Date.now() && data.needSnapshot()) {
|
||
await setTakeRankSnapshotSchedule(activityId, serverId, data);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
async function setSendRankMailSchedule(activityId: number, serverId: number, data: TimeLimitRankData) {
|
||
console.log('########### setSendRankMailSchedule1 ########', activityId, serverId, data.sendMailTime)
|
||
if(scheduledJobs[`rankMail_${serverId}_${activityId}`]) {
|
||
scheduledJobs[`rankMail_${serverId}_${activityId}`].cancel();
|
||
}
|
||
|
||
scheduleJob(`rankMail_${serverId}_${activityId}`, data.sendMailTime, async () => {
|
||
console.log('########### setSendRankMailSchedule2 ########')
|
||
await sendRankMail(data, serverId);
|
||
});
|
||
}
|
||
|
||
|
||
async function setTakeRankSnapshotSchedule(activityId: number, serverId: number, data: TimeLimitRankData) {
|
||
console.log('########### setTakeRankSnapshotSchedule1 ########', activityId, serverId, data.rankEndTime)
|
||
|
||
if(scheduledJobs[`snapshot_${serverId}_${activityId}`]) {
|
||
scheduledJobs[`snapshot_${serverId}_${activityId}`].cancel();
|
||
}
|
||
|
||
scheduleJob(`snapshot_${serverId}_${activityId}`, data.rankEndTime, async () => {
|
||
console.log('########### setTakeRankSnapshotSchedule2 ########')
|
||
await takeSnapshot(data, serverId);
|
||
})
|
||
}
|
||
// —————————————— 活动 end —————————————— //
|
||
|
||
// —————————————— 名将擂台 start —————————————— //
|
||
export async function setLadderCountDown(battleCode: string, time: number, status: LADDER_STATUS) {
|
||
if(scheduledJobs[`ladder${battleCode}`]) {
|
||
scheduledJobs[`ladder${battleCode}`].cancel();
|
||
}
|
||
let endTime = time + (status == LADDER_STATUS.CHECK? LADDER.LADDER_BATTLE_PREPARE_COUNTDOWN: LADDER.LADDER_BATTLE_COUNTDOWN) * 1000;
|
||
scheduleJob(`ladder${battleCode}`, endTime, async () => {
|
||
await ladderTimeWillout(battleCode, status);
|
||
|
||
scheduledJobs[`ladder${battleCode}`].cancel();
|
||
scheduleJob(`ladder${battleCode}`, endTime + LADDER_SERVER_GAP_TIME * 1000, async () => {
|
||
await ladderTimeout(battleCode, status);
|
||
scheduledJobs[`ladder${battleCode}`].cancel();
|
||
});
|
||
})
|
||
}
|
||
|
||
export async function cancelLadderCountDown(battleCode: string) {
|
||
if(scheduledJobs[`ladder${battleCode}`]) {
|
||
scheduledJobs[`ladder${battleCode}`].cancel();
|
||
}
|
||
}
|
||
|
||
async function ladderDailyReward() {
|
||
scheduleJob('ladderDailyReward', '0 0 22 * * ?', async () => {
|
||
let servers = await ServerlistModel.findByEnv(pinus.app.get('env'));
|
||
for(let { id } of servers) {
|
||
await sendLadderDailyReward(id);
|
||
}
|
||
});
|
||
let recs = await LadderMatchRecModel.getUncompleteData();
|
||
let battleCodes: string[] = [];
|
||
for(let { battleCode, checkTime, battleTime, status, serverId, defenseInfo } of recs) {
|
||
if(status == LADDER_STATUS.CHECK) {
|
||
if(Date.now() > checkTime + LADDER.LADDER_BATTLE_PREPARE_COUNTDOWN * 1000) {
|
||
battleCodes.push(battleCode);
|
||
await LadderMatchModel.unlock(serverId, defenseInfo?.roleId);
|
||
} else {
|
||
await setLadderCountDown(battleCode, checkTime, status)
|
||
}
|
||
} else if (status == LADDER_STATUS.BATTLE) {
|
||
if(Date.now() > battleTime + LADDER.LADDER_BATTLE_COUNTDOWN * 1000 ) {
|
||
battleCodes.push(battleCode);
|
||
await LadderMatchModel.unlock(serverId, defenseInfo?.roleId);
|
||
} else {
|
||
await setLadderCountDown(battleCode, battleTime, status)
|
||
}
|
||
}
|
||
}
|
||
await LadderMatchRecModel.timeoutMany(battleCodes);
|
||
}
|
||
// —————————————— 名将擂台 end —————————————— //
|
||
|
||
// —————————————— 团购定时器 start —————————————— //
|
||
async function initGroupShopSchedule() {
|
||
|
||
let activities = await ActivityModel.findActivityByType(ACTIVITY_TYPE.GROUP_SHOP);
|
||
let scheduleMap = new Map<number, { activityId: number, itemId: number, sum: number }[]>(); // 时间 => data
|
||
for(let activity of activities) {
|
||
let timers = await getGroupShopTimers(activity);
|
||
for(let { time, itemId, sum } of timers) {
|
||
if(!scheduleMap.has(time)) {
|
||
scheduleMap.set(time, []);
|
||
}
|
||
scheduleMap.get(time).push({ activityId: activity.activityId, itemId, sum });
|
||
}
|
||
}
|
||
|
||
for(let [time, arr] of scheduleMap) {
|
||
if(scheduledJobs[`groupShopSetSum${time}`]) {
|
||
scheduledJobs[`groupShopSetSum${time}`].cancel();
|
||
}
|
||
scheduleJob(`groupShopSetSum${time}`, time, async () => {
|
||
await setGroupShopToSetSum(arr);
|
||
});
|
||
}
|
||
|
||
scheduleJob('groupShopRefund', '0 30 5 * * ?', refundGroupShop);
|
||
}
|
||
|
||
// —————————————— 团购定时器 end —————————————— //
|