package storage import ( actorRemote "f1-game/internal/actor_remote" capp "f1-game/internal/cherry_app" "f1-game/internal/code" "f1-game/internal/data" "f1-game/internal/enum" "f1-game/internal/event" facade "f1-game/internal/facade" nameRoute "f1-game/internal/name/route" "f1-game/internal/pb" "f1-game/internal/sessions" "f1-game/internal/types" assetOrigin "f1-game/nodes/game/player/asset/origin" mapCall "f1-game/nodes/map/internal/call" "f1-game/nodes/map/internal/db" clog "github.com/cherry-game/cherry/logger" cproto "github.com/cherry-game/cherry/net/proto" ) var ( srv = newService() ) func newService() *service { instance := &service{} instance.load() return instance } func Service() *service { return srv } type service struct { facade.PlayerServiceBase[*db.MapPlayerTable] } func (p *service) load() { } func (p *service) OnLogin(mapPlayerTable *db.MapPlayerTable, session *cproto.Session) { if session.Contains(sessions.IsNewPlayer) { clog.Infof("map storage init: playerID = %d", mapPlayerTable.PlayerID) // 初始化资源 p.AssetAdds(mapPlayerTable.PlayerID, data.Const.PlayerInitCurrencies, assetOrigin.CreatePlayer, false) } } // func (p *service) OnLogined(mapPlayerTable *db.MapPlayerTable, session *cproto.Session) { // } // func (p *service) OnLogout(mapPlayerTable *db.MapPlayerTable) { // } // func (p *service) OnReset(mapPlayerTable *db.MapPlayerTable, time *ctime.CherryTime, byLogin bool) { // } func (p *service) Push(mapPlayerTable *db.MapPlayerTable, session *cproto.Session) { mapPlayerStorageTable := db.GetMapPlayerStorageTable(mapPlayerTable.PlayerID) proto := mapPlayerStorageTable.Storage.ToProto() sessions.PushWithSession(session, nameRoute.PushMapPlayer_StorageList, proto) } // ChangePush 仓库变更推送 func (p *service) ChangePush(playerID int64, idList ...int32) { if len(idList) < 1 { return } var ( mapPlayerStorageTable = db.GetMapPlayerStorageTable(playerID) rsp = &pb.ItemList{ Currencies: map[int32]*pb.Currency{}, } ) for _, id := range idList { // 只推送地图里面的资源 if enum.IsMapCurrency(id) { entity := mapPlayerStorageTable.Storage.CurrencyMap.GetEntity(id) proto := entity.ToProto() rsp.Currencies[entity.ID] = &proto } } if len(rsp.Currencies) < 1 { return } sessions.PushPlayer(playerID, nameRoute.PushMapPlayer_StoragChange, rsp) } func (p *service) Contain(playerID int64, id int32) int32 { mapPlayerStorageTable := db.GetMapPlayerStorageTable(playerID) if _, found := data.Item.GetByID(id); !found { return code.ConfigNotFound_Item } if mapPlayerStorageTable.Storage.CurrencyMap.Get(id) < 1 { return code.ItemNotEnough } return code.OK } func (p *service) isEnough(playerID int64, id int32, num int64) bool { mapPlayerStorageTable := db.GetMapPlayerStorageTable(playerID) availableNum := mapPlayerStorageTable.Storage.CurrencyMap.Get(id) if availableNum < 1 || availableNum < num { return false } return true } func (p *service) add(playerID int64, id int32, num int64, origin assetOrigin.Origin, save, push bool) (types.Asset, int32) { mapPlayerStorageTable := db.GetMapPlayerStorageTable(playerID) row, found := data.Item.GetByID(id) if !found { return types.NoneAsset, code.ConfigNotFound_Item } if row.ParseType != enum.AssetType_Currency { return types.NoneAsset, code.CurrencyTypeError } if !enum.IsMapCurrency(id) { return types.NoneAsset, code.CurrencyTypeError } if num < 1 { return types.NoneAsset, code.ItemNumError } _, ok := mapPlayerStorageTable.Storage.CurrencyMap.Increase(id, num) if !ok { return types.NoneAsset, code.ItemNotEnough } if save { mapPlayerStorageTable.Save2Queue() } if push { p.ChangePush(playerID, id) } returnAsset := types.Asset{ ID: id, Num: num, } return returnAsset, code.OK } func (p *service) sub(playerID int64, id int32, num int64, origin assetOrigin.Origin, save, push bool) int32 { mapPlayerStorageTable := db.GetMapPlayerStorageTable(playerID) if num < 1 { return code.ItemNumError } _, found := data.Item.GetByID(id) if !found { return code.ConfigNotFound_Item } // 优先扣除绑定资源,再扣除非绑定资源 changeAssets, ok := mapPlayerStorageTable.Storage.CurrencyMap.Decrease(id, num) if !ok { return code.ItemNotEnough } if save { mapPlayerStorageTable.Save2Queue() } var changeIDList []int32 for _, asset := range changeAssets { changeIDList = append(changeIDList, asset.ID) } if push { p.ChangePush(playerID, changeIDList...) } return code.OK } // CheckAutoAdd 检查每几秒资源发放情况 func (p *service) CheckAutoAdd(playerTable *db.MapPlayerTable, nowSecond int64, isOnline bool) { var ( playerStorageTable = db.GetMapPlayerStorageTable(playerTable.PlayerID) itemAddSecond = data.Const.ItemAddSecond changePushIDList []int32 save bool ) for _, itemID := range enum.ItemMaxNumList { // 产出的量 yield := p.GetMapCurrencyYield(playerTable, itemID) if yield < 1 { continue } // 只有能产出的时候才初始化 UpdateTime if playerStorageTable.Storage.CurrencyMap.CheckInitUpdateTime(itemID, nowSecond) { save = true } if !playerStorageTable.Storage.CurrencyMap.CheckAutoAdd(itemID, nowSecond, yield, itemAddSecond) { continue } save = true changePushIDList = append(changePushIDList, itemID) if playerStorageTable.Storage.CurrencyMap.IsFull(itemID) { actorRemote.GamePlayer.GamePlayerCallSystemMsg(playerTable.GameNodeID, playerTable.PlayerID, enum.ChatMsgType_resourceMax, nil, nil, itemID) } } if save { playerStorageTable.Save2Queue() } if len(changePushIDList) > 0 && isOnline { p.ChangePush(playerTable.PlayerID, changePushIDList...) } } // 获取资源总产量 func (p *service) GetMapCurrencyYield(mapPlayerTable *db.MapPlayerTable, itemIDs ...int32) int64 { if len(itemIDs) <= 0 { return 0 } var ( mapResYields = mapPlayerTable.GetAllYield() totalYield = int64(0) ) for _, itemID := range itemIDs { // 绑定资源 if attrID, ok := enum.MapItemAttrs[itemID]; ok { totalYield += p.calcAssetOutputFinalAttr(mapPlayerTable, attrID) } // 非绑定资源 if _, ok := enum.UnBindingResourceMap[itemID]; ok { totalYield += mapResYields.GetValue(itemID) } } return totalYield } func (p *service) IsEnough(playerID int64, id int32, num int64) bool { if !enum.IsMapCurrency(id) { return true } if !p.isEnough(playerID, id, num) { return false } return true } func (p *service) IsAllEnough(playerID int64, assets types.Assets) bool { for _, asset := range assets { if !enum.IsMapCurrency(asset.ID) { continue } if !p.isEnough(playerID, asset.ID, asset.Num) { return false } } return true } // AssetAdds 增加多个资源,地图和游戏节点分别增加 func (p *service) AssetAdds(playerID int64, assets types.Assets, ao assetOrigin.Origin, isPush ...bool) (types.Assets, int32) { var ( mapPlayerTable = db.GetMapPlayerTable(playerID) mapAssets = types.Assets{} mapLeagueAssets = types.Assets{} gameAssets = types.Assets{} retAssets = types.Assets{} ) // 检查玩家表是否存在 if mapPlayerTable == nil { return retAssets, code.PlayerNotFound } if len(assets) == 0 { return retAssets, code.IllegalArgument } // TODO 默认推送 push := true if len(isPush) > 0 { push = isPush[0] } // 资源拆解分组,地图的资源和游戏的资源分开处理 for _, asset := range assets { if enum.IsLeagueCurrency(asset.ID) { mapLeagueAssets.AddAsset(asset) } else if enum.IsMapCurrency(asset.ID) { mapAssets.AddAsset(asset) } else { gameAssets.AddAsset(asset) } } if mapAssets.Len() > 0 { for _, asset := range mapAssets { retAsset, errCode := p.add(playerID, asset.ID, asset.Num, ao, true, push) if code.IsFail(errCode) { return retAssets, errCode } retAssets.AddAsset(&retAsset) } // 发送获得奖励的系统消息 actorRemote.GamePlayer.GamePlayerCallSystemMsg(mapPlayerTable.GameNodeID, playerID, enum.ChatMsgType_reward, mapAssets, nil) capp.PostEvent(event.NewItemAdd(playerID, mapAssets, ao)) } if mapLeagueAssets.Len() > 0 { mapCall.League.MapLeagueAddAssets(playerID, mapPlayerTable.LeagueID, mapLeagueAssets) } if gameAssets.Len() > 0 { retAssetsProto, errCode := actorRemote.GamePlayer.AssetAdd(mapPlayerTable.GameNodeID, playerID, gameAssets, ao) if code.IsFail(errCode) { return retAssets, errCode } for _, asset := range retAssetsProto.List { retAsset := types.Asset{ ID: asset.Id, Num: asset.Num, GUID: asset.Guid, } retAssets.AddAsset(&retAsset) } } return retAssets, code.OK } // AssetAddOne 单个资源增加,地图和游戏节点分别增加 func (p *service) AssetAddOne(playerID int64, id int32, num int64, origin assetOrigin.Origin, isPush ...bool) (types.Assets, int32) { assets := types.Assets{ { ID: id, Num: num, }, } return p.AssetAdds(playerID, assets, origin, isPush...) } // AssetSubs 扣除多个资源,地图和游戏节点分别扣除 func (p *service) AssetSubs(playerID int64, assets types.Assets, origin assetOrigin.Origin, isPush ...bool) (types.Assets, int32) { var ( mapAssets = types.Assets{} gameAssets = types.Assets{} retAssets = types.Assets{} ) if len(assets) == 0 { return nil, code.IllegalArgument } // TODO 默认推送 push := true if len(isPush) > 0 { push = isPush[0] } for _, asset := range assets { // 不支持联盟货币消耗 if enum.IsLeagueCurrency(asset.ID) { clog.Warnf("AssetSubs league currency not support, playerID: %d, asset: %+v", playerID, asset) return nil, code.LeagueCurrencyNotSupport } if enum.IsMapCurrency(asset.ID) { mapAssets.AddAsset(asset) } else { gameAssets.AddAsset(asset) } } // 先检查地图这边的资源是否足够 if !p.IsAllEnough(playerID, mapAssets) { return nil, code.CurrencyNotEnough } if len(gameAssets) > 0 { mapPlayerTable := db.GetMapPlayerTable(playerID) // 检查玩家表是否存在 if mapPlayerTable == nil { return retAssets, code.PlayerNotFound } // 发送到游戏节点扣除游戏资源 retAssetsProto, errCode := actorRemote.GamePlayer.AssetSub(mapPlayerTable.GameNodeID, playerID, gameAssets, origin) if code.IsFail(errCode) { return nil, errCode } for _, asset := range retAssetsProto.List { retAsset := types.Asset{ ID: asset.Id, Num: asset.Num, GUID: asset.Guid, } retAssets.AddAsset(&retAsset) } } for _, asset := range mapAssets { errCode := p.sub(playerID, asset.ID, asset.Num, origin, true, push) if code.IsFail(errCode) { return nil, errCode } retAssets.AddAsset(asset) } return retAssets, code.OK } // AssetSubOne 单个资源扣除,地图和游戏节点分别扣除 func (p *service) AssetSubOne(playerID int64, id int32, num int64, origin assetOrigin.Origin, isPush ...bool) (types.Assets, int32) { assets := types.Assets{ { ID: id, Num: num, }, } return p.AssetSubs(playerID, assets, origin, isPush...) } // Get 获取资源数量 func (p *service) Get(playerID int64, id int32) int64 { if !enum.IsMapCurrency(id) { return 0 } mapPlayerStorageTable := db.GetMapPlayerStorageTable(playerID) return mapPlayerStorageTable.Storage.CurrencyMap.Get(id) } // 计算各个模块给资源产出的加成 // 1,城建 // 2,VIP func (p *service) calcAssetOutputFinalAttr(mapPlayerTable *db.MapPlayerTable, attrID int32) int64 { facilityValue := mapPlayerTable.Facilities.Attrs.GetI64(attrID) vipValue := int64(0) if !mapPlayerTable.Vip.IsExpired { vipValue = mapPlayerTable.Vip.Attrs.GetI64(attrID) } attrValue := facilityValue + vipValue return attrValue }