game_sync/worldsrv/playercache.go

265 lines
6.9 KiB
Go
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.

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