game_sync/worldsrv/bagmgr.go

574 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"mongo.games.com/game/worldsrv/internal"
"strconv"
"time"
"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"
"mongo.games.com/game/srvdata"
)
const (
BagItemMax int32 = 200
)
// 道具功能 Function
const (
ItemCanUse = iota //可以使用
ItemCanGive //可以赠送
ItemCanSell //可以出售
ItemMax
)
type Item struct {
ItemId int32 // 物品ID
ItemNum int64 // 物品数量
////数据表数据
Name string // 名称
//ShowLocation []int32 // 显示位置
//Classify []int32 // 分页类型 1道具类 2资源类 3兑换类
//Type int32 // 道具种类 1宠物碎片 2角色碎片
Effect0 []int32 // 竖版道具功能 1使用 2赠送 3出售
Effect []int32 // 横版道具功能 1使用 2赠送 3出售
SaleType int32 // 出售类型
SaleGold int32 // 出售金额
//Composition int32 // 能否叠加 1能 2不能
//CompositionMax int32 // 叠加上限
//Time int32 // 道具时效 0为永久
//Location string // 跳转页面
//Describe string // 道具描述
//数据库数据
ObtainTime int64 //获取的时间
}
type BagInfo struct {
SnId int32 //玩家账号直接在这里生成
Platform string //平台
BagItem map[int32]*Item //背包数据 key为itemId
dirty bool
}
// BagMgrSingleton 背包管理器
var BagMgrSingleton = &BagMgr{
PlayerBag: make(map[int32]*BagInfo),
}
type BagMgr struct {
PlayerBag map[int32]*BagInfo // snid:背包
}
func (this *BagMgr) ModuleName() string {
return "BagMgr"
}
func (this *BagMgr) Init() {
}
func (this *BagMgr) GetBagInfo(snid int32) *BagInfo {
if v, exist := this.PlayerBag[snid]; exist {
return v
}
return nil
}
// 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.PBDB_GameItemMgr.GetData(itemId)
if itemX != nil {
item.Name = itemX.Name
//item.ShowLocation = itemX.ShowLocation
//item.Classify = itemX.Classify
//item.Type = itemX.Type
item.Effect0 = itemX.Effect
item.Effect = itemX.Effect
item.SaleType = itemX.SaleType
item.SaleGold = itemX.SaleGold
//item.Composition = itemX.Composition
//item.CompositionMax = itemX.CompositionMax
//item.Time = itemX.Time
//item.Location = itemX.Location
//item.Describe = itemX.Describe
}
}
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
}
// AddJybBagInfo 给玩家背包添加道具
// add 加成数量
// gainWay 记录类型
// oper 操作人
// remark 备注
func (this *BagMgr) AddJybBagInfo(p *Player, addItems []*Item, add int64, gainWay int32, oper, remark string) (*BagInfo, bag.OpResultCode) {
var items []*Item
for _, v := range addItems {
if v == nil || v.ItemNum == 0 {
continue
}
item := srvdata.PBDB_GameItemMgr.GetData(v.ItemId)
switch item.Type {
case common.ItemTypeCoin:
//增加金币
if item.Id == common.ItemIDCoin {
p.AddCoin(v.ItemNum, 0, gainWay, oper, remark)
}
case common.ItemTypeDiamond:
//增加钻石
if item.Id == common.ItemIDDiamond {
p.AddDiamond(v.ItemNum, 0, gainWay, oper, 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, gainWay, oper, remark)
}
default:
// 道具变化
items = append(items, v)
}
}
// 添加道具到背包
var changeItems []int32
var newBagInfo *BagInfo
if _, exist := this.PlayerBag[p.SnId]; !exist {
newBagInfo = &BagInfo{
SnId: p.SnId,
Platform: p.Platform,
BagItem: make(map[int32]*Item),
}
} else {
newBagInfo = this.PlayerBag[p.SnId]
}
if len(items) == 0 {
return newBagInfo, bag.OpResultCode_OPRC_Sucess
}
var code = bag.OpResultCode_OPRC_Sucess
for _, v := range items {
if v == nil || v.ItemNum == 0 {
continue
}
item := srvdata.PBDB_GameItemMgr.GetData(v.ItemId)
if item == nil {
code = bag.OpResultCode_OPRC_IdErr
continue
}
changeItems = append(changeItems, v.ItemId)
if itm, exist := newBagInfo.BagItem[v.ItemId]; exist {
itm.ItemNum += v.ItemNum
} else {
newBagInfo.BagItem[v.ItemId] = &Item{
ItemId: item.Id, // 物品id
ItemNum: v.ItemNum, // 数量
ObtainTime: time.Now().Unix(),
}
}
if v.ItemId == common.ItemIDWeekScore && v.ItemNum != 0 {
TaskSubjectSingleton.Touch(common.TaskTypeActivityScore, &TaskData{
SnId: p.SnId,
Num: v.ItemNum,
})
}
}
if len(changeItems) > 0 {
newBagInfo.dirty = true
p.dirty = true
this.PlayerBag[p.SnId] = newBagInfo
this.SyncBagData(p.SnId, changeItems...)
}
return newBagInfo, code
}
// SaleItem 出售道具,减少玩家道具数量
func (this *BagMgr) SaleItem(p *Player, itemId int32, num int64) bool {
item := this.GetItem(p.SnId, itemId)
if item != nil && item.ItemNum >= num {
return this.SalePlayerItem(p, item, num)
}
return false
}
// SalePlayerItem 出售道具,减少玩家道具数量
func (this *BagMgr) SalePlayerItem(p *Player, item *Item, num int64) bool {
item.ItemNum -= num
p.dirty = true
this.SyncBagData(p.SnId, item.ItemId)
return true
}
func (this *BagMgr) SaleItemV2(p *Player, itemId int32, num int64, gain int32, oper string, remark string) bool {
item := this.GetItem(p.SnId, itemId)
if item != nil && item.ItemNum >= num {
switch item.ItemId {
case common.ItemIDCoin:
p.AddCoin(-num, 0, gain, oper, remark)
return true
case common.ItemIDDiamond:
p.AddDiamond(-num, 0, gain, oper, remark)
return true
default:
return this.SalePlayerItem(p, item, num)
}
}
return false
}
// RecordItemLog 道具操作记录(获得,消耗)
func (this *BagMgr) RecordItemLog(platform string, snid, logType, itemId int32, itemName string, count int64, remark string) {
//logger.Logger.Trace("RecordItemLog:", platform, snid, logType, itemId, itemName, count, remark)
switch itemId {
case common.ItemIDCoin, common.ItemIDDiamond:
return
}
log := model.NewItemLogEx(platform, snid, logType, itemId, itemName, count, remark)
if log != nil {
LogChannelSingleton.WriteLog(log)
}
}
// AddMailByItem 赠送道具到邮件
// srcId 发送人 srcName发送人名字
// 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([]*Item, 0)
for _, v := range jyb.Award.Item {
items = append(items, &Item{
ItemId: v.ItemId, // 物品id
ItemNum: v.ItemNum, // 数量
ObtainTime: time.Now().Unix(),
})
}
if _, code := this.AddJybBagInfo(p, items, 0, common.GainWay_ActJybAward, "system", "礼包码兑换"); code != bag.OpResultCode_OPRC_Sucess { //TODO 添加失败 要回退礼包
logger.Logger.Errorf("CSPlayerSettingHandler AddJybBagInfo err", code)
pack.OpRetCode = playerproto.OpResultCode_OPRC_Error
proto.SetDefaults(pack)
p.SendToClient(int(playerproto.PlayerPacketID_PACKET_ALL_SETTING), pack)
} else {
for _, v := range items {
itemData := srvdata.PBDB_GameItemMgr.GetData(v.ItemId)
if itemData != nil {
BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, v.ItemId, itemData.Name, int64(v.ItemNum), "礼包领取")
}
}
PetMgrSington.CheckShowRed(p)
}
p.dirty = true
}
}
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,
})
}
if jyb.Award.Coin > 0 {
p.AddCoin(jyb.Award.Coin, 0, common.GainWay_ActJybAward, "system", "礼包码兑换")
LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_ActJybAward, model.SystemFreeGive_CoinType_Coin, int64(jyb.Award.Coin)))
}
if jyb.Award.Diamond > 0 {
p.AddDiamond(jyb.Award.Diamond, 0, common.GainWay_ActJybAward, "system", "礼包码兑换")
LogChannelSingleton.WriteMQData(model.GenerateSystemFreeGive(p.SnId, p.Name, p.Platform, model.SystemFreeGive_GiveType_ActJybAward, model.SystemFreeGive_CoinType_Diamond, int64(jyb.Award.Diamond)))
}
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()
// 先检查玩家背包是否足够
}
// SyncBagData 通知玩家背包数据变化
func (this *BagMgr) SyncBagData(snid int32, changeItemIds ...int32) {
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,
//Name: itemInfo.Name,
//Classify: itemInfo.Classify,
//Type: itemInfo.Type,
//Effect0: itemInfo.Effect0,
//Effect: itemInfo.Effect,
//SaleType: itemInfo.SaleType,
//SaleGold: itemInfo.SaleGold,
//Composition: itemInfo.Composition,
//CompositionMax: itemInfo.CompositionMax,
//Time: itemInfo.Time,
//Location: itemInfo.Location,
//Describe: itemInfo.Describe,
ObtainTime: itemInfo.ObtainTime,
})
if itemInfo.ItemId == VCard {
FriendMgrSington.UpdateInfo(p.Platform, p.SnId)
}
}
}
p.SyncBagData(itemInfos)
}
func (this *BagMgr) Update() {
}
func (this *BagMgr) Shutdown() {
module.UnregisteModule(this)
}
//========================implement IPlayerLoad ==============================
func (this *BagMgr) Load(platform string, snid int32, player any) *internal.PlayerLoadReplay {
data := model.GetBagInfo(snid, platform)
return &internal.PlayerLoadReplay{
Platform: platform,
Snid: snid,
Err: nil,
Data: data,
}
}
func (this *BagMgr) Callback(player any, ret *internal.PlayerLoadReplay) {
if ret == nil || ret.Data == nil {
return
}
bagInfo, ok := ret.Data.(*model.BagInfo)
if !ok || bagInfo == nil {
return
}
//数据表 数据库数据相结合
newBagInfo := &BagInfo{
SnId: ret.Snid,
Platform: ret.Platform,
BagItem: make(map[int32]*Item),
}
for k, bi := range bagInfo.BagItem {
item := srvdata.PBDB_GameItemMgr.GetData(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
}
func (this *BagMgr) LoadAfter(platform string, snid int32) *internal.PlayerLoadReplay {
// 查询最近游戏
gameID := model.GetRecentGame(platform, snid)
return &internal.PlayerLoadReplay{
Platform: platform,
Snid: snid,
Err: nil,
Data: gameID,
}
}
func (this *BagMgr) CallbackAfter(ret *internal.PlayerLoadReplay) {
if ret == nil {
return
}
if ret.Err != nil {
logger.Logger.Error("BagMgr LoadAfter err:", ret.Err)
return
}
p := PlayerMgrSington.GetPlayerBySnId(ret.Snid)
if p != nil {
p.GameID = ret.Data.([]int32)
}
}
func (this *BagMgr) Save(platform string, snid int32, isSync, force bool) {
bagInfo := this.PlayerBag[snid]
logger.Logger.Trace("SaveBagData:", bagInfo)
if bagInfo == nil || (!bagInfo.dirty && !force) {
return
}
type BagInfoMap struct {
SnId int32 //玩家账号直接在这里生成
Platform string //平台
BagItem []*Item //背包数据 key为itemId
}
// biMap 数据拷贝
var biMap = BagInfoMap{
SnId: bagInfo.SnId,
Platform: bagInfo.Platform,
}
for _, v := range bagInfo.BagItem {
biMap.BagItem = append(biMap.BagItem, &Item{ItemId: v.ItemId, ItemNum: v.ItemNum, ObtainTime: v.ObtainTime})
}
var err error
f := func() {
newBagInfo := &model.BagInfo{
SnId: biMap.SnId,
Platform: biMap.Platform,
BagItem: make(map[int32]*model.Item),
}
for _, v := range biMap.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 {
bagInfo.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()
}), "SaveBagData").StartByFixExecutor("SnId:" + strconv.Itoa(int(snid)))
}
func (this *BagMgr) Release(platform string, snid int32) {
delete(this.PlayerBag, snid)
}
func init() {
module.RegisteModule(BagMgrSingleton, time.Second, 0)
internal.RegisterPlayerLoad(BagMgrSingleton)
}