Files
ZYZ/game-server/app/services/timeTaskService.ts
T
2023-04-26 09:54:55 +08:00

1018 lines
43 KiB
TypeScript

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<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() {
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<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(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<number, ActivityModelType>();
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);
}