HJ_Server/src/public/gud.ts

120 lines
3.8 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 { ResLogin, playerAppend, playerInfo } from "../shared/protocols/user/PtlLogin";
//进程内数据玩家字典
const GUD : { [key: string]: {
gud: ResLogin['gud'],
version: {
pid : number,
ver?: number
}
} } = {};
export async function clearGud(uid) {
//玩家断线清空uid数据
if(GUD[uid]){
console.log("玩家断线清空GUD数据", uid);
delete GUD[uid];
G.ioredis.del(`gudVersion:${uid}_pid`);
G.ioredis.del(`gudVersion:${uid}_ver`);
}
//清空Redis缓存
G.ioredis.del(`tanxian:${uid}`);
G.ioredis.del(`dxlt:${uid}`);
};
/**
* 获取玩家的gud数据
* !!!警告:因为数据会进程内缓存,请将返回值当做引用类型看待
* @param uid 玩家的uid
* @returns
*/
export async function getGud(uid:string) : Promise<ResLogin['gud']>{
let gud;
if(GUD[uid]){
//如果本进程里存在,则判断版本号
let redisVersion = await G.ioredis.mget([`gudVersion:${uid}_pid`, `gudVersion:${uid}_ver`]);
if(redisVersion[0]==null || redisVersion[1]==null || redisVersion[0].toString() != (GUD[uid].version.pid).toString()){
//最新的数据,已经不是本进程生成的了,则说明本进程持久了一份脏数据
delete GUD[uid];
}else{
if(redisVersion[1].toString() != (GUD[uid].version.ver).toString()){
//虽然数据是本进程生成的,但是版本号不一致,说明其他进程里更新过数据了
gud = await getGudFromDB(uid);
GUD[uid].gud = gud;
GUD[uid].version.ver = parseInt(redisVersion[1]);
}else{
//本进程的数据是最新的,直接返回
gud = GUD[uid].gud;
//console.log("getGud==>", uid);
}
}
}
if(!gud){
//本进程里没有则直接从db获取
gud = await getGudFromDB(uid);
}
return gud;
}
/**
* 更新玩家的数据,注意,这里只负责更新进程内的缓存数据及维护数据版本
* 实际DB的操作需要调用者自己做
* @param uid
*/
export async function setGud(uid:string, updateKV:{[key:string]:any}) : Promise<ResLogin['gud']>{
let nver = await G.ioredis.incr(`gudVersion:${uid}_ver`);
//如果数据不在本进程,只更新版本即可
if(!GUD[uid])return;
GUD[uid].version.ver = nver;
Object.assign(GUD[uid].gud, updateKV);
return GUD[uid].gud;
}
/**
* 在本进程初始化玩家的数据
* 该过程会将玩家数据从db里加热到内存
* 仅允许在玩家登录的时候执行该方法
* 并且玩家离线时,需要销毁对应的数据
* 以避免多个进程持有同一个玩家的数据导致数据统一性错误
* @param uid
* @param _gud 在登录时前面的逻辑里已经查询过一次db了没必要再查一次浪费性能这种情况下则直接传入_gud
*/
export async function initGud(uid:string, _gud?:ResLogin['gud']) : Promise<ResLogin['gud']>{
let gud = _gud || await getGudFromDB(uid);
if(gud){
console.log('initGud==>', uid);
//设置gud版本号
await G.ioredis.set(`gudVersion:${uid}_pid`, process.pid );
let ver = await G.ioredis.incr(`gudVersion:${uid}_ver`);
GUD[uid] = {
gud : gud,
version : {
pid : process.pid,
ver : ver
}
};
}
return GUD?.[uid]?.gud;
}
/**
* 从db从获取玩家的gud数据
* 业务逻辑中请统一使用getGud方法不然可能会造成数据一致性错误
* @param uid
*/
export async function getGudFromDB(uid:string) : Promise<ResLogin['gud']>{
//console.warn("getGudFromDB", uid);
let gud = await G.mongodb.collection('user').findOne({uid: uid});
return gud;
}