game_sync/worldsrv/bagmgr.go

1025 lines
27 KiB
Go

package main
import (
"errors"
"fmt"
"time"
"github.com/globalsign/mgo/bson"
"golang.org/x/exp/maps"
"mongo.games.com/goserver/core/basic"
"mongo.games.com/goserver/core/i18n"
"mongo.games.com/goserver/core/logger"
"mongo.games.com/goserver/core/module"
"mongo.games.com/goserver/core/task"
"mongo.games.com/game/common"
"mongo.games.com/game/model"
"mongo.games.com/game/proto"
"mongo.games.com/game/protocol/bag"
playerproto "mongo.games.com/game/protocol/player"
webapiproto "mongo.games.com/game/protocol/webapi"
"mongo.games.com/game/srvdata"
"mongo.games.com/game/webapi"
"mongo.games.com/game/worldsrv/internal"
)
func init() {
module.RegisteModule(BagMgrSingleton, time.Second, 0)
internal.RegisterPlayerLoad(BagMgrSingleton)
BagMgrSingleton.AddOnChangeFuncs(func(param *model.ChangeItemParam) {
p := PlayerMgrSington.GetPlayerBySnId(param.SnId)
if p == nil {
return
}
itemData := srvdata.GameItemMgr.Get(p.Platform, param.ItemId)
if itemData == nil {
return
}
logType := ItemObtain
if param.ItemNum < 0 {
logType = ItemConsume
}
//获奖记录log
if logType == ItemObtain && param.ItemNum > 0 {
awardLogType := 0
if itemData.Type == common.ItemTypeChange {
//话费
awardLogType = 1
} else if itemData.Type == common.ItemTypeObjective {
//实物
awardLogType = 2
AwardLogMgr.UpdateAwardLog(p.Platform, itemData.Id, param.ItemNum)
}
if awardLogType != 0 {
data := model.AnnouncerLog{
Platform: p.Platform,
Snid: p.SnId,
Name: p.Name,
Phone: p.Tel,
ItemId: param.ItemId, //获得物品ID
TypeId: int32(awardLogType),
}
AwardLogMgr.UpdateAnnouncerLog(data)
}
}
// 皮肤自动解锁
if p != nil && itemData.GetType() == 21 && param.ItemNum > 0 {
p.AutoSkinUnlock()
}
if param.ItemNum > 0 {
switch param.ItemId {
case common.ItemIDWeekScore:
TaskSubjectSingleton.Touch(common.TaskTypeActivityScore, &TaskData{
SnId: p.SnId,
Num: param.ItemNum,
})
case common.ItemIDPetSkill:
PetMgrSington.CheckShowRed(p)
case common.ItemIDPermit, common.ItemIDLong:
var permitScore, long int64
if param.ItemId == common.ItemIDLong {
long = param.ItemNum
} else {
permitScore = param.ItemNum
}
LogChannelSingleton.WriteLog(&model.BackendPermitJoin{
Platform: p.Platform,
StartTs: PlatformMgrSingleton.GetConfig(p.Platform).PermitStartTs,
SnId: p.SnId,
Score: permitScore,
Long: long,
Ts: time.Now().Unix(),
})
}
switch itemData.GetType() {
case common.ItemTypeSkinChip:
PetMgrSington.CheckSkinRed(p)
}
}
// 统计 v卡兑换消耗数量
if p != nil && param.ItemId == common.ItemIDVCard && param.GainWay == common.GainWay_Exchange {
p.VCardCost += -param.ItemNum
if p.VCardCost < 0 {
p.VCardCost = 0
}
}
// 更新通行证赛季积分排行榜
if param.ItemId == common.ItemIDPermit {
item := BagMgrSingleton.GetItem(p.SnId, param.ItemId)
startTs := PlatformMgrSingleton.GetConfig(p.Platform).PermitStartTs
if item != nil && item.ItemNum > 0 && startTs > 0 {
// 赛季积分排行榜
LogChannelSingleton.WriteLog(&model.PermitScore{
Platform: p.Platform,
SnId: p.SnId,
Name: p.Name,
Exp: item.ItemNum,
ModId: p.Roles.ModId,
StartTs: startTs,
Ts: time.Now().Unix(),
})
}
}
// 更新好友信息
if param.ItemId == common.ItemIDVCard {
FriendMgrSington.UpdateInfo(p.Platform, p.SnId)
}
})
}
const (
BagItemMax int32 = 200
)
// 道具功能 Function
const (
ItemCanUse = iota //可以使用
ItemCanGive //可以赠送
ItemCanSell //可以出售
ItemCanExchange //可以兑换
ItemCanFen //可以分解
ItemMax
)
type Item struct {
ItemId int32 // 物品ID
ItemNum int64 // 物品数量
ObtainTime int64 //获取的时间
//数据表数据
Name string // 名称
Effect0 []int32 // 竖版道具功能 ItemCanUse ...
Effect []int32 // 横版道具功能 ItemCanUse ...
SaleType int32 // 出售类型
SaleGold int32 // 出售金额
}
type BagInfo struct {
SnId int32 //玩家id
Platform string //平台id
BagItem map[int32]*Item //背包数据 key为itemId
Ts int64 //更新时间戳
// 临时携带参数
dirty bool `bson:"-"` //是否需要更新数据库
LogId string `bson:"-"` //最后一次保存的日志id
}
func NewBagInfo(platform string, snid int32) *BagInfo {
return &BagInfo{
SnId: snid,
Platform: platform,
BagItem: make(map[int32]*Item),
Ts: time.Now().Unix(),
dirty: true,
LogId: "",
}
}
// BagMgrSingleton 背包管理器
var BagMgrSingleton = &BagMgr{
PlayerBag: make(map[int32]*BagInfo),
}
type BagMgr struct {
PlayerBag map[int32]*BagInfo // snid:背包
// 道具变更监听,玩家离线时道具变更会在登录时根据道具日志执行一遍
onChangeFuncs []func(param *model.ChangeItemParam)
}
func (this *BagMgr) ModuleName() string {
return "BagMgr"
}
func (this *BagMgr) Init() {
}
func (this *BagMgr) Update() {
}
func (this *BagMgr) Shutdown() {
module.UnregisteModule(this)
}
func (this *BagMgr) AddOnChangeFuncs(f ...func(param *model.ChangeItemParam)) {
this.onChangeFuncs = append(this.onChangeFuncs, f...)
}
func (this *BagMgr) OnChangeFuncs(param *model.ChangeItemParam) {
logger.Logger.Tracef("OnChangeFuncs %+v", *param)
for _, v := range this.onChangeFuncs {
v(param)
}
}
// GetItem 获取个人的指定道具信息
func (this *BagMgr) GetItem(snid, itemId int32) *Item {
p := PlayerMgrSington.GetPlayerBySnId(snid)
if p == nil {
return nil
}
item := &Item{
ItemId: itemId,
}
f := func() {
itemX := srvdata.GameItemMgr.Get(p.Platform, itemId)
if itemX != nil {
item.Name = itemX.Name
item.Effect0 = itemX.Effect
item.Effect = itemX.Effect
item.SaleType = itemX.SaleType
item.SaleGold = itemX.SaleGold
}
}
switch itemId {
case common.ItemIDCoin:
item.ItemNum = p.Coin
f()
case common.ItemIDDiamond:
item.ItemNum = p.Diamond
f()
default:
if bagItem, ok := this.PlayerBag[snid]; ok {
if bagItem != nil {
item = bagItem.BagItem[itemId]
if item != nil {
f()
return item
}
}
}
}
return item
}
// Range 遍历背包
func (this *BagMgr) Range(snid int32, fn func(item *Item) bool) {
if v, exist := this.PlayerBag[snid]; exist {
for k := range v.BagItem {
e := this.GetItem(snid, k)
if e == nil {
continue
}
if !fn(e) {
return
}
}
}
}
// GetBagInfo 获取背包信息
// 是复制的一份数据
func (this *BagMgr) GetBagInfo(snid int32) *BagInfo {
p := PlayerMgrSington.GetPlayerBySnId(snid)
if p == nil {
return nil
}
ret := NewBagInfo(p.Platform, p.SnId)
if v, exist := this.PlayerBag[snid]; exist {
ret.Ts = v.Ts
ret.dirty = v.dirty
ret.LogId = v.LogId
for k := range v.BagItem {
ret.BagItem[k] = this.GetItem(snid, k)
}
} else {
this.PlayerBag[snid] = NewBagInfo(p.Platform, p.SnId)
}
return ret
}
// SyncBagData 通知玩家背包数据变化
func (this *BagMgr) SyncBagData(snid int32, changeItemIds ...int32) {
if len(changeItemIds) == 0 {
return
}
p := PlayerMgrSington.GetPlayerBySnId(snid)
if p == nil || p.IsRob {
return
}
var itemInfos []*bag.ItemInfo
for _, itemId := range changeItemIds {
itemInfo := this.GetItem(snid, itemId)
if itemInfo != nil {
itemInfos = append(itemInfos, &bag.ItemInfo{
ItemId: itemInfo.ItemId,
ItemNum: itemInfo.ItemNum,
ObtainTime: itemInfo.ObtainTime,
})
}
}
pack := &bag.SCSyncBagData{
Infos: itemInfos,
}
p.SendToClient(int(bag.SPacketID_PACKET_SC_SYNCBAGDATA), pack)
logger.Logger.Tracef("背包数据变更(%v): %v", p.SnId, pack)
}
// AddItemCheck 校验道具是否充足
// 返回道具变化,操作结果,是否成功
func (this *BagMgr) AddItemCheck(param *model.AddItemParam) ([]*model.Item, bag.OpResultCode, bool) {
var items []*model.Item // 道具变化
p := PlayerMgrSington.GetPlayerBySnId(param.SnId)
if p == nil {
return items, bag.OpResultCode_OPRC_NotPlayer, false
}
// 获取背包
var newBagInfo *BagInfo
if _, exist := this.PlayerBag[p.SnId]; !exist {
newBagInfo = NewBagInfo(p.Platform, p.SnId)
this.PlayerBag[p.SnId] = newBagInfo
} else {
newBagInfo = this.PlayerBag[p.SnId]
}
// 参数校验
for _, v := range param.Change {
if v == nil || v.ItemNum == 0 {
continue
}
item := srvdata.GameItemMgr.Get(p.Platform, v.ItemId)
if item == nil {
return items, bag.OpResultCode_OPRC_IdErr, false
}
if itm, exist := newBagInfo.BagItem[v.ItemId]; exist {
if v.ItemNum < 0 && itm.ItemNum < -v.ItemNum {
return items, bag.OpResultCode_OPRC_UseUp, false
}
} else {
if v.ItemNum < 0 {
return items, bag.OpResultCode_OPRC_UseUp, false
}
}
items = append(items, &model.Item{
ItemId: v.ItemId,
ItemNum: v.ItemNum,
ObtainTime: v.ObtainTime,
})
}
return items, bag.OpResultCode_OPRC_Sucess, true
}
// AddItems 修改道具,玩家需在线
func (this *BagMgr) AddItems(param *model.AddItemParam) (*BagInfo, bag.OpResultCode, bool) {
p := PlayerMgrSington.GetPlayerBySnId(param.SnId)
if p == nil {
return nil, bag.OpResultCode_OPRC_NotPlayer, false
}
// 非道具
var realItems []*model.Item
for _, v := range param.Change {
if v == nil || v.ItemNum == 0 {
continue
}
item := srvdata.GameItemMgr.Get(p.Platform, v.ItemId)
if item == nil {
continue
}
switch item.Type {
case common.ItemTypeCoin:
//增加金币
if item.Id == common.ItemIDCoin {
p.AddCoin(v.ItemNum, param.Add, param.GainWay, param.Operator, param.Remark)
}
case common.ItemTypeDiamond:
//增加钻石
if item.Id == common.ItemIDDiamond {
p.AddDiamond(v.ItemNum, param.Add, param.GainWay, param.Operator, param.Remark)
}
case common.ItemTypeFishPower:
//增加炮台
//p.ItemUnPlayerPowerListEx(v.ItemId)
case common.ItemTypeMoneyPond:
//增加个人金币池
//if v.ItemId == common.ItemIDMoneyPond {
// p.MoneyPond += v.ItemNum
//}
case common.ItemTypeVipExp:
//增加玩家VIP经验
if v.ItemId == common.ItemIDVipExp {
p.AddMoneyPayTotal(v.ItemNum)
}
case common.ItemTypeShopScore:
if v.ItemId == common.ItemIDPhoneScore {
p.AddPhoneScore(v.ItemNum, 0, param.GainWay, param.Operator, param.Remark)
}
case common.ItemTypeExpireTime:
p.AddItemRecExpireTime(v.ItemId, v.ItemNum, 0, param.GainWay, param.Operator, param.Remark)
default:
// 道具变化
realItems = append(realItems, v)
}
}
param.Change = realItems
if len(realItems) == 0 {
return nil, bag.OpResultCode_OPRC_Sucess, true
}
// 非道具
items, code, ok := this.AddItemCheck(param)
if !ok {
return nil, code, ok
}
if len(items) == 0 {
return nil, bag.OpResultCode_OPRC_Sucess, true
}
newBagInfo, ok := this.PlayerBag[param.SnId]
if !ok {
newBagInfo = NewBagInfo(p.Platform, p.SnId)
this.PlayerBag[param.SnId] = newBagInfo
}
// 更新背包
var ts int64 // 最新日志纳秒时间戳
var itemInfos []int32
for _, v := range items {
itemData := srvdata.GameItemMgr.Get(p.Platform, v.ItemId)
if itemData == nil {
continue
}
if itm, exist := newBagInfo.BagItem[v.ItemId]; exist {
itm.ItemNum += v.ItemNum
} else {
newBagInfo.BagItem[v.ItemId] = &Item{
ItemId: v.ItemId, // 物品id
ItemNum: v.ItemNum, // 数量
ObtainTime: time.Now().Unix(),
}
}
num := v.ItemNum
logType := ItemObtain
if v.ItemNum < 0 {
logType = ItemConsume
num = -v.ItemNum
}
// 日志
log := model.NewItemLogEx(model.ItemParam{
Platform: p.Platform,
SnId: p.SnId,
LogType: int32(logType),
ItemId: v.ItemId,
ItemName: itemData.Name,
Count: num,
Remark: param.Remark,
TypeId: param.GainWay,
GameId: param.GameId,
GameFreeId: param.GameFreeId,
Cost: param.Cost,
LogId: param.LogId,
RoomConfigId: param.RoomConfigId,
})
if log != nil {
LogChannelSingleton.WriteLog(log)
ts = log.Ts
newBagInfo.LogId = log.LogId.Hex()
}
// 监听道具变化
this.OnChangeFuncs(&model.ChangeItemParam{
SnId: p.SnId,
ItemId: v.ItemId,
ItemNum: num,
GainWay: param.GainWay,
RoomConfigId: param.RoomConfigId,
GameId: param.GameId,
GameFreeId: param.GameFreeId,
Cost: param.Cost,
})
itemInfo := this.GetItem(p.SnId, v.ItemId)
if itemInfo != nil {
itemInfos = append(itemInfos, v.ItemId)
}
}
newBagInfo.dirty = true
if ts > newBagInfo.Ts {
newBagInfo.Ts = ts
}
this.PlayerBag[p.SnId] = newBagInfo
p.SendDiffData()
this.SyncBagData(p.SnId, itemInfos...)
return newBagInfo, bag.OpResultCode_OPRC_Sucess, true
}
// AddItemsOffline 修改道具,玩家离线
func (this *BagMgr) AddItemsOffline(param *model.AddItemParam, callback func(err error)) {
// 玩家在线时
p := PlayerMgrSington.GetPlayerBySnId(param.SnId)
if p != nil {
this.AddItems(param)
callback(nil)
return
}
// 玩家离线时
var findPlayer *model.PlayerBaseInfo
task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
findPlayer = model.GetPlayerBaseInfo(param.Platform, param.SnId)
if findPlayer == nil {
return nil
}
newBagInfo := &model.BagInfo{
SnId: findPlayer.SnId,
Platform: findPlayer.Platform,
BagItem: make(map[int32]*model.Item),
GainWay: param.GainWay,
}
for _, v := range param.Change {
if v == nil || v.ItemNum == 0 {
continue
}
itemData := srvdata.GameItemMgr.Get(param.Platform, v.ItemId)
if itemData == nil {
continue
}
newBagInfo.BagItem[v.ItemId] = &model.Item{ItemId: v.ItemId, ItemNum: v.ItemNum}
}
if err := model.SaveDBBagItem(newBagInfo); err != nil {
logger.Logger.Errorf("离线保存道具变更错误 %v", err)
return err
}
// 保存日志
if err := model.InsertItemLog(srvdata.GameItemMgr.GetItems(param.Platform), param, true); err != nil {
logger.Logger.Errorf("离线保存道具变更日志错误 %v", err)
return err
}
return nil
}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) {
logger.Logger.Tracef("AddItemsOffline error(%v) Player:%+v", data, *findPlayer)
if data != nil || findPlayer == nil {
logger.Logger.Errorf("AddItemsOffline Error %v", data)
callback(errors.New("AddItemsOffline failed"))
return
}
callback(nil)
})).StartByExecutor(fmt.Sprintf("Player%v", param.SnId))
}
// AddMailByItem 赠送道具到邮件
// srcId 发送人 srcName发送人名字
// showId 显示位置
// items[0]:道具id items[1]:道具数量 items[2]:道具id items[3]:道具数量
func (this *BagMgr) AddMailByItem(platform string, srcId int32, srcName string, snid int32, showId int64, items []int32) {
logger.Logger.Trace("AddMailByItem:", srcId, srcName, items)
var newMsg *model.Message
task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
content := i18n.Tr("languages", "GiftMail", srcName, srcName, srcName, srcName)
title := i18n.Tr("languages", "GiftMailTitle")
newMsg = model.NewMessageByPlayer("", 1, srcId, srcName, snid, model.MSGTYPE_ITEM, title, content,
0, 0, model.MSGSTATE_UNREAD, time.Now().Unix(), 0, "", items, platform, showId)
return model.InsertMessage(platform, newMsg)
}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) {
if data == nil {
p := PlayerMgrSington.GetPlayerBySnId(snid)
if p != nil {
p.AddMessage(newMsg)
}
}
}), "AddMailByItem").Start()
}
// VerifyUpJybInfo 兑换礼包
func (this *BagMgr) VerifyUpJybInfo(p *Player, args *model.VerifyUpJybInfoArgs) {
type VerifyInfo struct {
jyb *model.JybInfo
err error
}
pack := &playerproto.SCPlayerSetting{
OpRetCode: playerproto.OpResultCode_OPRC_Sucess,
}
task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
jyb, err := model.VerifyUpJybInfo(args)
info := &VerifyInfo{
jyb: jyb,
err: err,
}
return info
}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) {
if data != nil && data.(*VerifyInfo) != nil {
jyb := data.(*VerifyInfo).jyb
err := data.(*VerifyInfo).err
if jyb != nil && jyb.Award != nil { // 领取到礼包
pack.GainItem = &playerproto.JybInfoAward{}
if jyb.Award.Item != nil {
if len(jyb.Award.Item) > 0 {
items := make([]*model.Item, 0)
for _, v := range jyb.Award.Item {
items = append(items, &model.Item{
ItemId: v.ItemId, // 物品id
ItemNum: v.ItemNum, // 数量
ObtainTime: time.Now().Unix(),
})
}
BagMgrSingleton.AddItems(&model.AddItemParam{
Platform: p.Platform,
SnId: p.SnId,
Change: items,
GainWay: common.GainWay_ActJybAward,
Operator: "system",
Remark: "礼包码兑换",
})
p.dirty = true
}
}
jybLog := &model.JybLog{
Platform: p.Platform,
Id: jyb.JybId.Hex(),
Ts: time.Now().Unix(),
SnId: p.SnId,
Tp: jyb.CodeType,
Name: jyb.Name,
}
for _, v := range jyb.Award.Item {
//if _, code := BagMgrSingleton.UpBagInfo(p.SnId, p.Platform, v.ItemId, v.ItemNum); code == bag.OpResultCode_OPRC_Sucess { // 需修改
pack.GainItem.ItemId = append(pack.GainItem.ItemId, &playerproto.ItemInfo{
ItemId: v.ItemId,
ItemNum: v.ItemNum,
})
jybLog.Award = append(jybLog.Award, &model.JybItem{
Id: v.ItemId,
Num: int32(v.ItemNum),
})
}
if jyb.Award.Coin > 0 {
p.AddCoin(jyb.Award.Coin, 0, common.GainWay_ActJybAward, "system", "礼包码兑换")
if !p.IsRob {
LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, p.Channel, model.SystemFreeGive_GiveType_ActJybAward, model.SystemFreeGive_CoinType_Coin, int64(jyb.Award.Coin)))
}
jybLog.Award = append(jybLog.Award, &model.JybItem{
Id: common.ItemIDCoin,
Num: int32(jyb.Award.Coin),
})
}
if jyb.Award.Diamond > 0 {
p.AddDiamond(jyb.Award.Diamond, 0, common.GainWay_ActJybAward, "system", "礼包码兑换")
if !p.IsRob {
LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, p.Channel, model.SystemFreeGive_GiveType_ActJybAward, model.SystemFreeGive_CoinType_Diamond, int64(jyb.Award.Diamond)))
}
jybLog.Award = append(jybLog.Award, &model.JybItem{
Id: common.ItemIDDiamond,
Num: int32(jyb.Award.Coin),
})
}
// 领取日志
LogChannelSingleton.WriteLog(jybLog)
p.dirty = true
pack.GainItem.Coin = jyb.Award.Coin
pack.GainItem.Diamond = jyb.Award.Diamond
} else {
switch err.Error() {
case model.ErrJybISReceive.Error():
pack.OpRetCode = playerproto.OpResultCode_OPRC_Jyb_Receive
case model.ErrJYBPlCode.Error():
pack.OpRetCode = playerproto.OpResultCode_OPRC_Jyb_CodeExist
case model.ErrJybTsTimeErr.Error():
pack.OpRetCode = playerproto.OpResultCode_OPRC_Jyb_TimeErr
default:
pack.OpRetCode = playerproto.OpResultCode_OPRC_Jyb_CodeErr
}
}
} else {
proto.SetDefaults(pack)
p.SendToClient(int(playerproto.PlayerPacketID_PACKET_ALL_SETTING), pack)
}
proto.SetDefaults(pack)
p.SendToClient(int(playerproto.PlayerPacketID_PACKET_ALL_SETTING), pack)
}), "VerifyUpJybInfo").Start()
}
// 兑换话费卡
func (this *BagMgr) ItemExchangeCard(p *Player, itemId int32, money, cardType int32, logId string) bool {
// 兑换码奖品
var err error
var newMsg *model.Message
res := &webapiproto.SAGetMatchAwardCode{}
pack := &bag.SCItemExChangeRes{RetCode: bag.OpResultCode_OPRC_Sucess}
task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
// 获取兑换码
pack := &webapiproto.ASGetMatchAwardCode{
Platform: p.Platform,
Snid: p.SnId,
ItemID: itemId,
Money: int64(money),
Tel: p.Tel,
CardType: cardType,
Remark: "背包内使用兑换",
}
logger.Logger.Trace("ItemChangeCode ", pack)
var buff []byte
buff, err = webapi.APIGetMatchAwardCode(common.GetAppId(), pack)
if err != nil {
return err
}
if err = proto.Unmarshal(buff, res); err != nil {
return err
}
if res.GetCode() == "" || res.GetTag() != webapiproto.TagCode_SUCCESS {
return nil
}
var params []string
for range []string{"zh", "vi", "en", "kh"} {
params = append(params, res.GetMoney(), res.GetCode()) // 金额,兑换码
}
// 发送邮件
title := i18n.Tr("languages", "MatchAwardTitle")
content := i18n.Tr("languages", "MatchAward", params)
newMsg = model.NewMessage("", 0, "", p.SnId, model.MSGTYPE_ITEM_CHANGE,
title, content, 0, 0, model.MSGSTATE_UNREAD, time.Now().Unix(), 0, "", nil, p.Platform, model.HallTienlen, nil)
err := model.InsertMessage(p.Platform, newMsg)
if err != nil {
logger.Logger.Errorf("发送邮件失败 snid:%v itemID:%v err:%v", p.SnId, itemId, err)
return err
}
return nil
}), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) {
if err != nil || res.GetCode() == "" || res.GetTag() != webapiproto.TagCode_SUCCESS {
//返回道具
items := make([]*model.Item, 0)
items = append(items, &model.Item{
ItemId: itemId, // 物品id
ItemNum: 1, // 数量
ObtainTime: time.Now().Unix(),
})
this.AddItems(&model.AddItemParam{
Platform: p.Platform,
SnId: p.SnId,
Change: items,
Add: 0,
GainWay: common.GainWayItemChange,
Operator: "system",
Remark: "背包内使用兑换失败",
GameId: 0,
GameFreeId: 0,
LogId: logId,
})
logger.Logger.Errorf("获取兑换码失败 snid:%v itemID:%v res:%v err:%v", p.SnId, itemId, res, err)
pack.RetCode = bag.OpResultCode_OPRC_Error
p.SendToClient(int(bag.SPacketID_PACKET_SC_ITEM_EXCHANGE_RES), pack)
// 标记兑换失败
task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
model.UpdateItemState(&model.UpdateParam{
Platform: p.Platform,
LogId: bson.ObjectIdHex(logId),
State: 1,
})
return nil
}), nil).Start()
return
}
p := PlayerMgrSington.GetPlayerBySnId(p.SnId)
if p != nil {
p.AddMessage(newMsg)
//已兑换log
itemData := srvdata.GameItemMgr.Get(p.Platform, itemId)
AwardLogMgr.UpdateAwardLog(p.Platform, itemData.Id, int64(1))
}
p.SendToClient(int(bag.SPacketID_PACKET_SC_ITEM_EXCHANGE_RES), pack)
}), fmt.Sprintf("ItemExChange%d", p.SnId)).Start()
return true
}
// ========================implement IPlayerLoad ==============================
type LoadData struct {
BagInfo *model.BagInfo
}
func (this *BagMgr) Load(platform string, snid int32, player any) *internal.PlayerLoadReplay {
// 加载道具
bagInfo, err := model.GetBagInfo(snid, platform)
if err != nil {
return &internal.PlayerLoadReplay{
Platform: platform,
Snid: snid,
Err: err,
Data: nil,
}
}
// 对账时间戳初始化
if bagInfo.Ts == 0 {
bagInfo.Ts = time.Now().UnixNano()
}
return &internal.PlayerLoadReplay{
Platform: platform,
Snid: snid,
Err: err,
Data: &LoadData{
BagInfo: bagInfo,
},
}
}
func (this *BagMgr) Callback(player any, ret *internal.PlayerLoadReplay) {
if ret == nil || ret.Data == nil {
return
}
data, ok := ret.Data.(*LoadData)
if !ok || data == nil || data.BagInfo == nil {
return
}
// 背包数据
newBagInfo := NewBagInfo(ret.Platform, ret.Snid)
newBagInfo.Ts = data.BagInfo.Ts
for k, bi := range data.BagInfo.BagItem {
item := srvdata.GameItemMgr.Get(ret.Platform, bi.ItemId)
if item != nil {
if bi.ItemNum > 0 {
newBagInfo.BagItem[k] = &Item{
ItemId: bi.ItemId,
ItemNum: bi.ItemNum,
ObtainTime: bi.ObtainTime,
}
}
} else {
logger.Logger.Error("InitBagInfo err: item is nil. ItemId:", bi.ItemId)
}
}
this.PlayerBag[ret.Snid] = newBagInfo
}
type LoadAfterData struct {
GameID []int32
ItemLogs []*model.ItemLog
StarTs, EndTs int64
}
func (this *BagMgr) LoadAfter(platform string, snid int32) *internal.PlayerLoadReplay {
var err error
// 查询最近游戏
gameID := model.GetRecentGame(platform, snid)
// 道具变更记录
endTs := time.Now().UnixNano()
var itemLogs []*model.ItemLog
itemLogs, err = model.GetItemLog(platform, snid, this.PlayerBag[snid].Ts)
if err != nil {
logger.Logger.Errorf("LoadAfter GetItemLog err: %v", err)
return &internal.PlayerLoadReplay{
Platform: platform,
Snid: snid,
Err: err,
Data: nil,
}
}
return &internal.PlayerLoadReplay{
Platform: platform,
Snid: snid,
Err: nil,
Data: &LoadAfterData{
GameID: gameID,
ItemLogs: itemLogs,
StarTs: this.PlayerBag[snid].Ts,
EndTs: endTs,
},
}
}
func (this *BagMgr) CallbackAfter(ret *internal.PlayerLoadReplay) {
if ret == nil {
return
}
if ret.Err != nil {
logger.Logger.Error("BagMgr LoadAfter err:", ret.Err)
return
}
param, ok := ret.Data.(*LoadAfterData)
if !ok {
logger.Logger.Errorf("BagMgr LoadAfter BUGE 1")
return
}
p := PlayerMgrSington.GetPlayerBySnId(ret.Snid)
if p == nil {
logger.Logger.Errorf("BagMgr LoadAfter BUGE 2")
return
}
// 最近游戏
p.GameID = param.GameID
// 道具变更记录
bagInfo := this.PlayerBag[p.SnId]
if bagInfo != nil {
changeItems := make(map[int32]struct{})
for _, v := range param.ItemLogs {
if v == nil {
continue
}
if v.Ts > param.StarTs && v.Ts <= param.EndTs {
bagInfo.dirty = true
num := v.Count
if v.LogType == 1 {
num = -num
}
if v.Ts > bagInfo.Ts {
bagInfo.Ts = v.Ts
}
if v.Offline == 0 {
// 在线数据恢复
logger.Logger.Tracef("道具恢复 SnId:%v Item:%+v", p.SnId, *v)
if _, ok := bagInfo.BagItem[v.ItemId]; !ok {
bagInfo.BagItem[v.ItemId] = &Item{
ItemId: v.ItemId,
ItemNum: 0,
ObtainTime: v.CreateTs,
}
}
bagInfo.BagItem[v.ItemId].ItemNum += num
changeItems[v.ItemId] = struct{}{}
} else {
// 离线时的变更
logger.Logger.Tracef("处理离线道具变化 SnId:%v Item:%v", p.SnId, *v)
this.OnChangeFuncs(&model.ChangeItemParam{
SnId: p.SnId,
ItemId: v.ItemId,
ItemNum: num,
GainWay: v.TypeId,
RoomConfigId: v.RoomConfigId,
GameId: v.GameId,
GameFreeId: v.GameFreeId,
Cost: v.Cost,
})
}
}
}
this.SyncBagData(p.SnId, maps.Keys(changeItems)...)
}
}
func (this *BagMgr) Save(platform string, snid int32, isSync, force bool) {
bagInfo := this.GetBagInfo(snid)
if bagInfo == nil {
return
}
if !bagInfo.dirty && !force {
return
}
logger.Logger.Tracef("SaveBagData: %+v", *bagInfo)
var err error
f := func() {
newBagInfo := &model.BagInfo{
SnId: bagInfo.SnId,
Platform: bagInfo.Platform,
Ts: bagInfo.Ts,
BagItem: make(map[int32]*model.Item),
}
for _, v := range bagInfo.BagItem {
newBagInfo.BagItem[v.ItemId] = &model.Item{ItemId: v.ItemId, ItemNum: v.ItemNum, ObtainTime: v.ObtainTime}
}
err = model.UpBagItem(newBagInfo)
}
cf := func() {
if err == nil && this.PlayerBag[snid] != nil {
this.PlayerBag[snid].dirty = false
}
}
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()
})).StartByExecutor(fmt.Sprintf("Player%v", snid))
}
func (this *BagMgr) Release(platform string, snid int32) {
delete(this.PlayerBag, snid)
}