HJ_Server/src/public/jjc.ts
2024-01-09 22:30:35 +08:00

369 lines
15 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}