service.go 11 KB

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