1086 lines
43 KiB
TypeScript
1086 lines
43 KiB
TypeScript
import { KeyName, KeyNameParam, RankParam, GuildRankParam, RoleRankInfo, GuildLeader, LineupParam, myIdInter, GeneralRankParam, ValueConfig, GuildRankInfo } from "../domain/rank";
|
||
import { REDIS_RANK_TO_INFO, ROLE_SELECT, GUILD_SELECT, REDIS_KEY, REDIS_RANK_TO_EXTRA, HERO_SELECT, COMPOSE_FIELD_TYPE, KEY_TO_COMPOSE_FIELD, RANK_TYPE_TO_KEY, COUNTER } from "../consts";
|
||
import { redisClient, delKeys } from "./redisService";
|
||
import { RoleType, RoleModel } from "../db/Role";
|
||
import { GuildType, GuildModel } from "../db/Guild";
|
||
import { HeroModel, HeroType, HeroUpdate } from "../db/Hero";
|
||
import { PVPConfigModel } from "../db/SystemConfig";
|
||
import { PvpDefenseModel } from "../db/PvpDefense";
|
||
import { gameData } from "../pubUtils/data";
|
||
import { nowSeconds } from "../pubUtils/timeUtil";
|
||
import { getWorldChannelSid } from "./chatChannelService";
|
||
import { pinus } from "pinus";
|
||
import { RankFirstModel, RankFirstType, RankFirstUpdateParam } from "../db/RankFirst";
|
||
import { getRandSingleEelm } from "../pubUtils/util";
|
||
import { RANK_FIRST_REWARD_STATUS } from '../consts';
|
||
import { CounterModel } from "../db/Counter";
|
||
|
||
|
||
/**
|
||
* @description 排行榜相关操作
|
||
* @export
|
||
* @class Rank
|
||
*/
|
||
export class Rank {
|
||
isInit: boolean = false; // 初始排行榜
|
||
key: REDIS_KEY; // 排行榜原始key
|
||
keyName: KeyName; // 拼接之后的key
|
||
infoKey: REDIS_KEY; // 玩家数据key
|
||
extraKeys: string[];
|
||
isUnion: boolean; // 是否使用多个zset联合计算
|
||
limit: number = 200; // 排行榜长度
|
||
unionRankLife: number = 10;
|
||
valueConfig: ValueConfig[] = []; // 值的组合方案
|
||
|
||
constructor(key: REDIS_KEY, keyParam: KeyNameParam, isUnion = false, limit = 200) {
|
||
this.key = key;
|
||
this.keyName = new KeyName(key, keyParam);
|
||
this.infoKey = REDIS_RANK_TO_INFO.get(key);
|
||
this.extraKeys = REDIS_RANK_TO_EXTRA.get(key) || [];
|
||
this.isUnion = isUnion;
|
||
this.limit = limit;
|
||
this.setValueConfig(key);
|
||
}
|
||
|
||
public setIsInit(init: boolean) {
|
||
this.isInit = init;
|
||
}
|
||
|
||
private setValueConfig(key: REDIS_KEY) {
|
||
switch (key) {
|
||
case REDIS_KEY.TOP_LINEUP_RANK:
|
||
case REDIS_KEY.TOP_HERO_RANK:
|
||
case REDIS_KEY.SUM_CE_RANK:
|
||
case REDIS_KEY.HERO_RANK:
|
||
this.valueConfig = [
|
||
new ValueConfig(true, 'score', 0, false, false)
|
||
];
|
||
break;
|
||
case REDIS_KEY.RACE_ACTIVITY:
|
||
this.valueConfig = [
|
||
new ValueConfig(true, 'score', 0, false, false),
|
||
new ValueConfig(false, 'time', 10, true, true)
|
||
];
|
||
break;
|
||
case REDIS_KEY.GUILD_LV_RANK:
|
||
this.valueConfig = [
|
||
new ValueConfig(true, 'lv', 0, false, false),
|
||
new ValueConfig(false, 'active', 5, false, false)
|
||
];
|
||
break;
|
||
default:
|
||
this.valueConfig = [
|
||
new ValueConfig(true, 'score', 0, false, false),
|
||
new ValueConfig(false, 'time', 10, true, true)
|
||
];
|
||
break;
|
||
}
|
||
|
||
for(let i = 0; i < this.valueConfig.length; i++) {
|
||
let weight = 0;
|
||
for(let j = i + 1; j < this.valueConfig.length; j++) {
|
||
weight = this.valueConfig[j].len;
|
||
}
|
||
this.valueConfig[i].setWeight(weight);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 返回字段修改
|
||
* @param obj
|
||
*/
|
||
private async generFields(obj: GuildRankInfo | RoleRankInfo) {
|
||
return obj;
|
||
}
|
||
|
||
/**
|
||
* 从外部设置返回字段
|
||
* @param cb
|
||
*/
|
||
public setGenerFieldsFun(cb: (obj: GuildRankInfo | RoleRankInfo) => any) {
|
||
this.generFields = cb;
|
||
}
|
||
|
||
/**
|
||
* 设置合成的排行榜的生命时长 单位s
|
||
* @param unionRankLife
|
||
*/
|
||
public setUnionRankLife(unionRankLife: number) {
|
||
this.unionRankLife = unionRankLife;
|
||
}
|
||
|
||
/**
|
||
* 该排行是否为空
|
||
* @param void
|
||
*/
|
||
public async existsRank() {
|
||
const result = await redisClient().existsAsync(this.keyName.getName());
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 根据role表设置更新排行榜&玩家信息
|
||
* @param roleId 玩家id
|
||
* @param score 得分
|
||
* @param timestamp 时间点
|
||
* @param role 玩家数据库数据
|
||
* @param isInc 得分是累加上的还是直接设置的
|
||
*/
|
||
public async setRankWithRoleInfo(roleId: string, score: number, timestamp: number, role?: RoleType, isInc = false) {
|
||
// 如果没有信息,更新玩家信息
|
||
for (let infoKey of [this.infoKey, ...this.extraKeys]) {
|
||
const hasCurUser = await redisClient().hexistsAsync(infoKey, roleId);
|
||
if (!hasCurUser) {
|
||
await this.generParamAndSet(infoKey, { roleId }, { role });
|
||
}
|
||
}
|
||
let newScore = await this.setRank({ roleId }, [score, timestamp], isInc);
|
||
|
||
return newScore;
|
||
}
|
||
|
||
/**
|
||
* 根据军团表设置更新排行榜&军团信息
|
||
* @param guildCode 军团code
|
||
* @param score 得分
|
||
* @param timestamp 时间点
|
||
* @param guild 军团数据库
|
||
* @param isInc 得分是累加上的还是直接设置的
|
||
*/
|
||
public async setRankWithGuildInfo(guildCode: string, score: number, timestamp: number, guild?: GuildType, isInc = false) {
|
||
return await this.setRankWithGuildInfoArrParam(guildCode, [score, timestamp], guild, isInc);
|
||
}
|
||
|
||
public async setRankWithGuildInfo2(guildCode: string, lv: number, active: number, timestamp: number, guild?: GuildType, isInc = false) {
|
||
return await this.setRankWithGuildInfoArrParam(guildCode, [lv, active, timestamp], guild, isInc);
|
||
}
|
||
|
||
public async setRankWithGuildInfoArrParam(guildCode: string, scores: number[], guild?: GuildType, isInc = false) {
|
||
for (let infoKey of [this.infoKey, ...this.extraKeys]) {
|
||
const hasCurUser = await redisClient().hexistsAsync(infoKey, guildCode);
|
||
if (!hasCurUser) {
|
||
await this.generParamAndSet(infoKey, { guildCode }, { guild });
|
||
}
|
||
}
|
||
let newScore = await this.setRank({ guildCode }, scores, isInc);
|
||
|
||
return newScore;
|
||
}
|
||
|
||
/**
|
||
* 根据武将表设置更新排行榜&玩家信息
|
||
* @param roleId 玩家id
|
||
* @param hid 武将id
|
||
* @param score 得分
|
||
* @param timestamp 时间点
|
||
* @param hero 武将数据库
|
||
* @param isInc 得分是累加上的还是直接设置的
|
||
*/
|
||
public async setRankWithHeroInfo(roleId: string, hid: number, score: number, timestamp: number, hero?: HeroType, isInc = true) {
|
||
// 如果没有信息,更新玩家信息
|
||
for (let infoKey of [this.infoKey, ...this.extraKeys]) {
|
||
await this.generParamAndSet(infoKey, { roleId, hid }, { hero });
|
||
}
|
||
let newScore = await this.setRank({ roleId, hid }, [score, timestamp], isInc);
|
||
|
||
return newScore;
|
||
}
|
||
|
||
/**
|
||
* 将玩家信息按顺序组成 如:roleId:hid
|
||
* @param {{ string }} key redis key
|
||
* @param {{ roleId?: string; guildCode?: string; hid?: number }} param 玩家信息
|
||
*/
|
||
private composeFields(key: REDIS_KEY, param: myIdInter) {
|
||
let type = KEY_TO_COMPOSE_FIELD.get(key);
|
||
|
||
if (type == COMPOSE_FIELD_TYPE.ROLE) {
|
||
return param.roleId;
|
||
} else if (type == COMPOSE_FIELD_TYPE.GUILD) {
|
||
return param.guildCode;
|
||
} else if (type == COMPOSE_FIELD_TYPE.ROLE_HERO) {
|
||
return `${param.roleId}:${param.hid}`;
|
||
} else {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 将合成了的玩家顺序解开
|
||
* @param key redis key
|
||
* @param field
|
||
* @returns {{ roleId?: string; guildCode?: string; hid?: number }}
|
||
*/
|
||
private decodeFields(key: REDIS_KEY, field: string) {
|
||
let type = KEY_TO_COMPOSE_FIELD.get(key);
|
||
|
||
let arr = field.split(':');
|
||
if (type == COMPOSE_FIELD_TYPE.ROLE) {
|
||
return { roleId: arr[0] };
|
||
} else if (type == COMPOSE_FIELD_TYPE.GUILD) {
|
||
return { guildCode: arr[0] };
|
||
} else if (type == COMPOSE_FIELD_TYPE.ROLE_HERO) {
|
||
return { roleId: arr[0], hid: parseInt(arr[1]) };
|
||
} else {
|
||
return {};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 将玩家数据设置进redis
|
||
* @param infoKey 玩家信息的redis key
|
||
* @param fields 玩家id
|
||
* @param db 数据库内的数据
|
||
*/
|
||
public async generParamAndSet(infoKey: string, fields: myIdInter, db: { role?: RoleType, guild?: GuildType, hero?: HeroType }) {
|
||
let { roleId, guildCode, hid } = fields;
|
||
let { role, guild, hero } = db;
|
||
|
||
if (infoKey == REDIS_KEY.USER_INFO) {
|
||
if (!role) {
|
||
role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true);
|
||
}
|
||
let param = new RankParam(role, true);
|
||
await this.setUserInfo(infoKey, { roleId }, param);
|
||
} else if (infoKey == REDIS_KEY.GUILD_INFO) {
|
||
if (!guild) {
|
||
guild = await GuildModel.findByCode(guildCode, this.keyName.serverId, GUILD_SELECT.RANK)
|
||
}
|
||
let param = new GuildRankParam(guild, true);
|
||
await this.setUserInfo(infoKey, { guildCode }, param);
|
||
} else if (infoKey == REDIS_KEY.TOP_LINEUP_INFO) {
|
||
if (!role) {
|
||
role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true);
|
||
}
|
||
let { topLineup = [] } = role;
|
||
let heroes = await HeroModel.findByRole(roleId, [], HERO_SELECT.RANK_LINEUP);
|
||
let arr = new Array<LineupParam>();
|
||
for (let { hid } of topLineup) {
|
||
let curHero = heroes.find(cur => cur.hid == hid);
|
||
if (curHero) {
|
||
let param = new LineupParam(curHero);
|
||
arr.push(param);
|
||
}
|
||
}
|
||
await this.setUserInfo(infoKey, { roleId }, arr);
|
||
} else if (infoKey == REDIS_KEY.HERO_INFO) {
|
||
if (!hero) {
|
||
hero = await HeroModel.findByHidAndRole(hid, roleId, HERO_SELECT.RANK_LINEUP, true);
|
||
}
|
||
let arr = new Array<LineupParam>();
|
||
if (hero) {
|
||
let param = new LineupParam(hero);
|
||
arr.push(param);
|
||
await this.setUserInfo(infoKey, { roleId, hid }, arr);
|
||
}
|
||
} else if (infoKey == REDIS_KEY.DUNGEON_LINEUP) {
|
||
if (!role) {
|
||
role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true);
|
||
}
|
||
let { dungeonWarId, dungeonHeroes } = role;
|
||
if (dungeonWarId > 0) {
|
||
let cur = dungeonHeroes.find(cur => cur.battleId == dungeonWarId);
|
||
let lineup = cur ? cur.heroes : [];
|
||
let heroes = await HeroModel.findByRole(roleId, [], HERO_SELECT.RANK_LINEUP);
|
||
let arr = new Array<LineupParam>();
|
||
for (let seqId of lineup) {
|
||
let curHero = heroes.find(cur => cur.seqId == seqId);
|
||
if (curHero) {
|
||
let param = new LineupParam(curHero);
|
||
arr.push(param);
|
||
}
|
||
}
|
||
await this.setUserInfo(infoKey, { roleId }, arr);
|
||
}
|
||
} else if (infoKey == REDIS_KEY.SHOW_LINEUP) {
|
||
if (!role) {
|
||
role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true);
|
||
}
|
||
let { showLineup, topLineup = [] } = role;
|
||
let heroes = await HeroModel.findByRole(roleId, [], HERO_SELECT.RANK_LINEUP);
|
||
let arr = new Array<LineupParam>();
|
||
if (!showLineup) showLineup = topLineup.map(cur => cur.hid);
|
||
for (let hid of showLineup) {
|
||
let curHero = heroes.find(cur => cur.hid == hid);
|
||
if (curHero) {
|
||
let param = new LineupParam(curHero);
|
||
arr.push(param);
|
||
}
|
||
}
|
||
await this.setUserInfo(infoKey, { roleId }, arr);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置信息
|
||
* @param infoKey
|
||
* @param myId
|
||
* @param param
|
||
*/
|
||
private async setUserInfo(infoKey: REDIS_KEY, myId: myIdInter, param: RankParam | GuildRankParam | LineupParam[]) {
|
||
let field = this.composeFields(infoKey, myId);
|
||
let value = JSON.stringify(param);
|
||
return await redisClient().hsetAsync(infoKey, field, value);
|
||
}
|
||
|
||
/**
|
||
* 更新排行榜数据
|
||
* @param myId
|
||
* @param score
|
||
* @param timestamp
|
||
* @param isInc
|
||
*/
|
||
public async setRank(myId: myIdInter, scores: number[], isInc = false) {
|
||
let oldTop = await this.getRankByRank(0, 0);
|
||
let oldScore = oldTop.length > 0 && oldTop[0] ? oldTop[0].num : 0;
|
||
// 更新分数
|
||
let newScore = 0;
|
||
if (this.isUnion) {
|
||
newScore = await this.updateRankScoreAtom(myId, scores, isInc);
|
||
} else {
|
||
newScore = await this.updateRankScoreEncode(myId, scores, isInc)
|
||
}
|
||
this.checkMyRankAndPush(myId, oldScore, newScore);
|
||
return newScore;
|
||
}
|
||
|
||
/**
|
||
* @description 使用将score和time合成一个字段更新分数
|
||
* @param myId
|
||
* @param score
|
||
* @param timestamp
|
||
*/
|
||
private async updateRankScoreEncode(myId: myIdInter, scores: number[] = [], isInc = false) {
|
||
let newScore = 0;
|
||
let key = this.keyName.getName();
|
||
let newScores: number[] = [];
|
||
for(let index = 0; index < this.valueConfig.length; index ++) {
|
||
let config = this.valueConfig[index];
|
||
let score = scores[index];
|
||
if(config.isMain) {
|
||
if (isInc) {
|
||
let oldScore = await this.getMyScore(myId);
|
||
newScore = oldScore + score;
|
||
} else {
|
||
newScore = score;
|
||
}
|
||
newScores.push(newScore);
|
||
} else {
|
||
newScores.push(score);
|
||
}
|
||
}
|
||
|
||
let scoreStr = this.encodeScore(newScores);
|
||
await redisClient().zaddAsync(key, scoreStr, this.composeFields(this.key, myId));
|
||
return newScore
|
||
}
|
||
|
||
/**
|
||
* @description 使用分两个zset更新redis中某field的值
|
||
* @param myId
|
||
* @param score
|
||
* @param timestamp
|
||
* @param isInc
|
||
* @class Rank
|
||
*/
|
||
private async updateRankScoreAtom(myId: myIdInter, scores: number[], isInc = false) {
|
||
|
||
let newScore = 0;
|
||
for(let index = 0; index < this.valueConfig.length; index ++) {
|
||
let config = this.valueConfig[index];
|
||
let score = scores[index];
|
||
if(config.isMain) {
|
||
let key = this.keyName.getName();
|
||
// 分数zset
|
||
if (isInc) {
|
||
newScore = await redisClient().zincrbyAsync(key, score, this.composeFields(this.key, myId));
|
||
} else {
|
||
newScore = await redisClient().zaddAsync(key, score, this.composeFields(this.key, myId));
|
||
}
|
||
} else {
|
||
let key = this.keyName.getNameWithPlus(config.name);
|
||
if(config.isTimestamp) score = this.handleTimestamp(score, config.len);
|
||
if(config.reverse) {
|
||
let pow = Math.pow(10, config.len);
|
||
score = pow - 1 - score;
|
||
}
|
||
await redisClient().zaddAsync(key, score, this.composeFields(this.key, myId));
|
||
}
|
||
}
|
||
return parseInt(newScore.toString());
|
||
}
|
||
|
||
|
||
/**
|
||
* @description 设置到期时间
|
||
* @param time 到期时间,10位时间戳
|
||
* @class Rank
|
||
*/
|
||
public async setExpire(time: number) {
|
||
let key = this.keyName.getName();
|
||
let timeKey = this.keyName.getTimeName();
|
||
|
||
await redisClient().expireatAsync(key, time);
|
||
await redisClient().expireatAsync(timeKey, time);
|
||
}
|
||
|
||
public async generMyRankWithRole(roleId: string, score: number = 0, time: number = 0, role?: RoleType) {
|
||
// 如果没有信息,更新玩家信息
|
||
let param: RoleRankInfo;
|
||
|
||
let hasCurUser = await redisClient().hexistsAsync(this.infoKey, roleId);
|
||
if (!hasCurUser) {
|
||
if (!role) {
|
||
role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true);
|
||
}
|
||
param = new RoleRankInfo(role, true);
|
||
param.setInfo(0, { roleId }, score, time);
|
||
} else {
|
||
const info = await redisClient().hgetAsync(this.infoKey, roleId);
|
||
const userInfo = JSON.parse(info);
|
||
|
||
param = new RoleRankInfo(userInfo, false);
|
||
param.setInfo(0, { roleId }, score, time);
|
||
}
|
||
|
||
for (let extraKey of this.extraKeys) {
|
||
await this.setExInfoToParam(extraKey, param, { roleId }, { role });
|
||
}
|
||
|
||
return await this.generFields(param);
|
||
}
|
||
|
||
public async generMyRankWithGuild(guildCode: string, score: number, time: number, guild?: GuildType) {
|
||
// 如果没有信息,更新玩家信息
|
||
let param: GuildRankInfo;
|
||
|
||
let hasCurUser = await redisClient().hexistsAsync(this.infoKey, guildCode);
|
||
if (!hasCurUser) {
|
||
if (!guild) {
|
||
guild = await GuildModel.findByCode(guildCode, this.keyName.serverId, GUILD_SELECT.RANK);
|
||
}
|
||
param = new GuildRankInfo(guild, true);
|
||
param.setInfo(0, { guildCode }, score, time);
|
||
} else {
|
||
const info = await redisClient().hgetAsync(this.infoKey, guildCode);
|
||
const guildInfo = JSON.parse(info);
|
||
param = new GuildRankInfo(guildInfo, false);
|
||
param.setInfo(0, { guildCode }, score, time);
|
||
}
|
||
|
||
for (let extraKey of this.extraKeys) {
|
||
await this.setExInfoToParam(extraKey, param, { guildCode }, { guild });
|
||
}
|
||
|
||
return await this.generFields(param);
|
||
}
|
||
|
||
public async generMyRankWithGuild2(guildCode: string, score: number, time: number, active: number, guild?: GuildType) {
|
||
// 如果没有信息,更新玩家信息
|
||
let param: GuildRankInfo;
|
||
|
||
let hasCurUser = await redisClient().hexistsAsync(this.infoKey, guildCode);
|
||
if (!hasCurUser) {
|
||
if (!guild) {
|
||
guild = await GuildModel.findByCode(guildCode, this.keyName.serverId, GUILD_SELECT.RANK);
|
||
}
|
||
param = new GuildRankInfo(guild, true);
|
||
param.setInfo2(0, { guildCode }, score, time, active);
|
||
} else {
|
||
const info = await redisClient().hgetAsync(this.infoKey, guildCode);
|
||
const guildInfo = JSON.parse(info);
|
||
param = new GuildRankInfo(guildInfo, false);
|
||
param.setInfo2(0, { guildCode }, score, time, active);
|
||
}
|
||
|
||
for (let extraKey of this.extraKeys) {
|
||
await this.setExInfoToParam(extraKey, param, { guildCode }, { guild });
|
||
}
|
||
|
||
return await this.generFields(param);
|
||
}
|
||
|
||
public async generMyRankWithHero(roleId: string, hid: number, score: number, time: number, hero?: HeroType, role?: RoleType) {
|
||
// 如果没有信息,更新玩家信息
|
||
let param: RoleRankInfo;
|
||
|
||
let hasCurUser = await redisClient().hexistsAsync(this.infoKey, roleId);
|
||
if (!hasCurUser) {
|
||
if (!role) {
|
||
role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true);
|
||
}
|
||
param = new RoleRankInfo(role, true);
|
||
param.setInfo(0, { roleId, hid }, score, time);
|
||
} else {
|
||
const info = await redisClient().hgetAsync(this.infoKey, roleId);
|
||
const userInfo = JSON.parse(info);
|
||
|
||
param = new RoleRankInfo(userInfo, false);
|
||
param.setInfo(0, { roleId, hid }, score, time);
|
||
}
|
||
|
||
for (let extraKey of this.extraKeys) {
|
||
await this.setExInfoToParam(extraKey, param, { roleId, hid }, { hero, role });
|
||
}
|
||
|
||
return await this.generFields(param);
|
||
}
|
||
|
||
private async setExInfoToParam(extraKey: string, param: RoleRankInfo | GuildRankInfo, myId: myIdInter, db: { role?: RoleType, guild?: GuildType, hero?: HeroType }) {
|
||
let { role, guild, hero } = db;
|
||
|
||
if (extraKey == REDIS_KEY.TOP_LINEUP_INFO) {
|
||
param = <RoleRankInfo>param;
|
||
|
||
let hasCurUser = await redisClient().hexistsAsync(extraKey, this.composeFields(extraKey, myId));
|
||
if (!hasCurUser) {
|
||
if (!role) {
|
||
role = await RoleModel.findByRoleId(myId.roleId, ROLE_SELECT.RANK, true);
|
||
}
|
||
|
||
let { topLineup } = role;
|
||
let heroes = await HeroModel.findByRole(myId.roleId, [], HERO_SELECT.HERO_DETAIL);
|
||
let arr = new Array<LineupParam>();
|
||
for (let { hid } of topLineup) {
|
||
let curHero = heroes.find(cur => cur.hid == hid);
|
||
if (curHero) {
|
||
let param = new LineupParam(curHero);
|
||
arr.push(param);
|
||
}
|
||
}
|
||
param.setTopLine(arr);
|
||
} else {
|
||
const info = await redisClient().hgetAsync(extraKey, this.composeFields(extraKey, myId));
|
||
const infoObj = JSON.parse(info);
|
||
param.setTopLine(infoObj);
|
||
}
|
||
} else if (extraKey == REDIS_KEY.HERO_INFO) {
|
||
param = <RoleRankInfo>param;
|
||
|
||
let hasCurUser = await redisClient().hexistsAsync(extraKey, this.composeFields(extraKey, myId));
|
||
if (!hasCurUser) {
|
||
if (!hero) {
|
||
hero = await HeroModel.findByHidAndRole(myId.hid, myId.roleId, HERO_SELECT.RANK_LINEUP);
|
||
}
|
||
let arr = new Array<LineupParam>();
|
||
let lineParam = new LineupParam(hero);
|
||
arr.push(lineParam);
|
||
if (this.key == REDIS_KEY.HERO_RANK) {
|
||
param.setSingleHero(arr);
|
||
} else {
|
||
param.setTopLine(arr);
|
||
}
|
||
} else {
|
||
const info = await redisClient().hgetAsync(extraKey, this.composeFields(extraKey, myId));
|
||
const infoObj = JSON.parse(info);
|
||
if (this.key == REDIS_KEY.HERO_RANK) {
|
||
param.setSingleHero(infoObj);
|
||
} else {
|
||
param.setTopLine(infoObj);
|
||
}
|
||
}
|
||
} else if (extraKey == REDIS_KEY.DUNGEON_LINEUP) {
|
||
param = <RoleRankInfo>param;
|
||
|
||
let hasCurUser = await redisClient().hexistsAsync(extraKey, this.composeFields(extraKey, myId));
|
||
if (!hasCurUser) {
|
||
if (!role) {
|
||
role = await RoleModel.findByRoleId(myId.roleId, ROLE_SELECT.RANK, true);
|
||
}
|
||
let { dungeonWarId, dungeonHeroes } = role;
|
||
let cur = dungeonHeroes.find(cur => cur.battleId == dungeonWarId);
|
||
let lineup = cur ? cur.heroes : [];
|
||
let heroes = await HeroModel.findByRole(myId.roleId, [], HERO_SELECT.RANK_LINEUP);
|
||
let arr = new Array<LineupParam>();
|
||
for (let seqId of lineup) {
|
||
let curHero = heroes.find(cur => cur.seqId == seqId);
|
||
if (curHero) {
|
||
let param = new LineupParam(curHero);
|
||
arr.push(param);
|
||
}
|
||
}
|
||
param.setTopLine(arr);
|
||
} else {
|
||
const info = await redisClient().hgetAsync(extraKey, this.composeFields(extraKey, myId));
|
||
const infoObj = JSON.parse(info);
|
||
param.setTopLine(infoObj);
|
||
}
|
||
} else if (extraKey == REDIS_KEY.SHOW_LINEUP) {
|
||
param = <RoleRankInfo>param;
|
||
|
||
let hasCurUser = await redisClient().hexistsAsync(extraKey, this.composeFields(extraKey, myId));
|
||
if (!hasCurUser) {
|
||
if (!role) {
|
||
role = await RoleModel.findByRoleId(myId.roleId, ROLE_SELECT.RANK, true);
|
||
}
|
||
|
||
let { showLineup, topLineup = [] } = role;
|
||
let heroes = await HeroModel.findByRole(myId.roleId, [], HERO_SELECT.HERO_DETAIL);
|
||
let arr = new Array<LineupParam>();
|
||
if (!showLineup) showLineup = topLineup.map(cur => cur.hid);
|
||
for (let hid of showLineup) {
|
||
let curHero = heroes.find(cur => cur.hid == hid);
|
||
if (curHero) {
|
||
let param = new LineupParam(curHero);
|
||
arr.push(param);
|
||
}
|
||
}
|
||
param.setTopLine(arr);
|
||
} else {
|
||
const info = await redisClient().hgetAsync(extraKey, this.composeFields(extraKey, myId));
|
||
const infoObj = JSON.parse(info);
|
||
param.setTopLine(infoObj);
|
||
}
|
||
}
|
||
}
|
||
|
||
public async getRankListWithMyRank(myId: myIdInter) {
|
||
let ranks = await this.getRankByRange();
|
||
let newRanks = [], newMyRank: RoleRankInfo | GuildRankInfo;
|
||
let myRank = ranks.find(cur => {
|
||
return cur.isMyInfo(myId);
|
||
});
|
||
if (this.generFields) {
|
||
for (let rank of ranks) {
|
||
let n = await this.generFields(rank);
|
||
newRanks.push(n);
|
||
}
|
||
if (myRank) {
|
||
newMyRank = await this.generFields(myRank);
|
||
}
|
||
}
|
||
|
||
return { myRank: newMyRank, ranks: newRanks }
|
||
}
|
||
|
||
public async getRankByRange(from: number | string = '+inf', to: number | string = '-inf') {
|
||
|
||
let ranks = new Array<RoleRankInfo | GuildRankInfo>();
|
||
let key = this.keyName.getName();
|
||
if (this.isUnion) {
|
||
key = await this.generateUnionRank();
|
||
}
|
||
|
||
const rankFromDb = await redisClient().zrevrangebyscoreAsync(key, from, to, "WITHSCORES");
|
||
|
||
let num = 0;
|
||
for (let ii = 0; ii < rankFromDb.length; ii += 2) {
|
||
if (num >= this.limit) break;
|
||
|
||
const field = rankFromDb[ii];
|
||
let myId = this.decodeFields(this.key, field);
|
||
const scores = this.decodeScore(rankFromDb[ii + 1]);
|
||
|
||
const info = await redisClient().hgetAsync(this.infoKey, this.composeFields(this.infoKey, myId));
|
||
const userInfo = JSON.parse(info);
|
||
|
||
let param: RoleRankInfo | GuildRankInfo;
|
||
if (this.infoKey == REDIS_KEY.USER_INFO) {
|
||
if (nowSeconds() - userInfo.updatedAt > 2 * 30 * 24 * 60 * 60) {
|
||
continue;
|
||
}
|
||
param = new RoleRankInfo(userInfo, false);
|
||
param.setInfo(Math.floor(ii / 2) + 1, myId, scores[0], scores[1]);
|
||
} else if (this.infoKey == REDIS_KEY.GUILD_INFO) {
|
||
if(this.key == REDIS_KEY.GUILD_LV_RANK) {
|
||
param = new GuildRankInfo(userInfo, false);
|
||
param.setInfo2(Math.floor(ii / 2) + 1, myId, scores[0], scores[1], scores[2]);
|
||
} else {
|
||
param = new GuildRankInfo(userInfo, false);
|
||
param.setInfo(Math.floor(ii / 2) + 1, myId, scores[0], scores[1]);
|
||
}
|
||
}
|
||
|
||
for (let extraKey of this.extraKeys) {
|
||
await this.setExInfoToParam(extraKey, param, myId, {});
|
||
}
|
||
ranks.push(param);
|
||
num++;
|
||
}
|
||
return ranks
|
||
}
|
||
|
||
public async getRankByRank(start: number, end: number) {
|
||
|
||
let ranks = new Array<RoleRankInfo | GuildRankInfo>();
|
||
let key = this.keyName.getName();
|
||
if (this.isUnion) {
|
||
key = await this.generateUnionRank();
|
||
}
|
||
|
||
const rankFromDb = await redisClient().zrevrangeAsync(key, start, end, "WITHSCORES");
|
||
|
||
// console.log(key, start, end, rankFromDb)
|
||
|
||
let num = 0;
|
||
for (let ii = 0; ii < rankFromDb.length; ii += 2) {
|
||
if (num >= this.limit) break;
|
||
|
||
const field = rankFromDb[ii];
|
||
let myId = this.decodeFields(this.key, field);
|
||
const scores = this.decodeScore(rankFromDb[ii + 1]);
|
||
const info = await redisClient().hgetAsync(this.infoKey, this.composeFields(this.infoKey, myId));
|
||
const userInfo = JSON.parse(info);
|
||
|
||
let param: RoleRankInfo | GuildRankInfo;
|
||
if (this.infoKey == REDIS_KEY.USER_INFO) {
|
||
if (nowSeconds() - userInfo.updatedAt > 2 * 30 * 24 * 60 * 60) {
|
||
continue;
|
||
}
|
||
param = new RoleRankInfo(userInfo, false);
|
||
param.setInfo(Math.floor(ii / 2) + 1, myId, scores[0], scores[1]);
|
||
} else if (this.infoKey == REDIS_KEY.GUILD_INFO) {
|
||
if(this.key == REDIS_KEY.GUILD_LV_RANK) {
|
||
param = new GuildRankInfo(userInfo, false);
|
||
param.setInfo2(Math.floor(ii / 2) + 1, myId, scores[0], scores[1], scores[2]);
|
||
} else {
|
||
param = new GuildRankInfo(userInfo, false);
|
||
param.setInfo(Math.floor(ii / 2) + 1, myId, scores[0], scores[1]);
|
||
}
|
||
}
|
||
|
||
for (let extraKey of this.extraKeys) {
|
||
await this.setExInfoToParam(extraKey, param, myId, {});
|
||
}
|
||
ranks.push(param);
|
||
num++;
|
||
}
|
||
return ranks
|
||
}
|
||
|
||
|
||
// 获取我的排名
|
||
public async getMyRank(myId: myIdInter) {
|
||
let key = this.keyName.getName();
|
||
if (this.isUnion) {
|
||
key = await this.generateUnionRank();
|
||
}
|
||
// console.log('**getMyRank',key, this.composeFields(this.key, myId), this.key, myId)
|
||
let myRank = await redisClient().zrevrankAsync(key, this.composeFields(this.key, myId));
|
||
if (!myRank && myRank != 0) {
|
||
return 0;
|
||
} else {
|
||
return myRank + 1;
|
||
}
|
||
}
|
||
|
||
// 获取排名第几名的信息
|
||
public async getUserByRank(rank: number) {
|
||
let key = this.keyName.getName();
|
||
let myRank = await redisClient().zrevrangeAsync(key, rank - 1, rank - 1);
|
||
return myRank;
|
||
}
|
||
|
||
|
||
// 获取排名第几名的信息
|
||
public async getMyScore(myId: myIdInter) {
|
||
let key = this.keyName.getName();
|
||
let score = await redisClient().zscoreAsync(key, this.composeFields(this.key, myId));
|
||
if (!score) score = 0;
|
||
if (!this.isUnion) {
|
||
let result = this.decodeScore(score.toString());
|
||
score = result[0];
|
||
}
|
||
return parseInt(score.toString());
|
||
}
|
||
|
||
// 从排行榜中移除
|
||
public async removeFromRank(myId: myIdInter) {
|
||
let key = this.keyName.getName();
|
||
await redisClient().zremAsync(key, this.composeFields(this.key, myId));
|
||
|
||
return true;
|
||
}
|
||
|
||
private async generateUnionRank() {
|
||
let unionKey = this.keyName.getUnionName(); // 联合的key
|
||
|
||
let keys = [], weights = [];
|
||
this.valueConfig.forEach(({ isMain, name, weight }) => {
|
||
if(isMain) {
|
||
keys.push(this.keyName.getName());
|
||
} else {
|
||
keys.push(this.keyName.getNameWithPlus(name));
|
||
}
|
||
weights.push(Math.pow(10, weight))
|
||
|
||
})
|
||
|
||
await redisClient().zunionstoreAsync(unionKey, 2, ...keys, 'WEIGHTS', ...weights);
|
||
await redisClient().expireAsync(unionKey, this.unionRankLife); // 10秒更新一次
|
||
return unionKey;
|
||
}
|
||
|
||
// 有序排行综合时间和得分排序
|
||
private encodeScore(scores: number[]) {
|
||
let encodeResult = 0;
|
||
this.valueConfig.forEach(({ reverse, isTimestamp, len, weight }, index) => {
|
||
let score = scores[index];
|
||
if(isTimestamp) score = this.handleTimestamp(score, len);
|
||
if(!reverse) {
|
||
encodeResult += score * Math.pow(10, weight);
|
||
} else {
|
||
let pow = Math.pow(10, len);
|
||
encodeResult += pow - 1 - score;
|
||
}
|
||
})
|
||
return encodeResult
|
||
}
|
||
|
||
private decodeScore(num: string) {
|
||
let _num = parseInt(num);
|
||
let scores: number[] = [];
|
||
this.valueConfig.forEach(({ reverse, isTimestamp, len, weight }) => {
|
||
let pow = Math.pow(10, weight);
|
||
let score = Math.floor(_num / pow);
|
||
_num -= pow * score;
|
||
if(isTimestamp) score = this.handleTimestamp(score, len);
|
||
if(reverse) {
|
||
let pow = Math.pow(10, len);
|
||
score = pow - 1 - score;
|
||
}
|
||
scores.push(score);
|
||
})
|
||
return scores;
|
||
}
|
||
|
||
private handleTimestamp(timestamp: number = 0, len: number) {
|
||
let l = timestamp.toString().length;
|
||
if (l > len) {
|
||
timestamp = Math.floor(timestamp / Math.pow(10, l - len));
|
||
}
|
||
return timestamp
|
||
}
|
||
|
||
private async checkMyRankAndPush(myId: myIdInter, oldScore: number, newScore: number) {
|
||
let ts = this;
|
||
if (ts.isInit) return;
|
||
let myRank = await this.getMyRank(myId);
|
||
// console.log('*******', ts.key, myRank, newScore, oldScore);
|
||
if (myRank == 1 && newScore > oldScore) {
|
||
let serverId = this.keyName.serverId;
|
||
let sid = await getWorldChannelSid(serverId);
|
||
let dicRank = gameData.rank.find(({ id }) => {
|
||
let redisKey = RANK_TYPE_TO_KEY.get(id);
|
||
return redisKey == ts.key
|
||
});
|
||
// console.log('*******', dicRank);
|
||
if(dicRank) {
|
||
let r = new Rank(ts.key, { serverId }, false, 1);
|
||
let ranks = await r.getRankByRange();
|
||
let rankFirstRecs: (RankFirstType & { status: number })[] = [];
|
||
if(ranks.length > 0 && ranks[0]) {
|
||
let userInfo: RankParam, guildInfo: GuildRankParam;
|
||
if(ranks[0] instanceof RoleRankInfo) {
|
||
userInfo = ranks[0];
|
||
}
|
||
if(ranks[0] instanceof GuildRankInfo) {
|
||
guildInfo = ranks[0];
|
||
}
|
||
|
||
for(let [id, { rankId, condition }] of gameData.generalRankReward) {
|
||
// console.log(rankId, dicRank.id, oldScore, condition, newScore);
|
||
if(dicRank.id == rankId && oldScore < condition && newScore >= condition) {
|
||
let rankFirstRec = await RankFirstModel.createRankFirst(serverId, id, rankId, { userInfo, guildInfo });
|
||
let roleServers = pinus.app.getServersByType('role');
|
||
for(let server of roleServers) {
|
||
pinus.app.rpc.role.roleRemote.setRankFirst.toServer(server.id, rankFirstRec);
|
||
}
|
||
rankFirstRecs.push({ ... rankFirstRec, status: 1});
|
||
}
|
||
}
|
||
}
|
||
if(rankFirstRecs.length > 0) {
|
||
pinus.app.rpc.chat.chatRemote.sendRankTopUpdated.toServer(sid, serverId, rankFirstRecs);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 从数据库内获取排行榜存入redis
|
||
* @param type 排行榜类型
|
||
* @param serverId 分服
|
||
*/
|
||
export async function setRankRedisFromDb(type: string, args?: { serverId?: number }) {
|
||
if (type == REDIS_KEY.TOWER_RANK) {
|
||
let serverId = args.serverId;
|
||
let ranks = await RoleModel.getRank('tower', serverId, ROLE_SELECT.RANK);
|
||
let r = new Rank(type, { serverId });
|
||
r.setIsInit(true);
|
||
for (let role of ranks) {
|
||
// console.log(roleId);
|
||
if(role.towerLv > 1) {
|
||
await r.setRankWithRoleInfo(role.roleId, role.towerLv, role.towerUpTime ? role.towerUpTime.getTime() : 0, role);
|
||
}
|
||
}
|
||
} else if (type == REDIS_KEY.GUILD_ACTIVE_RANK) {
|
||
let serverId = args.serverId;
|
||
let ranks = await GuildModel.getRank(type, serverId);
|
||
let r = new Rank(type, { serverId });
|
||
r.setIsInit(true);
|
||
for (let guild of ranks) {
|
||
await r.setRankWithGuildInfo(guild.code, guild.activeWeekly, guild.activeUpdateTime, guild);
|
||
}
|
||
} else if (type == REDIS_KEY.GUILD_LV_RANK) {
|
||
let serverId = args.serverId;
|
||
let ranks = await GuildModel.getRank(type, serverId);
|
||
let r = new Rank(type, { serverId });
|
||
r.setIsInit(true);
|
||
for (let guild of ranks) {
|
||
await r.setRankWithGuildInfoArrParam(guild.code, [guild.lv, guild.activeWeekly, guild.activeUpdateTime], guild);
|
||
}
|
||
} else if (type == REDIS_KEY.PVP_RANK) {
|
||
let seasonNum = await CounterModel.getCounter(COUNTER.PVP_SEASON_NUM);
|
||
if(seasonNum > 0) {
|
||
let keyName = new KeyName(type, { seasonNum })
|
||
await delKeys(keyName.getName());
|
||
let ranks = await PvpDefenseModel.getRank(seasonNum);//获得全服前1000名的排名,加入到redis中
|
||
|
||
let r = new Rank(type, { seasonNum });
|
||
r.setIsInit(true);
|
||
for (let { roleId, role: _role, score, updatedAt } of ranks) {
|
||
let role = <RoleType>_role;
|
||
if (!role) {
|
||
continue;
|
||
}
|
||
await r.setRankWithRoleInfo(roleId, score, updatedAt.getTime(), role);
|
||
}
|
||
}
|
||
} else if (type == REDIS_KEY.TOP_LINEUP_RANK) {
|
||
let serverId = args.serverId;
|
||
let ranks = await RoleModel.getRank('topLineup', serverId, ROLE_SELECT.RANK);
|
||
let r = new Rank(type, { serverId });
|
||
r.setIsInit(true);
|
||
for (let role of ranks) {
|
||
// console.log(roleId);
|
||
await r.setRankWithRoleInfo(role.roleId, role.topLineupCe, role.updatedAt.getTime(), role);
|
||
}
|
||
} else if (type == REDIS_KEY.TOP_HERO_RANK) {
|
||
let serverId = args.serverId;
|
||
let ranks = await HeroModel.getAllRank(serverId, HERO_SELECT.RANK_LINEUP);
|
||
let r = new Rank(type, { serverId });
|
||
r.setIsInit(true);
|
||
for (let hero of ranks) {
|
||
await r.setRankWithHeroInfo(hero.roleId, hero.hid, hero.ce, hero.updatedAt.getTime(), hero);
|
||
}
|
||
} else if (type == REDIS_KEY.HERO_NUM_RANK) {
|
||
let serverId = args.serverId;
|
||
let ranks = await RoleModel.getRank('heroNum', serverId, ROLE_SELECT.RANK);
|
||
let r = new Rank(type, { serverId });
|
||
r.setIsInit(true);
|
||
for (let role of ranks) {
|
||
await r.setRankWithRoleInfo(role.roleId, role.heroNum, role.heroNumUpdatedAt, role);
|
||
}
|
||
} else if (type == REDIS_KEY.USER_LV) {
|
||
let serverId = args.serverId;
|
||
let ranks = await RoleModel.getRank('lv', serverId, ROLE_SELECT.RANK);
|
||
let r = new Rank(type, { serverId });
|
||
r.setIsInit(true);
|
||
for (let role of ranks) {
|
||
await r.setRankWithRoleInfo(role.roleId, role.lv, role.updatedAt.getTime(), role);
|
||
}
|
||
} else if (type == REDIS_KEY.SUM_CE_RANK) {
|
||
let serverId = args.serverId;
|
||
let ranks = await RoleModel.getRank('ce', serverId, ROLE_SELECT.RANK);
|
||
let r = new Rank(type, { serverId });
|
||
r.setIsInit(true);
|
||
for (let role of ranks) {
|
||
await r.setRankWithRoleInfo(role.roleId, role.ce, role.updatedAt.getTime(), role);
|
||
}
|
||
} else if (type == REDIS_KEY.DUNGEON_RANK) {
|
||
let serverId = args.serverId;
|
||
let ranks = await RoleModel.getRank('dungeon', serverId, ROLE_SELECT.RANK);
|
||
let r = new Rank(type, { serverId });
|
||
r.setIsInit(true);
|
||
for (let role of ranks) {
|
||
await r.setRankWithRoleInfo(role.roleId, role.dungeonWarId, role.dungeonUpdatedAt, role);
|
||
}
|
||
} else if (type == REDIS_KEY.MAIN_RANK) {
|
||
let serverId = args.serverId;
|
||
let ranks = await RoleModel.getRank('main', serverId, ROLE_SELECT.RANK);
|
||
let r = new Rank(type, { serverId });
|
||
r.setIsInit(true);
|
||
for (let role of ranks) {
|
||
await r.setRankWithRoleInfo(role.roleId, role.mainWarId, role.mainUpdatedAt, role);
|
||
}
|
||
} else if (type == REDIS_KEY.MAIN_ELITE_RANK) {
|
||
let serverId = args.serverId;
|
||
let ranks = await RoleModel.getRank('mainElite', serverId, ROLE_SELECT.RANK);
|
||
let r = new Rank(type, { serverId });
|
||
r.setIsInit(true);
|
||
for (let role of ranks) {
|
||
await r.setRankWithRoleInfo(role.roleId, role.mainEliteWarId, role.mainEliteUpdatedAt, role);
|
||
}
|
||
} else if (type == REDIS_KEY.HERO_RANK) {
|
||
let serverId = args.serverId;
|
||
|
||
for (let hid of gameData.dicMyHeroes) {
|
||
let ranks = await HeroModel.getRank(hid, serverId, HERO_SELECT.RANK_LINEUP);
|
||
let r = new Rank(type, { serverId, hid });
|
||
r.setIsInit(true);
|
||
for (let hero of ranks) {
|
||
await r.setRankWithHeroInfo(hero.roleId, hid, hero.ce, hero.updatedAt.getTime(), hero);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取排行榜总览
|
||
* @param role 玩家数据
|
||
* @param serverId 所在服务器
|
||
*/
|
||
export async function getGeneralRank(role: RoleType & { rankReceived: number[] }, serverId: number) {
|
||
let { rankReceived = [] } = role;
|
||
|
||
let result: (GeneralRankParam & {general: number})[] = [];
|
||
|
||
for (let { id, general } of gameData.rank) {
|
||
let redisKey = RANK_TYPE_TO_KEY.get(id);
|
||
if (redisKey) {
|
||
let received = rankReceived.filter(rewardId => {
|
||
let dic = gameData.generalRankReward.get(rewardId);
|
||
return dic && dic.rankId == id;
|
||
});
|
||
let r = new Rank(redisKey, { serverId }, false, 1);
|
||
let ranks = await r.getRankByRange();
|
||
// if (ranks.length > 0) {
|
||
let param = new GeneralRankParam(id, ranks[0] || new RoleRankInfo({}, false), general, received);
|
||
result.push({...param, general});
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 获取排行榜总览
|
||
* @param role 玩家数据
|
||
* @param serverId 所在服务器
|
||
*/
|
||
export async function getRankFirstReward(role: RoleType & { rankReceived: number[] }, serverId: number) {
|
||
let { rankReceived = [] } = role;
|
||
|
||
let result: (RankFirstUpdateParam & {status: number})[] = [];
|
||
|
||
for (let [id, { rankId: type } ] of gameData.generalRankReward) {
|
||
let rankFirst: RankFirstType;
|
||
if(pinus.app.getServerType() == 'role') {
|
||
rankFirst = pinus.app.get('rankFirstRewards').get(serverId)?.get(id);
|
||
} else {
|
||
let roleServers = pinus.app.getServersByType('role');
|
||
let server = getRandSingleEelm(roleServers);
|
||
rankFirst = await pinus.app.rpc.role.roleRemote.getRankFirstById.toServer(server.id, serverId, id);
|
||
}
|
||
|
||
if(rankFirst) {
|
||
let status = rankReceived.includes(id)?RANK_FIRST_REWARD_STATUS.RECEIVED: RANK_FIRST_REWARD_STATUS.CAN_RECEIVE;
|
||
result.push({...rankFirst, status});
|
||
} else {
|
||
result.push({ id, type, status: RANK_FIRST_REWARD_STATUS.WAITING });
|
||
}
|
||
}
|
||
return result;
|
||
} |