diff --git a/src/server/game/champship_mgr.go b/src/server/game/champship_mgr.go index 37e84737..fe777854 100644 --- a/src/server/game/champship_mgr.go +++ b/src/server/game/champship_mgr.go @@ -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 +} diff --git a/src/server/game/player_back.go b/src/server/game/player_back.go index cb327656..57ad6ed8 100644 --- a/src/server/game/player_back.go +++ b/src/server/game/player_back.go @@ -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 } // 返回好友信息 diff --git a/src/server/game_util/mapUtil.go b/src/server/game_util/mapUtil.go index 26aa25c8..6fc931e0 100644 --- a/src/server/game_util/mapUtil.go +++ b/src/server/game_util/mapUtil.go @@ -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 +}