```
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful

refactor(socket): 移除重复保存锁并优化panic错误处理

移除了ServerEvent.go中的sync.Once保存锁,因为可能存在重复保存问题,
同时将panic错误处理从fmt.Println改为使用cool.Logger.Error进行统一日志记录

feat(player): 优化踢人功能并添加超时机制

移除kick.go中的复杂异步超时逻辑
This commit is contained in:
昔念
2026-02-02 23:11:14 +08:00
parent cdb7cec4ad
commit b25f033939
8 changed files with 36 additions and 79 deletions

View File

@@ -65,11 +65,7 @@ func (s *Server) OnClose(c gnet.Conn, err error) (action gnet.Action) {
v, _ := c.Context().(*player.ClientData)
v.LF.Close()
if v.Player != nil {
v.SaveL.Do(func() { //使用保存锁,确保在踢人和掉线的时候只保存一次
v.Player.Save() //保存玩家数据
})
v.Player.Save() //保存玩家数据
}
@@ -110,7 +106,7 @@ func (s *Server) OnTraffic(c gnet.Conn) (action gnet.Action) {
if err := recover(); err != nil { // 恢复 panicerr 为 panic 错误值
// 1. 打印错误信息
fmt.Println(context.TODO(), "panic 错误:", err)
cool.Logger.Error(context.TODO(), "panic 错误:", err)
}
}()

View File

@@ -4,7 +4,6 @@ import (
"blazing/common/socket/errorcode"
"blazing/logic/service/common"
"blazing/logic/service/player"
"blazing/modules/base/service"
"fmt"
"time"
)
@@ -34,25 +33,7 @@ func (s *Server) KickPerson(a int) error {
return nil
}
// 1. 创建结果通道
resultChan := make(chan error, 1)
// 2. 启动 goroutine 执行踢人操作
go func() {
err := player.KickPlayer(uint32(a))
resultChan <- err
}()
// 3. 用 select 监听结果和超时信号
select {
case err := <-resultChan:
// 正常完成
return err
case <-time.After(kickTimeout):
service.NewBaseSysLogService().RecordKick(uint32(a), fmt.Errorf("踢人操作超时(超时时间:%v", kickTimeout).Error())
// 超时触发,返回自定义或内置超时错误
return fmt.Errorf("踢人操作超时(超时时间:%v", kickTimeout)
}
return player.KickPlayer(uint32(a))
}
// 参数不为0是强制踢出

View File

@@ -67,7 +67,7 @@ func (h Controller) UsePetItemOutOfFight(data *item.C2S_USE_PET_ITEM_OUT_OF_FIGH
result = &item.S2C_USE_PET_ITEM_OUT_OF_FIGHT{}
currentPet.CalculatePetPane(false)
copier.Copy(&result, currentPet)
defer c.Service.Info.Save(*c.Info)
return result, 0
}

View File

@@ -18,7 +18,6 @@ import (
"github.com/shirou/gopsutil/v4/mem"
"blazing/modules/base/service"
blservice "blazing/modules/player/service"
"net/http"
@@ -80,6 +79,7 @@ func main() {
// if cool.Config.PortBL == 1 || cool.Config.PortBL == 2 { //只分析1服务器的
// go PprofWeb()
// }
go PprofWeb()
go monitorMemAndQuit()
fmt.Println("Process start, pid:", os.Getpid())

View File

@@ -133,8 +133,8 @@ type ClientData struct {
ERROR_CONNUT int
Wsmsg *WsCodec
Conn gnet.Conn
SaveL sync.Once //保存锁
LF *lockfree.Lockfree[[]byte]
//SaveL sync.Once //保存锁
LF *lockfree.Lockfree[[]byte]
//SaveDone chan struct{}
}

View File

@@ -7,7 +7,9 @@ import (
"blazing/logic/service/common"
"blazing/logic/service/fight/info"
"blazing/logic/service/space"
"fmt"
"sync/atomic"
"time"
"blazing/modules/base/service"
@@ -224,27 +226,35 @@ func (p *Player) ItemAdd(ItemId, ItemCnt uint32) (result bool) {
}
func (player1 *Player) Kick(qtype int) {
//取成功,否则创建
//player1.Save() //先保存数据再返回
// 取成功,否则创建
// player1.Save() //先保存数据再返回
head := common.NewTomeeHeader(1001, player1.Info.UserID)
head.Result = uint32(errorcode.ErrorCodes.ErrAccountLoggedInElsewhere)
if qtype == 1 {
head.Result = uint32(errorcode.ErrorCodes.ErrXinPlanSleepMode)
}
//实际上这里有个问题,会造成重复保存问题
// 实际上这里有个问题,会造成重复保存问题
player1.SendPack(head.Pack(nil))
CloseChan := make(chan struct{})
player1.MainConn.CloseWithCallback(func(c gnet.Conn, err error) error {
close(CloseChan)
return nil
})
<-CloseChan
// --- 新增超时机制核心代码 ---
// 设定超时时间可根据业务需求调整这里以3秒为例
const kickTimeout = 3 * time.Second
select {
case <-CloseChan:
// 正常流程连接关闭回调已执行CloseChan 被关闭
case <-time.After(kickTimeout):
service.NewBaseSysLogService().RecordKick(uint32(player1.Info.UserID), fmt.Errorf("踢人操作超时(超时时间:%v", kickTimeout).Error())
// 超时处理:避免永久阻塞,可添加日志便于排查问题
// 注意:这里不会中断 CloseWithCallback 的执行,仅解除当前协程的阻塞
}
}
func (p *Player) Cheak(b error) {

View File

@@ -22,30 +22,6 @@ func (p *Player) Save() {
return
}
// if p.FightC != nil {
// //ov := make(chan struct{})
// go func() {
// defer func() {
// if err := recover(); err != nil { // 恢复 panicerr 为 panic 错误值
// // 1. 打印错误信息
// cool.Logger.Error(context.TODO(), "panic 错误:", err)
// }
// }()
// p.FightC.Over(p, info.BattleOverReason.PlayerOffline) //玩家逃跑,但是不能锁线程
// }()
// //<-ov
// select {
// case <-p.FightC.GetOverChan(): //等待结束
// case <-time.After(time.Second * 5): //等待5秒
// cool.Logger.Error(context.TODO(), "战斗崩溃", p.Info.UserID)
// }
// }
newtime := uint32(time.Now().Unix())
p.Info.TimeToday = p.Info.TimeToday + newtime - uint32(p.Logintime) //保存电池时间

View File

@@ -54,26 +54,20 @@ func (s *PetFusionMaterialService) Data(Material1 [4]uint32) int32 {
"is_enable": 1,
}
m.Where(condition).Scan(&effect)
//这时候有可能效果是空的,那么这时候就再次查询默认的特性,保证每次必会生成一个数据库有的特性
//也许这个时候的特性配方就是随机从数据库中查找一个特性
if effect != nil {
// if effect == nil {
// effect2s := service.NewDictInfoService().GetData("effect")
// for _, v := range effect2s {
// return gconv.Uint32(v.Value)
// }
// }
r := grand.Intn(4)
switch r {
case 0:
return int32(effect.Trait1Idx)
case 1:
return int32(effect.Trait2Idx)
case 2:
return int32(effect.Trait3Idx)
case 3:
return int32(effect.Trait4Idx)
r := grand.Intn(4)
switch r {
case 0:
return int32(effect.Trait1Idx)
case 1:
return int32(effect.Trait2Idx)
case 2:
return int32(effect.Trait3Idx)
case 3:
return int32(effect.Trait4Idx)
}
}
return -1
}