service.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  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. var (
  156. playerStorageTable = db.GetMapPlayerStorageTable(playerTable.PlayerID)
  157. itemAddSecond = data.Const.ItemAddSecond
  158. changePushIDList []int32
  159. save bool
  160. )
  161. for _, itemID := range enum.ItemMaxNumList {
  162. // 产出的量
  163. yield := p.GetMapCurrencyYield(playerTable, itemID)
  164. if yield < 1 {
  165. continue
  166. }
  167. // 只有能产出的时候才初始化 UpdateTime
  168. if playerStorageTable.Storage.CurrencyMap.CheckInitUpdateTime(itemID, nowSecond) {
  169. save = true
  170. }
  171. if !playerStorageTable.Storage.CurrencyMap.CheckAutoAdd(itemID, nowSecond, yield, itemAddSecond) {
  172. continue
  173. }
  174. save = true
  175. changePushIDList = append(changePushIDList, itemID)
  176. if playerStorageTable.Storage.CurrencyMap.IsFull(itemID) {
  177. actorRemote.GamePlayer.GamePlayerCallSystemMsg(playerTable.GameNodeID, playerTable.PlayerID, enum.ChatMsgType_resourceMax, nil, nil, itemID)
  178. }
  179. }
  180. if save {
  181. playerStorageTable.Save2Queue()
  182. }
  183. if len(changePushIDList) > 0 && isOnline {
  184. p.ChangePush(playerTable.PlayerID, changePushIDList...)
  185. }
  186. }
  187. // 获取资源总产量
  188. func (p *service) GetMapCurrencyYield(mapPlayerTable *db.MapPlayerTable, itemIDs ...int32) int64 {
  189. if len(itemIDs) <= 0 {
  190. return 0
  191. }
  192. var (
  193. mapResYields = mapPlayerTable.GetAllYield()
  194. totalYield = int64(0)
  195. )
  196. for _, itemID := range itemIDs {
  197. // 绑定资源
  198. if attrID, ok := enum.MapItemAttrs[itemID]; ok {
  199. totalYield += p.calcAssetOutputFinalAttr(mapPlayerTable, attrID)
  200. // 资源田产出可能是绑定的
  201. if yield, ok := mapResYields.Get(itemID); ok {
  202. totalYield += yield
  203. }
  204. }
  205. // 非绑定资源
  206. if _, ok := enum.UnBindingResourceMap[itemID]; ok {
  207. totalYield += mapResYields.GetValue(itemID)
  208. }
  209. }
  210. return totalYield
  211. }
  212. func (p *service) IsEnough(playerID int64, id int32, num int64) bool {
  213. if !enum.IsMapCurrency(id) {
  214. return true
  215. }
  216. if !p.isEnough(playerID, id, num) {
  217. return false
  218. }
  219. return true
  220. }
  221. func (p *service) IsAllEnough(playerID int64, assets types.Assets) bool {
  222. for _, asset := range assets {
  223. if !enum.IsMapCurrency(asset.ID) {
  224. continue
  225. }
  226. if !p.isEnough(playerID, asset.ID, asset.Num) {
  227. return false
  228. }
  229. }
  230. return true
  231. }
  232. // AssetAdds 增加多个资源,地图和游戏节点分别增加
  233. func (p *service) AssetAdds(playerID int64, assets types.Assets, ao assetOrigin.Origin, isPush ...bool) (types.Assets, int32) {
  234. var (
  235. mapPlayerTable = db.GetMapPlayerTable(playerID)
  236. mapAssets = types.Assets{}
  237. mapLeagueAssets = types.Assets{}
  238. gameAssets = types.Assets{}
  239. retAssets = types.Assets{}
  240. )
  241. // 检查玩家表是否存在
  242. if mapPlayerTable == nil {
  243. return retAssets, code.PlayerNotFound
  244. }
  245. if len(assets) == 0 {
  246. return retAssets, code.IllegalArgument
  247. }
  248. // TODO 默认推送
  249. push := true
  250. if len(isPush) > 0 {
  251. push = isPush[0]
  252. }
  253. // 资源拆解分组,地图的资源和游戏的资源分开处理
  254. for _, asset := range assets {
  255. if enum.IsLeagueCurrency(asset.ID) {
  256. mapLeagueAssets.AddAsset(asset)
  257. } else if enum.IsMapCurrency(asset.ID) {
  258. mapAssets.AddAsset(asset)
  259. } else {
  260. gameAssets.AddAsset(asset)
  261. }
  262. }
  263. if mapAssets.Len() > 0 {
  264. for _, asset := range mapAssets {
  265. retAsset, errCode := p.add(playerID, asset.ID, asset.Num, ao, true, push)
  266. if code.IsFail(errCode) {
  267. return retAssets, errCode
  268. }
  269. retAssets.AddAsset(&retAsset)
  270. }
  271. // 发送获得奖励的系统消息
  272. actorRemote.GamePlayer.GamePlayerCallSystemMsg(mapPlayerTable.GameNodeID, playerID, enum.ChatMsgType_reward, mapAssets, nil)
  273. capp.PostEvent(event.NewItemAdd(playerID, mapAssets, ao))
  274. }
  275. if mapLeagueAssets.Len() > 0 {
  276. mapCall.League.MapLeagueAddAssets(playerID, mapPlayerTable.LeagueID, mapLeagueAssets)
  277. }
  278. if gameAssets.Len() > 0 {
  279. retAssetsProto, errCode := actorRemote.GamePlayer.AssetAdd(mapPlayerTable.GameNodeID, playerID, gameAssets, ao)
  280. if code.IsFail(errCode) {
  281. return retAssets, errCode
  282. }
  283. for _, asset := range retAssetsProto.List {
  284. retAsset := types.Asset{
  285. ID: asset.Id,
  286. Num: asset.Num,
  287. GUID: asset.Guid,
  288. }
  289. retAssets.AddAsset(&retAsset)
  290. }
  291. }
  292. return retAssets, code.OK
  293. }
  294. // AssetAddOne 单个资源增加,地图和游戏节点分别增加
  295. func (p *service) AssetAddOne(playerID int64, id int32, num int64, origin assetOrigin.Origin, isPush ...bool) (types.Assets, int32) {
  296. assets := types.Assets{
  297. {
  298. ID: id,
  299. Num: num,
  300. },
  301. }
  302. return p.AssetAdds(playerID, assets, origin, isPush...)
  303. }
  304. // AssetSubs 扣除多个资源,地图和游戏节点分别扣除
  305. func (p *service) AssetSubs(playerID int64, assets types.Assets, origin assetOrigin.Origin, isPush ...bool) (types.Assets, int32) {
  306. var (
  307. mapAssets = types.Assets{}
  308. gameAssets = types.Assets{}
  309. retAssets = types.Assets{}
  310. )
  311. if len(assets) == 0 {
  312. return nil, code.IllegalArgument
  313. }
  314. // TODO 默认推送
  315. push := true
  316. if len(isPush) > 0 {
  317. push = isPush[0]
  318. }
  319. for _, asset := range assets {
  320. // 不支持联盟货币消耗
  321. if enum.IsLeagueCurrency(asset.ID) {
  322. clog.Warnf("AssetSubs league currency not support, playerID: %d, asset: %+v", playerID, asset)
  323. return nil, code.LeagueCurrencyNotSupport
  324. }
  325. if enum.IsMapCurrency(asset.ID) {
  326. mapAssets.AddAsset(asset)
  327. } else {
  328. gameAssets.AddAsset(asset)
  329. }
  330. }
  331. // 先检查地图这边的资源是否足够
  332. if !p.IsAllEnough(playerID, mapAssets) {
  333. return nil, code.CurrencyNotEnough
  334. }
  335. if len(gameAssets) > 0 {
  336. mapPlayerTable := db.GetMapPlayerTable(playerID)
  337. // 检查玩家表是否存在
  338. if mapPlayerTable == nil {
  339. return retAssets, code.PlayerNotFound
  340. }
  341. // 发送到游戏节点扣除游戏资源
  342. retAssetsProto, errCode := actorRemote.GamePlayer.AssetSub(mapPlayerTable.GameNodeID, playerID, gameAssets, origin)
  343. if code.IsFail(errCode) {
  344. return nil, errCode
  345. }
  346. for _, asset := range retAssetsProto.List {
  347. retAsset := types.Asset{
  348. ID: asset.Id,
  349. Num: asset.Num,
  350. GUID: asset.Guid,
  351. }
  352. retAssets.AddAsset(&retAsset)
  353. }
  354. }
  355. for _, asset := range mapAssets {
  356. errCode := p.sub(playerID, asset.ID, asset.Num, origin, true, push)
  357. if code.IsFail(errCode) {
  358. return nil, errCode
  359. }
  360. retAssets.AddAsset(asset)
  361. }
  362. return retAssets, code.OK
  363. }
  364. // AssetSubOne 单个资源扣除,地图和游戏节点分别扣除
  365. func (p *service) AssetSubOne(playerID int64, id int32, num int64, origin assetOrigin.Origin, isPush ...bool) (types.Assets, int32) {
  366. assets := types.Assets{
  367. {
  368. ID: id,
  369. Num: num,
  370. },
  371. }
  372. return p.AssetSubs(playerID, assets, origin, isPush...)
  373. }
  374. // Get 获取资源数量
  375. func (p *service) Get(playerID int64, id int32) int64 {
  376. if !enum.IsMapCurrency(id) {
  377. return 0
  378. }
  379. mapPlayerStorageTable := db.GetMapPlayerStorageTable(playerID)
  380. return mapPlayerStorageTable.Storage.CurrencyMap.Get(id)
  381. }
  382. // 计算各个模块给资源产出的加成
  383. // 1,城建
  384. // 2,VIP
  385. func (p *service) calcAssetOutputFinalAttr(mapPlayerTable *db.MapPlayerTable, attrID int32) int64 {
  386. facilityValue := mapPlayerTable.Facilities.Attrs.GetI64(attrID)
  387. vipValue := int64(0)
  388. if !mapPlayerTable.Vip.IsExpired {
  389. vipValue = mapPlayerTable.Vip.Attrs.GetI64(attrID)
  390. }
  391. attrValue := facilityValue + vipValue
  392. return attrValue
  393. }