锦标赛优化

This commit is contained in:
hahwu 2026-02-04 15:00:04 +08:00
parent 00809c8484
commit 5da2f68a0c
3 changed files with 163 additions and 34 deletions

View File

@ -1,9 +1,11 @@
package game
import (
"fmt"
"math"
champshipCfg "server/conf/champship"
randnameCfg "server/conf/randname"
"server/db"
"server/game/mod/msg"
GoUtil "server/game_util"
proto "server/msg"
@ -116,37 +118,72 @@ func (c *ChampshipMgr) ZeroUpdate() (interface{}, error) {
log.Debug("ChampshipMgr ZeroUpdate")
data := c.getData()
data.mu.Lock()
defer data.mu.Unlock()
data.ZeroTime = GoUtil.ZeroTimestamp()
data.PreRank = data.Rank
data.PreRobot = data.Robot
data.PreGroupInfo = data.GroupInfo
// 深拷贝 map避免多个协程持有同一个 map 引用导致并发读写
oldRank := make(map[int][]*ChampshipRank, len(data.Rank))
for k, v := range data.Rank {
oldRank[k] = v
}
oldRobot := make(map[int]*ChampshipRobot, len(data.Robot))
for k, v := range data.Robot {
oldRobot[k] = v
}
oldGroupInfo := make(map[int]int, len(data.GroupInfo))
for k, v := range data.GroupInfo {
oldGroupInfo[k] = v
}
data.PreRank = oldRank
data.PreRobot = oldRobot
data.PreGroupInfo = oldGroupInfo
data.AutoId = 0
data.RobotId = 1
data.Robot = make(map[int]*ChampshipRobot, 0)
data.Rank = make(map[int][]*ChampshipRank, 0)
data.GroupInfo = make(map[int]int, 0)
c.update = true
// 0点更新排行榜缓存
for k := range data.PreGroupInfo {
c.SetRankCache(k)
}
data.mu.Unlock()
c.mDispatr.AfterFunc(time.Duration(GoUtil.NextZeroTimestampDuration())*time.Second, func() {
c.ZeroUpdate()
})
c.NotifyPlayer()
c.mDispatr.AfterFunc(time.Duration(1800)*time.Second, func() {
c.NotifyAll()
})
// 在锁外通知玩家,避免在持有锁时调用外部函数
go c.NotifyPlayer()
return nil, nil
}
func (c *ChampshipMgr) NotifyPlayer() {
List := c.getData().PreRank
for _, v := range List {
for i := 0; i < 3; i++ {
data := c.getData()
data.mu.RLock()
// 深拷贝需要通知的数据,避免在锁外访问 map
notifyList := make([]struct {
Uid int
Rank int
}, 0)
for _, v := range data.PreRank {
for i := 0; i < 3 && i < len(v); i++ {
if v[i].Type == RANK_PLAYER_ROBOT {
continue
}
NotifyChampshipResult(v[i].Uid, i+1)
notifyList = append(notifyList, struct {
Uid int
Rank int
}{Uid: v[i].Uid, Rank: i + 1})
}
}
data.mu.RUnlock()
// 在锁外通知,避免持锁时间过长
for _, item := range notifyList {
NotifyChampshipResult(item.Uid, item.Rank)
}
}
func (c *ChampshipMgr) ai() (interface{}, error) {
@ -154,6 +191,7 @@ func (c *ChampshipMgr) ai() (interface{}, error) {
ChampshipData.mu.Lock()
defer ChampshipData.mu.Unlock()
Now := GoUtil.Now()
uids := make(map[int]struct{})
for k, v := range ChampshipData.Rank {
Notify := make(map[int]int)
for e, r := range v {
@ -190,18 +228,26 @@ func (c *ChampshipMgr) ai() (interface{}, error) {
continue
}
if Notify[r.Uid] != e {
NotifyPlayer(r.Uid, &msg.Msg{
Type: msg.HANDLE_TYPE_CHAMPSHIP_NOTIFY,
})
c.SetRankCache(r.Uid)
uids[r.Uid] = struct{}{}
}
}
ChampshipData.Rank[k] = v
}
// 在锁外通知玩家,避免在持锁时启动 goroutine 访问可能被并发修改的数据
for uid := range uids {
go NotifyPlayer(uid, &msg.Msg{
Type: msg.HANDLE_TYPE_CHAMPSHIP_NOTIFY,
})
}
c.mDispatr.AfterFunc(time.Duration(60)*time.Second, func() {
c.ai()
})
return nil, nil
}
func (c *ChampshipMgr) GetPreRankMsg(Uid int) *proto.ResChampshipPreRank {
ChampshipData := c.getData()
ChampshipData.mu.RLock()
@ -403,12 +449,20 @@ func (c *ChampshipMgr) group() (interface{}, error) {
}
}
for k := range ChampshipData.Pool { // 分组完成通知
NotifyPlayer(k, &msg.Msg{
// 收集需要通知的玩家
notifyList := make([]int, 0, len(ChampshipData.Pool))
for k := range ChampshipData.Pool {
c.SetRankCache(k) // SetRankCache 使用 unsafe 方法,在持有锁时是安全的
notifyList = append(notifyList, k)
}
c.getData().Pool = make(map[int]*GroupInfo) // 清空未分配池
// 在锁外通知玩家,避免在持有锁时调用外部函数
for _, uid := range notifyList {
go NotifyPlayer(uid, &msg.Msg{
Type: msg.HANDLE_TYPE_CHAMPSHIP_NOTIFY,
})
}
c.getData().Pool = make(map[int]*GroupInfo) // 清空未分配池
return nil, nil
}
@ -427,11 +481,19 @@ func (c *ChampshipMgr) getGroupId(Uid int) int {
// 进去榜单
func (c *ChampshipMgr) inRank(m *msg.Msg) (interface{}, error) {
data := m.Extra.(CRank)
// 在加锁前获取 GroupId避免在持有写锁时调用会获取读锁的 getGroupId 导致死锁
GroupId := c.getGroupId(data.Uid)
ChampshipData := c.getData()
ChampshipData.mu.Lock()
defer ChampshipData.mu.Unlock()
data := m.Extra.(CRank)
GroupId := c.getGroupId(data.Uid)
// 再次检查 GroupId因为可能在等待锁期间被其他协程修改
if currentGroupId, ok := ChampshipData.GroupInfo[data.Uid]; ok {
GroupId = currentGroupId
}
if GroupId == 0 {
ChampshipData.Pool[data.Uid] = &GroupInfo{
Uid: data.Uid,
@ -475,15 +537,23 @@ func (c *ChampshipMgr) inRank(m *msg.Msg) (interface{}, error) {
}
return false
})
// 收集需要通知的玩家
notifyList := make([]int, 0)
for k, v := range RankList {
if Notify[v.Uid] != k {
NotifyPlayer(v.Uid, &msg.Msg{
Type: msg.HANDLE_TYPE_CHAMPSHIP_NOTIFY,
})
c.SetRankCache(v.Uid)
notifyList = append(notifyList, v.Uid)
}
}
ChampshipData.Rank[GroupId] = RankList
// 在锁外通知玩家
for _, uid := range notifyList {
go NotifyPlayer(uid, &msg.Msg{
Type: msg.HANDLE_TYPE_CHAMPSHIP_NOTIFY,
})
}
return nil, nil
}
@ -507,7 +577,44 @@ func (c *ChampshipMgr) getMyRank(Uid int) int {
return 0
}
func (c *ChampshipMgr) unsafe_getMyRank(Uid int) int {
ChampshipData := c.getData()
GroupId := ChampshipData.GroupInfo[Uid]
if GroupId == 0 {
return 0
}
RankList, ok := ChampshipData.Rank[GroupId]
if !ok {
return 0
}
for k, v := range RankList {
if v.Uid == Uid {
return k + 1
}
}
return 0
}
func (c *ChampshipMgr) getLastMyRank(Uid int) int {
ChampshipData := c.getData()
ChampshipData.mu.RLock()
defer ChampshipData.mu.RUnlock()
GroupId := ChampshipData.PreGroupInfo[Uid]
if GroupId == 0 {
return 0
}
RankList, ok := ChampshipData.PreRank[GroupId]
if !ok {
return 0
}
for k, v := range RankList {
if v.Uid == Uid {
return k + 1
}
}
return 0
}
func (c *ChampshipMgr) unsafe_getLastMyRank(Uid int) int {
ChampshipData := c.getData()
GroupId := ChampshipData.PreGroupInfo[Uid]
if GroupId == 0 {
@ -736,3 +843,23 @@ func CreateRobot(M float64, GroupId int) *ChampshipRobot {
PerScore: PerScore,
}
}
func (c *ChampshipMgr) SetRankCache(Uid int) {
PreRank := c.unsafe_getLastMyRank(Uid)
Rank := c.unsafe_getMyRank(Uid)
PreGroupId := c.getData().PreGroupInfo[Uid]
GroupId := c.getData().GroupInfo[Uid]
key := fmt.Sprintf("champship_rank_cache_%d", Uid)
db.RedisSetKey(key, fmt.Sprintf("%d_%d_%d_%d", PreRank, Rank, PreGroupId, GroupId), 86400*2)
}
func GetRankCache(Uid int) (int, int, int, int) {
key := fmt.Sprintf("champship_rank_cache_%d", Uid)
data, err := db.RedisGetKey(key)
if err != nil || data == "" {
return 0, 0, 0, 0
}
var PreRank, Rank, PreGroupId, GroupId int
fmt.Sscanf(data, "%d_%d_%d_%d", &PreRank, &Rank, &PreGroupId, &GroupId)
return PreRank, Rank, PreGroupId, GroupId
}

View File

@ -5,7 +5,6 @@ import (
playroomCfg "server/conf/playroom"
"server/game/mod/item"
limitedTimeEvent "server/game/mod/limited_time_event"
"server/game/mod/msg"
GoUtil "server/game_util"
proto "server/msg"
)
@ -295,22 +294,14 @@ func (p *Player) ChargeBackData() {
func (p *Player) BackChampship() {
ChampshipMod := p.PlayMod.getChampshipMod()
MyRank, MyPreRank := p.GetChampshipRank()
p.PushClientRes(ChampshipMod.BackData(MyRank, MyPreRank))
rank, preRank := p.GetChampshipRank()
p.PushClientRes(ChampshipMod.BackData(preRank, rank))
}
// 获取冠军赛排名 redis缓存
func (p *Player) GetChampshipRank() (int, int) {
MyRank := 0
MyPreRank := 0
res, _ := SendMsgToCenterSync(&msg.Msg{
From: int(p.M_DwUin),
HandleType: msg.HANDLE_MOD_CHAMPSHIP_RANK_INFO,
})
if res != nil {
MyRank = res.Extra.([]int)[0]
MyPreRank = res.Extra.([]int)[1]
}
return MyRank, MyPreRank
preRank, rank, _, _ := GetRankCache(int(p.M_DwUin))
return rank, preRank
}
// 返回好友信息

View File

@ -95,3 +95,14 @@ func DiffMap(a, b map[int]int) map[int]int {
}
return diff
}
func GetMapIntValueByKey(m interface{}, key string) int {
mi, ok := m.(map[string]interface{})
if !ok {
return 0
}
if v, ok := mi[key]; ok {
return Int(v)
}
return 0
}