diff --git a/src/server/ga/log.go b/src/server/ga/log.go index 18fc609f..bfb46b0b 100644 --- a/src/server/ga/log.go +++ b/src/server/ga/log.go @@ -1,8 +1,6 @@ package ga import ( - "fmt" - galog "github.com/tuyou/galog" ) @@ -24,7 +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 { - 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) + newProperties["proj_"+k] = v } properties = newProperties glogger. 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 6b04a35e..93c9e6bb 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) @@ -154,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) } @@ -176,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 @@ -193,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 { @@ -212,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 } @@ -256,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 @@ -742,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) @@ -757,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{}, @@ -769,17 +819,40 @@ 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 + } 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 } diff --git a/src/server/game/player_data.go b/src/server/game/player_data.go index c0d8e3f0..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,10 +1055,10 @@ func (p *Player) TeLog(Type string, Param map[string]interface{}) { if agent != nil { Param["Ip"] = agent.RemoteAddr().String() } - Param["#zone_offset"] = -5 - 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) } 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 } }