diff --git a/common/slice.go b/common/slice.go index 4fc046f..cfc077f 100644 --- a/common/slice.go +++ b/common/slice.go @@ -452,6 +452,22 @@ func SliceValueWeight(sl []int, index int) float64 { return float64(value) / float64(totle) } +func GetMapKeys[K comparable, V any](data map[K]V) []K { + var ret []K + for k := range data { + ret = append(ret, k) + } + return ret +} + +func GetMapValues[K comparable, V any](data map[K]V) []V { + var ret []V + for _, v := range data { + ret = append(ret, v) + } + return ret +} + type Int32Slice []int32 func (p Int32Slice) Len() int { return len(p) } diff --git a/dbproxy/svc/u_playergamedata.go b/dbproxy/svc/u_playergamedata.go new file mode 100644 index 0000000..f3dc288 --- /dev/null +++ b/dbproxy/svc/u_playergamedata.go @@ -0,0 +1,71 @@ +package svc + +import ( + "errors" + "net/rpc" + + "github.com/globalsign/mgo" + "github.com/globalsign/mgo/bson" + + "mongo.games.com/game/dbproxy/mongo" + "mongo.games.com/game/model" +) + +var ( + PlayerGameDataDBName = "user" + PlayerGameDataCollName = "user_gamedata" + PlayerGameDataColError = errors.New("PlayerGameData collection open failed") + PlayerGameDataSvcSingle = &PlayerGameDataSvc{} +) + +func PlayerGameDataCollection(plt string) *mongo.Collection { + s := mongo.MgoSessionMgrSington.GetPltMgoSession(plt, PlayerGameDataDBName) + if s != nil { + c, first := s.DB().C(PlayerGameDataCollName) + if first { + c.EnsureIndex(mgo.Index{Key: []string{"snid", "id"}, Unique: true, Background: true, Sparse: true}) + c.EnsureIndex(mgo.Index{Key: []string{"id"}, Background: true, Sparse: true}) + } + return c + } + + return nil +} + +func init() { + rpc.Register(PlayerGameDataSvcSingle) +} + +type PlayerGameDataSvc struct{} + +func (p *PlayerGameDataSvc) Save(req *model.PlayerGameSaveReq, b *bool) error { + c := PlayerGameDataCollection(req.Platform) + if c == nil { + return PlayerGameDataColError + } + + for _, v := range req.Data { + _, err := c.Upsert(bson.M{"snid": v.SnId, "id": v.Id}, v) + if err != nil { + return err + } + } + *b = true + + return nil +} + +func (p *PlayerGameDataSvc) Find(req *model.PlayerGameDataFindReq, res *model.PlayerGameDataFindRes) error { + c := PlayerGameDataCollection(req.Platform) + if c == nil { + return PlayerGameDataColError + } + + var ret []*model.PlayerGameData + err := c.Find(bson.M{"snid": req.SnId}).All(&ret) + if err != nil { + return err + } + res.Data = ret + return nil +} diff --git a/gamesrv/base/player.go b/gamesrv/base/player.go index 721aeef..9b29b76 100644 --- a/gamesrv/base/player.go +++ b/gamesrv/base/player.go @@ -68,8 +68,8 @@ const ( ) type Player struct { - model.PlayerData //po 持久化对象 - ExtraData interface{} //扩展接口 + model.WGPlayerInfo + ExtraData interface{} //具体游戏对局中的玩家扩展信息 gateSess *netlib.Session //所在GateServer的session worldSess *netlib.Session //所在WorldServer的session scene *Scene //当前所在个Scene @@ -141,13 +141,15 @@ func NewPlayer(sid int64, data []byte, ws, gs *netlib.Session) *Player { RankScore: make(map[int32]int64), } - // 需要make的,统一在这里初始化默认值,别的地方就不用再初始化了 - p.PlayerData = model.PlayerData{ - //TotalGameData: make(map[int][]*model.PlayerGameTotal), - GDatas: make(map[string]*model.PlayerGameInfo), - ShopTotal: make(map[int32]*model.ShopTotal), - ShopLastLookTime: make(map[int32]int64), - IsFoolPlayer: make(map[string]bool), + //todo 初始化 + p.WGPlayerInfo = model.WGPlayerInfo{ + PlayerData: &model.PlayerData{ + GDatas: make(map[string]*model.PlayerGameInfo), + ShopTotal: make(map[int32]*model.ShopTotal), + ShopLastLookTime: make(map[int32]int64), + IsFoolPlayer: make(map[string]bool), + }, + GameData: make(map[int32]*model.PlayerGameData), } if p.init(data) { @@ -373,7 +375,20 @@ func (this *Player) OnAudienceLeave(reason int) { } func (this *Player) MarshalData(gameid int) (d []byte, e error) { - d, e = netlib.Gob.Marshal(&this.PlayerData) + // 防止参数遗漏 + for k, v := range this.GameData { + if v.SnId == 0 { + v.SnId = this.SnId + } + if v.Platform == "" { + v.Platform = this.Platform + } + if v.Id == 0 { + v.Id = k + } + } + + d, e = netlib.Gob.Marshal(&this.WGPlayerInfo) logger.Logger.Trace("(this *Player) MarshalData(gameid int)") return } @@ -382,7 +397,7 @@ func (this *Player) UnmarshalData(data []byte) bool { if len(data) == 0 { return true } - err := netlib.Gob.Unmarshal(data, &this.PlayerData) + err := netlib.Gob.Unmarshal(data, &this.WGPlayerInfo) if err == nil { this.dirty = true return true diff --git a/gamesrv/base/scene.go b/gamesrv/base/scene.go index 4ac97eb..fd3f0f7 100644 --- a/gamesrv/base/scene.go +++ b/gamesrv/base/scene.go @@ -1984,7 +1984,7 @@ func (this *Scene) TryBillExGameDrop(p *Player) { itemData := srvdata.GameItemMgr.Get(p.Platform, id) if itemData != nil { p.AddItems(&model.AddItemParam{ - P: &p.PlayerData, + P: p.PlayerData, Change: nil, GainWay: common.GainWay_Game, Operator: "system", @@ -2021,7 +2021,7 @@ func (this *Scene) DropCollectBox(p *Player) { if itemData != nil { pack.Items = map[int32]int32{itemData.Id: 1} p.AddItems(&model.AddItemParam{ - P: &p.PlayerData, + P: p.PlayerData, Change: []*model.Item{ { ItemId: itemData.Id, diff --git a/gamesrv/tienlen/scenepolicy_tienlen.go b/gamesrv/tienlen/scenepolicy_tienlen.go index 58c5db6..34581f3 100644 --- a/gamesrv/tienlen/scenepolicy_tienlen.go +++ b/gamesrv/tienlen/scenepolicy_tienlen.go @@ -1803,7 +1803,7 @@ func (this *SceneBilledStateTienLen) OnEnter(s *base.Scene) { // vip加成分 vipScore = int64(math.Ceil(float64(rankScore) * float64(losePlayer.VipExtra) / 100.0)) // 角色加成分 - _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(&losePlayer.PlayerData, common.RoleAddRankScore) + _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(losePlayer.PlayerData, common.RoleAddRankScore) roleScore = int64(math.Ceil(float64(rankScore) * float64(roleAdd) / 100.0)) //周卡加成 if losePlayer.GetWeekCardPrivilege(2) { @@ -1945,7 +1945,7 @@ func (this *SceneBilledStateTienLen) OnEnter(s *base.Scene) { // vip加成分 vipScore = int64(math.Ceil(float64(rankScore) * float64(lastWinPlayer.VipExtra) / 100.0)) // 角色加成分 - _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(&lastWinPlayer.PlayerData, common.RoleAddRankScore) + _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(lastWinPlayer.PlayerData, common.RoleAddRankScore) roleScore = int64(math.Ceil(float64(rankScore) * float64(roleAdd) / 100.0)) //周卡加成 if lastWinPlayer.GetWeekCardPrivilege(2) { @@ -2056,7 +2056,7 @@ func (this *SceneBilledStateTienLen) OnEnter(s *base.Scene) { // vip加成分 vipScore = int64(math.Ceil(float64(rankScore) * float64(playerEx.VipExtra) / 100.0)) // 角色加成分 - _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(&playerEx.PlayerData, common.RoleAddRankScore) + _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(playerEx.PlayerData, common.RoleAddRankScore) roleScore = int64(math.Ceil(float64(rankScore) * float64(roleAdd) / 100.0)) //周卡加成 if playerEx.GetWeekCardPrivilege(2) { @@ -2161,7 +2161,7 @@ func (this *SceneBilledStateTienLen) OnEnter(s *base.Scene) { // vip加成分 vipScore = int64(math.Ceil(float64(rankScore) * float64(playerEx.VipExtra) / 100.0)) // 角色加成分 - _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(&playerEx.PlayerData, common.RoleAddRankScore) + _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(playerEx.PlayerData, common.RoleAddRankScore) roleScore = int64(math.Ceil(float64(rankScore) * float64(roleAdd) / 100.0)) //周卡加成 if playerEx.GetWeekCardPrivilege(2) { @@ -2310,7 +2310,7 @@ func (this *SceneBilledStateTienLen) OnEnter(s *base.Scene) { // vip加成分 vipScore = int64(math.Ceil(float64(rankScore) * float64(playerEx.VipExtra) / 100.0)) // 角色加成分 - _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(&playerEx.PlayerData, common.RoleAddRankScore) + _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(playerEx.PlayerData, common.RoleAddRankScore) roleScore = int64(math.Ceil(float64(rankScore) * float64(roleAdd) / 100.0)) //周卡加成 if playerEx.GetWeekCardPrivilege(2) { @@ -2442,7 +2442,7 @@ func (this *SceneBilledStateTienLen) OnEnter(s *base.Scene) { // vip加成分 vipScore = int64(math.Ceil(float64(rankScore) * float64(playerEx.VipExtra) / 100.0)) // 角色加成分 - _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(&playerEx.PlayerData, common.RoleAddRankScore) + _, roleAdd = srvdata.RolePetMgrSington.GetRoleAdd(playerEx.PlayerData, common.RoleAddRankScore) roleScore = int64(math.Ceil(float64(rankScore) * float64(roleAdd) / 100.0)) //周卡加成 if playerEx.GetWeekCardPrivilege(2) { @@ -2613,7 +2613,7 @@ func (this *SceneBilledStateTienLen) OnEnter(s *base.Scene) { } } p.AddItems(&model.AddItemParam{ - P: &p.PlayerData, + P: p.PlayerData, Change: items, GainWay: common.GainWayRoomGain, Operator: "system", diff --git a/model/player.go b/model/player.go index 6f867ea..ca719c8 100644 --- a/model/player.go +++ b/model/player.go @@ -344,6 +344,13 @@ type MatchFreeSignupRec struct { UseTimes int32 //累计使用免费次数 } +// WGPlayerInfo 游戏服玩家信息 +// 大厅玩家信息发送给游戏服 +type WGPlayerInfo struct { + *PlayerData + GameData map[int32]*PlayerGameData // 游戏数据,只允许存储玩家对应某个游戏需要持久化的数据 +} + type PlayerData struct { Id bson.ObjectId `bson:"_id"` AccountId string //账号id diff --git a/model/playergamedata.go b/model/playergamedata.go new file mode 100644 index 0000000..0fd4a07 --- /dev/null +++ b/model/playergamedata.go @@ -0,0 +1,56 @@ +package model + +import ( + "time" + + "mongo.games.com/goserver/core/logger" +) + +type PlayerGameData struct { + Platform string `bson:"-"` + SnId int32 + Id int32 // 游戏id或场次id + Data interface{} // 数据 +} + +type PlayerGameSaveReq struct { + Platform string + Data []*PlayerGameData +} + +func SavePlayerGameData(platform string, data []*PlayerGameData) error { + if rpcCli == nil { + logger.Logger.Error("model.SavePlayerGameData rpcCli == nil") + return nil + } + b := false + err := rpcCli.CallWithTimeout("PlayerGameDataSvc.Save", &PlayerGameSaveReq{Platform: platform, Data: data}, &b, time.Second*30) + if err != nil { + logger.Logger.Error("model.SavePlayerGameData err:%v", err) + return err + } + return nil +} + +type PlayerGameDataFindReq struct { + Platform string + SnId int32 +} + +type PlayerGameDataFindRes struct { + Data []*PlayerGameData +} + +func GetPlayerGameData(platform string, snid int32) ([]*PlayerGameData, error) { + if rpcCli == nil { + logger.Logger.Error("model.GetPlayerGameData rpcCli == nil") + return nil, nil + } + res := &PlayerGameDataFindRes{} + err := rpcCli.CallWithTimeout("PlayerGameDataSvc.Find", &PlayerGameDataFindReq{Platform: platform, SnId: snid}, res, time.Second*30) + if err != nil { + logger.Logger.Error("model.GetPlayerGameData err:%v", err) + return nil, err + } + return res.Data, nil +} diff --git a/worldsrv/player.go b/worldsrv/player.go index 654d23a..874ebae 100644 --- a/worldsrv/player.go +++ b/worldsrv/player.go @@ -1663,14 +1663,21 @@ func (this *Player) OnLogouted() { } func (this *Player) MarshalData() (d []byte, e error) { - d, e = netlib.Gob.Marshal(this.PlayerData) + data := &model.WGPlayerInfo{ + PlayerData: this.PlayerData, + } + info := PlayerInfoMgrSingle.Players[this.SnId] + if info != nil { + data.GameData = info.GameData + } + d, e = netlib.Gob.Marshal(data) return } // UnmarshalData 更新玩家数据 // 例如游戏服数据同步 func (this *Player) UnmarshalData(data []byte, scene *Scene) { - pd := &model.PlayerData{} + pd := &model.WGPlayerInfo{} if err := netlib.Gob.Unmarshal(data, pd); err != nil { logger.Logger.Warn("Player.SyncData err:", err) return @@ -1687,6 +1694,12 @@ func (this *Player) UnmarshalData(data []byte, scene *Scene) { this.GDatas[v] = d } } + // PlayerInfo 同步 + info := PlayerInfoMgrSingle.Players[this.SnId] + if info == nil { + PlayerInfoMgrSingle.Players[this.SnId] = &PlayerInfo{} + } + info.GameData = pd.GameData this.LastRechargeWinCoin = pd.LastRechargeWinCoin oldRecharge := int64(0) diff --git a/worldsrv/playerinfo.go b/worldsrv/playerinfo.go new file mode 100644 index 0000000..850c073 --- /dev/null +++ b/worldsrv/playerinfo.go @@ -0,0 +1,128 @@ +package main + +import ( + "strconv" + + "mongo.games.com/goserver/core/basic" + "mongo.games.com/goserver/core/logger" + "mongo.games.com/goserver/core/task" + + "mongo.games.com/game/common" + "mongo.games.com/game/model" + "mongo.games.com/game/worldsrv/internal" +) + +/* +玩家信息加载,缓存,持久化,缓存释放 +*/ + +func init() { + internal.RegisterPlayerLoad(PlayerInfoMgrSingle) +} + +var PlayerInfoMgrSingle = &PlayerInfoMgr{ + Players: make(map[int32]*PlayerInfo), +} + +type AllPlayerInfo struct { + GameData []*model.PlayerGameData +} + +// PlayerInfo 玩家信息 +type PlayerInfo struct { + GameData map[int32]*model.PlayerGameData // 游戏数据 +} + +type PlayerInfoMgr struct { + Players map[int32]*PlayerInfo +} + +func (p *PlayerInfoMgr) Load(platform string, snid int32, player any) *internal.PlayerLoadReplay { + var err error + allPlayerInfo := &AllPlayerInfo{ + GameData: make([]*model.PlayerGameData, 0), + } + // 游戏数据 + gameData, err := model.GetPlayerGameData(platform, snid) + if err != nil { + logger.Logger.Errorf("GetPlayerGameData snid:%v error: %v", snid, err) + goto here + } + allPlayerInfo.GameData = gameData + // ... + +here: + return &internal.PlayerLoadReplay{ + Platform: platform, + Snid: snid, + Err: err, + Data: allPlayerInfo, + } +} + +func (p *PlayerInfoMgr) Callback(player any, ret *internal.PlayerLoadReplay) { + if ret.Err != nil { + return + } + data, ok := ret.Data.(*AllPlayerInfo) + if !ok { + return + } + info := &PlayerInfo{ + GameData: make(map[int32]*model.PlayerGameData), + } + + // 游戏数据 + for _, v := range data.GameData { + info.GameData[v.Id] = v + } + // ... + + p.Players[ret.Snid] = info +} + +func (p *PlayerInfoMgr) LoadAfter(platform string, snid int32) *internal.PlayerLoadReplay { + return nil +} + +func (p *PlayerInfoMgr) CallbackAfter(ret *internal.PlayerLoadReplay) { + +} + +func (p *PlayerInfoMgr) Save(platform string, snid int32, isSync, force bool) { + var err error + f := func() { + data, ok := p.Players[snid] + if !ok { + return + } + + // 游戏数据 + err = model.SavePlayerGameData(platform, common.GetMapValues(data.GameData)) + if err != nil { + logger.Logger.Errorf("SavePlayerGameData snid:%v error: %v", snid, err) + } + // ... + } + + cf := func() { + + } + + if isSync { + f() + cf() + return + } + + task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { + f() + return nil + }), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) { + cf() + }), "SavePlayerInfo").StartByFixExecutor("SnId:" + strconv.Itoa(int(snid))) +} + +func (p *PlayerInfoMgr) Release(platform string, snid int32) { + delete(p.Players, snid) +}