军团活动:排行榜&军功上报

This commit is contained in:
luying
2021-03-18 11:59:53 +08:00
parent 4340f9a0b0
commit 9a463afd0d
27 changed files with 1963 additions and 29 deletions
@@ -0,0 +1,185 @@
import { Application, BackendSession, ChannelService } from "pinus";
import { setMedianCe, getMedianCe, getGuildActivityStatus, getRecordScore } from "../../../services/guildActivityService";
import { resResult } from "../../../pubUtils/util";
import { STATUS, GUILD_ACTIVITY_TYPE } from "../../../consts";
import { GameModel } from "../../../db/Game";
import { ServerlistModel } from "../../../db/Serverlist";
import { UserGuildActivityRecModel, Record } from "../../../db/UserGuildActivityRec";
import { UserGuildModel } from "../../../db/UserGuild";
import { GUILDACTIVITY } from "../../../pubUtils/dicParam";
import { GuildActivityRecordModel } from "../../../db/GuildActivityRec";
import { RoleModel, RoleType } from "../../../db/Role";
import { GuildModel } from "../../../db/Guild";
import { RankParam, GuildRankParam } from "../../../domain/rank";
import { setRank, getGuildKeyName, getRankScore, getRank, getUnionRank } from "../../../services/redisService";
import { REDIS_KEY } from "../../../consts";
export default function (app: Application) {
return new GateActivityHandler(app);
}
export class GateActivityHandler {
channelService: ChannelService;
constructor(private app: Application) {
this.channelService = app.get('channelService');
}
private gateHp: Map<string, number> = new Map(); // 城门血条,每个军团有一条血条
private members: Map<string, Array<string>> = new Map(); // 每个军团参与的成员
private aid = GUILD_ACTIVITY_TYPE.GATE_ACTIVITY; // 蛮夷入侵id
// 进入蛮夷入侵界面
async getGateActivity(msg: {}, session: BackendSession) {
const roleId = session.get('roleId');
const roleName = session.get('roleName');
const serverId = session.get('serverId');
const guildCode = session.get('guildCode');
let statusResult = getGuildActivityStatus(this.aid);
if(!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND);
let guildActivityRec = await GuildActivityRecordModel.getRecord(guildCode, serverId, this.aid);
if(!guildActivityRec) return resResult(STATUS.INTERNAL_ERR);
let { code: sourceCode } = guildActivityRec;
let myGuildActivityRec = await UserGuildActivityRecModel.getRecord(roleId, roleName, guildCode, serverId, sourceCode, [], this.aid);
let { challengeCnt } = myGuildActivityRec;
// TODO 处理多余字段
let {ranks: guildRank, myRank: myGuildRank} = await getUnionRank(REDIS_KEY.GUILD_ACTIVITY, serverId, guildCode, 10);
let {ranks: memberRank, myRank: myMemberRank} = await getRank(getGuildKeyName(REDIS_KEY.USER_GUILD_ACTIVITY, guildCode), serverId, roleId);
if(!myGuildRank) {
let guild = await GuildModel.findByCode(guildCode, serverId, 'name');
myGuildRank = {
rank: 0,
code: guildCode,
name: guild.name,
num: 0
}
}
if(!myMemberRank) {
myMemberRank = {
rank: 0,
roleId,
roleName,
num: 0
}
}
return resResult(STATUS.SUCCESS, {
...statusResult,
challengeCnt: GUILDACTIVITY.GATEACTIVITY_CHALLENGE_TIMES - challengeCnt,
guildRank, myGuildRank,
memberRank, myMemberRank
})
}
// 开启挑战
async checkBattle(msg: { heroes: number[] }, session: BackendSession) {
let { heroes } = msg;
if(!heroes || heroes.length <= 0) return resResult(STATUS.WRONG_PARMS);
const roleId = session.get('roleId');
const roleName = session.get('roleName');
const serverId = session.get('serverId');
const guildCode = session.get('guildCode');
if(!guildCode) return resResult(STATUS.GUILD_AUTH_NOT_ENOUGH);
let statusResult = getGuildActivityStatus(this.aid);
if(!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND);
let guildActivityRec = await GuildActivityRecordModel.getRecord(guildCode, serverId, this.aid);
if(!guildActivityRec) return resResult(STATUS.INTERNAL_ERR);
let { code: sourceCode } = guildActivityRec;
let myGuildActivityRec = await UserGuildActivityRecModel.getRecord(roleId, roleName, guildCode, serverId, sourceCode, heroes, this.aid);
let { code, challengeCnt } = myGuildActivityRec;
// 返回当前军团总军功
let guildScore = await getRankScore(REDIS_KEY.GUILD_ACTIVITY, serverId, guildCode);
// 城门血条
let gateHp = this.gateHp.get(guildCode)||GUILDACTIVITY.GATEACTIVITY_GATEHP;
// 前一天中位数战力
let medianCe = await getMedianCe(serverId);
return resResult(STATUS.SUCCESS, {
code,
...statusResult,
guildScore: guildScore||0,
myScore: 0,
gateHp,
challengeCnt: GUILDACTIVITY.GATEACTIVITY_CHALLENGE_TIMES - challengeCnt,
medianCe
});
}
// 上报玩家获得军功
async action(msg: { code: string, record: { round: number, dataId: number }[] }, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let guildCode = session.get('guildCode');
let serverId = session.get('serverId');
let { code, record } = msg;
let statusResult = getGuildActivityStatus(this.aid);
if(!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND);
// 计算record内得分
// TODO 防刷分,应加入记录玩家操作
let scoreResult = getRecordScore(this.aid, record);
if(!scoreResult) return resResult(STATUS.DIC_DATA_NOT_FOUND);
let { score, newRecords } = scoreResult;
// 更新redis数据
let role = await RoleModel.findByRoleId(roleId);
let { lv, vLv, head, frame, spine, title } = role;
let userParam = new RankParam(roleName, lv, vLv, head, frame, spine, title);
let myScore = await setRank(getGuildKeyName(REDIS_KEY.USER_GUILD_ACTIVITY, guildCode), serverId, guildCode, score, Date.now(), userParam);
let guild = await GuildModel.findByCode(guildCode, serverId);
let leader = <RoleType>guild.leader;
let params = new GuildRankParam(guild.icon, guild.name, guild.lv, leader);
let guildScore = await setRank(REDIS_KEY.GUILD_ACTIVITY, serverId, guild.code, 0, Date.now(), params, true);
// 更新数据库
let rec = await UserGuildActivityRecModel.pushRecord(code, newRecords);
let gateHp = this.gateHp.get(guildCode)||GUILDACTIVITY.GATEACTIVITY_GATEHP;
return resResult(STATUS.SUCCESS, {
code: rec.code,
...statusResult,
guildScore: guildScore||0,
myScore: myScore||0,
gateHp
});
}
// 上报敌军攻打城门情况
async hitGate(msg: {}, session: BackendSession) {
}
// 结束挑战
async battleEnd(msg: { count: number }, session: BackendSession) {
}
// TODO debug接口,可以不用
async newServer() {
let serverlist = await GameModel.getAllServerList();
for(let server of serverlist) {
await ServerlistModel.newServer(server.id, server.serverType, {...server});
}
}
async test() {
let result = await setMedianCe();
return resResult(STATUS.SUCCESS, {result});
}
}
@@ -0,0 +1,106 @@
import { ServerlistModel } from "../db/Serverlist";
import { RoleModel } from "../db/Role";
import { reduceCe } from "../pubUtils/util";
import { GUILDACTIVITY } from "../pubUtils/dicParam";
import { gameData } from "../pubUtils/data";
import { getCurHourPoint, getCutDay, nowSeconds } from "../pubUtils/timeUtil";
import { GUILD_ACTIVITY_STATUS } from "../consts";
import { Record } from "../db/UserGuildActivityRec";
/**
* 定时任务,获得前一天的活跃玩家中位数武将战力
*/
export async function setMedianCe() {
let servers = await ServerlistModel.getAllServerList();
for(let server of servers) {
if(server.serverType == 'official') {
let medianRole = await RoleModel.getMedianRole(server.id);
if(!medianRole) {
await ServerlistModel.updateByServerId(server.id, { medianCe: GUILDACTIVITY.GATEACTIVITY_ENEMYCE });
} else {
let { topLineup } = medianRole;
let topHero = topLineup.sort((a, b) => b.ce - a.ce);
let medianCe = reduceCe(topHero[0].ce);
await ServerlistModel.updateByServerId(server.id, { medianCe });
}
}
}
return servers;
}
/**
* 获得本服前一天活跃玩家中位数玩家的最强武将战力
* @param serverId
*/
export async function getMedianCe(serverId: number) {
let server = await ServerlistModel.findByServerId(serverId);
if(server) {
return server.medianCe;
}
return GUILDACTIVITY.GATEACTIVITY_ENEMYCE;
}
/**
* 根据当前时间判断军团活动倒计时
* @param id 军团活动id
*/
export function getGuildActivityStatus(id: number) {
let dicGuildActivity = gameData.guildActivity.get(id);
if(!dicGuildActivity) return false;
let startTime = getCurHourPoint(dicGuildActivity.startTime);
let countdownTime = getCurHourPoint(dicGuildActivity.countDown);
let { duringTime, openDay } = dicGuildActivity;
let endTime = startTime + duringTime;
let status = 0, time = 0;
let now = nowSeconds();
if(now >= countdownTime && now < startTime) {
status = GUILD_ACTIVITY_STATUS.WAITING;
time = startTime - now;
} else if (now >= startTime && now < endTime) {
status = GUILD_ACTIVITY_STATUS.START;
time = endTime - now;
} else {
status = GUILD_ACTIVITY_STATUS.END;
}
let day = getCutDay();
let isOpen = openDay.includes(day);
return {
status, time, isOpen
}
}
export function getRecordScore(aid: number, record: { round: number, dataId: number }[]) {
let dicGuildActivity = gameData.guildActivity.get(aid);
console.log('xxxx', dicGuildActivity.warid)
if(!dicGuildActivity) return false;
let dicWarJson = gameData.warJson.get(dicGuildActivity.warid);
if(!dicWarJson) return false;
let curRound = record[0]?.round;
let sum = 0, newRecords = new Array<Record>()
for(let {round, dataId} of record) {
let enemy = dicWarJson.find(cur => cur.dataId == dataId);
console.log('yyyy', enemy.enemyType);
let enemyType = enemy.enemyType;
let score = gameData.gateActivityPoint.get(enemyType);
newRecords.push({ round, dataId, score});
sum += score;
console.log('***%*****', sum, score)
if(round != curRound) { // 每回合开始得10分
sum += gameData.gateActivityPoint.get(1);
console.log('***%%*****', sum)
curRound = round;
}
}
console.log('***&**', sum)
return { score: sum, newRecords }
}
+148 -13
View File
@@ -10,7 +10,7 @@ import { SystemConfigModel } from '../db/SystemConfig';
import { GuildRankParam, GuildLeader, RankParam } from '../domain/rank';
import { GuildModel } from '../db/Guild';
import { comBtlRanges } from '../pubUtils/gamedata';
import { stringify } from 'querystring';
import { getNextHourPoint } from '../pubUtils/timeUtil';
/**
* 在服务重新启动时,将信息存入redis
*/
@@ -103,14 +103,28 @@ export async function existsRank(key: string, serverId: number) {
return result;
}
export function getKeyName(key: string, serverId?: number) {
/**
* 获得redis key的名字
* @param key REDIS_KEY中配置的key
* @param serverId 服务器id
* @param plus 后面再加
*/
export function getKeyName(key: string, serverId?: number, plus: string = '') {
let newKey = '';
if(serverId) {
return `${key}:${serverId}`;
newKey = `${key}:${serverId}`;
} else {
return key;
newKey = key;
}
if(plus) {
newKey += `:${plus}`;
}
return newKey;
}
// 更新玩家信息
export async function redisUserInfoUpdate(key: string, roleId: string, arr: Array<{field: string, value:(string|number|GuildLeader)}>) {
let params = await redisClient().hgetAsync(key, roleId);
@@ -129,16 +143,31 @@ export async function redisUserInfoAdd(key: string, roleId: string, params: Rank
return await redisClient().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) {
/**
* 更新排行榜
* @param key 配置在REDIS_KEY中的key
* @param serverId 区服id 如pvp这样跨服的,serverId传0
* @param myId 玩家roleId或军团code
* @param score 得分
* @param timestamp 时间 13位时间戳
* @param params 玩家数据
* @param isAtom 是否是原子性的更新
* @param limit
*/
export async function setRank(key: string, serverId: number, myId: string, score: number, timestamp: number, params: RankParam|GuildRankParam, isAtom = false, limit = 100) {
// 更新分数
const _score = encodeScoreWithTime(score, timestamp);
await redisClient().zaddAsync(getKeyName(key, serverId), _score, myId);
let newScore = score;
if(isAtom) {
newScore = await updateRankAtom(key, serverId, myId, score, timestamp);
} else {
const _score = encodeScoreWithTime(score, timestamp);
await redisClient().zaddAsync(getKeyName(key, serverId), _score, myId);
}
// 移除100名以外
await redisClient().zremrangebyrankAsync(getKeyName(key, serverId), limit, 10000);
let infoKey = REDIS_RANK_TO_INFO.get(key);
let infoKey = REDIS_RANK_TO_INFO.get(key)||REDIS_KEY.USER_INFO;
// 如果没有信息,更新玩家信息
const hasCurUser = await redisClient().hexistsAsync(infoKey, myId);
if(!hasCurUser) {
@@ -148,12 +177,12 @@ export async function setRank(key: string, serverId: number, myId: string, score
}
// 获取排行榜
export async function getRank(key: string, serverId: number, roleId: string) {
export async function getRank(key: string, serverId: number, roleId: string, limit = 100) {
let ranks = [], myRank = null;
const rankFromDb = await redisClient().zrevrangebyscoreAsync(getKeyName(key, serverId), '+inf', '-inf', "WITHSCORES", "LIMIT", 0, 100);
const rankFromDb = await redisClient().zrevrangebyscoreAsync(getKeyName(key, serverId), '+inf', '-inf', "WITHSCORES", "LIMIT", 0, limit);
let infoKey = REDIS_RANK_TO_INFO.get(key);
let infoKey = REDIS_RANK_TO_INFO.get(key)||REDIS_KEY.USER_INFO;
for(let ii = 0; ii < rankFromDb.length; ii+=2) {
const _roleId = rankFromDb[ii];
@@ -496,6 +525,20 @@ async function setServerList() {
}
}
export async function getAllServers() {
let servers = await redisClient().hgetallAsync(REDIS_KEY.DB_GAME);
let serverlist = new Array<number>();
for(let serverStr in servers) {
let arr = serverStr.split('_');
let serverId = parseInt(arr[1]);
if(!isNaN(serverId) && !serverlist.includes(serverId)) {
serverlist.push(serverId);
}
}
return serverlist;
}
export async function getServerName(serverType: string, serverId: number) {
let name = await redisClient().hgetAsync(REDIS_KEY.DB_GAME, `${serverType}_${serverId}`);
return name
@@ -506,4 +549,96 @@ function redisClient() {
return client;
}
/**************** 数据库表end */
/**************** 数据库表end */
/**************** 军团活动排行 */
/**
* 更新排行榜(将得分和时间拆分开)
* @param key 配置在REDIS_KEY中的key
* @param serverId 区服id
* @param field 玩家id/军团id
* @param score 得分
* @param time 事件
*/
async function updateRankAtom(key: string, serverId: number, field: string, score: number, timestamp: number) {
let originKey = getKeyName(key, serverId);
let timeKey = getKeyName(key, serverId, 'time');
await redisClient().expireatAsync(originKey, getNextHourPoint(5));
await redisClient().expireatAsync(timeKey, getNextHourPoint(5));
let timelen = 10;
let pow = Math.pow(10, timelen + 1);
let newScore = await redisClient().zincrbyAsync(originKey, score, field);
await redisClient().zaddAsync(timeKey, pow - 1 - Math.floor(timestamp/1000), field);
return newScore;
}
async function generateUnionRank(key: string, serverId: number) {
let unionKey = getKeyName(key, serverId, 'union'); // 联合的key
let existsKey = await redisClient().existsAsync(unionKey);
if(!existsKey) {
await redisClient().expireatAsync(unionKey, 10); // 10秒更新一次
let originKey = getKeyName(key, serverId);
let timeKey = getKeyName(key, serverId, 'time');
let timelen = 10;
let pow = Math.pow(10, timelen + 1);
await redisClient().zunionstoreAsync(unionKey, 2, originKey, timeKey, 'WEIGHTS', pow, 1);
}
return unionKey;
}
/**
* 使用updateRankAtom更新的排行榜,使用这个方法获取列表
* @param key 配置在REDIS_KEY中的key
* @param serverId 区服id
* @param roleId 自己的id
*/
export async function getUnionRank(key: string, serverId: number, roleId: string, limit = 100) {
let ranks = [], myRank = null;
let unionKey = await generateUnionRank(key, serverId);
const rankFromDb = await redisClient().zrevrangebyscoreAsync(unionKey, '+inf', '-inf', "WITHSCORES", "LIMIT", 0, limit);
let infoKey = REDIS_RANK_TO_INFO.get(key)||REDIS_KEY.USER_INFO;
for(let ii = 0; ii < rankFromDb.length; ii+=2) {
const _roleId = rankFromDb[ii];
const _score = decodeScoreWithTime(rankFromDb[ii + 1]);
const info = await redisClient().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 getMyUnionRank(key: string, serverId: number, roleId: string) {
let unionKey = await generateUnionRank(key, serverId);
let myRank = await redisClient().zrevrankAsync(unionKey, roleId);
return myRank + 1;
}
export async function getRankScore(key: string, serverId: number, field: string) {
let score = await redisClient().zscoreAsync(getKeyName(key, serverId), field);
return score;
}
/**
* 按军团名拼接key
* @param key
* @param guildCode 军团编号
*/
export function getGuildKeyName(key: string, guildCode: string) {
return `${key}:${guildCode}`;
}
/**************** 军团活动排行end */
+29
View File
@@ -42,6 +42,35 @@ export function battle(session: Session, msg: any, app: Application, cb: (err: E
cb(null, res.id);
}
export function guild(session: Session, msg: any, app: Application, cb: (err: Error , serverId ?: string) => void) {
let guildServers = app.getServersByType('guild');
if(!guildServers || guildServers.length === 0) {
cb(new Error('can not find guild servers.'));
return;
}
let rid = session.get('roleId');
if (msg.args && msg.args.length > 0) {
for (let arg of msg.args) {
if (!arg.route) continue;
if (['guild.gateActivityHandler.checkBattle', // 军团活动蛮夷入侵路由,按军团路由
'guild.gateActivityHandler.action',
'guild.gateActivityHandler.hitGate',
'guild.gateActivityHandler.battleEnd'
].indexOf(arg.route) !== -1) {
rid = session.get('guildCode');
}
}
}
let res = dispatch(rid, guildServers);
cb(null, res.id);
}
export function gm(session: Session, msg: any, app: Application, cb: (err: Error , serverId ?: string) => void) {
let gmServers = app.getServersByType('gm');
+3
View File
@@ -19,4 +19,7 @@ module.exports = [{
}, {
'type': 'systimer',
'token': 'agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn'
}, {
'type': 'guild',
'token': 'agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn'
}];
+8
View File
@@ -10,6 +10,8 @@ declare module 'redis' {
delAsync(key: string): Promise<number>;
// 设置过期时间
expireAsync(key: string, time: number): Promise<number>;
// 按时间戳设置过期时间
expireatAsync(key: string, time: number): Promise<number>;
// string类型存储
setexAsync(key: string, time: number, value: string): Promise<number>;
// 获取存储string类型value
@@ -52,6 +54,12 @@ declare module 'redis' {
zrevrankAsync(key: string, field: string): Promise<number>;
// 命令返回有序集中,指定区间内的成员,从大到小
zrevrangeAsync(key: string, start: number, end: number): Promise<string[]>;
// 对有序集合中指定成员的分数加上增量 increment
zincrbyAsync(key: string, increment: number, member: string): Promise<number>;
// 对有序集合中指定成员的分数加上增量 increment
zunionstoreAsync(destination: string, numkeys: number, ...args: any): Promise<number>;
// 返回有序集中,成员的分数值
zscoreAsync(key: string, member: string): Promise<number>;
// 显示集合成员
smembersAsync(key: string): Promise<string[]>;
// 增加集合成员
+20
View File
@@ -43,6 +43,10 @@ module.exports = {
],
'systimer': [
{'id': 'systimer-server-1', 'host': '127.0.0.1', 'port': 6056, "args": " --inspect=9233"}
],
'guild': [
{'id': 'guild-server-1', 'host': '127.0.0.1', 'port': 6057, "args": " --inspect=9238"},
{'id': 'guild-server-2', 'host': '127.0.0.1', 'port': 6058, "args": " --inspect=9238"}
]
},
'production': {
@@ -75,6 +79,10 @@ module.exports = {
],
'systimer': [
{'id': 'systimer-server-1', 'host': '127.0.0.1', 'port': 6056}
],
'guild': [
{'id': 'guild-server-1', 'host': '127.0.0.1', 'port': 6057, "args": " --inspect=9238"},
{'id': 'guild-server-2', 'host': '127.0.0.1', 'port': 6058, "args": " --inspect=9238"}
]
},
'alpha': {
@@ -109,6 +117,10 @@ module.exports = {
],
'systimer': [
{'id': 'systimer-server-1', 'host': '127.0.0.1', 'port': 6056}
],
'guild': [
{'id': 'guild-server-1', 'host': '127.0.0.1', 'port': 6057, "args": " --inspect=9238"},
{'id': 'guild-server-2', 'host': '127.0.0.1', 'port': 6058, "args": " --inspect=9238"}
]
},
'dev': {
@@ -144,6 +156,10 @@ module.exports = {
],
'systimer': [
{'id': 'systimer-server-1', 'host': '127.0.0.1', 'port': 6056, "args": " --inspect=9233"}
],
'guild': [
{'id': 'guild-server-1', 'host': '127.0.0.1', 'port': 6057, "args": " --inspect=9238"},
{'id': 'guild-server-2', 'host': '127.0.0.1', 'port': 6058, "args": " --inspect=9238"}
]
},
'isbn': {
@@ -176,6 +192,10 @@ module.exports = {
],
'systimer': [
{'id': 'systimer-server-1', 'host': '127.0.0.1', 'port': 6056}
],
'guild': [
{'id': 'guild-server-1', 'host': '127.0.0.1', 'port': 6057, "args": " --inspect=9238"},
{'id': 'guild-server-2', 'host': '127.0.0.1', 'port': 6058, "args": " --inspect=9238"}
]
}
};