120 lines
3.8 KiB
TypeScript
120 lines
3.8 KiB
TypeScript
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;
|
||
} |