添加排行榜

This commit is contained in:
luying
2021-01-08 14:00:25 +08:00
parent b46681c909
commit 7458ed8c6c
11 changed files with 148 additions and 47 deletions

View File

@@ -8,8 +8,8 @@ import { resResult, reduceCe, genCode } from '../../../pubUtils/util';
import { SystemConfigModel } from '../../../db/SystemConfig'
import { PvpDefenseModel, PvpDefenseType, OppPlayers } from '../../../db/PvpDefense';
import { oppHeroesDefenseInter, pvpEndParamInter } from '../../../pubUtils/interface';
import { PVP_HERO_POS } from '../../../consts';
import { oppHeroesDefenseInter, pvpEndParamInter, RankParam } from '../../../pubUtils/interface';
import { PVP_HERO_POS, REDIS_KEY } from '../../../consts';
import { HeroType, HeroModel } from '../../../db/Hero';
import { CeAttrNumber } from '../../../db/generalField';
@@ -17,6 +17,8 @@ import { checkBattleHeroesByHid } from '../../../services/normalBattleService';
import { BattleRecordModel } from '../../../db/BattleRecord';
import { PvpRecordModel, HeroesRecord } from '../../../db/PvpRecord';
import { setPvpDefResult } from '../../../services/timeTaskService';
import { existsRank, initRank, getRank, setRank, getMyRank } from '../../../services/redisService';
export default function(app: Application) {
return new PvpHandler(app);
}
@@ -25,17 +27,10 @@ export class PvpHandler {
constructor(private app: Application) {
}
async test(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let role = await RoleModel.findByRoleId(roleId);
const result = await initPvpInfo(role);
return resResult(STATUS.SUCCESS, {result});
}
async getData (msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId);
let oppPlayers = getEnemies(pvpDefense.oppPlayers, pvpDefense.winStreakNum);
let oppPlayers = await getEnemies(pvpDefense.oppPlayers, pvpDefense.winStreakNum);
let {warId, seasonNum, seasonEndTime} = await SystemConfigModel.findSystemConfig();
let {heroes, score, pLv, winStreakNum, refOppCnt, challengeCnt, challengeRefTime, receivedBox, hisScore, heroScores, isFirstEntry} = pvpDefense;
if (pvpDefense.seasonNum !== seasonNum) {
@@ -44,7 +39,7 @@ export class PvpHandler {
if (isFirstEntry) {
await PvpDefenseModel.updateInfo(roleId, {isFirstEntry:false});
}
let myRank = 999;//TODO去redis中获取
let myRank = await getMyRank(REDIS_KEY.PVP_RANK, 0, roleId);//去redis中获取排名
let data = {warId, seasonNum, seasonEndTime, myRank, oppPlayers, heroes, score, pLv, winStreakNum, refOppCnt, challengeCnt, challengeRefTime, receivedBox, hisScore, heroScores, isFirstEntry}
return resResult(STATUS.SUCCESS, data);
}
@@ -59,7 +54,9 @@ export class PvpHandler {
let oppPlayers = await refreshEnemies(role, pvpDefense.score, pvpDefense.pLv);
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { oppPlayers });
let result = getEnemies(pvpDefense.oppPlayers, pvpDefense.winStreakNum);
let result = await getEnemies(pvpDefense.oppPlayers, pvpDefense.winStreakNum);
// TODO 刷新次数及消耗
return resResult(STATUS.SUCCESS, { oppPlayers: result, refOppCnt: 0 });
}
@@ -146,6 +143,8 @@ export class PvpHandler {
let checkHeroes = await checkBattleHeroesByHid(roleId, heroes);
if(!checkHeroes) return resResult(STATUS.BATTLE_HERO_NOT_FOUND);
// TODO 检查挑战次数
const pvpDefense = await PvpDefenseModel.findByRoleId(roleId);
if(!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
let { oppPlayers } = pvpDefense;
@@ -256,15 +255,6 @@ export class PvpHandler {
const role = await RoleModel.findByRoleId(roleId);
let newOppPlayers: Array<OppPlayers> = await refreshEnemies(role, score, pLv);
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { oppPlayers: newOppPlayers, heroScores, score, pLv, winStreakNum });
let result = getEnemies(pvpDefense.oppPlayers, pvpDefense.winStreakNum);
// 更新battleRecord
await BattleRecordModel.updateBattleRecordByCode(battleCode, {
$set: { status: isSuccess?1:2 }
}, true);
let oppRole;
if(curOpp.isRobot) {
oppRole = { ...curOpp.robot, title: 1, topFiveCe: curOpp.robot.defCe};
@@ -279,9 +269,23 @@ export class PvpHandler {
roleId: oppRole.roleId, roleName: oppRole.roleName, lv: oppRole.lv, sHid: oppRole.sHid, headHid: oppRole.headHid, title: oppRole.title, ce: oppRole.topFiveCe, heroes: myHeroRecords, isSuccess: !isSuccess, score: 0
}})
// 更新battleRecord
await BattleRecordModel.updateBattleRecordByCode(battleCode, {
$set: { status: isSuccess?1:2 }
}, true);
// TODO 增加挑战次数
pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { oppPlayers: newOppPlayers, heroScores, score, pLv, winStreakNum });
let result = await getEnemies(pvpDefense.oppPlayers, pvpDefense.winStreakNum);
// 加入排行榜
let params = new RankParam(roleName, role.lv, role.vLv, role.headHid, role.sHid, role.title);
await setRank(REDIS_KEY.PVP_RANK, 0, roleId, pvpDefense.score, pvpDefense.updatedAt.getTime(), params);
let myRank = await getMyRank(REDIS_KEY.PVP_RANK, 0, roleId);
return resResult(STATUS.SUCCESS, {
battleCode, isSuccess,
score, pLv, myRank: 0,
score, pLv, myRank,
heroScores: showHeroScores,
oppPlayers: result
});
@@ -333,5 +337,26 @@ export class PvpHandler {
}
}
async getRank(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let serverId = session.get('serverId')
const hasRank = await existsRank(REDIS_KEY.PVP_RANK, serverId);
if(!hasRank) await initRank(serverId);
let {ranks, myRank} = await getRank(REDIS_KEY.PVP_RANK, 0, roleId);
if(!myRank) {
let pvpDefense = await PvpDefenseModel.findByRoleId(roleId);
let role = await RoleModel.findByRoleId(roleId);
let { lv, vLv, headHid, sHid, title} = role;
let rankRank = new RankParam(roleName, lv, vLv, headHid, sHid, title);
myRank = {
rank: 0, roleId, ...rankRank, num: pvpDefense.score, str: ''
}
}
return resResult(STATUS.SUCCESS, { ranks: ranks.slice(0, 100), myRank });
}
}

View File

@@ -12,6 +12,7 @@ import { calcuHangUpReward, checkTaskConditions, checkHangUpSpdUpCnt, createCurT
import { handleFixedReward, addItems, handleCost } from '../../../services/rewardService';
import { checkBattleHeroes } from '../../../services/normalBattleService';
import { getRank, setRank, initRank, existsRank } from '../../../services/redisService';
import { RankParam } from '../../../pubUtils/interface';
export default function(app: Application) {
return new TowerBattleHandler(app);
@@ -33,8 +34,9 @@ export class TowerBattleHandler {
towerLv = 1;
let role = await RoleModel.towerLvUp(roleId);
// 更新redis
let { roleName, towerUpTime, lv, vLv } = role;
await setRank(REDIS_KEY.TOWER_RANK, serverId, roleId, towerLv, towerUpTime.getTime(), {roleName, lv, vLv, guildName:"", head: "zhaoyun"});
let { roleName, towerUpTime, lv, vLv, headHid, sHid, title } = role;
let rankRank = new RankParam(roleName, lv, vLv, headHid, sHid, title);
await setRank(REDIS_KEY.TOWER_RANK, serverId, roleId, towerLv, towerUpTime.getTime(), rankRank);
}
let towerRec = await TowerRecordModel.getRecordByLv(roleId, towerLv);

View File

@@ -85,6 +85,7 @@ export class EntryHandler {
let apJson = await getAp(Date.now(), role.roleId);
role['apJson'] = apJson;
role['ce'] = reduceCe(role.ce);
role['topFiveCe'] = reduceCe(role.topFiveCe);
return resResult(STATUS.SUCCESS, { role });
}

View File

@@ -11,6 +11,7 @@ import { STATUS } from '../consts/statusCode';
import { HangUpSpdUpRecModel } from '../db/HangUpSpdUpRec';
import { TowerTaskRecModel } from '../db/TowerTaskRec';
import { setRank } from './redisService';
import { RankParam } from '../pubUtils/interface';
export async function checkTowerWar(roleId: string, battleId: number, heroes: Array<number>) {
const battleIdStr = `${battleId}`;
@@ -71,8 +72,9 @@ export async function towerBattleEnd(sid: string, roleId: string, serverId: numb
if (inc === 1) {
let role = await RoleModel.towerLvUp(roleId);
// 更新redis
let { roleName, towerLv, towerUpTime, lv, vLv } = role;
await setRank(REDIS_KEY.TOWER_RANK, serverId, roleId, towerLv, towerUpTime.getTime(), {roleName, lv, vLv, guildName:"", head: "zhaoyun"});
let { roleName, towerLv, towerUpTime, lv, vLv, headHid, sHid, title } = role;
let rankRank = new RankParam(roleName, lv, vLv, headHid, sHid, title);
await setRank(REDIS_KEY.TOWER_RANK, serverId, roleId, towerLv, towerUpTime.getTime(), rankRank);
const nextTowerInfo = getTowerDataByLv(towerLv + 1);
if (nextTowerInfo) {

View File

@@ -3,14 +3,15 @@
*/
import { PvpDefenseModel, Heroes, OppPlayers, Robot, PvpDefenseType, HeroScores } from '../db/PvpDefense';
import { RoleType } from '../db/Role';
import { PVP_HERO_POS, ROBOT_NAME } from '../consts';
import { PVP_HERO_POS, ROBOT_NAME, REDIS_KEY } from '../consts';
import { dicPvpOpponent, DicPvpOpponent } from "../pubUtils/dictionary/DicPvpOpponent";
import { getRandomIndexByLen, genCode, getRandomByLen } from '../pubUtils/util';
import { oppPlayersInter } from '../pubUtils/interface';
import { oppPlayersInter, RankParam } from '../pubUtils/interface';
import { gameData, getPLvByScore } from "../pubUtils/data";
import { PVP } from '../pubUtils/dicParam';
import { SystemConfigModel } from '../db/SystemConfig'
import { setRank, getMyRank } from './redisService';
const _ = require('underscore');
@@ -33,7 +34,12 @@ export async function initPvpInfo(role: RoleType) {
let {warId, seasonNum } = await SystemConfigModel.findSystemConfig();
let challengeCnt = PVP.PVP_CHALLENGE_COUNTS;
let result = await PvpDefenseModel.createPvpDefense({ roleId: role.roleId, roleName: role.roleName, role: role._id, heroes, oppPlayers, defCe: role.topFiveCe, seasonNum, warId, challengeCnt });
//加入排行榜 TODO
//加入排行榜
let { roleId, roleName, lv, vLv, headHid, sHid, title } = role;
let params = new RankParam(roleName, lv, vLv, headHid, sHid, title);
await setRank(REDIS_KEY.PVP_RANK, 0, roleId, result.score, result.updatedAt.getTime(), params);
return result;
}
@@ -51,7 +57,7 @@ export async function checkPvp(role: RoleType) {
* @param oppPlayers pvpDefense表中的oppPlayers字段需要populate过的
* @param pLv 玩家本人的队伍等级
*/
export function getEnemies(oppPlayers: OppPlayers[], winStreakNum: number) {
export async function getEnemies(oppPlayers: OppPlayers[], winStreakNum: number) {
let result = new Array<oppPlayersInter>();
for(let {pos, isRobot, oppDef, robot} of oppPlayers) {
let dicOpponent = dicPvpOpponent.get(pos);
@@ -68,10 +74,11 @@ export function getEnemies(oppPlayers: OppPlayers[], winStreakNum: number) {
let role = <RoleType>opp.role;
let { roleId, roleName, headHid, sHid } = role;
let { pLv, defCe } = opp;
let rankLv = await getMyRank(REDIS_KEY.PVP_RANK, 0, roleId);
result.push({
pos, roleId, roleName, headHid, sHid, pLv, defCe,
addScore: dicOpponent.score,
rankLv: 0, // TODO读取排名
rankLv, // 读取排名
plusScore: getPlusScore(winStreakNum)
});
}
@@ -108,7 +115,9 @@ async function matchPlayer(oppPlayers: OppPlayers[], roleId: string, pLv: number
let result = range[index];
if(!result) return false;
if(result.roleId == roleId) {
console.log(range.length)
range.splice(index, 1);
console.log(JSON.stringify(range))
if(range.length <= 0) return false;
index = getRandomIndexByLen(range.length);
result = range[index];

View File

@@ -1,10 +1,12 @@
import { GOOD_QUALITY } from './../consts';
import { RoleModel } from "../db/Role";
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 { RankParam } from '../pubUtils/interface';
/**
* 在服务重新启动时将信息存入redis
@@ -13,6 +15,7 @@ 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.PVP_RANK);
for(let {id} of serverList) {
await client.delAsync(getKeyName(REDIS_KEY.TOWER_RANK, id));
await initRank(id);
@@ -23,13 +26,27 @@ 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);
let ranks = await RoleModel.getRank('tower', serverId, ['roleId', 'roleName', 'towerLv', 'lv', 'vLv']);
for(let {towerLv, roleId, roleName, lv, vLv, towerUpTime} of ranks) {
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);
await redisUserInfoAdd(roleId, {roleName, lv, vLv, guildName:"", head: "zhaoyun"});
let rankPram = new RankParam(roleName, lv, vLv, headHid, sHid, title);
await redisUserInfoAdd(roleId, rankPram);
}
let pvpRank = await PvpDefenseModel.getRank();
for(let {roleId, role: _role, score, updatedAt } of pvpRank) {
let role = <RoleType>_role;
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(roleId, rankPram);
}
}
}
@@ -40,8 +57,12 @@ export async function existsRank(key: string, serverId: number) {
return result;
}
export function getKeyName(key: string, serverId: number) {
return `${key}:${serverId}`;
export function getKeyName(key: string, serverId?: number) {
if(serverId) {
return `${key}:${serverId}`;
} else {
return key;
}
}
// 更新玩家信息
@@ -58,21 +79,21 @@ export async function redisUserInfoUpdate(roleId: string, arr: Array<{field: str
}
// 添加玩家信息缓存
export async function redisUserInfoAdd(roleId: string, params: {roleName: string, lv: number, vLv: number, guildName: string, head: string}) {
export async function redisUserInfoAdd(roleId: string, params: RankParam) {
const client: Redis.RedisClient = pinus.app.get('redis');
let value = JSON.stringify(params);
return await client.hsetAsync(REDIS_KEY.USER_INFO, roleId, value);
}
// 更新排行榜
export async function setRank(key: string, serverId: number, roleId: string, score: number, timestamp: number, params: {roleName: string, lv: number, vLv: number, guildName: string, head: string}) {
// 更新排行榜, 如pvp这样跨服的serverId传0
export async function setRank(key: string, serverId: number, roleId: string, score: number, timestamp: number, params: RankParam, limit = 100) {
const client: Redis.RedisClient = pinus.app.get('redis');
// 更新分数
const _score = encodeScoreWithTime(score, timestamp);
await client.zaddAsync(getKeyName(key, serverId), _score, roleId);
// 移除100名以外
await client.zremrangebyrankAsync(getKeyName(key, serverId), 100, 10000);
await client.zremrangebyrankAsync(getKeyName(key, serverId), limit, 10000);
// 如果没有信息,更新玩家信息
const hasCurUser = await client.hexistsAsync(REDIS_KEY.USER_INFO, roleId);
if(!hasCurUser) {
@@ -99,16 +120,23 @@ export async function getRank(key: string, serverId: number, roleId: string) {
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;
}
// 有序排行综合时间和得分排序
function encodeScoreWithTime(score: number, timestamp: number): number {
// value = score * Math.power(10, 14) + max_time - timestamp
let timelen = 13;
let timelen = 10;
let pow = Math.pow(10, timelen + 1);
return score * pow + pow - 1 - timestamp
return score * pow + pow - 1 - Math.floor(timestamp/1000)
}
function decodeScoreWithTime(num: string): number {
let timelen = 13;
let timelen = 10;
let pow = Math.pow(10, timelen + 1);
let _num = parseInt(num);
return Math.floor(_num/pow);
@@ -184,7 +212,7 @@ export async function checkRoleInQueue(roleId: string, sid: string, qualityArr:
export async function clearComBtlQueue() {
const client: Redis.RedisClient = pinus.app.get('redis');
for (let q of GOOD_QUALITY) {
client.delAsync(`${REDIS_KEY.COM_TEAM_SEARCH_PRE}${q}`);
await client.delAsync(`${REDIS_KEY.COM_TEAM_SEARCH_PRE}${q}`);
}
}

View File

@@ -17,6 +17,8 @@ declare module 'redis' {
hsetAsync(key: string, field: string, value: string): Promise<number>;
// 获取存储在哈希表中指定字段的值
hgetAsync(key: string, field: string): Promise<string>;
// 获取存储在哈希表中指定字段的值。
hexistsAsync(key: string, field: string): Promise<number>;
// 移除并返回集合中的一个随机元素
spopAsync(key: string, count?: number): Promise<number>;
@@ -35,8 +37,10 @@ declare module 'redis' {
zcardAsync(key: string): Promise<number>;
// 移除有序集合中给定的排名区间的所有成员
zremrangebyrankAsync(key: string, start: number, stop: number): Promise<number>;
// 获取存储在哈希表中指定字段的值。
hexistsAsync(key: string, field: string): Promise<number>;
// 返回有序集中指定成员的排名,从小到大
zrankAsync(key: string, field: string): Promise<number>;
// 返回有序集中指定成员的排名,从大到小
zrevrankAsync(key: string, field: string): Promise<number>;
}
export interface Multi extends Commands<Multi> {
execAsync(...args: any[]): Promise<any>;

View File

@@ -220,6 +220,7 @@ export const REDIS_KEY = {
USER_INFO: "userInfo", // 玩家缓存信息
TOWER_RANK: "towerRank", // 天梯排行榜
COM_TEAM_SEARCH_PRE: 'comTeamSerQ', // 匹配中的玩家,按品质分
PVP_RANK: "pvpRank", // pvp排行榜
}
export const FUNC_OPT_TYPE = {

View File

@@ -321,6 +321,7 @@ export const REDIS_KEY = {
USER_INFO: "userInfo", // 玩家缓存信息
TOWER_RANK: "towerRank", // 天梯排行榜
COM_TEAM_SEARCH_PRE: 'comTeamSerQ', // 匹配中的玩家,按品质分
PVP_RANK: 'pvpRank'
}
export const FUNC_OPT_TYPE = {

View File

@@ -226,6 +226,14 @@ export default class PvpDefense extends BaseModel {
let result: Array<PvpDefenseType> = await PvpDefenseModel.find().sort({ score: -1 }).limit(limitNum).lean(lean);
return result;
}
public static async getRank(page = 1, limit = 1000, lean = true) {
let sortBy = { score: -1, updatedAt: 1 };
const ranks: PvpDefenseType[] = await PvpDefenseModel.find().limit(limit).skip(page - 1).select('roleId role score updatedAt')
.populate('role', 'roleId roleName headHid sHid title lv vLv')
.sort(sortBy).limit(limit).skip((page - 1) * limit).lean(lean);
return ranks;
}
}
export const PvpDefenseModel = getModelForClass(PvpDefense);

View File

@@ -95,4 +95,24 @@ export interface pvpEndParamInter {
damage: number;
heal: number;
underDamage: number;
}
export class RankParam {
roleName: string;
lv: number;
vLv: number;
guildName: string = "";
head: string = 'zhaoyun';
headHid: number = 19;
sHid: number = 19;
title: number;
constructor(roleName: string, lv: number, vLv: number, headHid: number, sHid: number, title: number) {
this.roleName = roleName;
this.lv = lv;
this.vLv = vLv;
this.headHid = headHid;
this.sHid = sHid;
this.title = title;
}
}