Files
ZYZ/game-server/app/services/pushService.ts
T

391 lines
18 KiB
TypeScript

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 { GVGLeagueModel } from "../db/GVGLeague";
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 队伍在地图上受到攻击
PUSH_ROUTE.GVG_SPINE_AREA_ADD,
PUSH_ROUTE.GVG_SPINE_AREA_LEAVE,
PUSH_ROUTE.GVG_SPINE_POINT_CHANGE,
].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<void>((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;
}