import { ACTIVITY_TYPE, getRedisKeyByRankType, GUILD_ACTIVITY_TYPE } from './../consts'; import * as Redis from 'redis'; import {REDIS_KEY} from '../consts' import { pinus, ServerInfo } from 'pinus'; import { GuildRankParam, GuildLeader, RankParam, LineupParam } from '../domain/rank'; import { comBtlRanges } from '../pubUtils/data'; import { Rank, setRankRedisFromDb } from './rankService'; import { ServerlistModel } from '../db/Serverlist'; import moment = require('moment'); import { getRedisSubChannel } from '../pubUtils/sdkUtil'; import { getRandSingleEelm } from '../pubUtils/util'; import { ActivityModel } from '../db/Activity'; import { TimeLimitRankData } from '../domain/activityField/timeLimitRankField'; import { ActivityTimeLimitRankModel } from '../db/ActivityTimeLimitRank'; import { RoleModel, RoleType } from '../db/Role'; import { GuildType } from '../db/Guild'; import { ActivityGroupModel } from '../db/ActivityGroup'; import { LadderMatchModel, LadderMatchType } from '../db/LadderMatch'; /** * 在服务重新启动时,将信息存入redis */ export async function initAllRank() { console.log('******* initAllRank ******') const serverList = await ServerlistModel.findByEnv(pinus.app.get('env')); await delKeys(REDIS_KEY.ONLINE_USERS); await delKeys(REDIS_KEY.USER_INFO); await delKeys(REDIS_KEY.GUILD_INFO); await delKeys(REDIS_KEY.TOP_LINEUP_INFO); await delKeys(REDIS_KEY.HERO_INFO); await delKeys(REDIS_KEY.TOWER_RANK); await delKeys(REDIS_KEY.GUILD_LV_RANK); await delKeys(REDIS_KEY.GUILD_ACTIVE_RANK); await delKeys(REDIS_KEY.TOP_LINEUP_RANK); await delKeys(REDIS_KEY.TOP_HERO_RANK); await delKeys(REDIS_KEY.HERO_NUM_RANK); await delKeys(REDIS_KEY.USER_LV); await delKeys(REDIS_KEY.SUM_CE_RANK); // await delKeys(REDIS_KEY.DUNGEON_RANK); await delKeys(REDIS_KEY.DUNGEON_LINEUP); await delKeys(REDIS_KEY.MAIN_RANK); await delKeys(REDIS_KEY.MAIN_ELITE_RANK); await delKeys(REDIS_KEY.HERO_RANK); await delKeys(REDIS_KEY.SHOW_LINEUP); // await delKeys(REDIS_KEY.PVP_RANK); await delKeys(REDIS_KEY.GUILD_FUND); await delKeys(REDIS_KEY.SUM_CE_SNAPSHOT); await delKeys(REDIS_KEY.LADDER); await setRankRedisFromDb(REDIS_KEY.PVP_RANK, {}); for(let {id} of serverList) { await initRank(id); await initActivitiesRank(id) } } /** * 分服内初始redis排行榜 * * @param serverId 服务器 */ export async function initRank(serverId: number) { // console.log('*****', 'initRank') await setRankRedisFromDb(REDIS_KEY.TOWER_RANK, { serverId }); await setRankRedisFromDb(REDIS_KEY.GUILD_ACTIVE_RANK, { serverId }); await setRankRedisFromDb(REDIS_KEY.GUILD_LV_RANK, { serverId }); await setRankRedisFromDb(REDIS_KEY.TOP_LINEUP_RANK, { serverId }); await setRankRedisFromDb(REDIS_KEY.TOP_HERO_RANK, { serverId }); await setRankRedisFromDb(REDIS_KEY.HERO_NUM_RANK, { serverId }); await setRankRedisFromDb(REDIS_KEY.USER_LV, { serverId }); await setRankRedisFromDb(REDIS_KEY.SUM_CE_RANK, { serverId }); // await setRankRedisFromDb(REDIS_KEY.DUNGEON_RANK, { serverId }); await setRankRedisFromDb(REDIS_KEY.MAIN_RANK, { serverId }); await setRankRedisFromDb(REDIS_KEY.MAIN_ELITE_RANK, { serverId }); await setRankRedisFromDb(REDIS_KEY.HERO_RANK, { serverId }); await setRankRedisFromDb(REDIS_KEY.LADDER, { serverId }); } export async function initActivitiesRank(serverId: number) { let serverTime = await getServerCreateTime(serverId); let activityGroup = await ActivityGroupModel.findByServerId(serverId); let activityGroupId = activityGroup.map(cur => cur.groupId); let activities = await ActivityModel.findOpenActivityByType(activityGroupId, ACTIVITY_TYPE.TIME_LIMIT_RANK); for(let activity of activities) { let data = new TimeLimitRankData(activity, 0, serverTime); if(data.beginTime <= Date.now() && data.endTime >= Date.now()) { let redisKey = getRedisKeyByRankType(data.rankType, true); let ranks = await ActivityTimeLimitRankModel.getRank(serverId, data.activityId); let r = new Rank(redisKey, { serverId, activityId: data.activityId }); r.setIsInit(true); for (let rank of ranks) { if(r.infoKey == REDIS_KEY.USER_INFO) { await r.setRankWithRoleInfo(rank.roleId, rank.score, rank.time||0, rank.role); } else if(r.infoKey == REDIS_KEY.GUILD_INFO) { await r.setRankWithGuildInfo(rank.guildCode, rank.score, rank.time||0, rank.guild); } } } } } /** * 初始化某一个排行榜,如定时器内 * @param key redis内的key */ export async function initSingleRank(key: string) { await delKeys(key); if(key == REDIS_KEY.PVP_RANK) { await setRankRedisFromDb(key, {}); } else { const serverList = await ServerlistModel.findByEnv(pinus.app.get('env')); for(let { id } of serverList) { await setRankRedisFromDb(key, { serverId: id }); } } } /** * debug接口使用,直接删除排行榜数据 * @param params serverId => guildCodes */ export async function delGuildActivityRank(aid: number) { if(aid == GUILD_ACTIVITY_TYPE.GATE_ACTIVITY) { await delKeys(REDIS_KEY.USER_GATE_ACTIVITY); await delKeys(REDIS_KEY.GATE_ACTIVITY); } else if (aid == GUILD_ACTIVITY_TYPE.CITY_ACTIVITY) { await delKeys(REDIS_KEY.USER_CITY_ACTIVITY); await delKeys(REDIS_KEY.CITY_ACTIVITY); } else if (aid == GUILD_ACTIVITY_TYPE.RACE_ACTIVITY) { await delKeys(REDIS_KEY.RACE_ACTIVITY); } } export async function delKeys(key: string) { let keys = await redisClient().keysAsync(`${key}*`); for(let key of keys) { await redisClient().delAsync(key); } return keys } export async function setUserInfo(key: string, roleId: string, params: RankParam|GuildRankParam|LineupParam[]) { let value = JSON.stringify(params); return await redisClient().hsetAsync(key, roleId, value); } export async function updateUserInfo(key: string, roleId: string, arr: Array<{field: string, value:(string|number|GuildLeader)}>) { let params = await redisClient().hgetAsync(key, roleId); if(params) { let obj = JSON.parse(params); for(let {field, value} of arr) { obj[field] = value; } return await redisClient().hsetAsync(key, roleId, JSON.stringify(obj)); } } /********排行榜结束 */ /**************** 寻宝相关 start */ /** * @description 拼接匹配分组的 key * @param {number} lv 藏宝图品阶 */ function getComTeamKey(lv: number) { return `${REDIS_KEY.COM_TEAM_SEARCH_PRE}:${lv}`; } /** * @description 拼接匹配分组中 member 的值 * @param {string} roleId * @param {string} sid connector serverId * @returns */ function getComTeamValue(roleId: string, sid: string) { return `${roleId}:${sid}`; } /** * @description 把寻宝的玩家信息存入 redis * @export * @param {string} roleId * @param {string} sid * @param {Array} qualityArr * @param {number} lvRange * @returns */ export async function setTeamSearchReq(roleId: string, sid: string, lvs: number[]) { let cmds = []; lvs.forEach(lv => { if (lv) { cmds.push(['sadd', getComTeamKey(lv), getComTeamValue(roleId, sid)]); } }); const res = await redisClient().multi(cmds).execAsync(); return res; } /** * @description 从匹配队列中删除某个用户 * @export * @param {string} roleId * @param {string} sid * @param {number} lv 如果不填表示所有品阶 */ export async function rmRoleFromQueue(roleId: string, sid: string, lvs?: number[]) { let cmds = []; if (lvs) { for(let lv of lvs) { cmds.push(['srem', getComTeamKey(lv), getComTeamValue(roleId, sid)]); } } else { for (let range of comBtlRanges()) { cmds.push(['srem', getComTeamKey(range), getComTeamValue(roleId, sid)]); } } await redisClient().multi(cmds).execAsync(); } /** * @description 在寻宝匹配队列中随机两个玩家 * @export * @param {number} quality 队伍品质 * @param {number} lvRange 等级范围 * @returns */ export async function getTeamSearchByLv(lv: number) { // TODO: 操作不具有原子性 const userInfos = await redisClient().srandmemberAsync(getComTeamKey(lv), 2); console.log('getTeamSearchByLv: ' + userInfos); if (!userInfos || !userInfos.length) return null; let res = []; for (let userInfo of userInfos) { const decodeData = `${userInfo}`.split(':'); if (decodeData.length !== 2) return null; res.push({roleId: decodeData[0], sid: decodeData[1]}); } console.log('getTeamSearchByLv res: ', res); return res; } /** * @description 检查玩家是否在某个队列中等待寻宝匹配 * @export * @param {string} roleId * @param {string} sid * @param {number} lv * @returns */ export async function checkRoleInQueue(roleId: string, sid: string, lvs: number[]) { for (let lv of lvs) { let res = await redisClient().sismemberAsync(getComTeamKey(lv), `${roleId}:${sid}`); if (res) { return true; } }; return false; } /** * @description 清除所有的寻宝匹配队列 * @export */ export async function clearComBtlQueue() { await delKeys(REDIS_KEY.COM_TEAM_SEARCH_PRE); } export function setRedis(key: string, data: string) { redisClient().setAsync(key, data); } export async function getRedis(key: string) { const str = await redisClient().getAsync(key); return str; } export async function delRedis(key: string) { await redisClient().delAsync(key); } export function redisSidKey(roleId: string) { return `login_roleId_${roleId}`; } export async function redisChannelServer(roomId: string) { const sid = await redisClient().hgetAsync(REDIS_KEY.CHANNEL_SERVERS, roomId); return sid; } export async function addRedisChannel(roomId: string, sid: string) { const result = await redisClient().hsetAsync(REDIS_KEY.CHANNEL_SERVERS, roomId, sid); return result; } export async function clearChannelServers() { const result = await redisClient().delAsync(REDIS_KEY.CHANNEL_SERVERS); return result; } /** * 玩家上线 * @param roleId role表id * @param userCode user表唯一字符串标识 * @param sid connector服的那个sid */ export async function roleLogin(roleId: string, userCode: string, sid: string, createTime: number, serverId: number, lv: number, topLineupCe: number) { await redisClient().hincrbyAsync(REDIS_KEY.ONLINE_CNT, roleId, 1); await redisClient().hsetAsync(REDIS_KEY.USER_CODE, userCode, roleId); return await redisClient().hsetAsync(REDIS_KEY.ONLINE_USERS, roleId, `${userCode}|${sid}|${createTime}|${serverId}|${lv}|${topLineupCe}`); } export async function updateRoleOnlineInfo(roleId: string, updateData: {lv?: number, topLineupCe?: number}) { let info = await getRoleOnlineInfo(roleId); if(info.isOnline) { return await redisClient().hsetAsync(REDIS_KEY.ONLINE_USERS, roleId, `${info.userCode}|${info.sid}|${info.createtime}|${info.serverId}|${updateData?.lv??info.lv}|${updateData?.topLineupCe??info.topLineupCe}`); } } /** * 玩家下线 * @param roleId role表id */ export async function roleLeave(roleId: string) { let role = await getRoleOnlineInfo(roleId); let count = await redisClient().hincrbyAsync(REDIS_KEY.ONLINE_CNT, roleId, -1); if(count <= 0) { await redisClient().hdelAsync(REDIS_KEY.ONLINE_CNT, roleId); await redisClient().hdelAsync(REDIS_KEY.ONLINE_USERS, roleId); await redisClient().hdelAsync(REDIS_KEY.USER_CODE, role.userCode); await redisClient().hdelAsync(REDIS_KEY.ONLINE_CNT, roleId); } return role; } /** * 判断玩家是否在线 * @param roleId role表id */ export async function isRoleOnline(roleId: string) { let result = await redisClient().hexistsAsync(REDIS_KEY.ONLINE_USERS, roleId); return !!result; } export async function getOnlineRoleByUserCode(userCode: string) { let roleId = await redisClient().hgetAsync(REDIS_KEY.USER_CODE, userCode); return roleId; } /** * 获得在线玩家userCode和sid * @param roleId */ export async function getRoleOnlineInfo(roleId: string) { let str = await redisClient().hgetAsync(REDIS_KEY.ONLINE_USERS, roleId); if(str) { try { let result = str.split('|'); let [userCode, sid, createTime, serverId, lv, topLineupCe] = result; return { isOnline: true, userCode, sid, createtime: parseInt(createTime), serverId: parseInt(serverId), lv: parseInt(lv), topLineupCe: parseInt(topLineupCe) } } catch(e) { return { isOnline: false } } } else { return { isOnline: false } } } export async function getRoleCreateTime(roleId: string, role?: RoleType) { if(role && role.createTime) return role.createTime; let onlineInfo = await getRoleOnlineInfo(roleId); if(onlineInfo.isOnline) { return onlineInfo.createtime; } else { let role = await RoleModel.findByRoleId(roleId); return role?.createTime||0; } } /** * 获得所有在线的玩家 */ export async function getAllOnlineRoles() { let allRoles = await redisClient().hgetallAsync(REDIS_KEY.ONLINE_USERS); let result = new Array<{roleId: string, userCode: string, sid: string, serverId: number, lv: number, topLineupCe: number}>(); for(let roleId in allRoles) { try{ let param = allRoles[roleId].split('|'); let [userCode, sid, _createTime, serverId, lv, topLineupCe] = param; result.push({ roleId, userCode, sid, serverId: parseInt(serverId), lv: parseInt(lv), topLineupCe: parseInt(topLineupCe) }); } catch(e) { continue; } } return result; } /** * 将玩家登录时间记录到 * @param userCode 玩家user表里的userCode * @param isToday 累计记录还是当日记录 * @returns 分钟 */ export async function incOnlineTime(userCode: string, isToday: boolean, incTime: number) { moment.locale('zh-cn'); let today = moment().format('YYYYMMDD'); let key = isToday? `${REDIS_KEY.ONLINE_TIME}:${today}`: REDIS_KEY.ONLINE_TIME; if(isToday) { let tomorrow = moment().add(1, 'days').hours(0).minutes(0).seconds(0).unix(); let result = await redisClient().hincrbyAsync(key, userCode, incTime); let result2 = await redisClient().expireatAsync(key, tomorrow); // console.log(result2) return result; } else { return await redisClient().hincrbyAsync(key, userCode, incTime); } } export async function setOnlineTime(userCode: string, isToday: boolean, setTime: number) { moment.locale('zh-cn'); let today = moment().format('YYYYMMDD'); let key = isToday? REDIS_KEY.ONLINE_TIME: `${REDIS_KEY.ONLINE_TIME}:${today}`; if(isToday) { let tomorrow = moment().add(1, 'days').hours(0).minutes(0).seconds(0).format('x'); await redisClient().expireatAsync(key, parseInt(tomorrow)); } return await redisClient().hsetAsync(key, userCode, setTime); } // 排行榜是否存在 export async function smembersAsync(key: string) { const result = await redisClient().smembersAsync(key); return result; } export async function saddAsync(key: string, values: Array) { const result = await redisClient().saddAsync(key, values); return result; } export async function sismemberAsync(key: string, value: string) { const result = await redisClient().sismemberAsync(key, value); return result; } export async function delAsync(key:string) { await redisClient().delAsync(key); } export async function sremAsync(key:string, member:string) { await redisClient().sremAsync(key, member); } /**************** 寻宝相关 end */ /**************** 数据库表存入缓存 */ export async function readDataBase() { await setServerList(); } async function setServerList() { const serverList = await ServerlistModel.getAllServerList(); await redisClient().delAsync(REDIS_KEY.SERVER); await redisClient().delAsync(REDIS_KEY.SERVER_OPEN_TIME); for(let { id, name, openTime } of serverList) { // console.log(roleId); await redisClient().hsetAsync(REDIS_KEY.SERVER, `${id}`, `${name}`); await redisClient().hsetAsync(REDIS_KEY.SERVER_OPEN_TIME, `${id}`, `${openTime}`); } } export async function getAllServers() { let servers = await redisClient().hgetallAsync(REDIS_KEY.SERVER); let serverlist = new Array(); for(let serverStr in servers) { let serverId = parseInt(serverStr); if(!isNaN(serverId) && !serverlist.includes(serverId)) { serverlist.push(serverId); } } return serverlist; } export async function getServerName(serverId: number) { let name = await redisClient().hgetAsync(REDIS_KEY.SERVER, `${serverId}`); return name||'常山少年' } export async function getServerCreateTime(serverId: number) { let time = await redisClient().hgetAsync(REDIS_KEY.SERVER_OPEN_TIME, `${serverId}`); return parseInt(time); } export function redisClient() { const client: Redis.RedisClient = pinus.app.get('redis'); return client; } export async function hasKey(key: string) { return await redisClient().existsAsync(key); } /**************** 数据库表end */ /**************** 名将擂台防守战力 */ export async function saveLadderDefCeByData(roleId: string, ladderMatch?: LadderMatchType) { if(!ladderMatch) { ladderMatch = await LadderMatchModel.findByRoleId(roleId); if(!ladderMatch) return; } let heroes = ladderMatch.defense?.heroes||[]; let defCe = heroes.reduce((pre, cur) => pre + cur.ce, 0); await saveLadderDefCe(roleId, defCe); } export async function saveLadderDefCe(roleId: string, defCe: number) { await redisClient().hsetAsync(REDIS_KEY.LADDER_DEFCE, `${roleId}`, defCe); } export async function getLadderDefCe(roleId: string) { let defCe = await redisClient().hgetAsync(REDIS_KEY.LADDER_DEFCE, `${roleId}`); return defCe||0; } /**************** 名将擂台防守战力end */ /*************** 将connector服插入redis *******/ export async function setConnectors(servers: ServerInfo[]) { for(let server of servers) { if(server.serverType == 'connector' && server.id != 'connector-server-gm') { let { serverType, clientHost, clientPort, id } = server; await redisClient().hsetAsync(REDIS_KEY.SYS_SERVER, server.id, JSON.stringify({ serverType, clientHost, clientPort, id })); } } } export async function removeConnectors(servers: string[]) { for(let server of servers) { let hasServer = await redisClient().hgetAsync(REDIS_KEY.SYS_SERVER, server); if(!!hasServer) await redisClient().hdelAsync(REDIS_KEY.SYS_SERVER, server); } } export async function checkConnectors() { let servers = pinus.app.getServersByType('connector'); let redisServers = await redisClient().hgetallAsync(REDIS_KEY.SYS_SERVER); for(let id in redisServers) { let server = servers.find(cur => cur.id == id); if(!server || server.id == 'connector-server-gm') { await redisClient().hdelAsync(REDIS_KEY.SYS_SERVER, id); } } } /*************** 将connector服插入redis *******/ /*************** 订阅短链接redis *******/ export function redisClientPub() { const client: Redis.RedisClient = pinus.app.get('redisPub'); return client; } export async function redisSubScribe() { let env = pinus.app.get('env'); if(env == 'development') env = 'local'; // 本地处理 let payChannel = getRedisSubChannel(REDIS_KEY.PAY_CHANNEL, env); let refundChannel = getRedisSubChannel(REDIS_KEY.REFUND_CHANNEL, env); let treatRoleChannel = getRedisSubChannel(REDIS_KEY.TREAT_ROLE_CHANNEL, env); let treatGuildChannel = getRedisSubChannel(REDIS_KEY.TREAT_GUILD_CHANNEL, env); let surveyChannel = getRedisSubChannel(REDIS_KEY.SURVEY_CHANNEL, env); let userChannel = getRedisSubChannel(REDIS_KEY.USER_CHANNEL, env); await redisClientPub().subscribeAsync(payChannel, treatRoleChannel, treatGuildChannel, refundChannel, surveyChannel, userChannel); redisClientPub().on('message', (channel, message) => { console.log('**********redisSubScribe*******') console.log('channel: ', channel, payChannel); if(channel == payChannel) { let servers = pinus.app.getServersByType('order'); let server = getRandSingleEelm(servers); pinus.app.rpc.order.orderRemote.settleOrderFromRedisPub.toServer(server.id, message); } else if (channel == treatRoleChannel) { let servers = pinus.app.getServersByType('role'); let server = getRandSingleEelm(servers); pinus.app.rpc.role.roleRemote.treatRoleName.toServer(server.id, message); } else if (channel == treatGuildChannel) { let servers = pinus.app.getServersByType('guild'); let server = getRandSingleEelm(servers); pinus.app.rpc.guild.guildRemote.treatGuildName.toServer(server.id, message); } else if (channel == refundChannel) { let servers = pinus.app.getServersByType('order'); let server = getRandSingleEelm(servers); pinus.app.rpc.order.orderRemote.refundOrderFromRedisPub.toServer(server.id, message); } else if (channel == surveyChannel) { let servers = pinus.app.getServersByType('role'); let server = getRandSingleEelm(servers); pinus.app.rpc.role.roleRemote.sendSurveyMail.toServer(server.id, message); } else if(channel == userChannel) { let [sid, roleId] = (message||'').split('|'); if(!!sid && !!roleId) pinus.app.rpc.connector.connectorRemote.remoteLogin.toServer(sid, roleId); } }); } /*************** 订阅短链接redis *******/