369 lines
15 KiB
TypeScript
369 lines
15 KiB
TypeScript
import {ApiCall} from 'tsrpc';
|
||
import {Wjjl} from '../module/collection_wjjl';
|
||
import {formatNpcData} from '../shared/fightControl/fightFun';
|
||
import {fightResult} from '../shared/fightControl/fightType';
|
||
import {hongdianVal} from '../shared/protocols/hongdian/PtlGet';
|
||
import {jjcChange} from '../shared/protocols/jjc/PtlOpen';
|
||
import {rankInfo} from '../shared/protocols/type';
|
||
import {PublicShared} from '../shared/public/public';
|
||
import {Queue} from '../shared/public/queue';
|
||
import {FightFun} from './fight';
|
||
import {UserFun} from './user';
|
||
import {sortEd} from '../module/redis'
|
||
|
||
const jjcQueue = new Queue();
|
||
|
||
/**++++++++++++++++++特别注意:redis内的排名数据"rank:jjc:sort"数组值均为唯一,不要手动修改为重复值,否则排名计算逻辑会不准确+++++++++++++++++++++++++ */
|
||
/**++++++++++++++++++mongodb rank表的update与insert对性能有影响时可删除,保留只为尽量保持原有逻辑的数据逻辑不改变*/
|
||
export class JJCFun {
|
||
/**当玩家挑战或者被挑战时不可被其他玩家挑战 */
|
||
static lockPlayer: k_v<boolean> = {};
|
||
|
||
static async getMyData(uid: string) {
|
||
let data = await G.mongodb.collection('jjc').findOne(
|
||
{uid: uid}
|
||
);
|
||
if (!data) {
|
||
data = (await G.mongodb.collection('jjc').findOneAndUpdate(
|
||
{uid: uid}, {
|
||
$set: {resetTime: G.time, receivedArr: [], buyFightNum: 0, useFightNum: 0, recordWin: 0}
|
||
}, {upsert: true, returnDocument: 'after'}
|
||
)).value
|
||
}
|
||
return data
|
||
}
|
||
|
||
// 设置挑战状态
|
||
static async setLockPlayer(uid, boolean) {
|
||
return boolean ? await G.redis.rawSet(`rank:jjc:lockPlayer:${uid}`, "1", {EX: 10}) : await G.redis.rawDel(`rank:jjc:lockPlayer:${uid}`);
|
||
}
|
||
|
||
// 获取挑战状态
|
||
static async getLockPlayer(uid) {
|
||
return await G.redis.rawGet(`rank:jjc:lockPlayer:${uid}`);
|
||
}
|
||
|
||
/**排名缓存 */
|
||
// static rankList: rankInfo[];
|
||
static uTimeOffset: number = 60;
|
||
|
||
/**
|
||
* 获取指定条数的rankList
|
||
* @param min
|
||
* @param max
|
||
* @param isUpdate 是否更新数据
|
||
*/
|
||
static async getRankList(min: number = 0, max: number = 50, isUpdate: boolean = true) {
|
||
// 获取指定排名用户uid
|
||
let sortInfo = await this.getRankListUid(min, max)
|
||
if (!sortInfo.length) return []
|
||
// 获取用户详细数据
|
||
let rankList = []
|
||
let updateArr = []
|
||
for (let i = 0; i < sortInfo.length; i++) {
|
||
let uid = sortInfo[i]
|
||
let rankInfo = await G.redis.hGet('rank:jjc:data', uid)
|
||
if (!rankInfo?.player) continue
|
||
rankList.push(rankInfo)
|
||
// 比对utime,判断是否更新数据
|
||
if (!rankInfo.player.isNpc) {
|
||
if (!rankInfo.utime || (rankInfo.utime && rankInfo.utime < (G.time - this.uTimeOffset))) {
|
||
updateArr.push(uid)
|
||
}
|
||
}
|
||
//if (!rankInfo.player.isNpc && rankInfo.utime && rankInfo.utime < (G.time - this.uTimeOffset)) updateArr.push(uid)
|
||
}
|
||
// 更新数据
|
||
if (isUpdate && updateArr.length > 0) {
|
||
let playerArrInfo = await G.mongodb.collection("user").find({uid: {$in: updateArr}}).toArray()
|
||
for (let i = 0; i < playerArrInfo.length; i++) {
|
||
let playerInfo = playerArrInfo[i]
|
||
let index = rankList.findIndex(x => x.player.uid == playerInfo.uid)
|
||
rankList[index].player = playerInfo
|
||
rankList[index].utime = G.time
|
||
this.updatePlayerData(playerInfo.uid, rankList[index]);
|
||
}
|
||
}
|
||
return rankList
|
||
}
|
||
|
||
/**
|
||
* 每10分钟刷新前一千名玩家的用户数据
|
||
* @param min
|
||
* @param max
|
||
* @param uTimeOffset
|
||
* @param isUpdate 是否更新数据
|
||
*/
|
||
// static async checkUpdatePlayer(min: number = 0, max: number = 1000, uTimeOffset: number = 600, isUpdate: boolean = true) {
|
||
// // 获取指定排名用户uid
|
||
// let sortInfo = await this.getRankListUid(min, max)
|
||
// if (!sortInfo.length) return []
|
||
// let updateArr = []
|
||
|
||
// let users = await G.redis.hGetAll('rank:jjc:data')
|
||
|
||
// for (let i = 0; i < sortInfo.length; i++) {
|
||
// let uid = sortInfo[i]
|
||
// let rankInfo = users[uid]
|
||
// if (!rankInfo?.player) continue
|
||
// // 比对utime,判断是否更新数据
|
||
// if (!rankInfo.player.isNpc) {
|
||
// if (!rankInfo.utime || (rankInfo.utime && rankInfo.utime < (G.time - uTimeOffset))) {
|
||
// updateArr.push(rankInfo)
|
||
// }
|
||
// }
|
||
// }
|
||
// // 更新数据
|
||
// if (isUpdate && updateArr.length > 0) {
|
||
// let playerArrInfo = await G.mongodb.collection("user").find({uid: {$in: updateArr.map(i => i.uid)}}).toArray()
|
||
// for (let i = 0; i < playerArrInfo.length; i++) {
|
||
// let playerInfo = playerArrInfo[i]
|
||
// let index = updateArr.findIndex(x => x.player.uid == playerInfo.uid)
|
||
// updateArr[index].player = playerInfo
|
||
// updateArr[index].utime = G.time
|
||
// this.updatePlayerData(playerInfo.uid, updateArr[index]);
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
/**
|
||
* 获取指定范围排名的用户,仅返回uid[]
|
||
* @param min
|
||
* @param max
|
||
* @return uid[]
|
||
*/
|
||
static async getRankListUid(min: number = 0, max: number = 50) {
|
||
let sortInfo = await G.redis.zRange('rank:jjc:sort', min, max - 1)
|
||
return sortInfo
|
||
}
|
||
|
||
/**
|
||
* 获取玩家排名
|
||
* @param uid
|
||
* @return number
|
||
*/
|
||
static async getRankSortByUid(uid: string): Promise<number> {
|
||
let rank = await G.redis.zRank('rank:jjc:sort', uid) // 原逻辑索引未+1,此处返回索引。
|
||
return rank === 0 || rank > 0 ? rank : -1
|
||
}
|
||
|
||
/**
|
||
* 获取指定玩家数据
|
||
* @param uid
|
||
* @return <rankInfo>
|
||
*/
|
||
static async getPlayerData(uid: string): Promise<rankInfo> {
|
||
let rankInfo = await G.redis.hGet('rank:jjc:data', uid) // 原逻辑索引未+1,此处返回索引。
|
||
if (!rankInfo || (!rankInfo.player.isNpc && rankInfo.utime && rankInfo.utime < (G.time - this.uTimeOffset))) {
|
||
// 新玩家数据,更新
|
||
if (!rankInfo) {
|
||
this.addNewPlayerSort(uid)
|
||
rankInfo = <rankInfo>{}
|
||
}
|
||
rankInfo.player = await G.mongodb.collection("user").findOne({uid: uid})
|
||
rankInfo.utime = G.time
|
||
this.updatePlayerData(uid, rankInfo)
|
||
}
|
||
return rankInfo
|
||
}
|
||
|
||
/**
|
||
* 获取指定排名玩家数据
|
||
* @param rank
|
||
* @return <rankInfo>
|
||
*/
|
||
static async getRankPlayerData(rank: number): Promise<rankInfo> {
|
||
// 函数内max存在-1操作,查自己需保持min=max
|
||
let rankList = await this.getRankList(rank, rank + 1)
|
||
let rankInfo = rankList[0]
|
||
return rankInfo || {}
|
||
}
|
||
|
||
// 写入新玩家排名
|
||
static async addNewPlayerSort(uid: string) {
|
||
let rankLen = await G.redis.zCard('rank:jjc:sort'); // 获取排名的总条数,确认新玩家的排名值(ps:总条数既新玩家排名,不需+1)
|
||
this.updatePlayerRank(<sortEd>{value: uid, score: rankLen})
|
||
}
|
||
|
||
/**更新玩家排名数据 */
|
||
static updatePlayerRank(sortEd: sortEd | sortEd[]) {
|
||
G.redis.zAdd('rank:jjc:sort', sortEd)
|
||
return
|
||
}
|
||
|
||
/**更新玩家数据 */
|
||
static updatePlayerData(uid: string, rankInfo: rankInfo) {
|
||
G.redis.hSet('rank:jjc:data', uid, rankInfo)
|
||
return
|
||
}
|
||
|
||
/**新玩家第一次进入或者排名变更 */
|
||
static async addRankInfo(rankInfo: rankInfo, changeInfo?: rankInfo) {
|
||
// jjcQueue.enqueue(async () => {
|
||
if (!changeInfo) {
|
||
// 空数据预防
|
||
if (!rankInfo) return
|
||
// redis写入新玩家数据
|
||
this.updatePlayerData(rankInfo.player.uid, rankInfo)
|
||
// redis写入新玩家排名
|
||
this.addNewPlayerSort(rankInfo.player.uid)
|
||
let rankLen = await G.redis.zCard('rank:jjc:sort'); // 获取排名的总条数,确认新玩家的排名值(ps:总条数既新玩家排名,不需+1)
|
||
// this.updatePlayerRank(<sortEd>{value: rankInfo.player.uid, score: rankLen})
|
||
Wjjl.setVal(rankInfo.player.uid, 'jjc_rank', rankLen);
|
||
//
|
||
G.mongodb.collection('rank').updateOne({type: 'jjc'}, {$push: {rankList: rankInfo}});
|
||
} else {
|
||
// 获取自己与对手的排名索引
|
||
const myIndex = await G.redis.zScore('rank:jjc:sort', rankInfo.player.uid)
|
||
const heIndex = await G.redis.zScore('rank:jjc:sort', changeInfo.player.uid)
|
||
|
||
if (myIndex <= heIndex) return;
|
||
|
||
Wjjl.setVal(rankInfo.player.uid, 'jjc_rank', heIndex + 1);
|
||
UserFun.activeHeadFrame(rankInfo.player.uid, 'jjc_rank', heIndex + 1);
|
||
// 交换更新排名索引值,无需更新数据
|
||
this.updatePlayerRank([
|
||
<sortEd>{value: rankInfo.player.uid, score: heIndex},
|
||
<sortEd>{value: changeInfo.player.uid, score: myIndex}
|
||
])
|
||
let change = G.mongodb.createTreeObj({
|
||
key: `rankList.${heIndex}`,
|
||
val: rankInfo
|
||
}, {key: `rankList.${myIndex}`, val: changeInfo});
|
||
G.mongodb.collection('rank').updateOne({type: 'jjc'}, {$set: change});
|
||
}
|
||
// });
|
||
|
||
return rankInfo;
|
||
}
|
||
|
||
|
||
/**刷新对手 */
|
||
static async randomEnemy(uid: string) {
|
||
const enemy: number[] = [];
|
||
const curIndex = await this.getRankSortByUid(uid);
|
||
if (curIndex == 0) enemy.push(1, 2, [3, 4].random());
|
||
else if (curIndex == 1) enemy.push(0, [2, 3].random(), 4);
|
||
else if (curIndex == 2) enemy.push(0, 1, [3, 4].random());
|
||
else if (curIndex == 3) enemy.push(0, [1, 2].random(), 4);
|
||
else if (curIndex == 4) enemy.push([0, 1].random(), 2, 3);
|
||
else if (curIndex == 5) enemy.push(1, [2, 3].random(), 4);
|
||
else if (curIndex == 6) enemy.push(2, [3, 4].random(), 5);
|
||
else if (curIndex == 7) enemy.push(3, [4, 5].random(), 6);
|
||
else if (curIndex == 8) enemy.push(4, [5, 6].random(), 7);
|
||
else if (curIndex == 9) enemy.push(5, [6, 7].random(), 8);
|
||
else {
|
||
enemy.push(...[
|
||
[Math.floor(curIndex * .7), Math.floor(curIndex * .8) - 1],
|
||
[Math.floor(curIndex * .8), Math.floor(curIndex * .9) - 1],
|
||
[Math.floor(curIndex * .9), curIndex - 1],
|
||
].map(arr => PublicShared.randomNum(arr[0], arr[1])));
|
||
}
|
||
let enemyRes = []
|
||
for (let e of enemy) {
|
||
if (e >= 0) {
|
||
let rankInfo = await this.getRankPlayerData(e)
|
||
let rank = await this.getRankSortByUid(rankInfo.player.uid)
|
||
enemyRes.push({...rankInfo, rank: rank})
|
||
}
|
||
}
|
||
return {
|
||
rank: curIndex,
|
||
enemy: enemyRes
|
||
};
|
||
}
|
||
|
||
/**存录像 */
|
||
static async saveFightLog(uid: string, fightLog: fightResult) {
|
||
if (uid.indexOf('npc_') != -1) return;
|
||
FightFun.saveLog(uid, 'jjc', fightLog);
|
||
}
|
||
|
||
/**服务器启动后初始化缓存数据 */
|
||
static async init() {
|
||
console.log('加载竞技场缓存...');
|
||
// redis数据存在,不初始化, mongodb在此的数据可以剔除。
|
||
// 或建立与redis"rank:jjc:sort"相对应的表,但走新表逻辑要考虑初始化的影响,暂缓。
|
||
// 重新初始化一定要删除redis内数据
|
||
if ((await this.getRankList()).length > 0) return
|
||
let rankInfo = await G.mongodb.collection('rank').findOne({type: 'jjc'});
|
||
if (!rankInfo) {
|
||
rankInfo = {
|
||
_id: null,
|
||
type: 'jjc',
|
||
rankList: Object.values(G.gc.jjc_npc).map(npc => {
|
||
let {id, npcId, ...attr} = npc;
|
||
return {
|
||
...formatNpcData(npcId, {...attr, uid: id})
|
||
};
|
||
})
|
||
};
|
||
G.mongodb.collection('rank').insertOne({type: 'jjc', rankList: rankInfo.rankList});
|
||
}
|
||
// 剔除空数据
|
||
rankInfo.rankList = rankInfo.rankList.filter(x => x && x.player)
|
||
// 获取全部用户uid
|
||
let uidArr = rankInfo.rankList.map(x => x.player.uid)
|
||
// 数组去重
|
||
uidArr = [...new Set(uidArr)]
|
||
// 整理写入redis的排名数据
|
||
let rank: sortEd[] = uidArr.map((uid, index) => ({value: uid, score: index}))
|
||
this.updatePlayerRank(rank)
|
||
// 记录ranklist详细数据到redis
|
||
rankInfo.rankList && rankInfo.rankList.forEach(item => {
|
||
this.updatePlayerData(item.player.uid, item)
|
||
})
|
||
console.log('加载竞技场缓存 succ');
|
||
}
|
||
|
||
/**获取玩家jjc数据 */
|
||
static async getData(call: ApiCall) {
|
||
let jjsPlayer = await this.getMyData(call.uid);
|
||
if (jjsPlayer) {
|
||
let rank = await this.getRankSortByUid(call.uid);
|
||
let data = JSON.parse(JSON.stringify(jjsPlayer))
|
||
data.rank = rank;
|
||
}
|
||
// 排名数据即时取
|
||
return jjsPlayer;
|
||
}
|
||
|
||
/**修改玩家jjc数据 */
|
||
static async changeData(call: ApiCall, change: jjcChange) {
|
||
//console.log(change);
|
||
// Object.entries(change).forEach(val => G.redis.set('jjc', call.uid, val[0] as any, val[1]));
|
||
await G.mongodb.collection('jjc').updateOne({uid: call.uid}, {$set: change});
|
||
}
|
||
|
||
/**红点 */
|
||
static async getHongDian(call: ApiCall) {
|
||
let _res: hongdianVal = {
|
||
show: false
|
||
};
|
||
// 挑战次数不足
|
||
let _mydata = await this.getData(call) || {
|
||
_id: null, uid: call.uid, ...{
|
||
resetTime: G.time,
|
||
receivedArr: [],
|
||
buyFightNum: 0,
|
||
useFightNum: 0
|
||
}
|
||
};
|
||
if (_mydata.useFightNum < _mydata.buyFightNum + G.gc.jjc_com.fightNum) {
|
||
_res.show = true;
|
||
}
|
||
|
||
if (!_res.show) {
|
||
let _con = JSON.parse(JSON.stringify(G.gc.jjc_tz));
|
||
for (let index = 0; index < Object.keys(_con).length; index++) {
|
||
const element = Object.keys(_con)[index];
|
||
let _tmp = _con[element];
|
||
if (_mydata.receivedArr.find(r => r == _tmp.id)) continue;
|
||
if (_mydata.useFightNum < _tmp.num) continue;
|
||
_res.show = true;
|
||
break;
|
||
}
|
||
}
|
||
return _res;
|
||
}
|
||
} |