feat(player): 添加玩家断开连接时的安全保存机制

- 实现 SaveOnDisconnect 方法,确保玩家数据在断开连接时安全保存
- 添加并发控制防止重复保存操作,使用互斥锁和完成通道确保一次保存
- 在 socket 关闭事件中改为异步调用 SaveOnDisconnect 避免阻塞
- 添加 panic 恢复机制保护保存过程中的异常情况

refactor(login): 优化登录时的踢人逻辑和超时处理
This commit is contained in:
昔念
2026-04-05 11:14:25 +08:00
parent 37cd641942
commit c3da3162ee
6 changed files with 155 additions and 27 deletions

View File

@@ -17,6 +17,7 @@ import (
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/grand"
"github.com/google/uuid"
csmap "github.com/mhmtszr/concurrent-swiss-map"
)
@@ -247,18 +248,64 @@ func (s *InfoService) Save(data model.PlayerInfo) {
return
}
for i := 0; i < 3; i++ {
_, err := s.dbm_fix(s.Model).Data("data", data).Update()
_ = s.saveWithRetry(data, true)
}
func (s *InfoService) SaveUntilSuccess(data model.PlayerInfo) {
if cool.Config.ServerInfo.IsVip != 0 {
return
}
var (
attempt = 0
backoff = time.Second
maxBackoff = 30 * time.Second
)
for {
attempt++
fallback := attempt == 1 || attempt%10 == 0
err := s.saveWithRetry(data, fallback)
if err == nil {
return
}
if i == 2 {
cool.Logger.Error(context.TODO(), "player save failed after retries, fallback to local file", data.UserID, err)
s.saveToLocalFile(&data, err)
return
if attempt == 1 || attempt%10 == 0 {
cool.Logger.Error(context.TODO(), "player save retrying until success", data.UserID, attempt, err)
}
halfBackoff := int(backoff / 2)
if halfBackoff < 1 {
halfBackoff = 1
}
jitter := time.Duration(grand.Intn(halfBackoff))
sleepFor := backoff + jitter
if sleepFor > maxBackoff {
sleepFor = maxBackoff
}
time.Sleep(sleepFor)
if backoff < maxBackoff {
backoff *= 2
if backoff > maxBackoff {
backoff = maxBackoff
}
}
}
}
func (s *InfoService) saveWithRetry(data model.PlayerInfo, fallback bool) error {
var lastErr error
for i := 0; i < 3; i++ {
_, err := s.dbm_fix(s.Model).Data("data", data).Update()
if err == nil {
return nil
}
lastErr = err
}
if fallback && lastErr != nil {
cool.Logger.Error(context.TODO(), "player save failed after retries, fallback to local file", data.UserID, lastErr)
s.saveToLocalFile(&data, lastErr)
}
return lastErr
}