Files
ZYZ/game-server/app/services/redisService.ts
2021-02-01 17:10:14 +08:00

381 lines
14 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 { GOOD_QUALITY, REDIS_RANK_TO_INFO, COM_BTL_QUALITY } from './../consts';
import { RoleModel, RoleType } from "../db/Role";
import * as Redis from 'redis';
import {REDIS_KEY} from '../consts'
import { GameModel } from "../db/Game";
import { promisifyAll } from 'bluebird';
import { pinus } from 'pinus';
import { PvpDefenseModel } from '../db/PvpDefense';
import { SystemConfigModel } from '../db/SystemConfig';
import { GuildRankParam, GuildLeader, RankParam } from '../domain/rank';
import { GuildModel } from '../db/Guild';
import { comBtlRanges } from '../pubUtils/gamedata';
/**
* 在服务重新启动时将信息存入redis
*/
export async function initAllRank() {
const client: Redis.RedisClient = pinus.app.get('redis');
const serverList = await GameModel.getAllServerList();
await client.delAsync(REDIS_KEY.USER_INFO);
await client.delAsync(REDIS_KEY.GUILD_INFO);
await client.delAsync(REDIS_KEY.PVP_RANK);
for(let {id} of serverList) {
await client.delAsync(getKeyName(REDIS_KEY.TOWER_RANK, id));
await client.delAsync(getKeyName(REDIS_KEY.GUILD_ACTIVE_RANK, id));
await initRank(id);
}
}
/**
* 分服内初始redis排行榜
*
* @param serverId 服务器
*/
export async function initRank(serverId: number) {
// console.log('*****', 'initRank')
const client: Redis.RedisClient = pinus.app.get('redis');
await client.expireAsync(getKeyName(REDIS_KEY.TOWER_RANK, serverId), 30 * 24 * 60 * 60);
await client.expireAsync(REDIS_KEY.PVP_RANK, 30 * 24 * 60 * 60);
await client.expireAsync(REDIS_KEY.USER_INFO, 30 * 24 * 60 * 60);
await client.expireAsync(REDIS_KEY.GUILD_INFO, 30 * 24 * 60 * 60);
await client.expireAsync(getKeyName(REDIS_KEY.GUILD_ACTIVE_RANK, serverId), 30 * 24 * 60 * 60);
await setRankRedisFromDb(REDIS_KEY.TOWER_RANK, serverId);
await setRankRedisFromDb(REDIS_KEY.GUILD_ACTIVE_RANK, serverId);
}
/**
* 初始化某一个排行榜,如定时器内
* @param key redis内的key
*/
export async function initSingleRank(key: string) {
const serverList = await GameModel.getAllServerList();
for(let {id} of serverList) {
await initSingleRankWithServer(key, id);
}
}
/**
* 初始化某一服内的某一种排行榜
* @param key redis key
*/
export async function initSingleRankWithServer(key: string, serverId: number) {
const client: Redis.RedisClient = pinus.app.get('redis');
await client.delAsync(getKeyName(key, serverId));
await client.expireAsync(key, 30 * 24 * 60 * 60);
await setRankRedisFromDb(key, serverId);
}
/**
* 从数据库内获取排行榜存入redis
* @param type 排行榜类型
* @param serverId 分服
*/
async function setRankRedisFromDb(type: string, serverId: number) {
const client: Redis.RedisClient = pinus.app.get('redis');
if(type == REDIS_KEY.TOWER_RANK) {
let ranks = await RoleModel.getRank('tower', serverId, ['roleId', 'roleName', 'towerLv', 'lv', 'vLv', 'headHid', 'sHid', 'title', 'updatedAt']);
for(let {towerLv, roleId, roleName, lv, vLv, towerUpTime, headHid, sHid, title} of ranks) {
// console.log(roleId);
await client.zaddAsync(getKeyName(REDIS_KEY.TOWER_RANK, serverId), encodeScoreWithTime(towerLv, towerUpTime?towerUpTime.getTime():0), roleId);
let rankPram = new RankParam(roleName, lv, vLv, headHid, sHid, title);
await redisUserInfoAdd(REDIS_KEY.USER_INFO, roleId, rankPram);
}
} else if (type == REDIS_KEY.GUILD_ACTIVE_RANK) {
let ranks = await GuildModel.getRank(serverId);
for(let { code, icon, name, lv, leader, activeWeekly = 0, activeUpdateTime = 0 } of ranks) {
let _leader = <RoleType>leader;
let { roleName, title, sHid, headHid, lv: leaderLv } = _leader;
await client.zaddAsync(getKeyName(REDIS_KEY.GUILD_ACTIVE_RANK, serverId), encodeScoreWithTime(activeWeekly, activeUpdateTime * 1000 ), code);
let rankParam = new GuildRankParam(icon, name, lv, { roleName, title, sHid, headHid, lv: leaderLv });
await redisUserInfoAdd(REDIS_KEY.GUILD_INFO, code, rankParam);
}
}
}
// 排行榜是否存在
export async function existsRank(key: string, serverId: number) {
const client: Redis.RedisClient = pinus.app.get('redis');
const result = await client.existsAsync(getKeyName(key, serverId));
return result;
}
export function getKeyName(key: string, serverId?: number) {
if(serverId) {
return `${key}:${serverId}`;
} else {
return key;
}
}
// 更新玩家信息
export async function redisUserInfoUpdate(key: string, roleId: string, arr: Array<{field: string, value:(string|number|GuildLeader)}>) {
const client: Redis.RedisClient = pinus.app.get('redis');
let params = await client.hgetAsync(key, roleId);
if(params) {
let obj = JSON.parse(params);
for(let {field, value} of arr) {
obj[field] = value;
}
return await client.hsetAsync(key, roleId, JSON.stringify(obj));
}
}
// 添加玩家信息缓存
export async function redisUserInfoAdd(key: string, roleId: string, params: RankParam|GuildRankParam) {
const client: Redis.RedisClient = pinus.app.get('redis');
let value = JSON.stringify(params);
return await client.hsetAsync(key, roleId, value);
}
// 更新排行榜, 如pvp这样跨服的serverId传0
export async function setRank(key: string, serverId: number, myId: string, score: number, timestamp: number, params: RankParam|GuildRankParam, limit = 100) {
const client: Redis.RedisClient = pinus.app.get('redis');
// 更新分数
const _score = encodeScoreWithTime(score, timestamp);
await client.zaddAsync(getKeyName(key, serverId), _score, myId);
// 移除100名以外
await client.zremrangebyrankAsync(getKeyName(key, serverId), limit, 10000);
let infoKey = REDIS_RANK_TO_INFO.get(key);
// 如果没有信息,更新玩家信息
const hasCurUser = await client.hexistsAsync(infoKey, myId);
if(!hasCurUser) {
await redisUserInfoAdd(infoKey, myId, params);
}
return true;
}
// 获取排行榜
export async function getRank(key: string, serverId: number, roleId: string) {
const client: Redis.RedisClient = pinus.app.get('redis');
let ranks = [], myRank = null;
const rankFromDb = await client.zrevrangebyscoreAsync(getKeyName(key, serverId), '+inf', '-inf', "WITHSCORES", "LIMIT", 0, 100);
let infoKey = REDIS_RANK_TO_INFO.get(key);
for(let ii = 0; ii < rankFromDb.length; ii+=2) {
const _roleId = rankFromDb[ii];
const _score = decodeScoreWithTime(rankFromDb[ii + 1]);
const info = await client.hgetAsync(infoKey, _roleId);
const _userInfo = JSON.parse(info);
const tmp = {..._userInfo, num: _score, rank: Math.floor(ii/2)+1};
if(infoKey == REDIS_KEY.USER_INFO) {
tmp["roleId"] = _roleId;
} else if(infoKey == REDIS_KEY.GUILD_INFO) {
tmp["code"] = _roleId;
}
ranks.push(tmp);
if(roleId == _roleId) myRank = tmp;
}
return {ranks, myRank}
}
// 获取我的排名
export async function getMyRank(key: string, serverId: number, roleId: string) {
const client: Redis.RedisClient = pinus.app.get('redis');
let myRank = await client.zrevrankAsync(getKeyName(key, serverId), roleId);
return myRank + 1;
}
// 获取排名第几名的信息
export async function getFieldByRank(key: string, serverId: number, rank: number) {
const client: Redis.RedisClient = pinus.app.get('redis');
let myRank = await client.zrevrangeAsync(getKeyName(key, serverId), rank - 1, rank - 1);
return myRank;
}
// 有序排行综合时间和得分排序
function encodeScoreWithTime(score: number, timestamp: number): number {
// value = score * Math.power(10, 14) + max_time - timestamp
let timelen = 10;
let pow = Math.pow(10, timelen + 1);
return score * pow + pow - 1 - Math.floor(timestamp/1000)
}
function decodeScoreWithTime(num: string): number {
let timelen = 10;
let pow = Math.pow(10, timelen + 1);
let _num = parseInt(num);
return Math.floor(_num/pow);
}
// 从排行榜中移除
export async function removeFromRank(key: string, serverId: number, myId: string) {
const client: Redis.RedisClient = pinus.app.get('redis');
await client.zremAsync(getKeyName(key, serverId), myId);
return true;
}
/**************** 寻宝相关 start */
/**
* @description 拼接匹配分组的 key
* @param {number} quality
* @param {number} lvRange
*/
function getComTeamKey(quality: number, lvRange: number) {
return `${REDIS_KEY.COM_TEAM_SEARCH_PRE}:${quality}_${lvRange}`;
}
// 把寻宝的玩家信息存入 redis
export async function setTeamSearchReq(roleId: string, sid: string, qualityArr: Array<number>, lvRange: number) {
const client: Redis.RedisClient = pinus.app.get('redis');
let cmds = [];
qualityArr.forEach(quality => {
if (quality) {
cmds.push(['sadd', getComTeamKey(quality, lvRange), `${roleId}:${sid}`]);
}
});
const multiClient = client.multi(cmds) as Redis.Multi;
const newMulti = promisifyAll(multiClient) as Redis.Multi;
const res = await newMulti.execAsync();
console.log('setTeamSearchReq: ', res);
return res;
}
// 取出 0 - 2 个某品质的匹配中玩家,并在其它品质中删除此玩家信息
export async function rmRoleFromQueue(roleId: string, sid: string, qualityArr: Array<number>, lvRange: number) {
const client: Redis.RedisClient = pinus.app.get('redis');
let cmds = [];
for (let q of qualityArr) {
if (lvRange) {
cmds.push(['srem', getComTeamKey(q, lvRange), `${roleId}:${sid}`]);
} else {
for (let range of comBtlRanges()) {
cmds.push(['srem', getComTeamKey(q, range), `${roleId}:${sid}`]);
}
}
};
const multiClient = client.multi(cmds) as Redis.Multi;
const newMulti = promisifyAll(multiClient) as Redis.Multi;
await newMulti.execAsync();
}
export async function getTeamSearchByQuality(quality: number, lvRange: number) {
const client: Redis.RedisClient = pinus.app.get('redis');
// TODO: 操作不具有原子性
const userInfos = await client.srandmemberAsync(getComTeamKey(quality, lvRange), 2);
console.log('getTeamSearchByQuality: ' + userInfos);
if (!userInfos || !userInfos.length) return null;
let cmds = [];
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]});
COM_BTL_QUALITY.forEach((q) => {
cmds.push(['srem', getComTeamKey(q, lvRange), userInfo]);
})
const multiClient = client.multi(cmds) as Redis.Multi;
const newMulti = promisifyAll(multiClient) as Redis.Multi;
await newMulti.execAsync();
}
console.log('getTeamSearchByQuality res: ', res);
return res;
}
export async function checkRoleInQueue(roleId: string, sid: string, qualityArr: Array<number>, lvRange: number) {
const client: Redis.RedisClient = pinus.app.get('redis');
for (let quality of qualityArr) {
let res = await client.sismemberAsync(getComTeamKey(quality, lvRange), `${roleId}:${sid}`);
if (res) {
return true;
}
};
return false;
}
export async function clearComBtlQueue() {
const client: Redis.RedisClient = pinus.app.get('redis');
for (let q of COM_BTL_QUALITY) {
for (let lvRange of comBtlRanges()) {
await client.delAsync(getComTeamKey(q, lvRange));
}
}
}
export function setRedis(key: string, data: string) {
const client: Redis.RedisClient = pinus.app.get('redis');
client.setAsync(key, data);
}
export async function getRedis(key: string) {
const client: Redis.RedisClient = pinus.app.get('redis');
const str = await client.getAsync(key);
return str;
}
export async function delRedis(key: string) {
const client: Redis.RedisClient = pinus.app.get('redis');
await client.delAsync(key);
}
export async function isRoleOnline(roleId: string) {
let key = `login_roleId_${roleId}`;
let result = await getRedis(key);
return !!result;
}
export async function resetPvpRanks() {
const client: Redis.RedisClient = pinus.app.get('redis');
await client.delAsync(REDIS_KEY.PVP_RANK);
let { seasonNum } = await SystemConfigModel.findSystemConfig();
console.log('execute season resetPvpRanks seasonNum = ' + seasonNum);
let pvpRank = await PvpDefenseModel.getRank(seasonNum);
for(let {roleId, role: _role, score, updatedAt } of pvpRank) {
let role = <RoleType>_role;
if (!role) {
continue;
}
let { roleName, headHid, sHid, title, lv, vLv } = role;
await client.zaddAsync(getKeyName(REDIS_KEY.PVP_RANK), encodeScoreWithTime(score, updatedAt?updatedAt.getTime():0), roleId);
const hasCurUser = await client.hexistsAsync(REDIS_KEY.USER_INFO, roleId);
if(!hasCurUser) {
let rankPram = new RankParam(roleName, lv, vLv, headHid, sHid, title);
await redisUserInfoAdd(REDIS_KEY.USER_INFO, roleId, rankPram);
}
}
}
// 排行榜是否存在
export async function smembersAsync(key: string) {
const client: Redis.RedisClient = pinus.app.get('redis');
const result = await client.smembersAsync(key);
return result;
}
export async function saddAsync(key: string, values: Array<string>) {
const client: Redis.RedisClient = pinus.app.get('redis');
const result = await client.saddAsync(key, values);
return result;
}
export async function sismemberAsync(key: string, value: string) {
const client: Redis.RedisClient = pinus.app.get('redis');
const result = await client.sismemberAsync(key, value);
return result;
}
export async function delAsync(key:string) {
const client: Redis.RedisClient = pinus.app.get('redis');
await client.delAsync(key);
}
export async function sremAsync(key:string, member:string) {
const client: Redis.RedisClient = pinus.app.get('redis');
await client.sremAsync(key, member);
}
/**************** 寻宝相关 end */