Files
ZYZ/game-server/app/services/redisService.ts
2022-07-19 17:14:11 +08:00

626 lines
22 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 { 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, <RoleType>rank.role);
} else if(r.infoKey == REDIS_KEY.GUILD_INFO) {
await r.setRankWithGuildInfo(rank.guildCode, rank.score, rank.time||0, <GuildType>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<number>} 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);
}
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<string>) {
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<number>();
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 *******/