Files
ZYZ/game-server/app/services/timeTaskService.ts

1019 lines
43 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 { 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';
import { setHiddenData } from './memoryCache/hiddenData';
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);
}