220 lines
5.8 KiB
Go
220 lines
5.8 KiB
Go
package main
|
||
|
||
import (
|
||
"strconv"
|
||
"time"
|
||
|
||
"mongo.games.com/goserver/core"
|
||
"mongo.games.com/goserver/core/basic"
|
||
"mongo.games.com/goserver/core/module"
|
||
"mongo.games.com/goserver/core/task"
|
||
|
||
"mongo.games.com/game/model"
|
||
"mongo.games.com/game/worldsrv/internal"
|
||
)
|
||
|
||
const (
|
||
// InvalidPlayerCacheSec 数据不存在,60秒内不再查询
|
||
InvalidPlayerCacheSec int64 = 60
|
||
|
||
InvalidPlayerCacheMax int = 100000
|
||
ListNumber int32 = 300
|
||
)
|
||
|
||
func init() {
|
||
module.RegisteModule(PlayerCacheMgrSingleton, time.Second, 0)
|
||
//GameEventHandlerMgrSingleton.RegisteGameEventHandler(GAMEEVENT_LOGIN, PlayerCacheMgrSingleton)
|
||
//GameEventHandlerMgrSingleton.RegisteGameEventHandler(GAMEEVENT_LOGOUT, PlayerCacheMgrSingleton)
|
||
}
|
||
|
||
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),
|
||
DbSaver: &DbSaver{
|
||
Tick: ListNumber,
|
||
index: 0,
|
||
init: false,
|
||
list: make([]*SaverArray, ListNumber),
|
||
queue: make([]*BalanceQueue, 10),
|
||
pool: make(map[SaveTaskHandler]*SaverArray),
|
||
},
|
||
}
|
||
|
||
type PlayerCacheItem struct {
|
||
*model.PlayerData
|
||
}
|
||
|
||
func (p *PlayerCacheItem) CanDel() bool {
|
||
//return !p.isOnline && time.Now().Unix()-p.lastTs > int64(ListNumber)
|
||
return true
|
||
}
|
||
|
||
func (p *PlayerCacheItem) Time2Save() {
|
||
// 并没有对缓存数据做什么操作,只是释放缓存
|
||
if p.CanDel() {
|
||
PlayerCacheMgrSingleton.playerWaitClr = append(PlayerCacheMgrSingleton.playerWaitClr, p)
|
||
}
|
||
}
|
||
|
||
type PlayerCacheMgr struct {
|
||
*DbSaver
|
||
playerMap map[int32]*PlayerCacheItem // snid; 玩家信息缓存
|
||
playerCbs map[int32][]func(*PlayerCacheItem, bool, bool) // snid; 等待执行的回掉方法
|
||
playerInvalidIds map[int32]int64 // snid; 防止频繁访问数据库
|
||
playerWaitClr []*PlayerCacheItem // 根据DbSaver缓冲失效策略释放玩家数据
|
||
}
|
||
|
||
func (c *PlayerCacheMgr) Get(plt string, snid int32, cb func(*PlayerCacheItem, bool, 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 {
|
||
c.cacheInvalidPlayerId(snid)
|
||
delete(c.playerCbs, snid)
|
||
for _, cb := range c.playerCbs[snid] {
|
||
cb(nil, true, false)
|
||
}
|
||
return
|
||
}
|
||
|
||
if len(replays) == len(internal.GetPlayerLoads()) {
|
||
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.RegisterDbSaverTask(item)
|
||
}
|
||
if cbs, exist := c.playerCbs[snid]; exist {
|
||
delete(c.playerCbs, snid)
|
||
for _, cb := range cbs {
|
||
cb(item, true, isnew)
|
||
}
|
||
}
|
||
|
||
}), "PlayerCacheMgr.Get").StartByExecutor(strconv.Itoa(int(snid)))
|
||
}
|
||
|
||
func (c *PlayerCacheMgr) GetMore(plt string, snid []int32, cb func([]*PlayerCacheItem, bool)) {
|
||
isAsyn := false
|
||
count := len(snid)
|
||
result := make([]*PlayerCacheItem, 0, count)
|
||
innerCb := func(item *PlayerCacheItem, asyn, isnew bool) {
|
||
if item != nil {
|
||
result = append(result, item)
|
||
}
|
||
if asyn {
|
||
isAsyn = true
|
||
}
|
||
count--
|
||
if count == 0 {
|
||
cb(result, isAsyn)
|
||
}
|
||
}
|
||
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) ModuleName() string {
|
||
return "PlayerCacheMgr"
|
||
}
|
||
|
||
func (c *PlayerCacheMgr) Init() {
|
||
c.DbSaver.Init()
|
||
}
|
||
|
||
func (c *PlayerCacheMgr) Update() {
|
||
// 执行Time2Save之后清除缓存
|
||
c.DbSaver.Update()
|
||
for _, p := range c.playerWaitClr {
|
||
delete(c.playerMap, p.SnId)
|
||
c.UnregisteDbSaveTask(p)
|
||
}
|
||
c.playerWaitClr = c.playerWaitClr[0:0]
|
||
}
|
||
|
||
func (c *PlayerCacheMgr) Shutdown() {
|
||
module.UnregisteModule(c)
|
||
}
|