From c734417b1cfc1d14c1c872e05332b6972bd26d6a Mon Sep 17 00:00:00 2001 From: hahwu <31872165+hahwu@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:49:45 +0800 Subject: [PATCH 01/11] =?UTF-8?q?catnip=20bug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/register_network_func.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/game/register_network_func.go b/src/server/game/register_network_func.go index 26dee3cc..6e88dfba 100644 --- a/src/server/game/register_network_func.go +++ b/src/server/game/register_network_func.go @@ -5779,6 +5779,9 @@ func ReqFriendReplyHandle(player *Player, buf []byte) error { case friend.REPLY_TYPE_CATNIP: // 猫草大作战同意邀请 GameId := GoUtil.Int(ReplyInfo.Param) activityInfo := player.GetActivityInfo(player.GetActivityId(activity.ACT_TYPE_CATNIP)) + if activityInfo == nil { + break + } err := player.SetCatnipPartner(GameId, ReplyInfo.Uid, activityInfo.EndT) if err == nil { CatnipMod := player.PlayMod.getCatnipMod() From b9f6cceda1c897601184c9734c4328f565f0adb6 Mon Sep 17 00:00:00 2001 From: hahwu <31872165+hahwu@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:02:21 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=E6=97=A5=E5=BF=97=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/message_mgr.go | 1 + src/server/game/var.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/server/game/message_mgr.go b/src/server/game/message_mgr.go index 19948823..e32512e9 100644 --- a/src/server/game/message_mgr.go +++ b/src/server/game/message_mgr.go @@ -625,6 +625,7 @@ func TimeoutMiddleware(timeout time.Duration) MessageMiddleware { return result.Data, result.Error case <-time.After(timeout): log.Error("[Middleware] Message : %v timeout after %v", message, timeout) + GoUtil.SendFeishuFatal(0, "message_mgr", fmt.Sprintf("Message Handler Timeout\nMessage: %v\nTimeout: %v", message, timeout)) return nil, fmt.Errorf("message handler timeout") } } diff --git a/src/server/game/var.go b/src/server/game/var.go index 72b51563..fd6b26b6 100644 --- a/src/server/game/var.go +++ b/src/server/game/var.go @@ -22,7 +22,7 @@ func (p *Player) GetVarData(key string) interface{} { cache := &VarExpireData{} err := LoadCacheVarData(key, cache) if err != nil { - log.Error("GetVarData err : %s, key: %s", err, key) + // log.Error("GetVarData err : %s, key: %s", err, key) return nil } return cache.D @@ -32,7 +32,7 @@ func (p *Player) GetUserVarData(key string) interface{} { cache := map[string]*VarExpireData{} err := LoadCacheVarData(GoUtil.GetVarKey(int(p.M_DwUin)), &cache) if err != nil { - log.Error("GetUserVarData err : %s, key: %s", err, key) + // log.Error("GetUserVarData err : %s, key: %s", err, key) return nil } data, ok := cache[key] From 02002e6c1664790c3167a61a9a1e7f6e0c675874 Mon Sep 17 00:00:00 2001 From: hahwu <31872165+hahwu@users.noreply.github.com> Date: Fri, 9 Jan 2026 18:07:53 +0800 Subject: [PATCH 03/11] =?UTF-8?q?=E9=80=BB=E8=BE=91bug=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/GameLogic.go | 8 ++++---- src/server/game/log_mgr.go | 26 +++++++++++++++++++------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/server/game/GameLogic.go b/src/server/game/GameLogic.go index 64b1f6a0..09111df9 100644 --- a/src/server/game/GameLogic.go +++ b/src/server/game/GameLogic.go @@ -464,6 +464,7 @@ func G_getGameLogic() *GameLogic { G_GameLogicPtr.M_Players = sync.Map{} G_GameLogicPtr.NotInitPlayer = new(Player) G_GameLogicPtr.M_LimitActiveList = []int{} + G_GameLogicPtr.CreateMessageMgr() // 创建消息管理器 G_GameLogicPtr.OpenTimestampTick() // 开启时间戳计时器 G_GameLogicPtr.RegisterEvent() // 注册事件 G_GameLogicPtr.RegisterNetWorkFunc() // 注册客户端接口 @@ -475,10 +476,9 @@ func G_getGameLogic() *GameLogic { G_GameLogicPtr.CreateChampshipMgr() // 创建竞标赛管理器 G_GameLogicPtr.CreateVarMgr() // 创建变量管理器 } - G_GameLogicPtr.CreateRankMgr() //创建排行榜管理器 - G_GameLogicPtr.CreateMailMgr() //创建邮件管理器 - G_GameLogicPtr.CreateMessageMgr() // 创建消息管理器 - ClusterMgrInit() //初始化集群 + G_GameLogicPtr.CreateRankMgr() //创建排行榜管理器 + G_GameLogicPtr.CreateMailMgr() //创建邮件管理器 + ClusterMgrInit() //初始化集群 G_GameLogicPtr.StartTime = time.Now().Unix() // G_GameLogicPtr.CreateHttpManager() } diff --git a/src/server/game/log_mgr.go b/src/server/game/log_mgr.go index e333826d..e30a375b 100644 --- a/src/server/game/log_mgr.go +++ b/src/server/game/log_mgr.go @@ -199,13 +199,25 @@ func (L *LogMgr) InitManager() { func (L *LogMgr) AddLog(logs *Log) { // 复制结构体和 Param map,避免并发修改导致 json.Marshal 时 panic copyLog := *logs + + // 安全地复制 map,使用 defer recover 防止并发迭代时的 panic if logs.Param != nil { - newParam := make(map[string]interface{}, len(logs.Param)) - for k, v := range logs.Param { - newParam[k] = v - } - copyLog.Param = newParam + func() { + defer func() { + if r := recover(); r != nil { + // 发生 panic 时使用空 map + log.Debug("AddLog: concurrent map read/write detected, using empty map") + copyLog.Param = make(map[string]interface{}) + } + }() + newParam := make(map[string]interface{}, len(logs.Param)) + for k, v := range logs.Param { + newParam[k] = v + } + copyLog.Param = newParam + }() } + // 如果已经开始关闭,直接丢弃 L.Lock.Lock() if L.closing { @@ -214,7 +226,7 @@ func (L *LogMgr) AddLog(logs *Log) { } // 非阻塞入队:若通道满则先丢弃最旧一条再入队,避免阻塞调用者 select { - case L.L <- logs: + case L.L <- ©Log: L.Lock.Unlock() return default: @@ -225,7 +237,7 @@ func (L *LogMgr) AddLog(logs *Log) { } // 再尝试入队一次 select { - case L.L <- logs: + case L.L <- ©Log: default: // 放不下就直接丢弃 } From 248405d98488de352b2d1ddd12edc4686fb9c7f1 Mon Sep 17 00:00:00 2001 From: hahwu <31872165+hahwu@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:47:02 +0800 Subject: [PATCH 04/11] =?UTF-8?q?map=E8=AF=BB=E5=86=99=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/cluster/Cluster.go | 2 ++ src/server/cluster/cluster_func.go | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/server/cluster/Cluster.go b/src/server/cluster/Cluster.go index e6a12c53..4890647e 100644 --- a/src/server/cluster/Cluster.go +++ b/src/server/cluster/Cluster.go @@ -16,7 +16,9 @@ const ( ) var FuncMap = map[int]func(*Agent, *msg.Msg) error{} // 消息处理函数 +var funcMapMu sync.RWMutex // FuncMap 的读写锁 var CallbackChan = make(map[string]chan *msg.Msg) // 回调消息队列 +var callbackChanMu sync.RWMutex // CallbackChan 的读写锁 var ( server *network.TCPServer // 服务器 Center *network.TCPClient // 集群中心 diff --git a/src/server/cluster/cluster_func.go b/src/server/cluster/cluster_func.go index d02bdce9..d9ccbd31 100644 --- a/src/server/cluster/cluster_func.go +++ b/src/server/cluster/cluster_func.go @@ -172,7 +172,11 @@ func SendMsgAll(m *msg.Msg) { func processMsg(a *Agent, m *msg.Msg) error { var err error - if fun, ok := FuncMap[m.Type]; ok { + funcMapMu.RLock() + fun, ok := FuncMap[m.Type] + funcMapMu.RUnlock() + + if ok { err = fun(a, m) } else { MsgChan <- m //交由game Module消息处理 @@ -181,15 +185,21 @@ func processMsg(a *Agent, m *msg.Msg) error { } func registerFunc(key int, fun func(*Agent, *msg.Msg) error) { + funcMapMu.Lock() FuncMap[key] = fun + funcMapMu.Unlock() } func registerChanel(key string, chanel chan *msg.Msg) { + callbackChanMu.Lock() CallbackChan[key] = chanel + callbackChanMu.Unlock() } func unregisterChanel(key string) { + callbackChanMu.Lock() delete(CallbackChan, key) + callbackChanMu.Unlock() } func sendGameMsg(m *msg.Msg) { From e1f086fb8571136d44c80ae6b99a3e24e0e8bb6e Mon Sep 17 00:00:00 2001 From: hahwu <31872165+hahwu@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:53:05 +0800 Subject: [PATCH 05/11] =?UTF-8?q?=E5=A2=9E=E5=8A=A0map=E8=AF=BB=E5=86=99?= =?UTF-8?q?=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/cluster/Cluster.go | 5 +++++ src/server/game/message_mgr.go | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/server/cluster/Cluster.go b/src/server/cluster/Cluster.go index 4890647e..71835dd4 100644 --- a/src/server/cluster/Cluster.go +++ b/src/server/cluster/Cluster.go @@ -49,6 +49,11 @@ func Init() { registerFunc(msg.CLUSTER_EXIT, ClusterExit) } +// GetCallbackChanMu 返回 CallbackChan 的读写锁,供外部包安全访问 CallbackChan +func GetCallbackChanMu() *sync.RWMutex { + return &callbackChanMu +} + func Destroy() { if server != nil { log.Release("closing cluster server") diff --git a/src/server/game/message_mgr.go b/src/server/game/message_mgr.go index e32512e9..6b04a35e 100644 --- a/src/server/game/message_mgr.go +++ b/src/server/game/message_mgr.go @@ -282,7 +282,11 @@ func PlayerMsgHandler(data *msg.Msg) (interface{}, error) { func PlayerReplyMsgHandler(data *msg.Msg) (interface{}, error) { // 先处理同步回调 if data.UniKey != "" { - if chanel, ok := mergeCluster.CallbackChan[data.UniKey]; ok { + mergeCluster.GetCallbackChanMu().RLock() + chanel, ok := mergeCluster.CallbackChan[data.UniKey] + mergeCluster.GetCallbackChanMu().RUnlock() + + if ok { log.Debug("reply message ") chanel <- data } From 2612647e741ea40c95cba353a080ec97dfb379bf Mon Sep 17 00:00:00 2001 From: hahwu <31872165+hahwu@users.noreply.github.com> Date: Mon, 12 Jan 2026 10:07:47 +0800 Subject: [PATCH 06/11] =?UTF-8?q?=E9=94=A6=E6=A0=87=E8=B5=9B=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/message_handler.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/server/game/message_handler.go b/src/server/game/message_handler.go index 18fc979d..598a8442 100644 --- a/src/server/game/message_handler.go +++ b/src/server/game/message_handler.go @@ -697,14 +697,13 @@ func NotifyChampshipResult(Uid, Rank int) { return } for _, v := range PlayerSimpleData.Friend { - Msg := &msg.Msg{ + SendPlayerMsgAsync(&msg.Msg{ Type: msg.HANDLE_TYPE_CHAMPSHIP_RESULT, From: Uid, To: v, SendT: GoUtil.Now(), Extra: []int{Rank, GoUtil.GetServerOpenDay()}, - } - CenterPlayerMsgHandler(Msg) + }) } } From dcb0396a73f03c84b83638ae3ea423c2c98ab91e Mon Sep 17 00:00:00 2001 From: hahwu <31872165+hahwu@users.noreply.github.com> Date: Mon, 12 Jan 2026 18:38:12 +0800 Subject: [PATCH 07/11] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/ga/log.go | 9 ++++++++- src/server/game/player_data.go | 6 +++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/server/ga/log.go b/src/server/ga/log.go index 18fc609f..ba776cbe 100644 --- a/src/server/ga/log.go +++ b/src/server/ga/log.go @@ -1,6 +1,7 @@ package ga import ( + "encoding/json" "fmt" galog "github.com/tuyou/galog" @@ -24,7 +25,13 @@ func init() { func GAlogEvent(event string, userID string, deviceID string, properties map[string]interface{}) { newProperties := make(map[string]interface{}) for k, v := range properties { - newProperties["proj_"+k] = fmt.Sprintf("%v", v) + // JSON encode the value + jsonBytes, err := json.Marshal(v) + if err != nil { + newProperties["proj_"+k] = fmt.Sprintf("%v", v) // 无法解析直接转为字符串 + continue + } + newProperties["proj_"+k] = string(jsonBytes) } properties = newProperties glogger. diff --git a/src/server/game/player_data.go b/src/server/game/player_data.go index a8228145..d8da9b74 100644 --- a/src/server/game/player_data.go +++ b/src/server/game/player_data.go @@ -27,7 +27,6 @@ import ( "server/game/mod/quest" GoUtil "server/game_util" "server/msg" - telog "server/thinkdata" "strconv" "sync" "time" @@ -1056,9 +1055,10 @@ func (p *Player) TeLog(Type string, Param map[string]interface{}) { if agent != nil { Param["Ip"] = agent.RemoteAddr().String() } - go telog.Te.Track(p.GetPlayerBaseMod().GetName(), p.GetPlayerBaseMod().GetName(), Type, Param) + Param["#zone_offset"] = -5 + // 游戏内TE日志 + // go telog.Te.Track(p.GetPlayerBaseMod().GetName(), p.GetPlayerBaseMod().GetName(), Type, Param) BaseMod := p.PlayMod.getBaseMod() - //途游GA go ga.GAlogEvent(Type, BaseMod.Account, "", Param) } From 9d9c5634e2f4d43070ef2d3bb6cdafc1038ca0b0 Mon Sep 17 00:00:00 2001 From: hahwu <31872165+hahwu@users.noreply.github.com> Date: Tue, 13 Jan 2026 09:58:30 +0800 Subject: [PATCH 08/11] =?UTF-8?q?=E6=97=A5=E5=BF=97=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/ga/log.go | 18 ++++++++---------- src/server/game/message_mgr.go | 6 ++++++ src/server/game/player_data.go | 3 ++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/server/ga/log.go b/src/server/ga/log.go index ba776cbe..bfb46b0b 100644 --- a/src/server/ga/log.go +++ b/src/server/ga/log.go @@ -1,9 +1,6 @@ package ga import ( - "encoding/json" - "fmt" - galog "github.com/tuyou/galog" ) @@ -25,13 +22,14 @@ func init() { func GAlogEvent(event string, userID string, deviceID string, properties map[string]interface{}) { newProperties := make(map[string]interface{}) for k, v := range properties { - // JSON encode the value - jsonBytes, err := json.Marshal(v) - if err != nil { - newProperties["proj_"+k] = fmt.Sprintf("%v", v) // 无法解析直接转为字符串 - continue - } - newProperties["proj_"+k] = string(jsonBytes) + // // JSON encode the value + // jsonBytes, err := json.Marshal(v) + // if err != nil { + // newProperties["proj_"+k] = fmt.Sprintf("%v", v) // 无法解析直接转为字符串 + // continue + // } + // newProperties["proj_"+k] = string(jsonBytes) + newProperties["proj_"+k] = v } properties = newProperties glogger. diff --git a/src/server/game/message_mgr.go b/src/server/game/message_mgr.go index 6b04a35e..299e7d34 100644 --- a/src/server/game/message_mgr.go +++ b/src/server/game/message_mgr.go @@ -770,10 +770,16 @@ func getMessge(PlayerId int64) *MessageList { } func deleteMessage(m *msg.Msg) error { + if m == nil { + return nil + } messages := getMessge(int64(m.To)) messages.mu.Lock() defer messages.mu.Unlock() for i, msgItem := range messages.Messages { + if msgItem == nil { + continue + } if msgItem.UniKey == m.UniKey { // 删除消息 messages.Messages = append(messages.Messages[:i], messages.Messages[i+1:]...) diff --git a/src/server/game/player_data.go b/src/server/game/player_data.go index d8da9b74..2297886d 100644 --- a/src/server/game/player_data.go +++ b/src/server/game/player_data.go @@ -27,6 +27,7 @@ import ( "server/game/mod/quest" GoUtil "server/game_util" "server/msg" + telog "server/thinkdata" "strconv" "sync" "time" @@ -1057,7 +1058,7 @@ func (p *Player) TeLog(Type string, Param map[string]interface{}) { } Param["#zone_offset"] = -5 // 游戏内TE日志 - // go telog.Te.Track(p.GetPlayerBaseMod().GetName(), p.GetPlayerBaseMod().GetName(), Type, Param) + go telog.Te.Track(p.GetPlayerBaseMod().GetName(), p.GetPlayerBaseMod().GetName(), Type, Param) BaseMod := p.PlayMod.getBaseMod() //途游GA go ga.GAlogEvent(Type, BaseMod.Account, "", Param) From ce8dabf112cbf808e85a86bfbd5dad2ae32afaf1 Mon Sep 17 00:00:00 2001 From: hahwu <31872165+hahwu@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:02:37 +0800 Subject: [PATCH 09/11] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/message_mgr.go | 25 +++++++++++++++++++++++-- src/server/game/server_mod.go | 4 ++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/server/game/message_mgr.go b/src/server/game/message_mgr.go index 299e7d34..72fa1acb 100644 --- a/src/server/game/message_mgr.go +++ b/src/server/game/message_mgr.go @@ -7,8 +7,13 @@ import ( "runtime/debug" mergeCluster "server/cluster" "server/conf" + "server/game/mod/card" + "server/game/mod/friend" + "server/game/mod/item" + limitedTimeEvent "server/game/mod/limited_time_event" "server/game/mod/msg" GoUtil "server/game_util" + proto "server/msg" "server/pkg/github.com/name5566/leaf/log" "sync" "time" @@ -78,8 +83,24 @@ func (m *MessageMgr) MessageMgrInit() { MessageList: make(map[int64]*MessageList), PlayerList: make(map[int64]int), } - gob.Register(msg.VarData{}) - gob.Register(GameResult{}) + // 注册所有可能在消息中使用的类型 + gob.Register(&limitedTimeEvent.MoneyCat{}) + gob.Register(&limitedTimeEvent.LuckyCat{}) + gob.Register(&msg.HandbookMsg{}) + gob.Register(&limitedTimeEvent.CatTrick{}) + gob.Register(&VarOpration{}) + gob.Register(&VarUserData{}) + gob.Register(&ActivityInfo{}) + gob.Register(&ChargeExtra{}) + gob.Register(CatnipMsg{}) + gob.Register(&CatnipLock{}) + gob.Register(CRank{}) + gob.Register(&proto.ResChampshipRank{}) + gob.Register(&proto.ResChampshipPreRank{}) + gob.Register(card.CardInfo{}) + gob.Register(item.Item{}) + gob.Register([]*item.Item{}) // 注册 []*item.Item 类型 + gob.Register(friend.ReplyInfo{}) // 注册处理函数 m.init() m.handler = make(map[int]MessageHandlerFunc) diff --git a/src/server/game/server_mod.go b/src/server/game/server_mod.go index b76b4479..eb1ce64a 100644 --- a/src/server/game/server_mod.go +++ b/src/server/game/server_mod.go @@ -166,7 +166,7 @@ func (s *ServerMod) LoadData() { DbData.UpdataTime = GoUtil.Now() err = db.InsertServerData(&DbData) if err != nil { - log.Debug("LoadData sql exec ,Mod Key: %s err:%v", s.key, err) + log.Error("LoadData sql exec ,Mod Key: %s err:%v", s.key, err) } return } @@ -175,7 +175,7 @@ func (s *ServerMod) LoadData() { } err = GoUtil.GobUnmarshal(DbData.ModData, s.data) if err != nil { - log.Debug("LoadData Unmarshal failed,Mod Key: %s err:%v", s.key, err) + log.Error("LoadData Unmarshal failed,Mod Key: %s err:%v", s.key, err) return } } From 1d0ed79b1334332c77ee83c26eeaa1a7de9f3ee9 Mon Sep 17 00:00:00 2001 From: hahwu <31872165+hahwu@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:33:50 +0800 Subject: [PATCH 10/11] =?UTF-8?q?=E6=97=A5=E5=BF=97=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/player_data.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/server/game/player_data.go b/src/server/game/player_data.go index 2297886d..3a204b32 100644 --- a/src/server/game/player_data.go +++ b/src/server/game/player_data.go @@ -27,7 +27,6 @@ import ( "server/game/mod/quest" GoUtil "server/game_util" "server/msg" - telog "server/thinkdata" "strconv" "sync" "time" @@ -1056,9 +1055,9 @@ func (p *Player) TeLog(Type string, Param map[string]interface{}) { if agent != nil { Param["Ip"] = agent.RemoteAddr().String() } - Param["#zone_offset"] = -5 + //Param["#zone_offset"] = -5 // 游戏内TE日志 - go telog.Te.Track(p.GetPlayerBaseMod().GetName(), p.GetPlayerBaseMod().GetName(), Type, Param) + //go telog.Te.Track(p.GetPlayerBaseMod().GetName(), p.GetPlayerBaseMod().GetName(), Type, Param) BaseMod := p.PlayMod.getBaseMod() //途游GA go ga.GAlogEvent(Type, BaseMod.Account, "", Param) From 650db03198f270e943baf27c9b311b1a14bc447a Mon Sep 17 00:00:00 2001 From: hahwu <31872165+hahwu@users.noreply.github.com> Date: Tue, 13 Jan 2026 12:09:27 +0800 Subject: [PATCH 11/11] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=94=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/external.go | 2 + src/server/game/message_mgr.go | 80 ++++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/src/server/game/external.go b/src/server/game/external.go index 8cf8505a..8b4b1f98 100644 --- a/src/server/game/external.go +++ b/src/server/game/external.go @@ -150,6 +150,7 @@ func HandleClientReq(args []interface{}) { detail := &msg.ReqLogin{} proto.Unmarshal(buf, detail) accountInfo := db.GetAccountInfoFromDb(detail.UserName) + log.Debug("player %s start login", detail.UserName) defer func() { if r := recover(); r != nil { GoUtil.SendFeishuFatal(0, m.GetFunc(), fmt.Sprintf("username: %s, fatal: %s", detail.UserName, r)) @@ -177,6 +178,7 @@ func HandleClientReq(args []interface{}) { db.UpdateAccountInfoDeviceToDb(accountInfo) p, _ := internal.Agents.Load(a) if p != nil { + log.Debug("player %s login success", detail.UserName) p.(*Player).PlayMod.getBaseMod().DiviceId = detail.Device //加锁 p.(*Player).PushClientRes(ResLogin) p.(*Player).LoginBackData() diff --git a/src/server/game/message_mgr.go b/src/server/game/message_mgr.go index 72fa1acb..93c9e6bb 100644 --- a/src/server/game/message_mgr.go +++ b/src/server/game/message_mgr.go @@ -175,7 +175,16 @@ func ChampshipRankInfoHandler(data *msg.Msg) (interface{}, error) { func NotifyAllPlayerMsg(m *msg.Msg) { messageMgrData := getMessageData() - for PlayerId, node := range messageMgrData.PlayerList { + // 先复制 PlayerList,避免长时间持有锁 + messageMgrData.mu.Lock() + playerListCopy := make(map[int64]int, len(messageMgrData.PlayerList)) + for k, v := range messageMgrData.PlayerList { + playerListCopy[k] = v + } + messageMgrData.mu.Unlock() + + // 在锁外发送消息 + for PlayerId, node := range playerListCopy { m.To = int(PlayerId) SendMsgToNodeAsync(m, node) } @@ -197,7 +206,11 @@ func CatnipPartnerHandler(data *msg.Msg) (interface{}, error) { func ReplyPlayerMsgASync(m *msg.Msg, reply interface{}) (interface{}, error) { clone := m.Reply(reply) messageMgrData := getMessageData() - if node, ok := messageMgrData.PlayerList[int64(m.From)]; ok { + messageMgrData.mu.Lock() + node, ok := messageMgrData.PlayerList[int64(m.From)] + messageMgrData.mu.Unlock() + + if ok { SendMsgToNodeAsync(clone, node) } return nil, nil @@ -214,12 +227,13 @@ func ClusterSyncHandler(data *msg.Msg) (interface{}, error) { }) return true }) - // 发送暂存区消息 + // 发送暂存区消息(先复制再释放锁,避免长时间持有锁) messageMgrData := getMessageData() messageMgrData.mu.Lock() TempMessageList := messageMgrData.MessageList messageMgrData.MessageList = make(map[int64]*MessageList) - defer messageMgrData.mu.Unlock() + messageMgrData.mu.Unlock() // 立即释放锁,在锁外发送消息 + log.Debug("[Middleware] Cluster sync send temp message len: %d", len(TempMessageList)) for _, Message := range TempMessageList { for _, msgItem := range Message.Messages { @@ -233,25 +247,34 @@ func PlayerLoginHandler(data *msg.Msg) (interface{}, error) { // 关闭 Worker Pool node := data.Extra.(int) messageMgrData := getMessageData() + // 先更新 PlayerList(需要加锁) + messageMgrData.mu.Lock() messageMgrData.PlayerList[int64(data.From)] = node + messageMgrData.mu.Unlock() + log.Debug("[Middleware] Player login success player id: %v, node: %v", data.From, data.Extra.(int)) // 对玩家消息列表加锁 messages := getMessge(int64(data.From)) messages.mu.Lock() - defer messages.mu.Unlock() - // 发送离线消息 - len := len(messages.Messages) - for _, message := range messages.Messages { + // 复制消息列表,避免在锁内发送消息 + messagesToSend := make([]*msg.Msg, len(messages.Messages)) + copy(messagesToSend, messages.Messages) + messages.mu.Unlock() + + // 在锁外发送离线消息 + for _, message := range messagesToSend { SendMsgToNodeAsync(message, node) } - log.Debug("[Middleware] Player sync logout message player id: %v, len: %d", data.From, len) + log.Debug("[Middleware] Player sync logout message player id: %v, len: %d", data.From, len(messagesToSend)) ReplyPlayerMsgASync(data, nil) return nil, nil } func PlayerLogoutHandler(data *msg.Msg) (interface{}, error) { messageMgrData := getMessageData() + messageMgrData.mu.Lock() delete(messageMgrData.PlayerList, int64(data.From)) + messageMgrData.mu.Unlock() log.Debug("[Middleware] Player logout success player id: %v", data.From) return nil, nil } @@ -277,9 +300,15 @@ func CenterPlayerMsgHandler(data *msg.Msg) (interface{}, error) { // 遍历消息列表,发送消息给在线玩家 messages := getMessge(PlayerId) messages.mu.Lock() - defer messages.mu.Unlock() messages.Messages = append(messages.Messages, data) - if node, ok := messageMgrData.PlayerList[int64(PlayerId)]; ok { + messages.mu.Unlock() + + // 检查玩家是否在线(需要加锁) + messageMgrData.mu.Lock() + node, ok := messageMgrData.PlayerList[int64(PlayerId)] + messageMgrData.mu.Unlock() + + if ok { SendMsgToNodeAsync(data, node) } return nil, nil @@ -763,7 +792,8 @@ func saveMessage(m *msg.Msg) error { data := getMessageData() data.mu.Lock() defer data.mu.Unlock() - messages := getMessge(int64(m.To)) + // 使用不加锁的内部方法,避免死锁 + messages := getMessgeUnsafe(int64(m.To)) messages.mu.Lock() defer messages.mu.Unlock() messages.Messages = append(messages.Messages, m) @@ -778,10 +808,9 @@ func GetUserData(PlayerId int64, Key string) (*msg.Msg, error) { }) } -func getMessge(PlayerId int64) *MessageList { +// getMessgeUnsafe 获取消息列表(不加锁,调用者需要持有锁) +func getMessgeUnsafe(PlayerId int64) *MessageList { messageMgrData := getMessageData() - messageMgrData.mu.Lock() - defer messageMgrData.mu.Unlock() if _, ok := messageMgrData.MessageList[int64(PlayerId)]; !ok { messageMgrData.MessageList[int64(PlayerId)] = &MessageList{ Messages: []*msg.Msg{}, @@ -790,6 +819,14 @@ func getMessge(PlayerId int64) *MessageList { return messageMgrData.MessageList[int64(PlayerId)] } +// getMessge 获取消息列表(加锁版本) +func getMessge(PlayerId int64) *MessageList { + messageMgrData := getMessageData() + messageMgrData.mu.Lock() + defer messageMgrData.mu.Unlock() + return getMessgeUnsafe(PlayerId) +} + func deleteMessage(m *msg.Msg) error { if m == nil { return nil @@ -797,16 +834,25 @@ func deleteMessage(m *msg.Msg) error { messages := getMessge(int64(m.To)) messages.mu.Lock() defer messages.mu.Unlock() + + // 使用更安全的方式删除元素:找到索引后再删除,避免在range中修改切片 + foundIndex := -1 for i, msgItem := range messages.Messages { if msgItem == nil { continue } if msgItem.UniKey == m.UniKey { - // 删除消息 - messages.Messages = append(messages.Messages[:i], messages.Messages[i+1:]...) + foundIndex = i log.Debug("[Middleware] send message success; message: %v, player id: %v", msgItem, msgItem.From) break } } + + if foundIndex >= 0 { + // 删除消息:将后面的元素前移,避免内存泄漏 + copy(messages.Messages[foundIndex:], messages.Messages[foundIndex+1:]) + messages.Messages[len(messages.Messages)-1] = nil // 清除最后一个元素的引用 + messages.Messages = messages.Messages[:len(messages.Messages)-1] + } return nil }