import { scheduleJob, Job, scheduledJobs, } from 'node-schedule'; import { PVPConfigModel, PVPConfigType } from '../db/PvpConfig'; import { nowSeconds, getTimeFun, getSeconds } from '../pubUtils/timeUtil'; import { getTodayGuildActivity, gameData } from '../pubUtils/data'; import { pvpSeasonEnd } from './pvpService'; import { getAllOnlineRoles, getAllServers, delGuildActivityRank, getServerCreateTime, redisClient } 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, GVG_PERIOD, SDK_PUSH_MSG_TYPE, MAIL_TYPE } from '../consts'; import { pinus } from 'pinus'; import { settleGuildWeekly } from './guildService'; import { sendMailByContent, SendMailFun, sendMailsByGmMail, } from './mailService'; import { sendEndMsgToAllServer, 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 { initMarquee, setServerMainten } from './gmService'; import moment = require('moment'); import { reportOneOnline } from './authenticateService'; import { GVG, 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, errlogger } 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 { pushClientMsg, sendMessageToServer } from './pushService'; import { getRandEelm, getRandSingleEelm, 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'; import { HiddenDataModel, HiddenDataModelType } from '../db/HiddenData'; import { setHiddenData, setHiddenDataToMemory } from './dataService'; import { GVGConfigModel } from '../db/GVGConfig'; import { createNewGVGConfig, initLeaguePrepare } from './gvg/gvgService'; import { getFightTimeByPeriod, saveVestigeRankSchedule, gvgBattlePeriodSchedule } from './gvg/gvgFightService'; import { gvgBattleEnd } from './gvg/gvgBattleService'; import { ActivityMonthlyFundModel } from '../db/ActivityMonthlyFund'; import { getActivityById } from './activity/activityService'; import { MonthlyFundData } from "../domain/activityField/monthlyFundField"; import { RewardInter } from '../pubUtils/interface'; import { stringToRewardInter } from './activity/giftPackageService'; import { autoCreateServerSchedule } from './serverService'; const PER_SECOND = 1 * 1000; const PER_DAY = 24 * 60 * 60; const PER_HOUR = 1 * 60 * 60; const PER_MINUTE = 1 * 60; var seasonEndJob: Job; 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 setPvpSeasonSchedule(); // 周功勋结算任务 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(); // 隐藏数据定时器 initHiddenDataSchedule(true); // gvg每周日 initGVGConfigSchedule(); // 定时推送消息 initPushMsgSchedule(); // 月基金每月未领取 initMonthlyFundSchedule(); } // 每日刷新 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 及赛季相关 —————————————— // export async function setPvpSeasonSchedule(fromBackend = false) { let pvpConfig = await PVPConfigModel.setCurrentPvp(); await setSeasonEndJob(pvpConfig); await setPvpSeasonMakeRewardJob(pvpConfig); // 发送奖励定时器 await setNextSeasonJob(pvpConfig); // 赛季开始定时器 if(fromBackend) { setPvpSeasonNumToRemote(pvpConfig); setPvpSettleSeasonNumToRemote(); } else { setPvpSeasonNum(pvpConfig); setPvpSettleSeasonNum(); } } async function setSeasonEndJob(pvpConfig: PVPConfigType) { if (!!seasonEndJob) { seasonEndJob.cancel(); } if(!pvpConfig || pvpConfig.seasonEndTime < nowSeconds()) { pvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig? pvpConfig.seasonNum + 1: 1); if(!pvpConfig) return; } console.log('####### setSeasonEndJob', JSON.stringify(pvpConfig)) seasonEndJob = scheduleJob('seasonEndJob', pvpConfig.seasonEndTime * 1000, async () => { console.log('************ setSeasonEndJob *********'); setPvpSeasonNumToRemote(pvpConfig); let nextPvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig.seasonNum + 1); await setSeasonEndJob(nextPvpConfig); }); } async function setPvpSeasonMakeRewardJob(pvpConfig: PVPConfigType) { if (!!seasonMakeRewardTimJobId) { seasonMakeRewardTimJobId.cancel(); } if(!pvpConfig || pvpConfig.seasonRewardTime - 60 < nowSeconds()) { pvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig? pvpConfig.seasonNum + 1: 1); if(!pvpConfig) return; } console.log('####### setPvpSeasonMakeRewardJob', JSON.stringify(pvpConfig)) seasonMakeRewardTimJobId = scheduleJob('seasonMakeRewardTimJobId', pvpConfig.seasonRewardTime * 1000 - 60 * 1000, async () => { console.log('************ seasonMakeRewardTimJobId *********'); await pvpSeasonEnd(pvpConfig.seasonNum); let nextPvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig.seasonNum + 1); await setPvpSeasonMakeRewardJob(nextPvpConfig); }); } async function setNextSeasonJob(pvpConfig: PVPConfigType) { if (!!seasonRefreshTimeJobId) { seasonRefreshTimeJobId.cancel(); } if(!pvpConfig || pvpConfig.seasonStartTime < nowSeconds()) { pvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig? pvpConfig.seasonNum + 1: 1); if(!pvpConfig) return; } console.log('####### setNextSeasonJob', JSON.stringify(pvpConfig)) seasonRefreshTimeJobId = scheduleJob('seasonRefreshTimeJobId', pvpConfig.seasonStartTime * 1000, async () => { console.log('************ setNextSeasonJob *********'); setPvpSeasonNumToRemote(pvpConfig); await PVPConfigModel.setNextPvp(pvpConfig.seasonNum); let nextPvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig.seasonNum + 1); await setNextSeasonJob(nextPvpConfig); }); } export async function setPvpSeasonNumToRemote(pvpConfig: PVPConfigType) { await setPvpSeasonNum(pvpConfig); await pinus.app.rpc.battle.battleRemote.setPvpSeasonNum.broadcast(pvpConfig); await pinus.app.rpc.role.roleRemote.setPvpSeasonNum.broadcast(pvpConfig); await pinus.app.rpc.connector.connectorRemote.setPvpSeasonNum.broadcast(pvpConfig); await pinus.app.rpc.guild.guildRemote.setServerGroup.broadcast(); await pinus.app.rpc.chat.chatRemote.setServerGroup.broadcast(); await pinus.app.rpc.connector.connectorRemote.setServerGroup.broadcast(); await pinus.app.rpc.gm.gmRemote.setServerGroup.broadcast(); await pinus.app.rpc.systimer.systimerRemote.setServerGroup.broadcast(); await pinus.app.rpc.battle.battleRemote.setServerGroup.broadcast(); } export async function setPvpSeasonNum(pvpConfig?: PVPConfigType) { if(!pvpConfig) { pvpConfig = await PVPConfigModel.findCurPVPConfig(); } let now = nowSeconds(); pinus.app.set('pvpSeasonNum', pvpConfig?.seasonNum||0); pinus.app.set('pvpSeasonStartTime', pvpConfig?.seasonStartTime||0); pinus.app.set('pvpSeasonEndTime', pvpConfig?.seasonEndTime||0); pinus.app.set('pvpSeasonRewardTime', pvpConfig?.seasonRewardTime||0); if(!pvpConfig || pvpConfig.seasonEndTime <= now) { // 赛季结束,需要显示下一赛季的倒计时 let nextPvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig? pvpConfig.seasonNum + 1: 1); if(nextPvpConfig) { pinus.app.set('pvpSeasonStartTime', nextPvpConfig.seasonStartTime); } } } export async function setPvpSettleSeasonNumToRemote(settledPvpConfig?: PVPConfigType) { setPvpSettleSeasonNum(settledPvpConfig); await pinus.app.rpc.battle.battleRemote.setPvpSettleSeasonNum.broadcast(settledPvpConfig); await pinus.app.rpc.role.roleRemote.setPvpSettleSeasonNum.broadcast(settledPvpConfig); await pinus.app.rpc.connector.connectorRemote.setPvpSettleSeasonNum.broadcast(settledPvpConfig); } export async function setPvpSettleSeasonNum(settledPvpConfig?: PVPConfigType) { if(!settledPvpConfig) { settledPvpConfig = await PVPConfigModel.getSettledConfig(); if(!settledPvpConfig) return; } pinus.app.set('pvpSettleSeasonNum', settledPvpConfig.seasonNum); } 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 = await dispatch(redisClient(), 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); } pushClientMsg(SDK_PUSH_MSG_TYPE.GUILD_ACTIVITY_START); 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 = await dispatch(redisClient(), 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(); // 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() { scheduleJob('autoServer', '0 0 */1 * * ?', autoCreateServerSchedule) } // —————————————— 自动开服 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() { initSumSchedule(); scheduleJob('groupShopRefund', '0 30 5 * * ?', () => { refundGroupShop(); }); } let initSumJobs = []; export async function initSumSchedule() { let servers = await ServerlistModel.findByEnv(pinus.app.get('env')); let groupIds: number[] = []; for(let { id } of servers) { let groups = await ActivityGroupModel.findByServerId(id); for(let { groupId } of groups) { groupIds.push(groupId); } } let activities = await ActivityModel.findOpenActivityByType(groupIds, ACTIVITY_TYPE.GROUP_SHOP); let scheduleMap = new Map(); // 时间 => data for(let activity of activities) { let timers = await getGroupShopTimers(activity); for(let { time, itemId, sum } of timers) { if(time < Date.now()) continue; if(!scheduleMap.has(time)) { scheduleMap.set(time, []); } scheduleMap.get(time).push({ activityId: activity.activityId, itemId, sum }); } } console.log('#### initSumJobs', initSumJobs.length); console.log('#### scheduleMap', scheduleMap); for(let job of initSumJobs) { job.cancel(); } for(let [time, arr] of scheduleMap) { if(scheduledJobs[`groupShopSetSum${time}`]) { scheduledJobs[`groupShopSetSum${time}`].cancel(); } let job = scheduleJob(`groupShopSetSum${time}`, time, async () => { await setGroupShopToSetSum(arr); }); initSumJobs.push(job); } } // —————————————— 团购定时器 end —————————————— // // —————————————— 配表兼容 start —————————————— // let hiddenDataJob: Job; export async function initHiddenData(data?: HiddenDataModelType, nextData?: HiddenDataModelType) { let now = nowSeconds(); if(!data) data = await HiddenDataModel.findCurData(now); if(!nextData) nextData = await HiddenDataModel.findNextData(now); setHiddenData(data?.heroes, data?.goods, nextData?.refTime); } export async function initHiddenDataSchedule(isInit: boolean, data?: HiddenDataModelType) { if(hiddenDataJob) hiddenDataJob.cancel(); let now = nowSeconds(); if(!data) data = await HiddenDataModel.findCurData(now); let nextData = await HiddenDataModel.findNextData(data?.refTime??now); if(isInit) { await initHiddenData(data, nextData) } else { await setHiddenDataToMemory(data, nextData); } console.log('######## initHiddenDataSchedule', nextData); if(nextData) { scheduleJob(`hiddenData`, nextData.refTime * 1000, async () => { console.log('######## hiddenDataSchedule', nextData); await initHiddenDataSchedule(false, nextData); }); } } // —————————————— 配表兼容 end —————————————— // // —————————————— gvg start —————————————— // let gvgBattleSecondJob: Job; // gvg每5秒的定时器 let gvgBattleCatapultJob: Job; // gvg投石车定时器 export async function initGVGConfigSchedule() { let config = await GVGConfigModel.findConfig(); if(!config || nowSeconds() > config.scheduleTime) { config = await createNewGVGConfig(); } if(scheduledJobs[`gvgTeam`]) scheduledJobs[`gvgTeam`].cancel(); scheduleJob(`gvgTeam`, config.teamTime * 1000, async () => { await pinus.app.rpc.guild.guildRemote.setGVGConfig.broadcast(config); }) if(scheduledJobs[`gvgConfig`]) scheduledJobs[`gvgConfig`].cancel(); scheduleJob(`gvgConfig`, config.scheduleTime * 1000, createNewGVGConfig); if(nowSeconds() > config.prepareTime) { initLeaguePrepare(); } else { if(scheduledJobs[`gvgPrepare`]) scheduledJobs[`gvgPrepare`].cancel(); scheduleJob(`gvgPrepare`, config.prepareTime * 1000, initLeaguePrepare); } if(scheduledJobs[`gvgFight`]) scheduledJobs[`gvgFight`].cancel(); scheduleJob(`gvgFight`, '0 0 22 * * ?', saveVestigeRankSchedule); if(scheduledJobs[`gvgBattle`]) scheduledJobs[`gvgBattle`].cancel(); scheduleJob(`gvgBattle`, config.battleTime * 1000, gvgBattlePeriodSchedule); let { startFightTime, endFightTime } = getFightTimeByPeriod(GVG_PERIOD.BATTLE, config.battleTime); if(nowSeconds() > startFightTime - GVG.GVG_GUARD_START_TIME && nowSeconds() < endFightTime) { if(gvgBattleSecondJob) gvgBattleSecondJob.cancel(); gvgBattleSecondJob = scheduleJob('gvgBattleSecond', '*/5 * * * * *', gvgBattleSecondSchedule); if(gvgBattleCatapultJob) gvgBattleCatapultJob.cancel(); gvgBattleCatapultJob = scheduleJob('gvgBattleCatapult', `*/${GVG.GVG_CATAPULT_TIME} * * * * *`, gvgBattleCatapult); } else { scheduleJob(`gvgBattleStartSchedule`, (startFightTime - GVG.GVG_GUARD_START_TIME) * 1000, gvgBattleStartSchedule); } if(scheduledJobs[`gvgBattleEndSchedule`]) scheduledJobs[`gvgBattleEndSchedule`].cancel(); scheduleJob(`gvgBattleEndSchedule`, endFightTime * 1000, gvgBattleEndSchedule); } // gvg激战期开始定时器 export async function gvgBattleStartSchedule() { console.log('##### gvgBattleStartSchedule', Date.now()) if(gvgBattleSecondJob) gvgBattleSecondJob.cancel(); gvgBattleSecondJob = scheduleJob('gvgBattleSecond', '*/5 * * * * *', gvgBattleSecondSchedule); // 初始化投石车 let guildServers = pinus.app.getServersByType('guild'); if(guildServers.length > 0) { pinus.app.rpc.guild.guildRemote.gvgBattleStart.toServer(getRandSingleEelm(guildServers).id); } if(gvgBattleCatapultJob) gvgBattleCatapultJob.cancel(); gvgBattleCatapultJob = scheduleJob('gvgBattleCatapult', `*/${GVG.GVG_CATAPULT_TIME} * * * * *`, gvgBattleCatapult); setTimeout(() => { pushClientMsg(SDK_PUSH_MSG_TYPE.GVG_BATTLE_START); }, GVG.GVG_GUARD_START_TIME * 1000); } // 每隔5秒的积分计算定时器 async function gvgBattleSecondSchedule() { console.log('********** gvgBattleSecondJob *************'); pinus.app.rpc.guild.guildRemote.gvgBattleSeconds.broadcast(); } // 每隔10秒的投石车投机定时器 async function gvgBattleCatapult() { console.log('********** gvgBattleCatapultJob *************'); pinus.app.rpc.guild.guildRemote.catapultHurt.broadcast(); } // gvg激战期结束定时器 export async function gvgBattleEndSchedule() { let guildServers = pinus.app.getServersByType('guild'); if(guildServers.length > 0) { pinus.app.rpc.guild.guildRemote.gvgBattleEnd.toServer(getRandSingleEelm(guildServers).id); } // 定时器关闭 if(gvgBattleSecondJob) { gvgBattleSecondJob.cancel(); gvgBattleSecondJob = undefined; } if(gvgBattleCatapultJob) { gvgBattleCatapultJob.cancel(); gvgBattleCatapultJob = undefined; } } // —————————————— gvg end —————————————— // async function initPushMsgSchedule() { scheduleJob('sendDinner', '0 0 19 * * ?', async () => { // pushClientMsg(SDK_PUSH_MSG_TYPE.AFK_ATTENTION); pushClientMsg(SDK_PUSH_MSG_TYPE.AP_DINNER); }); scheduleJob('sendLunch', '0 0 12 * * ?', async () => { pushClientMsg(SDK_PUSH_MSG_TYPE.AP_LUNCH); }); } // 月基金未领取的发送到邮件,每月发送 async function initMonthlyFundSchedule() { scheduleJob('initMonthlyFundSchedule', '0 0 5 1 * ?', async () => { let roundIndex = parseInt(moment().add(-1, 'M').format('YYYYMM')); await monthlyFundSchedule(roundIndex); }); } export async function monthlyFundSchedule(roundIndex: number) { let activityMap = new Map(); let playerDatas = await ActivityMonthlyFundModel.findNotReceivedReward(roundIndex); let _ids: string[] = []; for(let playerData of playerDatas) { if(!activityMap.has(playerData.activityId)) { let activity = await getActivityById(playerData.activityId); activityMap.set(playerData.activityId, activity); } let activityData = activityMap.get(playerData.activityId); if(!activityData) continue; let data = new MonthlyFundData(activityData, 0, 0); let page = data.findPage(playerData.pageIndex); page.setPlayerRecord(playerData); let rewards: RewardInter[] = []; for(let { reward, hasReceived } of page.rewards) { if(!hasReceived) rewards.push(...stringToRewardInter(reward)); } if(rewards.length > 0) { await sendMailByContent(MAIL_TYPE.MONTHLY_FUND, playerData.roleId, { goods: rewards }); } _ids.push(playerData._id); } await ActivityMonthlyFundModel.updateHasReceivedAll(_ids); }