diff --git a/game-server/app.ts b/game-server/app.ts index b5e855b75..2627a1996 100644 --- a/game-server/app.ts +++ b/game-server/app.ts @@ -8,7 +8,7 @@ import { RouteRecord } from 'pinus'; import * as mongoose from 'mongoose'; -import * as redis from 'redis'; +import * as redisService from './app/services/redisService'; import './app/servers/user.rpc.define' import * as routeUtil from './app/util/routeUtil'; import { preload } from './preload'; @@ -55,17 +55,7 @@ mongoose.connect(mongoAddr, { useNewUrlParser: true, useUnifiedTopology: true }, } }); -// 创建 redis 连接 -const client = redis.createClient(6379, redisArr, {detect_buffers: true}); -client.auth('zyz_2020', (err, reply) => { - if (err) { - console.log('redis err', err); - } else { - console.log('redis suc'); - } -}) -client.set('hello', 'redis', redis.print); - +redisService.initRank(); /** * Init app for client. */ diff --git a/game-server/app/servers/battle/handler/towerBattleHandler.ts b/game-server/app/servers/battle/handler/towerBattleHandler.ts index cd9729bfd..3196a431e 100644 --- a/game-server/app/servers/battle/handler/towerBattleHandler.ts +++ b/game-server/app/servers/battle/handler/towerBattleHandler.ts @@ -1,5 +1,5 @@ import { STATUS } from './../../../consts/statusCode'; -import { HANG_UP_CONSTS, GOLD_COST_RATIO, TOWER_TASK_CONST } from './../../../consts/consts'; +import { HANG_UP_CONSTS, GOLD_COST_RATIO, TOWER_TASK_CONST, REDIS_KEY } from './../../../consts/consts'; import { TowerTaskRecModel } from './../../../db/TowerTaskRec'; import { HangUpSpdUpRecModel } from './../../../db/HangUpSpdUpRec'; import { HangUpRecordModel } from './../../../db/HangUpRecord'; @@ -11,6 +11,7 @@ import { decodeArrayStr, resResult, shouldRefresh, calculateNum, getRefTime, gen import { calcuHangUpReward, checkTaskConditions, checkHangUpSpdUpCnt, createCurTasks, treatTask, getRemainTime, getTasksReward, getTaskStatus, getDoingOrWaitingTasks } from '../../../services/battleService'; import { handleFixedReward, handleReward } from '../../../services/rewardService'; import { checkBattleHeroes } from '../../../services/normalBattleService'; +import { getRank, setRank } from '../../../services/redisService'; export default function(app: Application) { return new TowerBattleHandler(app); @@ -29,7 +30,11 @@ export class TowerBattleHandler { let { towerLv } = await RoleModel.findByRoleId(roleId); if (!towerLv) { towerLv = 1; - await RoleModel.towerLvUp(roleId); + let role = await RoleModel.towerLvUp(roleId); + // 更新redis + let { roleName, towerUpTime, lv, vLv } = role; + await setRank(REDIS_KEY.TOWER_RANK, roleId, towerLv, towerUpTime.getTime(), {roleName, lv, vLv, guildName:"", head: "zhaoyun"}); + } let towerRec = await TowerRecordModel.getRecordByLv(roleId, towerLv); if (!towerRec) { @@ -303,17 +308,7 @@ export class TowerBattleHandler { let roleId = session.get('roleId'); let roleName = session.get('roleName'); - let ranks = await RoleModel.getRank('tower', ['roleId', 'roleName', 'towerLv', 'lv', 'vLv']); - let resultRanks = ranks.map((cur, index) => { - let { roleId, roleName, towerLv, lv, vLv } = cur; - return {rank: index+1, roleId, roleName, lv, vLv, guildName: "", head: "zhaoyun", num: towerLv, str: ''} - }); - let myRank = null; - for(let r of resultRanks) { - if(r.roleId == roleId) { - myRank = r; break - } - } + let {ranks, myRank} = await getRank(REDIS_KEY.TOWER_RANK, roleId); if(!myRank) { let role = await RoleModel.findByRoleId(roleId); let {towerLv, lv, vLv} = role; @@ -322,6 +317,6 @@ export class TowerBattleHandler { } } - return resResult(STATUS.SUCCESS, { ranks: resultRanks, myRank }); + return resResult(STATUS.SUCCESS, { ranks, myRank }); } } diff --git a/game-server/app/services/battleService.ts b/game-server/app/services/battleService.ts index 3cd778814..98671dbfd 100644 --- a/game-server/app/services/battleService.ts +++ b/game-server/app/services/battleService.ts @@ -1,7 +1,7 @@ import { HeroModel } from './../db/Hero'; import { HangUpRecordModel } from './../db/HangUpRecord'; import { ChannelService } from 'pinus'; -import { HANG_UP_CONSTS, TOWER_TASK_CONST } from './../consts/consts'; +import { HANG_UP_CONSTS, TOWER_TASK_CONST, REDIS_KEY } from './../consts/consts'; import { BattleRecordModel } from './../db/BattleRecord'; import { TowerRecordModel } from './../db/TowerRecord'; import { RoleModel } from './../db/Role'; @@ -11,6 +11,7 @@ import { handleFixedReward } from './rewardService'; import { STATUS } from '../consts/statusCode'; import { HangUpSpdUpRecModel } from '../db/HangUpSpdUpRec'; import { TowerTaskRecModel } from '../db/TowerTaskRec'; +import { setRank } from './redisService'; export async function checkTowerWar(roleId: string, battleId: number, heroes: Array) { const battleIdStr = `${battleId}`; @@ -69,7 +70,11 @@ export async function towerBattleEnd(channelService: ChannelService, sid: string let newRec = await TowerRecordModel.updateRecord(roleId, towerLv, battleCode, battleId, heroes, inc); let towerReward = null; if (inc === 1) { - await RoleModel.towerLvUp(roleId); + let role = await RoleModel.towerLvUp(roleId); + // 更新redis + let { roleName, towerLv, towerUpTime, lv, vLv } = role; + await setRank(REDIS_KEY.TOWER_RANK, roleId, towerLv, towerUpTime.getTime(), {roleName, lv, vLv, guildName:"", head: "zhaoyun"}); + const nextTowerInfo = getTowerDataByLv(towerLv + 1); if (nextTowerInfo) { const { warArray } = nextTowerInfo; diff --git a/game-server/app/services/normalBattleService.ts b/game-server/app/services/normalBattleService.ts index 87f41437e..0fc93d70f 100644 --- a/game-server/app/services/normalBattleService.ts +++ b/game-server/app/services/normalBattleService.ts @@ -2,6 +2,7 @@ import { HeroModel } from '../db/Hero'; import Role, { RoleModel } from '../db/Role' import { getLvByExp, getExpByLv } from '../pubUtils/gamedata'; +import { redisUserInfoUpdate } from './redisService'; export async function roleLevelup(roleId: string, kingExp: number) { let role = await RoleModel.findByRoleId(roleId); @@ -9,6 +10,9 @@ export async function roleLevelup(roleId: string, kingExp: number) { let newExp = exp + kingExp; let newLv = getLvByExp(newExp); await RoleModel.levelup(roleId, newLv, newExp); + if(newLv > lv) { // 升级 + await redisUserInfoUpdate(roleId, [{field: 'lv', value: newLv}]) + } let actordata = []; for(let i = lv; i <= newLv; i++) { let {sum, cur} = getExpByLv(i); diff --git a/game-server/app/services/redisService.ts b/game-server/app/services/redisService.ts new file mode 100644 index 000000000..db819f0ed --- /dev/null +++ b/game-server/app/services/redisService.ts @@ -0,0 +1,161 @@ +import { RoleModel } from "../db/Role"; +import * as redis from 'redis'; +import {REDIS_KEY} from '../consts/consts' + +let redisArr = 'r-8vb4i2kgl91886fkxd.redis.zhangbei.rds.aliyuncs.com'; +if(process.env.NODE_ENV == 'local') { + redisArr = '127.0.0.1'; +} +// 创建 redis 连接 +const client = redis.createClient(6379, redisArr, {detect_buffers: true}); +client.auth('zyz_2020', (err, reply) => { + if (err) { + console.log('redis err', err); + } else { + console.log('redis suc'); + } +}) +client.set('hello', 'redis', redis.print); + +/** + * 在服务重新启动时,将信息存入redis + */ +export async function initRank() { + await redisDel(REDIS_KEY.TOWER_RANK); // 清空天梯排行榜(ZSet) + await redisDel(REDIS_KEY.USER_INFO); // 清空用户缓存信息 + let ranks = await RoleModel.getRank('tower', ['roleId', 'roleName', 'towerLv', 'lv', 'vLv']); + for(let {towerLv, roleId, roleName, lv, vLv, towerUpTime} of ranks) { + await redisZadd(REDIS_KEY.TOWER_RANK, encodeScoreWithTime(towerLv, towerUpTime?towerUpTime.getTime():0), roleId); + await redisUserInfoAdd(roleId, {roleName, lv, vLv, guildName:"", head: "zhaoyun"}); + } +} + +// 更新玩家信息 +export async function redisUserInfoUpdate(roleId: string, arr: Array<{field: string, value:(string|number)}>) { + let params = await redisHget(REDIS_KEY.USER_INFO, roleId); + if(params) { + let obj = JSON.parse(params); + for(let {field, value} of arr) { + obj[field] = value; + } + return await redisHset(REDIS_KEY.USER_INFO, roleId, JSON.stringify(obj)); + } +} + +// 添加玩家信息缓存 +export async function redisUserInfoAdd(roleId: string, params: {roleName: string, lv: number, vLv: number, guildName: string, head: string}) { + let value = JSON.stringify(params); + return await redisHset(REDIS_KEY.USER_INFO, roleId, value); +} + +// 更新排行榜 +export async function setRank(key: string, roleId: string, score: number, timestamp: number, params: {roleName: string, lv: number, vLv: number, guildName: string, head: string}) { + // 更新分数 + const _score = encodeScoreWithTime(score, timestamp); + await redisZadd(key, _score, roleId); + // 移除100名以外 + await redisZremRangeByRank(key, 0, 100); + // 如果没有信息,更新玩家信息 + const hasCurUser = await redisHexists(REDIS_KEY.USER_INFO, roleId); + if(!hasCurUser) { + await redisUserInfoAdd(roleId, params); + } + return true; +} + +// 获取排行榜 +export async function getRank(key: string, roleId: string) { + let ranks = [], myRank = null; + const rankFromDb = await redisZrevrangeByScore(key, '+inf', '-inf', true, 100); + for(let ii = 0; ii < rankFromDb.length; ii+=2) { + const _roleId = rankFromDb[ii]; + const _score = decodeScoreWithTime(rankFromDb[ii + 1]); + const userInfo = await redisHget(REDIS_KEY.USER_INFO, _roleId); + const _userInfo = JSON.parse(userInfo); + const tmp = {..._userInfo, roleId: _roleId, num: _score, rank: ii%2+1} + ranks.push(tmp); + if(roleId == _roleId) myRank = tmp; + } + return {ranks, myRank} +} + +// 有序排行综合时间和得分排序 +function encodeScoreWithTime(score: number, timestamp: number): number { + // value = score * Math.power(10, 14) + max_time - timestamp + let timelen = 13; + let pow = Math.pow(10, timelen + 1); + return score * pow + pow - 1 - timestamp +} + +function decodeScoreWithTime(num: string): number { + let timelen = 13; + let pow = Math.pow(10, timelen + 1); + let _num = parseInt(num); + return Math.floor(_num/pow); +} + + +////// redis方法 + +//// key + +// 在 key 存在时删除 key。 +export async function redisDel(key: string) { + return await createPromise('del', [key]); +} + +//// 有序集合 + +// 返回有序集中指定分数区间内的成员,分数从高到低排序 +export async function redisZrevrangeByScore(key: string, max: (number|string), min: (number|string), withscores: boolean, limit?:number, offset=0): Promise> { + let param = [key, max, min]; + if(withscores) param.push('WITHSCORES'); + if(limit) param.push('LIMIT', offset, limit); + return await createPromise('zrevrangebyscore', param); +} + +// 向有序集合添加一个或多个成员,或者更新已存在成员的分数 +export async function redisZadd(key: string, field: number, value: string) { + return await createPromise('zadd', [key, field, value]); +} + +// 获取有序集合的成员数 +export async function redisZcard(key: string) { + return await createPromise('zcard', [key]); +} + +// 移除有序集合中给定的排名区间的所有成员 +export async function redisZremRangeByRank(key: string, start: number, stop: number) { + return await createPromise('zremrangebyrank', [key, start, stop]); +} + +//// 哈希 + +// 将哈希表 key 中的字段 field 的值设为 value +export async function redisHset(key: string, field: string, value: string) { + return await createPromise('hset', [key, field, value]); +} + +// 获取存储在哈希表中指定字段的值。 +export async function redisHget(key: string, field: string) { + return await createPromise('hget', [key, field]); +} + +// 获取存储在哈希表中指定字段的值。 +export async function redisHexists(key: string, field: string) { + return await createPromise('hexists', [key, field]); +} + +///// 封装promise +function createPromise(type: string, params: Array):Promise { + return new Promise((resolve, reject) => { + const cb = function(err: Error, result: any) { + if(err) { + reject(err); + } else { + resolve(result); + } + }; + client[type].apply(client, [...params, cb]); + }); +} \ No newline at end of file diff --git a/shared/consts/consts.ts b/shared/consts/consts.ts index e532d4ddf..d5d5f100a 100644 --- a/shared/consts/consts.ts +++ b/shared/consts/consts.ts @@ -255,3 +255,8 @@ export const COM_BATTLE_ROBOT_HURT_RAND_RATE = 0.05; export const COM_BATTLE_FRDCNT_MAX = 100; // 情谊点掉落数量 export const COM_BATTLE_FRDCNT_DROP = 10; + +export const REDIS_KEY = { + USER_INFO: "userInfo", // 玩家缓存信息 + TOWER_RANK: "towerRank" // 天梯排行榜 +}