feat(player): 添加玩家断开连接时的安全保存机制 - 实现 SaveOnDisconnect 方法,确保玩家数据在断开连接时安全保存 - 添加并发控制防止重复保存操作,使用互斥锁和完成通道确保一次保存 - 在 socket 关闭事件中改为异步调用 SaveOnDisconnect 避免阻塞 - 添加 panic 恢复机制保护保存过程中的异常情况 refactor(login): 优化登录时的踢人逻辑和超时处理
159 lines
3.3 KiB
Go
159 lines
3.3 KiB
Go
package player
|
||
|
||
import (
|
||
"blazing/common/data/share"
|
||
"blazing/cool"
|
||
"blazing/modules/player/model"
|
||
"fmt"
|
||
|
||
"blazing/logic/service/space"
|
||
"context"
|
||
"time"
|
||
|
||
"github.com/gogf/gf/v2/util/grand"
|
||
)
|
||
|
||
// Save 保存玩家数据
|
||
func (p *Player) Save() {
|
||
if p == nil || p.Info == nil {
|
||
return
|
||
|
||
}
|
||
cacheCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||
defer cancel()
|
||
cool.CacheManager.Remove(cacheCtx, fmt.Sprintf("player:%d", p.Info.UserID))
|
||
newtime := time.Now().Unix()
|
||
p.Info.TimeToday = p.Info.TimeToday + newtime - int64(p.Logintime) //保存电池时间
|
||
// p.Info.FightTime = p.Info.FightTime + (newtime - int64(p.Logintime))
|
||
p.Info.OnlineTime = p.Info.OnlineTime + (newtime-int64(p.Logintime))/60 //每次退出时候保存已经在线的分钟数
|
||
|
||
if p.FightC != nil {
|
||
|
||
//ov := make(chan struct{})
|
||
go func() {
|
||
|
||
defer func() {
|
||
if err := recover(); err != nil { // 恢复 panic,err 为 panic 错误值
|
||
// 1. 打印错误信息
|
||
|
||
cool.Logger.Error(context.TODO(), "panic 错误:", err)
|
||
|
||
}
|
||
}()
|
||
p.FightC.Over(p, model.BattleOverReason.PlayerOffline) //玩家逃跑,但是不能锁线程
|
||
}()
|
||
//<-ov
|
||
select {
|
||
case <-p.FightC.GetOverChan(): //等待结束
|
||
case <-time.After(time.Second * 5): //等待5秒
|
||
cool.Logger.Error(context.TODO(), "战斗崩溃", p.Info.UserID)
|
||
|
||
}
|
||
|
||
}
|
||
p.IsLogin = false
|
||
if p.Service != nil && p.Service.Info != nil {
|
||
p.Service.Info.SaveUntilSuccess(*p.Info)
|
||
} else {
|
||
cool.Logger.Error(context.TODO(), "player save skipped: service not ready", p.Info.UserID)
|
||
}
|
||
space.GetSpace(p.Info.MapID).LeaveMap(p)
|
||
|
||
p.MapNPC.Stop() //停止刷怪
|
||
|
||
Mainplayer.Delete(p.Info.UserID)
|
||
share.ShareManager.DeleteUserOnline(p.Info.UserID) //设置用户登录服务器
|
||
|
||
}
|
||
func (p *Player) SaveOnDisconnect() {
|
||
if p == nil {
|
||
return
|
||
}
|
||
|
||
p.logoutMu.Lock()
|
||
if p.logoutSaved {
|
||
p.logoutMu.Unlock()
|
||
return
|
||
}
|
||
if p.logoutDone != nil {
|
||
done := p.logoutDone
|
||
p.logoutMu.Unlock()
|
||
<-done
|
||
return
|
||
}
|
||
done := make(chan struct{})
|
||
p.logoutDone = done
|
||
p.logoutMu.Unlock()
|
||
|
||
defer func() {
|
||
if recoverErr := recover(); recoverErr != nil {
|
||
if p.Info != nil {
|
||
cool.Logger.Error(context.TODO(), "SaveOnDisconnect panic", p.Info.UserID, recoverErr)
|
||
} else {
|
||
cool.Logger.Error(context.TODO(), "SaveOnDisconnect panic", recoverErr)
|
||
}
|
||
}
|
||
|
||
p.logoutMu.Lock()
|
||
p.logoutSaved = true
|
||
if p.logoutDone == done {
|
||
close(done)
|
||
p.logoutDone = nil
|
||
}
|
||
p.logoutMu.Unlock()
|
||
}()
|
||
|
||
p.Save()
|
||
}
|
||
func (p *Player) CanGet() bool {
|
||
if p.Info.TimeToday >= p.Info.TimeLimit {
|
||
return false
|
||
}
|
||
|
||
islogintime := (int64(time.Now().Unix()) - int64(p.Logintime))
|
||
|
||
if islogintime > (p.Info.TimeLimit - p.Info.TimeToday) {
|
||
return false
|
||
}
|
||
return true
|
||
}
|
||
|
||
// 经验倍数返回
|
||
func (p *Player) CanGetExp() (float32, float32) {
|
||
|
||
var base float32 = 1
|
||
if p.Info.TwoTimes > 0 {
|
||
p.Info.TwoTimes--
|
||
base *= 2
|
||
|
||
}
|
||
if p.Info.ThreeTimes > 0 {
|
||
p.Info.ThreeTimes--
|
||
base *= 3
|
||
|
||
}
|
||
return (base), 0.2
|
||
}
|
||
func (p *Player) CanGetXUAN() bool {
|
||
var base = 1
|
||
if p.Info.EnergyTime > 0 {
|
||
base = 2
|
||
// p.Info.EnergyTime--
|
||
}
|
||
if grand.Meet(base, 2) {
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
func (p *Player) CanGetItem() bool {
|
||
var base = 3
|
||
if p.Info.EnergyTime > 0 {
|
||
base = 6
|
||
p.Info.EnergyTime--
|
||
}
|
||
if grand.Meet(base, 10) {
|
||
return true
|
||
}
|
||
return false
|
||
}
|