265 lines
6.9 KiB
Go
265 lines
6.9 KiB
Go
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"time"
|
||
|
||
"mongo.games.com/goserver/core"
|
||
"mongo.games.com/goserver/core/basic"
|
||
"mongo.games.com/goserver/core/logger"
|
||
"mongo.games.com/goserver/core/module"
|
||
"mongo.games.com/goserver/core/task"
|
||
|
||
"mongo.games.com/game/model"
|
||
"mongo.games.com/game/util/balancequeue"
|
||
"mongo.games.com/game/worldsrv/internal"
|
||
)
|
||
|
||
/*
|
||
查询不在线的玩家数据,缓存一段时间
|
||
*/
|
||
|
||
const (
|
||
// InvalidPlayerCacheSec 数据不存在,60秒内不再查询数据库
|
||
InvalidPlayerCacheSec int64 = 60
|
||
// InvalidPlayerCacheMax 缓存的无效玩家最大数量
|
||
InvalidPlayerCacheMax int = 100000
|
||
// ListNumber 平衡队列分组数量
|
||
ListNumber = 300
|
||
)
|
||
|
||
func init() {
|
||
module.RegisteModule(PlayerCacheMgrSingleton, time.Second, 0)
|
||
}
|
||
|
||
var PlayerCacheMgrSingleton = &PlayerCacheMgr{
|
||
playerMap: make(map[int32]*PlayerCacheItem),
|
||
playerCbs: make(map[int32][]func(*PlayerCacheItem, bool, bool)),
|
||
playerInvalidIds: make(map[int32]int64),
|
||
playerWaitClr: make([]*PlayerCacheItem, 0, 128),
|
||
}
|
||
|
||
type PlayerCacheItem struct {
|
||
*model.PlayerData
|
||
}
|
||
|
||
func (p *PlayerCacheItem) BalanceQueueHandler() {
|
||
// 并没有对缓存数据做什么操作,只是释放缓存
|
||
PlayerCacheMgrSingleton.playerWaitClr = append(PlayerCacheMgrSingleton.playerWaitClr, p)
|
||
}
|
||
|
||
type PlayerCacheMgr struct {
|
||
*balancequeue.BalanceQueue
|
||
playerMap map[int32]*PlayerCacheItem // snid; 玩家信息缓存
|
||
playerCbs map[int32][]func(*PlayerCacheItem, bool, bool) // snid; 等待执行的回掉方法
|
||
playerInvalidIds map[int32]int64 // snid; 防止频繁访问数据库
|
||
playerWaitClr []*PlayerCacheItem // 根据DbSaver缓冲失效策略释放玩家数据
|
||
}
|
||
|
||
// Get 获取玩家数据
|
||
// plt 平台
|
||
// snid 玩家id
|
||
// cb 回掉方法; playerInfo 玩家数据; isFindDB 是否查了数据库; isNew 是否新创建的玩家
|
||
// createIfNotExist 玩家不存在是否创建
|
||
func (c *PlayerCacheMgr) Get(plt string, snid int32, cb func(playerInfo *PlayerCacheItem, isFindDB bool, isNew bool), createIfNotExist bool) {
|
||
// 1.玩家缓冲数据存在
|
||
if p, exist := c.playerMap[snid]; exist {
|
||
cb(p, false, false)
|
||
return
|
||
}
|
||
|
||
// 2.查询频率限制(限制数据库没有数据的情况)
|
||
// 玩家不存在,避免打到db上
|
||
// 数据库查询失败或没有玩家信息就不要重复查了
|
||
if lastTs, exist := c.playerInvalidIds[snid]; exist {
|
||
if time.Now().Unix()-lastTs < InvalidPlayerCacheSec {
|
||
cb(nil, false, false)
|
||
return
|
||
}
|
||
delete(c.playerInvalidIds, snid)
|
||
}
|
||
|
||
// 3.有正在查询中,等待查询结果
|
||
// 查询队列,如果已经有在查数据库了,就等待数据库查询结果
|
||
if cbs, exist := c.playerCbs[snid]; exist {
|
||
cbs = append(cbs, cb)
|
||
c.playerCbs[snid] = cbs
|
||
return
|
||
}
|
||
c.playerCbs[snid] = []func(*PlayerCacheItem, bool, bool){cb}
|
||
|
||
var isNew bool
|
||
var replays []*internal.PlayerLoadReplay
|
||
task.New(core.CoreObject(), task.CallableWrapper(func(o *basic.Object) interface{} {
|
||
pi, flag := model.GetPlayerDataBySnId(plt, snid, true, createIfNotExist)
|
||
isNew = flag
|
||
// 查询其它玩家数据
|
||
for _, v := range internal.GetPlayerLoads() {
|
||
replays = append(replays, v.Load(plt, snid, pi))
|
||
}
|
||
return pi
|
||
}), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) {
|
||
su := true
|
||
pi, ok := data.(*model.PlayerData)
|
||
if !ok || pi == nil {
|
||
su = false
|
||
}
|
||
|
||
if su {
|
||
for _, v := range replays {
|
||
if v == nil {
|
||
continue
|
||
}
|
||
if v.Err != nil {
|
||
su = false
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
if su {
|
||
if len(replays) != len(internal.GetPlayerLoads()) {
|
||
su = false
|
||
}
|
||
}
|
||
|
||
if !su {
|
||
c.cacheInvalidPlayerId(snid)
|
||
for _, v := range c.playerCbs[snid] {
|
||
v(nil, true, false)
|
||
}
|
||
delete(c.playerCbs, snid)
|
||
return
|
||
}
|
||
|
||
for k, v := range internal.GetPlayerLoads() {
|
||
if v == nil || replays[k] == nil {
|
||
continue
|
||
}
|
||
v.Callback(pi, replays[k])
|
||
}
|
||
|
||
// 查询成功,缓存数据,执行cb方法
|
||
item, ok := c.playerMap[snid]
|
||
if !ok {
|
||
item = &PlayerCacheItem{PlayerData: pi}
|
||
c.playerMap[snid] = item
|
||
c.Push(item)
|
||
}
|
||
for _, v := range c.playerCbs[snid] {
|
||
v(item, true, isNew)
|
||
}
|
||
delete(c.playerCbs, snid)
|
||
})).StartByExecutor(fmt.Sprintf("Player%v", snid))
|
||
}
|
||
|
||
//func (c *PlayerCacheMgr) GetMore(plt string, snid []int32, cb func([]*PlayerCacheItem, bool)) {
|
||
// isDB := false
|
||
// count := len(snid)
|
||
// result := make([]*PlayerCacheItem, 0, count)
|
||
// innerCb := func(item *PlayerCacheItem, isFindDB, isNew bool) {
|
||
// if item != nil {
|
||
// result = append(result, item)
|
||
// }
|
||
// if isFindDB {
|
||
// isDB = true
|
||
// }
|
||
// count--
|
||
// if count == 0 {
|
||
// cb(result, isDB)
|
||
// }
|
||
// }
|
||
// for _, id := range snid {
|
||
// c.Get(plt, id, innerCb, false)
|
||
// }
|
||
//}
|
||
|
||
func (c *PlayerCacheMgr) cacheInvalidPlayerId(snid int32) {
|
||
if len(c.playerInvalidIds) >= InvalidPlayerCacheMax {
|
||
for id := range c.playerInvalidIds {
|
||
delete(c.playerInvalidIds, id)
|
||
if len(c.playerInvalidIds) < InvalidPlayerCacheMax {
|
||
break
|
||
}
|
||
}
|
||
}
|
||
c.playerInvalidIds[snid] = time.Now().Unix()
|
||
}
|
||
|
||
// UnCacheInvalidPlayerId 解除一次玩家数据查询频率限制
|
||
// 玩家数据不存在,限制查询频率,避免查询过于频繁
|
||
// 但是当玩家数据确认存在后,可以解除限制,提前允许查询到玩家数据
|
||
func (c *PlayerCacheMgr) UnCacheInvalidPlayerId(snid int32) {
|
||
delete(c.playerInvalidIds, snid)
|
||
}
|
||
|
||
// 保存没有登录缓存的玩家数据
|
||
func (c *PlayerCacheMgr) saveCache(p *PlayerCacheItem, isSync bool) {
|
||
if p == nil {
|
||
return
|
||
}
|
||
|
||
saveFunc := func(v internal.PlayerLoader) {
|
||
if v == nil {
|
||
return
|
||
}
|
||
v.Save(p.Platform, p.SnId, true, true)
|
||
logger.Logger.Infof("PlayerCacheMgr SaveCache snid:%v", p.SnId)
|
||
}
|
||
releaseFunc := func(v internal.PlayerLoader) {
|
||
if v == nil {
|
||
return
|
||
}
|
||
if PlayerMgrSington.GetPlayerBySnId(p.SnId) == nil {
|
||
v.Release(p.Platform, p.SnId)
|
||
logger.Logger.Infof("PlayerCacheMgr SaveCache Release snid:%v", p.SnId)
|
||
}
|
||
}
|
||
|
||
if PlayerMgrSington.GetPlayerBySnId(p.SnId) == nil {
|
||
if isSync {
|
||
for _, v := range internal.GetPlayerLoads() {
|
||
saveFunc(v)
|
||
releaseFunc(v)
|
||
}
|
||
} else {
|
||
for i := 0; i < len(internal.GetPlayerLoads()); i++ {
|
||
v := internal.GetPlayerLoads()[i]
|
||
task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
|
||
saveFunc(v)
|
||
return nil
|
||
}), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) {
|
||
releaseFunc(v)
|
||
})).StartByExecutor(fmt.Sprintf("Player%v", p.SnId))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
func (c *PlayerCacheMgr) ModuleName() string {
|
||
return "PlayerCacheMgr"
|
||
}
|
||
|
||
func (c *PlayerCacheMgr) Init() {
|
||
c.BalanceQueue = balancequeue.New(ListNumber)
|
||
}
|
||
|
||
func (c *PlayerCacheMgr) Update() {
|
||
c.BalanceQueue.Update()
|
||
for _, p := range c.playerWaitClr {
|
||
delete(c.playerMap, p.SnId)
|
||
c.Pop(p)
|
||
|
||
// 更新玩家数据
|
||
c.saveCache(p, false)
|
||
}
|
||
c.playerWaitClr = c.playerWaitClr[0:0]
|
||
}
|
||
|
||
func (c *PlayerCacheMgr) Shutdown() {
|
||
for _, v := range c.playerMap {
|
||
c.saveCache(v, true)
|
||
}
|
||
module.UnregisteModule(c)
|
||
}
|