import { Channel, pinus } from "pinus"; import { CHANNEL_PREFIX, PUSH_BATCH, PUSH_INTERVAL, PUSH_ROUTE, SDK_PUSH_MSG_PLAYER_TYPE, SDK_PUSH_MSG_TYPE, SDK_PUSH_TARGET_TYPE, STATUS } from "../consts"; import { genCode, resResult } from "../pubUtils/util"; import { getCityChannelSid, getGuildChannelSid, getWorldChannelSid, groupRoomId, getGroupShopSid, getGVGAreaChannelSid, getGVGAreaTeamChannelSid, getGVGCityTeamChannelSid } from "./chatService"; import { getAllOnlineRoles, getAllServers, getRoleOnlineInfo } from "./redisService"; import { errlogger, infologger } from '../util/logger'; import { MsgEncrypt } from "../pubUtils/sysUtil"; import { isSkipEncode, needPushMsg } from "../pubUtils/sdkUtil"; import { isDevelopEnv } from "./utilService"; import { gameData } from "../pubUtils/data"; import { pushMsg37 } from "./sdkService"; import { RoleModel, RoleType } from "../db/Role"; import { nowSeconds } from "../pubUtils/timeUtil"; import { GVGLeagueModel } from "../db/GVGLeague"; import { resolve } from "bluebird"; export async function sendMessageToAllWithSuc(route: string, data: any, filterCb?: ({ lv, topLineupCe }) => boolean) { await sendMessageToAll(route, resResult(STATUS.SUCCESS, data), filterCb); } export async function sendMessageToAll(route: string, data: any, filterCb?: ({ lv, topLineupCe }) => boolean) { let allOnlineUsers = await getAllOnlineRoles(); if(filterCb) { allOnlineUsers = allOnlineUsers.filter(filterCb); } let n = Math.ceil(allOnlineUsers.length / PUSH_BATCH); // 一共多少批 let sendToUser = (i: number) => { let users = allOnlineUsers.slice(i * PUSH_BATCH, (i + 1) * PUSH_BATCH - 1); let uids = users.map(cur => ({ uid: cur.roleId, sid: cur.sid })); sendMessageToUsers(route, data, uids); } let i = -1; sendToUser(++i); let interval = setInterval(() => { if (++i < n) { sendToUser(i); } else { clearInterval(interval); } }, PUSH_INTERVAL); } export async function sendMessageToAllServersWithSuc(route: string, data: any) { let servers = await getAllServers(); for(let serverId of servers) { await sendMessageToServerWithSuc(serverId, route, data); } } export async function sendMessageToServerWithSuc(serverId: number, route: string, data: any, isBatch = false) { await sendMessageToServer(serverId, route, resResult(STATUS.SUCCESS, data), isBatch); } export async function sendMessageToServer(serverId: number, route: string, data: any, isBatch = false) { let channelSid = await getWorldChannelSid(serverId); let roomId = groupRoomId(CHANNEL_PREFIX.WORLD, serverId); await pinus.app.rpc.chat.chatRemote.pushMessage.toServer(channelSid, roomId, route, data, isBatch); } export async function sendMessageToGuildWithSuc(guildCode: string, route: string, data: any) { await sendMessageToGuild(guildCode, route, resResult(STATUS.SUCCESS, data)); } export async function sendMessageToGuild(guildCode: string, route: string, data: any) { let channelSid = await getGuildChannelSid(guildCode); let roomId = groupRoomId(CHANNEL_PREFIX.GUILD, guildCode); await pinus.app.rpc.chat.chatRemote.pushMessage.toServer(channelSid, roomId, route, data); } export async function sendMessageToGVGAreaWithSuc(groupKey: string, areaId: number, route: string, data: any) { let channelSid = await getGVGAreaChannelSid(groupKey, areaId); let roomId = groupRoomId(CHANNEL_PREFIX.GVG_AREAS, `${groupKey}_${areaId}`); await pinus.app.rpc.chat.chatRemote.pushMessage.toServer(channelSid, roomId, route, resResult(STATUS.SUCCESS, data)); } export async function sendMessageToGVGAreaByTeamWithSuc(groupKey: string, areaId: number, route: string, data: any) { let channelSid = await getGVGAreaTeamChannelSid(groupKey, areaId); let roomId = groupRoomId(CHANNEL_PREFIX.GVG_AREA_BY_TEAM, `${groupKey}_${areaId}`); await pinus.app.rpc.chat.chatRemote.pushMessage.toServer(channelSid, roomId, route, resResult(STATUS.SUCCESS, data)); } export async function sendMessageToGVGCityWithSuc(groupKey: string, cityId: number, route: string, data: any) { let channelSid = await getGVGCityTeamChannelSid(groupKey, cityId); let roomId = groupRoomId(CHANNEL_PREFIX.GVG_CITY, `${groupKey}_${cityId}`); await pinus.app.rpc.chat.chatRemote.pushMessage.toServer(channelSid, roomId, route, resResult(STATUS.SUCCESS, data)); } export async function sendMessageToCityWithSuc(serverId: number, cityId: number, route: string, data: any) { await sendMessageToCity(serverId, cityId, route, resResult(STATUS.SUCCESS, data)); } export async function sendMessageToCity(serverId: number, cityId: number, route: string, data: any) { let channelSid = await getCityChannelSid(serverId, cityId); let roomId = groupRoomId(CHANNEL_PREFIX.CITY, `${serverId}_${cityId}`); await pinus.app.rpc.chat.chatRemote.pushMessage.toServer(channelSid, roomId, route, data); } export async function sendMessageToGroupShopWithSuc(route: string, data: any) { let channelSid = await getGroupShopSid(); let roomId = groupRoomId(CHANNEL_PREFIX.GROUP_SHOP); await pinus.app.rpc.chat.chatRemote.pushMessage.toServer(channelSid, roomId, route, resResult(STATUS.SUCCESS, data)); } export async function sendMessageToUserWithSuc(roleId: string, route: string, data: any, sid?: string) { await sendMessageToUser(roleId, route, resResult(STATUS.SUCCESS, data), sid); } export async function sendMessageToUser(roleId: string, route: string, data: any, sid?: string) { let uids = []; if (!sid) { let onlineUser = await getRoleOnlineInfo(roleId); sid = onlineUser.sid; } if (!!sid) { uids.push({ uid: roleId, sid }); sendMessageToUsers(route, data, uids); } } export async function sendMessageToUsersWithSuc(route: string, data: any, uids: { uid: string, sid: string }[]) { await sendMessageToUsers(route, resResult(STATUS.SUCCESS, data), uids); } // 推送给个人的方法收束于这个函数 export async function sendMessageToUsers(route: string, data: any, uids: { uid: string, sid: string }[]) { if(uids.length > 0) { pinus.app.get('channelService').pushMessageByUids(route, encryptMsg(route, data), uids); infologger.debug(`pushMessage route: ${route} data: ${JSON.stringify(data)} members: ${uids.map(obj => obj.uid).join()}`); } } export async function sendMessageToTeam(teamCode: string, route: string, data: any) { const channel = pinus.app.get('channelService').getChannel(teamCode); sendMessageToChannel(channel, route, resResult(STATUS.SUCCESS, data)); } /** * * @param user */ export function addUserToTeamChannel(teamCode: string, isCreate: boolean, roleId: string, sid: string) { let channel = pinus.app.get('channelService').getChannel(teamCode, isCreate); if(channel) addUserToChannel(channel, roleId, sid); return channel } export function addUserToChannel(channel: Channel, roleId: string, sid: string) { let member = channel.getMember(roleId); if(member && member.sid != sid) channel.removeMember(roleId); const users = channel.getMembers(); if (users.indexOf(roleId) === -1) { channel.add(roleId, sid); } } export function sendMessgeToChannelByBatch(channel: Channel, route: string, data: any) { let members = channel.getMembers(); if(members.length > PUSH_BATCH) { let n = Math.ceil(members.length / PUSH_BATCH); // 一共多少批 // console.log(n) let i = -1; let interval = setInterval(() => { if (++i < n) { let uidlist = members.slice(i * PUSH_BATCH, (i + 1) * PUSH_BATCH - 1); let uids: { uid: string, sid: string }[] = []; for (let uid of uidlist) { uids.push(channel.getMember(uid)); } sendMessageToUsers(route, data, uids); } else { clearInterval(interval); } }, PUSH_INTERVAL); } else { sendMessageToChannel(channel, route, data); } } // 抽下下来pushMessage收束 export function sendMessageToChannel(channel: Channel, route: string, data: any) { if(channel.getMembers().length <= 0) return; channel.pushMessage(route, encryptMsg(route, data)); infologger.debug(`pushMessage route: ${route} data: ${JSON.stringify(data)} members: ${channel.getMembers().join()}`); } export function delTeamChannel(teamCode: string) { let channel = pinus.app.get('channelService').getChannel(teamCode); if(!!channel) channel.destroy(); } export function removeFromTeamChannel(teamCode: string, roleId: string) { let channel = pinus.app.get('channelService').getChannel(teamCode); if(!channel) return null; let users: string[] = channel.getMembers(); if (users.indexOf(roleId) !== -1) { channel.removeMember(roleId); } } function getKvFromMemory() { let aesKey = pinus.app.get('aesKey'); let aesIV = pinus.app.get('aesIV'); let originK = pinus.app.get('originK'); let originV = pinus.app.get('originV'); let kvRefTime = pinus.app.get('kvRefTime'); let now = new Date().setMinutes(0, 0, 0); if(!kvRefTime || now - kvRefTime >= 60 * 60 * 1000) { originK = genCode(24); originV = genCode(16); let msgEncrypt = new MsgEncrypt({ k: originK, v: originV }); ({ aesKey, aesIV } = msgEncrypt.getEncodeKv()); setKvToRemote(originK, originV, aesKey, aesIV, now); } return { aesKey, aesIV, originK, originV, now } } function setKvToRemote(originK: string, originV: string, aesKey: string, aesIV: string, now: number) { pinus.app.rpc.activity.activityRemote.setKvToMemory.broadcast(originK, originV, aesKey, aesIV, now); pinus.app.rpc.battle.battleRemote.setKvToMemory.broadcast(originK, originV, aesKey, aesIV, now); pinus.app.rpc.chat.chatRemote.setKvToMemory.broadcast(originK, originV, aesKey, aesIV, now); pinus.app.rpc.connector.connectorRemote.setKvToMemory.broadcast(originK, originV, aesKey, aesIV, now); pinus.app.rpc.guild.guildRemote.setKvToMemory.broadcast(originK, originV, aesKey, aesIV, now); pinus.app.rpc.order.orderRemote.setKvToMemory.broadcast(originK, originV, aesKey, aesIV, now); pinus.app.rpc.role.roleRemote.setKvToMemory.broadcast(originK, originV, aesKey, aesIV, now); pinus.app.rpc.systimer.systimerRemote.setKvToMemory.broadcast(originK, originV, aesKey, aesIV, now); pinus.app.rpc.comBattle.comBattleRemote.setKvToMemory.broadcast(originK, originV, aesKey, aesIV, now); } export function setKvToMemory(originK: string, originV: string, aesKey: string, aesIV: string, now: number) { pinus.app.set('originK', originK); pinus.app.set('originV', originV); pinus.app.set('aesKey', aesKey); pinus.app.set('aesIV', aesIV); pinus.app.set('kvRefTime', now); } function encryptMsg(event: string, json: any) { if(checkNotEncryptRoute(event)) return json let { aesKey, aesIV, originK, originV } = getKvFromMemory(); let msgEncrypt = new MsgEncrypt({ originK, originV }); let data = msgEncrypt.encryptMsg(json); return { data, k: aesKey, v: aesIV } } function checkNotEncryptRoute(event: string) { return isSkipEncode(isDevelopEnv()) || [ PUSH_ROUTE.PUSH_CURRENT_TIME, // onPushCurrentTime 推送时间 PUSH_ROUTE.RACE_START, // onRaceStart 粮草先行开始 PUSH_ROUTE.GUILD_ACTIVITY_END, // onGuildActivityEnd 军团活动结束 PUSH_ROUTE.GUILD_RACE_UPDATE, // onRaceHorseUpdate 粮草先行状态 PUSH_ROUTE.GUILD_RACE_EVENT, // onRaceEventUpdate 更新木马事件 PUSH_ROUTE.TEAMMATE_ACT, // onTeammateAct 寻宝队友行动 PUSH_ROUTE.CITY_ACT_RANK, // onGuildCityRankUpdate 诸侯混战排行榜 PUSH_ROUTE.GUILD_CITY_ACT_HP, // onGuildCityGateHpUpdate 诸侯混战城门血条 PUSH_ROUTE.GUILD_GATE_ACT_HP, // onGuildGateHpUpdate 蛮夷入侵城门血条 PUSH_ROUTE.GATE_ACT_RANK, // onGuildGateRankUpdate 蛮夷入侵排行榜 PUSH_ROUTE.BOSS_HP_UPDATE, // onBossHpUpdate 演武台更新 PUSH_ROUTE.DIVIDEND_UPDATE, // onDividendsUpdate 拍卖行分红更新 PUSH_ROUTE.AUCTION_OVER, // onAuctionOver 拍卖价格超过 PUSH_ROUTE.GUILD_BOSS_ENCOURAGE, // onGuildBossEncourage 鼓舞 PUSH_ROUTE.GVG_TEAM_ATTACKED, // onTeamAttacked 当队伍受到攻击 PUSH_ROUTE.GVG_MY_TEAM_ATTACKED, // onMyTeamAttacked 当队伍受到攻击 PUSH_ROUTE.GVG_AREA_SPINE_CHANGE, // onAreaSpinesChange 可见区域内spine的变动,每隔5秒会下发 PUSH_ROUTE.GVG_AREA_POINT_CHANGE, // onMyAreaPointChange 积分点上的驻守人变更 PUSH_ROUTE.GVG_PLAYER_AREA_ADD, // onPlayerAddToArea 积分点上的驻守人变更 PUSH_ROUTE.GVG_PLAYER_LEAVE_AREA, // onPlayerLeaveArea 积分点上的驻守人变更 PUSH_ROUTE.GVG_CITY_RANK_UPDATE, // onGVGCityRankUpdate 城池积分排名 PUSH_ROUTE.GVG_SPINE_ATTACKED, // onSpinesAttacked 队伍在地图上受到攻击 ].indexOf(event) != -1 } /** * 推送客户端下拉消息 * @param type SDK_PUSH_MSG_TYPE * @returns */ export async function pushClientMsg(type: SDK_PUSH_MSG_TYPE) { let dicPushMessage = gameData.dicPushMessage.get(type); if(!dicPushMessage) return; let targetObj = await getPushTarget(dicPushMessage.playerType); if(!targetObj) return; if(!needPushMsg(pinus.app.get('env'))) return; let { target, audiences } = targetObj; let t = Date.now(); for(let i = 0; i < audiences.length; i++) { let audience = audiences[i]; // console.log(target, audience) if(audience || target == SDK_PUSH_TARGET_TYPE.ALL ) { await timeoutAsync(i * 10) await pushMsg37(t.toString(), dicPushMessage, target, audience); } } } function timeoutAsync(time: number) { return new Promise((resolve) => { setTimeout(() => { resolve(); }, time); }) } // 只推送all async function getPushTarget(playerType: number): Promise<{ target: SDK_PUSH_TARGET_TYPE, audiences: string[] }> { return { target: SDK_PUSH_TARGET_TYPE.ALL, audiences: [''] } // let uids: number[] = []; // switch(playerType) { // case SDK_PUSH_MSG_PLAYER_TYPE.HAS_GUILD: // { uids = await getHasGuildPlayers(); break; } // case SDK_PUSH_MSG_PLAYER_TYPE.HAS_LEAGUE: // { uids = await getHasLeaguePlayers(); break; } // case SDK_PUSH_MSG_PLAYER_TYPE.AFK: // { uids = await getAfkPlayers(); break; } // case SDK_PUSH_MSG_PLAYER_TYPE.ACTIVE_PLAYER: // { uids = await getActivePlayers(); break; } // } // let len = uids.length; // if(len == 0) return null; // if(len == 1) return { target: SDK_PUSH_TARGET_TYPE.SINGLE, audiences: [uids.join()] }; // let audiences: string[] = []; // for(let i = 0; i < Math.ceil(len/200); i++) { // audiences.push(uids.slice(i * 200, i * 200 + 200).join()) // } // return { target: SDK_PUSH_TARGET_TYPE.LIST, audiences }; } // 有军团且24小时内登录过但当前未在线的玩家 async function getHasGuildPlayers() { let uids: number[] = [], players: RoleType[] = []; let createdAt: Date; while(!createdAt || players.length > 0) { players = await RoleModel.findHasGuildPlayers(createdAt); if(players.length == 0) break; createdAt = players[players.length -1].createdAt; players.forEach(player => { if(player.userInfo && player.userInfo.channelInfo && player.quitTime != player.loginTime && !player.isClosePush) uids.push(player.userInfo.channelInfo.uid); }); } return uids; } // 有联军且48小时内登录过但当前未在线的玩家 async function getHasLeaguePlayers() { let leagues = await GVGLeagueModel.findActiveLeagueMembers(); let uids: number[] = [], roleIds: string[] = []; for(let { members } of leagues) roleIds.push(...members.map(member => member.roleId)); let len = roleIds.length; for(let i = 0; i < Math.ceil(len/1000); i++) { let curRoleIds = roleIds.slice(i * 1000, i * 1000 + 1000); let players = await RoleModel.findByRoleIds(curRoleIds, 'userInfo.channelInfo isClosePush'); players.forEach(player => { if(player.userInfo && player.userInfo.channelInfo && player.quitTime != player.loginTime && !player.isClosePush) uids.push(player.userInfo.channelInfo.uid); }); } return uids; } // 至少48小时未上线且等级>=20级的玩家 async function getAfkPlayers() { let uids: number[] = [], players: RoleType[] = []; let createdAt: Date; while(!createdAt || players.length > 0) { players = await RoleModel.findAfkPlayers(createdAt); if(players.length == 0) break; createdAt = players[players.length -1].createdAt; players.forEach(player => { if(player.userInfo && player.userInfo.channelInfo && player.quitTime != player.loginTime && !player.isClosePush) uids.push(player.userInfo.channelInfo.uid); }); } return uids; } // 24小时内登录过但当前未在线的玩家 async function getActivePlayers() { let uids: number[] = [], players: RoleType[] = []; let createdAt: Date; while(!createdAt || players.length > 0) { players = await RoleModel.findActivePlayers(createdAt); if(players.length == 0) break; createdAt = players[players.length -1].createdAt; players.forEach(player => { if(player.userInfo && player.userInfo.channelInfo && player.quitTime != player.loginTime) uids.push(player.userInfo.channelInfo.uid); }); } return uids; }