service.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. package storage
  2. import (
  3. actorRemote "f1-game/internal/actor_remote"
  4. capp "f1-game/internal/cherry_app"
  5. "f1-game/internal/code"
  6. "f1-game/internal/data"
  7. "f1-game/internal/enum"
  8. "f1-game/internal/event"
  9. facade "f1-game/internal/facade"
  10. nameRoute "f1-game/internal/name/route"
  11. "f1-game/internal/pb"
  12. "f1-game/internal/sessions"
  13. "f1-game/internal/types"
  14. assetOrigin "f1-game/nodes/game/player/asset/origin"
  15. mapCall "f1-game/nodes/map/internal/call"
  16. "f1-game/nodes/map/internal/db"
  17. clog "github.com/cherry-game/cherry/logger"
  18. cproto "github.com/cherry-game/cherry/net/proto"
  19. )
  20. var (
  21. srv = newService()
  22. )
  23. func newService() *service {
  24. instance := &service{}
  25. instance.load()
  26. return instance
  27. }
  28. func Service() *service {
  29. return srv
  30. }
  31. type service struct {
  32. facade.PlayerServiceBase[*db.MapPlayerTable]
  33. }
  34. func (p *service) load() {
  35. }
  36. func (p *service) OnLogin(mapPlayerTable *db.MapPlayerTable, session *cproto.Session) {
  37. if session.Contains(sessions.IsNewPlayer) {
  38. clog.Infof("map storage init: playerID = %d", mapPlayerTable.PlayerID)
  39. // 初始化资源
  40. p.AssetAdds(mapPlayerTable.PlayerID, data.Const.PlayerInitCurrencies, assetOrigin.CreatePlayer, false)
  41. }
  42. }
  43. // func (p *service) OnLogined(mapPlayerTable *db.MapPlayerTable, session *cproto.Session) {
  44. // }
  45. // func (p *service) OnLogout(mapPlayerTable *db.MapPlayerTable) {
  46. // }
  47. // func (p *service) OnReset(mapPlayerTable *db.MapPlayerTable, time *ctime.CherryTime, byLogin bool) {
  48. // }
  49. func (p *service) Push(mapPlayerTable *db.MapPlayerTable, session *cproto.Session) {
  50. mapPlayerStorageTable := db.GetMapPlayerStorageTable(mapPlayerTable.PlayerID)
  51. proto := mapPlayerStorageTable.Storage.ToProto()
  52. sessions.PushWithSession(session, nameRoute.PushMapPlayer_StorageList, proto)
  53. }
  54. // ChangePush 仓库变更推送
  55. func (p *service) ChangePush(playerID int64, idList ...int32) {
  56. if len(idList) < 1 {
  57. return
  58. }
  59. var (
  60. mapPlayerStorageTable = db.GetMapPlayerStorageTable(playerID)
  61. rsp = &pb.ItemList{
  62. Currencies: map[int32]*pb.Currency{},
  63. }
  64. )
  65. for _, id := range idList {
  66. // 只推送地图里面的资源
  67. if enum.IsMapCurrency(id) {
  68. entity := mapPlayerStorageTable.Storage.CurrencyMap.GetEntity(id)
  69. proto := entity.ToProto()
  70. rsp.Currencies[entity.ID] = &proto
  71. }
  72. }
  73. if len(rsp.Currencies) < 1 {
  74. return
  75. }
  76. sessions.PushPlayer(playerID, nameRoute.PushMapPlayer_StoragChange, rsp)
  77. }
  78. func (p *service) Contain(playerID int64, id int32) int32 {
  79. mapPlayerStorageTable := db.GetMapPlayerStorageTable(playerID)
  80. if _, found := data.Item.GetByID(id); !found {
  81. return code.ConfigNotFound_Item
  82. }
  83. if mapPlayerStorageTable.Storage.CurrencyMap.Get(id) < 1 {
  84. return code.ItemNotEnough
  85. }
  86. return code.OK
  87. }
  88. func (p *service) isEnough(playerID int64, id int32, num int64) bool {
  89. mapPlayerStorageTable := db.GetMapPlayerStorageTable(playerID)
  90. availableNum := mapPlayerStorageTable.Storage.CurrencyMap.Get(id)
  91. if availableNum < 1 || availableNum < num {
  92. return false
  93. }
  94. return true
  95. }
  96. func (p *service) add(playerID int64, id int32, num int64, origin assetOrigin.Origin, save, push bool) (types.Asset, int32) {
  97. mapPlayerStorageTable := db.GetMapPlayerStorageTable(playerID)
  98. row, found := data.Item.GetByID(id)
  99. if !found {
  100. return types.NoneAsset, code.ConfigNotFound_Item
  101. }
  102. if row.ParseType != enum.AssetType_Currency {
  103. return types.NoneAsset, code.CurrencyTypeError
  104. }
  105. if !enum.IsMapCurrency(id) {
  106. return types.NoneAsset, code.CurrencyTypeError
  107. }
  108. if num < 1 {
  109. return types.NoneAsset, code.ItemNumError
  110. }
  111. _, ok := mapPlayerStorageTable.Storage.CurrencyMap.Increase(id, num)
  112. if !ok {
  113. return types.NoneAsset, code.ItemNotEnough
  114. }
  115. if save {
  116. mapPlayerStorageTable.Save2Queue()
  117. }
  118. if push {
  119. p.ChangePush(playerID, id)
  120. }
  121. returnAsset := types.Asset{
  122. ID: id,
  123. Num: num,
  124. }
  125. return returnAsset, code.OK
  126. }
  127. func (p *service) sub(playerID int64, id int32, num int64, origin assetOrigin.Origin, save, push bool) int32 {
  128. mapPlayerStorageTable := db.GetMapPlayerStorageTable(playerID)
  129. if num < 1 {
  130. return code.ItemNumError
  131. }
  132. _, found := data.Item.GetByID(id)
  133. if !found {
  134. return code.ConfigNotFound_Item
  135. }
  136. // 优先扣除绑定资源,再扣除非绑定资源
  137. changeAssets, ok := mapPlayerStorageTable.Storage.CurrencyMap.Decrease(id, num)
  138. if !ok {
  139. return code.ItemNotEnough
  140. }
  141. if save {
  142. mapPlayerStorageTable.Save2Queue()
  143. }
  144. var changeIDList []int32
  145. for _, asset := range changeAssets {
  146. changeIDList = append(changeIDList, asset.ID)
  147. }
  148. if push {
  149. p.ChangePush(playerID, changeIDList...)
  150. }
  151. return code.OK
  152. }
  153. // CheckAutoAdd 处理自动资源发放
  154. func (p *service) CheckAutoAdd(playerTable *db.MapPlayerTable, nowSecond int64, isOnline bool) {
  155. playerStorageTable := db.GetMapPlayerStorageTable(playerTable.PlayerID)
  156. yields := p.GetAllCurrencyYields(playerTable)
  157. changePushIDList := p.processCurrencyAutoAdd(playerTable, playerStorageTable, yields, nowSecond)
  158. if len(changePushIDList) > 0 {
  159. playerStorageTable.Save2Queue()
  160. if isOnline {
  161. p.ChangePush(playerTable.PlayerID, changePushIDList...)
  162. }
  163. }
  164. }
  165. // processCurrencyAutoAdd 处理资源的自动添加
  166. func (p *service) processCurrencyAutoAdd(playerTable *db.MapPlayerTable, storageTable *db.MapPlayerStorageTable, yields map[int32]int64, nowSecond int64) []int32 {
  167. var changePushIDList []int32
  168. for _, itemID := range enum.ItemMaxNumList {
  169. yield := yields[itemID]
  170. if yield < 1 {
  171. continue
  172. }
  173. storageTable.Storage.CurrencyMap.CheckInitUpdateTime(itemID, nowSecond)
  174. if !storageTable.Storage.CurrencyMap.CheckAutoAdd(itemID, nowSecond, yield, data.Const.ItemAddSecond) {
  175. continue
  176. }
  177. changePushIDList = append(changePushIDList, itemID)
  178. if storageTable.Storage.CurrencyMap.IsFull(itemID) {
  179. p.notifyResourceFull(playerTable, itemID)
  180. }
  181. }
  182. return changePushIDList
  183. }
  184. // notifyResourceFull 通知资源已满
  185. func (p *service) notifyResourceFull(playerTable *db.MapPlayerTable, itemID int32) {
  186. actorRemote.GamePlayer.GamePlayerCallSystemMsg(playerTable.GameNodeID, playerTable.PlayerID, enum.ChatMsgType_resourceMax, nil, nil, itemID)
  187. }
  188. // GetCurrencyYield 获取单个资源产量
  189. func (p *service) GetCurrencyYield(mapPlayerTable *db.MapPlayerTable, itemID int32) int64 {
  190. if attrID, ok := enum.MapItemAttrs[itemID]; ok {
  191. yield := p.calcAssetOutputFinalAttr(mapPlayerTable, attrID)
  192. yield += mapPlayerTable.GetResYield(itemID)
  193. return yield
  194. }
  195. if _, ok := enum.UnBindingResourceMap[itemID]; ok {
  196. return mapPlayerTable.GetResYield(itemID)
  197. }
  198. return 0
  199. }
  200. // GetAllCurrencyYields 批量获取所有资源产量
  201. func (p *service) GetAllCurrencyYields(mapPlayerTable *db.MapPlayerTable) map[int32]int64 {
  202. mapResYields := mapPlayerTable.GetAllResYield()
  203. yields := make(map[int32]int64)
  204. for _, itemID := range enum.ItemMaxNumList {
  205. if attrID, ok := enum.MapItemAttrs[itemID]; ok {
  206. yield := p.calcAssetOutputFinalAttr(mapPlayerTable, attrID)
  207. yield += mapResYields.GetValue(itemID)
  208. yields[itemID] = yield
  209. continue
  210. }
  211. if _, ok := enum.UnBindingResourceMap[itemID]; ok {
  212. yields[itemID] = mapResYields.GetValue(itemID)
  213. }
  214. }
  215. return yields
  216. }
  217. func (p *service) IsEnough(playerID int64, id int32, num int64) bool {
  218. if !enum.IsMapCurrency(id) {
  219. return true
  220. }
  221. if !p.isEnough(playerID, id, num) {
  222. return false
  223. }
  224. return true
  225. }
  226. func (p *service) IsAllEnough(playerID int64, assets types.Assets) bool {
  227. for _, asset := range assets {
  228. if !enum.IsMapCurrency(asset.ID) {
  229. continue
  230. }
  231. if !p.isEnough(playerID, asset.ID, asset.Num) {
  232. return false
  233. }
  234. }
  235. return true
  236. }
  237. // AssetAdds 增加多个资源,地图和游戏节点分别增加
  238. func (p *service) AssetAdds(playerID int64, assets types.Assets, ao assetOrigin.Origin, isPush ...bool) (types.Assets, int32) {
  239. var (
  240. mapPlayerTable = db.GetMapPlayerTable(playerID)
  241. mapAssets = types.Assets{}
  242. mapLeagueAssets = types.Assets{}
  243. gameAssets = types.Assets{}
  244. retAssets = types.Assets{}
  245. )
  246. // 检查玩家表是否存在
  247. if mapPlayerTable == nil {
  248. return retAssets, code.PlayerNotFound
  249. }
  250. if len(assets) == 0 {
  251. return retAssets, code.IllegalArgument
  252. }
  253. // TODO 默认推送
  254. push := true
  255. if len(isPush) > 0 {
  256. push = isPush[0]
  257. }
  258. // 资源拆解分组,地图的资源和游戏的资源分开处理
  259. for _, asset := range assets {
  260. if enum.IsLeagueCurrency(asset.ID) {
  261. mapLeagueAssets.AddAsset(asset)
  262. } else if enum.IsMapCurrency(asset.ID) {
  263. mapAssets.AddAsset(asset)
  264. } else {
  265. gameAssets.AddAsset(asset)
  266. }
  267. }
  268. if mapAssets.Len() > 0 {
  269. for _, asset := range mapAssets {
  270. retAsset, errCode := p.add(playerID, asset.ID, asset.Num, ao, true, push)
  271. if code.IsFail(errCode) {
  272. return retAssets, errCode
  273. }
  274. retAssets.AddAsset(&retAsset)
  275. }
  276. // 发送获得奖励的系统消息
  277. actorRemote.GamePlayer.GamePlayerCallSystemMsg(mapPlayerTable.GameNodeID, playerID, enum.ChatMsgType_reward, mapAssets, nil)
  278. capp.PostEvent(event.NewItemAdd(playerID, mapAssets, ao))
  279. }
  280. if mapLeagueAssets.Len() > 0 {
  281. mapCall.League.MapLeagueAddAssets(playerID, mapPlayerTable.LeagueID, mapLeagueAssets)
  282. }
  283. if gameAssets.Len() > 0 {
  284. retAssetsProto, errCode := actorRemote.GamePlayer.AssetAdd(mapPlayerTable.GameNodeID, playerID, gameAssets, ao)
  285. if code.IsFail(errCode) {
  286. return retAssets, errCode
  287. }
  288. for _, asset := range retAssetsProto.List {
  289. retAsset := types.Asset{
  290. ID: asset.Id,
  291. Num: asset.Num,
  292. GUID: asset.Guid,
  293. }
  294. retAssets.AddAsset(&retAsset)
  295. }
  296. }
  297. return retAssets, code.OK
  298. }
  299. // AssetAddOne 单个资源增加,地图和游戏节点分别增加
  300. func (p *service) AssetAddOne(playerID int64, id int32, num int64, origin assetOrigin.Origin, isPush ...bool) (types.Assets, int32) {
  301. assets := types.Assets{
  302. {
  303. ID: id,
  304. Num: num,
  305. },
  306. }
  307. return p.AssetAdds(playerID, assets, origin, isPush...)
  308. }
  309. // AssetSubs 扣除多个资源,地图和游戏节点分别扣除
  310. func (p *service) AssetSubs(playerID int64, assets types.Assets, origin assetOrigin.Origin, isPush ...bool) (types.Assets, int32) {
  311. var (
  312. mapAssets = types.Assets{}
  313. gameAssets = types.Assets{}
  314. retAssets = types.Assets{}
  315. )
  316. if len(assets) == 0 {
  317. return nil, code.IllegalArgument
  318. }
  319. // TODO 默认推送
  320. push := true
  321. if len(isPush) > 0 {
  322. push = isPush[0]
  323. }
  324. for _, asset := range assets {
  325. // 不支持联盟货币消耗
  326. if enum.IsLeagueCurrency(asset.ID) {
  327. clog.Warnf("AssetSubs league currency not support, playerID: %d, asset: %+v", playerID, asset)
  328. return nil, code.LeagueCurrencyNotSupport
  329. }
  330. if enum.IsMapCurrency(asset.ID) {
  331. mapAssets.AddAsset(asset)
  332. } else {
  333. gameAssets.AddAsset(asset)
  334. }
  335. }
  336. // 先检查地图这边的资源是否足够
  337. if !p.IsAllEnough(playerID, mapAssets) {
  338. return nil, code.CurrencyNotEnough
  339. }
  340. if len(gameAssets) > 0 {
  341. mapPlayerTable := db.GetMapPlayerTable(playerID)
  342. // 检查玩家表是否存在
  343. if mapPlayerTable == nil {
  344. return retAssets, code.PlayerNotFound
  345. }
  346. // 发送到游戏节点扣除游戏资源
  347. retAssetsProto, errCode := actorRemote.GamePlayer.AssetSub(mapPlayerTable.GameNodeID, playerID, gameAssets, origin)
  348. if code.IsFail(errCode) {
  349. return nil, errCode
  350. }
  351. for _, asset := range retAssetsProto.List {
  352. retAsset := types.Asset{
  353. ID: asset.Id,
  354. Num: asset.Num,
  355. GUID: asset.Guid,
  356. }
  357. retAssets.AddAsset(&retAsset)
  358. }
  359. }
  360. for _, asset := range mapAssets {
  361. errCode := p.sub(playerID, asset.ID, asset.Num, origin, true, push)
  362. if code.IsFail(errCode) {
  363. return nil, errCode
  364. }
  365. retAssets.AddAsset(asset)
  366. }
  367. return retAssets, code.OK
  368. }
  369. // AssetSubOne 单个资源扣除,地图和游戏节点分别扣除
  370. func (p *service) AssetSubOne(playerID int64, id int32, num int64, origin assetOrigin.Origin, isPush ...bool) (types.Assets, int32) {
  371. assets := types.Assets{
  372. {
  373. ID: id,
  374. Num: num,
  375. },
  376. }
  377. return p.AssetSubs(playerID, assets, origin, isPush...)
  378. }
  379. // Get 获取资源数量
  380. func (p *service) Get(playerID int64, id int32) int64 {
  381. if !enum.IsMapCurrency(id) {
  382. return 0
  383. }
  384. mapPlayerStorageTable := db.GetMapPlayerStorageTable(playerID)
  385. return mapPlayerStorageTable.Storage.CurrencyMap.Get(id)
  386. }
  387. // 计算各个模块给资源产出的加成
  388. // 1,城建
  389. // 2,VIP
  390. func (p *service) calcAssetOutputFinalAttr(mapPlayerTable *db.MapPlayerTable, attrID int32) int64 {
  391. facilityValue := mapPlayerTable.Facilities.Attrs.GetI64(attrID)
  392. vipValue := int64(0)
  393. if !mapPlayerTable.Vip.IsExpired {
  394. vipValue = mapPlayerTable.Vip.Attrs.GetI64(attrID)
  395. }
  396. attrValue := facilityValue + vipValue
  397. return attrValue
  398. }