diff --git a/game-server/app/servers/gm/handler/gmHandler.ts b/game-server/app/servers/gm/handler/gmHandler.ts index 69e289170..a8e322af0 100644 --- a/game-server/app/servers/gm/handler/gmHandler.ts +++ b/game-server/app/servers/gm/handler/gmHandler.ts @@ -11,6 +11,7 @@ import { dispatch } from '../../../util/dispatcher'; import { SendMailFun } from '../../../services/mailService'; import { GM_MAIL_TYPE } from '../../../consts'; import { RewardInter } from '../../../pubUtils/interface'; +import { MarqueeModel } from '../../../db/Marquee'; export default function(app: Application) { return new GmHandler(app); } @@ -154,4 +155,21 @@ export class GmHandler { }); } } + + async createMarquee(msg: { startTime: string, endTime: string, interval: number, content: string }, session: BackendSession) { + const { startTime, endTime, interval, content } = msg; + let marquee = await MarqueeModel.createData({ serverIds: [1], type: 2, startTime: new Date(startTime), endTime: new Date(endTime), interval, content }); + return resResult(STATUS.SUCCESS, { + code: marquee.code + }); + } + + async sendMarquee(msg: { code: string }, session: BackendSession) { + const { code } = msg; + let systimerServers = this.app.getServersByType('systimer'); + for(let { id } of systimerServers) { + await this.app.rpc.systimer.systimerRemote.setMarquee.toServer(id, code); + } + return resResult(STATUS.SUCCESS); + } } \ No newline at end of file diff --git a/game-server/app/servers/systimer/remote/systimerRemote.ts b/game-server/app/servers/systimer/remote/systimerRemote.ts index de7b7010c..166dd783e 100644 --- a/game-server/app/servers/systimer/remote/systimerRemote.ts +++ b/game-server/app/servers/systimer/remote/systimerRemote.ts @@ -3,6 +3,8 @@ import { resetPvpSeasonTime, setPvpDefResult, guildActivityStart, gateActivityEn import PvpDefenseType from '../../../db/PvpDefense'; import { DicGuildActivity } from '../../../pubUtils/dictionary/DicGuildActivity'; import { reloadResources } from '../../../pubUtils/data'; +import { setMarquee } from '../../../services/marqueeService'; + export default function (app: Application) { return new SystimerRemote(app); } @@ -45,4 +47,12 @@ export class SystimerRemote { public async reloadResources() { reloadResources(); } + + /** + * @description 设置跑马灯配置 + * @param code 跑马灯唯一code + */ + public async setMarquee(code: string) { + await setMarquee(code); + } } diff --git a/game-server/app/services/marqueeService.ts b/game-server/app/services/marqueeService.ts new file mode 100644 index 000000000..c1d5d3135 --- /dev/null +++ b/game-server/app/services/marqueeService.ts @@ -0,0 +1,66 @@ +import { MarqueeType, MarqueeModel } from "../db/Marquee"; +import { MARQUEE_TYPE } from "../consts"; +import { scheduleJob } from 'node-schedule'; +import { createMarqueeMsg as sysCreateMarqueeMsg, pushMarqueeMsg as sysPushMarqueeMsg } from './sysChatService'; +import { GroupMessageType } from "../db/GroupMessage"; + +export async function initMarquee() { + const marquees = await MarqueeModel.findEffectiveMarque(); + for(let marquee of marquees) { + await generateMarqueeSchedule(marquee); + } +} + +export async function setMarquee(code: string) { + let marquee = await MarqueeModel.findByCode(code); + if(marquee.type == MARQUEE_TYPE.INSTANT) { + let msgDatas = await createMarqueeMsg(marquee); + await pushMarqueeMsg(msgDatas); + } else { + await generateMarqueeSchedule(marquee); + } +} + +async function generateMarqueeSchedule(marquee: MarqueeType) { + if(marquee.type != MARQUEE_TYPE.SCHEDULE) return false; + if(marquee.startTime.getTime() >= marquee.endTime.getTime()) return false + let startJob = scheduleJob(`start${marquee.code}`, marquee.startTime.getTime(), async () => { + console.log('************', `跑马灯定时器 ${marquee.code}开始`, '************'); + let msgDatas = await createMarqueeMsg(marquee); + await pushMarqueeMsg(msgDatas); + let secondsJob = scheduleJob(`setSeconds${marquee.code}`, `*/${marquee.interval} * * * * *`, async () => { + await pushMarqueeMsg(msgDatas); + }); + let endJob = scheduleJob(`end${marquee.code}`, marquee.endTime.getTime(), () => { + if(startJob) { + startJob.cancel(); + startJob = undefined; + } + if(secondsJob) { + secondsJob.cancel(); + secondsJob = undefined; + } + if(endJob) { + endJob.cancel(); + endJob = undefined; + } + }); + }); + return true; +} + +async function createMarqueeMsg(marquee: MarqueeType) { + let { serverIds, content } = marquee; + let msgDatas: GroupMessageType[] = []; + for(let serverId of serverIds) { + let msgData = await sysCreateMarqueeMsg('', '系统', serverId, content); + msgDatas.push(msgData); + } + return msgDatas; +} + +async function pushMarqueeMsg(msgDatas: GroupMessageType[]) { + for(let msgData of msgDatas) { + await sysPushMarqueeMsg(msgData); + } +} \ No newline at end of file diff --git a/game-server/app/services/sysChatService.ts b/game-server/app/services/sysChatService.ts index 741326f06..5137a7f10 100644 --- a/game-server/app/services/sysChatService.ts +++ b/game-server/app/services/sysChatService.ts @@ -10,6 +10,7 @@ import { pinus } from 'pinus'; import { resResult } from '../pubUtils/util'; import { BossInstanceType } from '../db/BossInstance'; import { getSimpleRoleInfo } from './roleService'; +import { GroupMessageType } from '../db/GroupMessage'; async function pushNormalHeroInfoBySource(roleId: string, roleName: string, serverId: number | string, source: number, heroInfo: Partial) { const hero = pick(heroInfo, ['hName', 'hid', 'seqId', 'quality', 'star', 'starStage', 'colorStar', 'colorStarStage']); @@ -140,6 +141,15 @@ export async function pushVestigeFirstMsg(roleId: string, roleName: string, serv return result; } +export async function createMarqueeMsg(roleId: string, roleName: string, serverId: number | string, content: string) { + const msgData = await createGroupMsg(roleId, roleName, CHANNEL_PREFIX.SYS, `${serverId}`, MSG_TYPE.RICH_TEXT, MSG_SOURCE.SEND_MARQUEE, content, null, null); + return msgData; +} + +export async function pushMarqueeMsg(msgData: GroupMessageType) { + await pushGroupMsgToRoom(msgData); +} + function shouldPushTowerMsg(lv: number) { // 100 层之后每 50 层触发 return lv >= 100 && lv % 50 === 0; diff --git a/game-server/app/services/timeTaskService.ts b/game-server/app/services/timeTaskService.ts index 6ad06b463..3ffacee5b 100644 --- a/game-server/app/services/timeTaskService.ts +++ b/game-server/app/services/timeTaskService.ts @@ -27,6 +27,7 @@ import { dispatch } from '../util/dispatcher'; import { Rank } from './rankService'; import { checkTask } from './taskService'; import { everydayRefresh } from './connectorService'; +import { initMarquee } from './marqueeService'; const PER_SECOND = 1 * 1000; const PER_DAY = 24 * 60 * 60; @@ -83,6 +84,9 @@ export async function init() { // 每天5点推送刷新时间消息 // 顺便每天0点计算前一天活跃玩家中位数战力 scheduleJob('everyDayRefresh', `0 0 ${REFRESH_TIME} * * ?`, everydayRefresh); + + // 跑马灯 + await initMarquee(); } function setPvpSeasonSchdule() { @@ -388,7 +392,7 @@ async function guildActivityStartSchedule() { } export async function guildActivityStart(dicGuildActivity?: DicGuildActivity) { - console.log('***********开始军团晚间活动****') + console.log('*******开始军团晚间活动****') if(!dicGuildActivity) dicGuildActivity = getTodayGuildActivity(); if(!dicGuildActivity) return; diff --git a/shared/consts/constModules/chatConst.ts b/shared/consts/constModules/chatConst.ts index 06a34ad25..57fe4e0fc 100644 --- a/shared/consts/constModules/chatConst.ts +++ b/shared/consts/constModules/chatConst.ts @@ -54,6 +54,7 @@ export const MSG_SOURCE = { EQUIP_REFRESH_BEST: 20, ACQUIRE_RARE_GOODS: 21, GROUP_SEND_GIFT: 22, + SEND_MARQUEE: 23 } export const ON_PRIVATE_MSG_ROUTE = 'onPrivateMessage'; diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index 50df5564f..5d3569750 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -668,3 +668,8 @@ export enum GACHA_CONTENT_TYPE { } export const GACHA_OCCUPY_HID = 9999; // 抽卡里占位的武将 + +export enum MARQUEE_TYPE { + INSTANT = 1, // 发送后立刻推送 + SCHEDULE = 2, // 定时器 +} \ No newline at end of file diff --git a/shared/db/Marquee.ts b/shared/db/Marquee.ts new file mode 100644 index 000000000..6e1e5c1d8 --- /dev/null +++ b/shared/db/Marquee.ts @@ -0,0 +1,72 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType, modelOptions } from '@typegoose/typegoose'; +import { MARQUEE_TYPE } from '../consts'; +import { genCode } from '../pubUtils/util'; +/** + * 跑马灯 +**/ +@modelOptions({ schemaOptions: { id: false } }) +export default class Marquee extends BaseModel { + @prop({ required: true }) + code: string; // 跑马灯唯一标识 + @prop({ required: true, type: Number }) + serverIds: number[]; // 推送服务器 + @prop({ required: true, enum: MARQUEE_TYPE }) + type: MARQUEE_TYPE; // 推送类型 + @prop({ required: true }) + startTime: Date; // 活动开始时间 + @prop({ required: true }) + endTime: Date; // 活动结束时间 + @prop({ required: true }) + interval: number; // 活动结束时间 + @prop({ required: true }) + content: string; // 广播内容 + + /** + * 创建跑马灯 + * @param params 参数 + * @param uid 后台操作人 + */ + public static async createData(params: MarqueeParam, uid = 1) { + const code = genCode(10); + const doc = new MarqueeModel(); + const update = Object.assign(doc.toJSON(), params, { code, createdBy: uid, updatedBy: uid }); + const result: MarqueeType = await MarqueeModel.findOneAndUpdate({ code }, { $setOnInsert: update }, { new: true, upsert: true }).lean(); + return result; + } + + /** + * 更新跑马灯 + * @param code 唯一标识 + * @param params 参数 + * @param uid 后台操作人 + */ + public static async updateData(code: string, params: MarqueeParam, uid = 1) { + const result: MarqueeType = await MarqueeModel.findOneAndUpdate({ code }, { $set: {...params, updatedBy: uid }}, { new: true }).lean(); + return result; + } + + + /** + * 根据唯一code查询一条数据 + * @param {String} code 唯一码 + */ + public static async findByCode(code: string) { + const result: MarqueeType = await MarqueeModel.findOne({ code }).lean(); + return result; + } + + /** + * 查询所有生效中的跑马灯 + */ + public static async findEffectiveMarque() { + const result: MarqueeType[] = await MarqueeModel.find({ type: MARQUEE_TYPE.SCHEDULE, startTime: { $lte: new Date() }, endTime: { $gte: new Date() } }).lean(); + return result; + } +} + + +export const MarqueeModel = getModelForClass(Marquee); + +export interface MarqueeType extends Pick, keyof Marquee> {}; +export type MarqueeParam = Partial;