sngmatchmanager.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. package sngmatch
  2. import (
  3. "os"
  4. "bet24.com/log"
  5. "bet24.com/servers/common"
  6. //gameHistory "bet24.com/servers/micros/gameHistory/proto"
  7. "encoding/json"
  8. "fmt"
  9. "sort"
  10. "strconv"
  11. "sync"
  12. "time"
  13. inventory "bet24.com/servers/micros/item_inventory/proto"
  14. item "bet24.com/servers/micros/item_inventory/proto"
  15. "bet24.com/servers/micros/matches/handler/matchbase"
  16. "bet24.com/servers/micros/matches/handler/simplematch"
  17. platformconfig "bet24.com/servers/micros/platformconfig/proto"
  18. robot "bet24.com/servers/micros/userservices/proto"
  19. user "bet24.com/servers/micros/userservices/proto"
  20. )
  21. const config_key = "sngmatch_config"
  22. const refresh_config_sec = 600
  23. const match_time_out_ended = 120
  24. type sngmatchMgr struct {
  25. MatchConfigs []*matchconfig
  26. matches map[int][]*matchInfo
  27. lock *sync.RWMutex
  28. lockConfig *sync.RWMutex
  29. }
  30. var matchMgr *sngmatchMgr
  31. func getMatchManager() *sngmatchMgr {
  32. if matchMgr == nil {
  33. matchMgr = new(sngmatchMgr)
  34. matchMgr.lock = &sync.RWMutex{}
  35. matchMgr.lockConfig = &sync.RWMutex{}
  36. matchMgr.matches = make(map[int][]*matchInfo)
  37. }
  38. return matchMgr
  39. }
  40. func (mm *sngmatchMgr) run() {
  41. mm.loadConfig()
  42. go mm.checkTimeout()
  43. }
  44. func (mm *sngmatchMgr) loadConfig() {
  45. defer func() {
  46. time.AfterFunc(refresh_config_sec*time.Second, mm.loadConfig)
  47. }()
  48. configString := platformconfig.GetConfig(config_key)
  49. if configString == "" {
  50. data, err := os.ReadFile("serviceconf/sngmatch.json")
  51. if err != nil {
  52. log.Release("sngmatch.sngmatchMgr.loadData read json failed")
  53. return
  54. }
  55. configString = string(data)
  56. platformconfig.SetConfig(config_key, configString)
  57. }
  58. var configs []*matchconfig
  59. err := json.Unmarshal([]byte(configString), &configs)
  60. if err != nil {
  61. log.Release("sngmatch.sngmatchMgr.loadData Unmarshal failed err:%v", err)
  62. return
  63. }
  64. for _, v := range configs {
  65. v.calTotalPrize()
  66. mm.updateConfig(v)
  67. }
  68. }
  69. func (mm *sngmatchMgr) getOnlineUserCount(matchId int) int {
  70. ret := 0
  71. mm.lock.RLock()
  72. matches, ok := mm.matches[matchId]
  73. mm.lock.RUnlock()
  74. if !ok {
  75. return ret
  76. }
  77. for _, v := range matches {
  78. ret += v.getOnlineUserCount()
  79. }
  80. return ret
  81. }
  82. func (mm *sngmatchMgr) updateConfig(config *matchconfig) {
  83. mm.lockConfig.Lock()
  84. defer mm.lockConfig.Unlock()
  85. //for _,v := range mm.MatchConfigs {
  86. for i := 0; i < len(mm.MatchConfigs); i++ {
  87. v := mm.MatchConfigs[i]
  88. if v.MatchId == config.MatchId {
  89. config.OnlineUser = v.OnlineUser
  90. mm.MatchConfigs[i] = config
  91. return
  92. }
  93. }
  94. // 没有
  95. mm.MatchConfigs = append(mm.MatchConfigs, config)
  96. }
  97. func (mm *sngmatchMgr) checkTimeout() {
  98. time.AfterFunc(30*time.Second, mm.checkTimeout)
  99. mm.lock.Lock()
  100. for k, v := range mm.matches {
  101. for i := 0; i < len(v); {
  102. if v[i].isTimeout() {
  103. v = append(v[:i], v[i+1:]...)
  104. } else {
  105. i++
  106. }
  107. }
  108. mm.matches[k] = v
  109. }
  110. mm.lock.Unlock()
  111. }
  112. func (mm *sngmatchMgr) dump(param1, param2 string) {
  113. log.Release("-------------------------------")
  114. log.Release("sngmatch.sngmatchMgr.dump[%s,%s]", param1, param2)
  115. defer func() {
  116. log.Release("+++++++++++++++++++++++++++++++")
  117. log.Release("")
  118. }()
  119. if param1 == "config" {
  120. mm.lockConfig.RLock()
  121. defer mm.lockConfig.RUnlock()
  122. for _, v := range mm.MatchConfigs {
  123. v.dump()
  124. }
  125. return
  126. }
  127. if param1 == "history" {
  128. getHistoryManager().dump(param2)
  129. return
  130. }
  131. if param1 == "dismiss" {
  132. matchNo, err := strconv.Atoi(param2)
  133. if err != nil {
  134. log.Release("invalid param %s", param2)
  135. return
  136. }
  137. mm.dismissMatch(matchNo)
  138. return
  139. }
  140. mm.lock.RLock()
  141. for k, v := range mm.matches {
  142. log.Release(" MatchId[%d]", k)
  143. for _, v1 := range v {
  144. v1.dump()
  145. }
  146. log.Release(" -------------")
  147. }
  148. mm.lock.RUnlock()
  149. }
  150. func (mm *sngmatchMgr) dismissMatch(matchNo int) {
  151. mi := mm.getMatchByMatchNo(matchNo)
  152. if mi == nil {
  153. log.Release("sngmatchMgr.dismissMatch [%d] not found", matchNo)
  154. return
  155. }
  156. config := mm.getConfig(mi.MatchId)
  157. if config == nil {
  158. log.Release("sngmatchMgr.dismissMatch matchId[%d] not found", mi.MatchId)
  159. return
  160. }
  161. if mi.getStatus() == matchbase.MatchStatus_Ended {
  162. log.Release("sngmatchMgr.dismissMatch matchId[%d] ended", mi.MatchId)
  163. return
  164. }
  165. userList := mi.getUserList()
  166. for _, v := range userList {
  167. // 退钱
  168. if len(config.EnrollFee) > 0 {
  169. itm := mi.getUserFee(v, true)
  170. if itm.Count > 0 {
  171. inventory.AddItems(v, []item.ItemPack{itm},
  172. "sngmatch fee return", common.LOGTYPE_SNGMATCH_ENTER_RETURN)
  173. } else if config.DailyFreeCount > 0 {
  174. getFreeCountManager().reduceFreeCount(v, config.MatchId)
  175. }
  176. }
  177. }
  178. simplematch.DismissMatch(matchNo)
  179. }
  180. func (mm *sngmatchMgr) enrollSngMatch(matchId int, userId int, nickname string, faceId int, faceUrl string, feeIndex int) (int, string) {
  181. config := mm.getConfig(matchId)
  182. if config == nil {
  183. log.Release("sngmatchMgr.enrollSngMatch matchId[%d] not found", matchId)
  184. return 0, "Wrong MatchId"
  185. }
  186. log.Debug("enrollSngMatch matchId = %d config = %v", matchId, config)
  187. if !config.IsInTime() {
  188. log.Release("sngmatchMgr.enrollSngMatch matchId[%d] not open %v", matchId, config)
  189. return 0, "Not in match time"
  190. }
  191. if mm.isUserPlaying(userId) {
  192. return 0, "Already in a match"
  193. }
  194. mi := mm.getOrCreateMatch(matchId, config)
  195. if mi == nil {
  196. log.Release("sngmatchMgr.enrollSngMatch matchId[%d] getOrCreateMatch failed", matchId)
  197. return 0, "Serve error"
  198. }
  199. if feeIndex >= len(config.EnrollFee) {
  200. feeIndex = len(config.EnrollFee) - 1
  201. }
  202. // 如果有免费次数
  203. if config.DailyFreeCount > 0 && getFreeCountManager().getUserFreeCount(userId, config.MatchId) < config.DailyFreeCount {
  204. getFreeCountManager().addFreeCount(userId, config.MatchId)
  205. } else {
  206. // 扣钱
  207. if len(config.EnrollFee) > 0 && !robot.IsRobot(userId) {
  208. ok, err := inventory.Consume(userId, config.EnrollFee[feeIndex].ItemId, common.LOGTYPE_SNGMATCH_ENTER, config.EnrollFee[feeIndex].Count, 0)
  209. if !ok {
  210. errMsg := fmt.Sprintf("not enough fee[%v] for play err = %s", config.EnrollFee[feeIndex], err)
  211. log.Release("sngmatch.enrollSngMatch failed %s", errMsg)
  212. return 0, errMsg
  213. }
  214. mi.setUserFee(userId, config.EnrollFee[feeIndex])
  215. }
  216. }
  217. mi.addEnrollUser(userId)
  218. // 玩家加入
  219. retCode, errMsg := simplematch.EnrollMatch(userId, nickname, faceId, faceUrl, mi.MatchNo)
  220. if retCode != 1 {
  221. return 0, errMsg
  222. }
  223. config.addOnline(1)
  224. return mi.MatchNo, "OK"
  225. }
  226. func (mm *sngmatchMgr) quitSngMatch(matchId int, userId int) bool {
  227. config := mm.getConfig(matchId)
  228. if config == nil {
  229. log.Release("sngmatchMgr.quitSngMatch matchId[%d] not found", matchId)
  230. return false
  231. }
  232. // 查询我的比赛
  233. mm.lock.RLock()
  234. matches, ok := mm.matches[matchId]
  235. mm.lock.RUnlock()
  236. if !ok || len(matches) == 0 {
  237. log.Release("sngmatchMgr.quitSngMatch matchId[%d] no match found", matchId)
  238. return false
  239. }
  240. for _, v := range matches {
  241. if v.getStatus() == matchbase.MatchStatus_Free && v.isUserEnrolled(userId) {
  242. // 退钱
  243. if len(config.EnrollFee) > 0 {
  244. itm := v.getUserFee(userId, true)
  245. if itm.Count > 0 {
  246. inventory.AddItems(userId, []item.ItemPack{itm},
  247. "sngmatch fee return", common.LOGTYPE_SNGMATCH_ENTER_RETURN)
  248. } else if config.DailyFreeCount > 0 {
  249. getFreeCountManager().reduceFreeCount(userId, config.MatchId)
  250. }
  251. }
  252. ok := simplematch.QuitMatch(userId, v.MatchNo)
  253. if ok {
  254. config.addOnline(-1)
  255. }
  256. return ok
  257. }
  258. }
  259. return false
  260. }
  261. func (mm *sngmatchMgr) getConfig(matchId int) *matchconfig {
  262. mm.lockConfig.RLock()
  263. defer mm.lockConfig.RUnlock()
  264. for _, v := range mm.MatchConfigs {
  265. if v.MatchId == matchId {
  266. return v
  267. }
  268. }
  269. return nil
  270. }
  271. func (mm *sngmatchMgr) getCurrentMatch(matchId int) *matchInfo {
  272. mm.lock.RLock()
  273. matches, ok := mm.matches[matchId]
  274. mm.lock.RUnlock()
  275. if ok {
  276. for _, v := range matches {
  277. if v.getStatus() == matchbase.MatchStatus_Free && !v.isFull() {
  278. return v
  279. }
  280. }
  281. }
  282. return nil
  283. }
  284. func (mm *sngmatchMgr) getOrCreateMatch(matchId int, config *matchconfig) *matchInfo {
  285. ret := mm.getCurrentMatch(matchId)
  286. if ret != nil {
  287. return ret
  288. }
  289. // 创建一个比赛
  290. matchNo, err := simplematch.CreateMatch(-1, config.GameId, config.GameRule, config.TotalUser,
  291. config.Target, config.TableUser, 0, 0, config.PlayTime, config.EleminateByScore, config.WinnerCount)
  292. if matchNo == 0 {
  293. log.Release("sngmatchMgr.getOrCreateMatch CreateMatch failed %s", err)
  294. return nil
  295. }
  296. mi := simplematch.GetMatchInstance(matchNo)
  297. if mi == nil {
  298. log.Release("sngmatchMgr.getOrCreateMatch GetMatchInstance == nil")
  299. return nil
  300. }
  301. matchInfo := newMatchInfo(config.MatchId, matchNo, mi, mm, config.RobotConfig)
  302. mi.RegisterReceiver(mm)
  303. mm.lock.Lock()
  304. mm.matches[matchId] = append(mm.matches[matchId], matchInfo)
  305. mm.lock.Unlock()
  306. return matchInfo
  307. }
  308. // MatchInstanceReceiver
  309. func (mm *sngmatchMgr) OnMatchStart(matchNo int) {
  310. log.Debug("sngmatchMgr.OnMatchStart [%d]", matchNo)
  311. mi := mm.getMatchByMatchNo(matchNo)
  312. if mi == nil {
  313. log.Release("sngmatchMgr.OnMatchStart [%d] matchInfo not found", matchNo)
  314. return
  315. }
  316. mi.StartTime = time.Now().Unix()
  317. go mi.onMatchStart()
  318. }
  319. func (mm *sngmatchMgr) OnMatchCancelled(matchNo int) {
  320. log.Debug("sngmatchMgr.OnMatchCancelled [%d]", matchNo)
  321. mi := mm.getMatchByMatchNo(matchNo)
  322. if mi == nil {
  323. log.Release("sngmatchMgr.OnMatchCancelled [%d] matchInfo not found", matchNo)
  324. return
  325. }
  326. config := mm.getConfig(mi.MatchId)
  327. if config == nil {
  328. log.Release("sngmatchMgr.OnMatchCancelled [%d] config not found", matchNo)
  329. return
  330. }
  331. matchInstance := simplematch.GetMatchInstance(matchNo)
  332. if matchInstance == nil {
  333. log.Release("sngmatchMgr.OnMatchCancelled [%d] matchInstance not found", matchNo)
  334. return
  335. }
  336. // 退费
  337. userList := matchInstance.GetUserList()
  338. for _, userId := range userList {
  339. // 退钱
  340. if len(config.EnrollFee) > 0 {
  341. itm := mi.getUserFee(userId, true)
  342. if itm.Count > 0 {
  343. inventory.AddItems(userId, []item.ItemPack{itm},
  344. "sngmatch fee return", common.LOGTYPE_SNGMATCH_ENTER_RETURN)
  345. } else if config.DailyFreeCount > 0 {
  346. getFreeCountManager().reduceFreeCount(userId, config.MatchId)
  347. }
  348. }
  349. }
  350. config.addOnline(-len(userList))
  351. }
  352. func (mm *sngmatchMgr) OnMatchEnd(matchNo int) {
  353. log.Debug("sngmatchMgr.OnMatchEnd [%d]", matchNo)
  354. mi := mm.getMatchByMatchNo(matchNo)
  355. if mi == nil {
  356. log.Release("sngmatchMgr.OnMatchEnd [%d] matchInfo not found", matchNo)
  357. return
  358. }
  359. mi.EndTime = time.Now().Unix()
  360. config := mm.getConfig(mi.MatchId)
  361. if config == nil {
  362. log.Release("sngmatchMgr.OnMatchEnd matchId[%d] not found", mi.MatchId)
  363. return
  364. }
  365. // 找出胜利者给发奖励
  366. matchInstance := simplematch.GetMatchInstance(matchNo)
  367. if matchInstance == nil {
  368. log.Release("sngmatchMgr.OnMatchEnd [%d] matchInstance not found", matchNo)
  369. return
  370. }
  371. winners := matchInstance.GetWinners()
  372. if len(winners) == 0 {
  373. log.Release("sngmatchMgr.OnMatchEnd [%d] no winners", matchNo)
  374. return
  375. }
  376. config.addOnline(-config.TotalUser)
  377. // 加入历史记录
  378. var h snghistory
  379. h.matchconfig = *config
  380. h.RobotConfig = nil
  381. h.MatchNo = matchNo
  382. h.StartTime = mi.StartTime
  383. h.EndTime = mi.EndTime
  384. h.Winners = winners
  385. matchUsers := matchInstance.GetAllMatchUsers()
  386. // 所有参与人员
  387. sort.Slice(matchUsers, func(i, j int) bool {
  388. return matchUsers[i].Rank < matchUsers[j].Rank
  389. })
  390. for _, v := range matchUsers {
  391. enrollFee := mi.getUserFee(v.UserId, false)
  392. h.EnrollUsers = append(h.EnrollUsers, historyUser{
  393. MatchUserBrief: *v.ToBrief(),
  394. enrollFeeItemId: enrollFee.ItemId,
  395. enrollFeeCount: enrollFee.Count,
  396. enrollTime: int(v.EnrollTime),
  397. prize: config.getPrizes(v.Rank),
  398. })
  399. // 已淘汰用户不再发奖励
  400. if mi.isUserAwarded(v.UserId) {
  401. continue
  402. }
  403. prize := config.getPrizes(v.Rank)
  404. if len(prize) > 0 {
  405. inventory.AddItems(v.UserId, prize, "sng_prize", common.LOGTYPE_SNGMATCH_PRIZE)
  406. postUserRankNotification(v.UserId, config.MatchId, matchNo, v.Rank, prize)
  407. log.Debug("OnMatchEnd 发送通知和奖励 [%d] %v", v.UserId, prize)
  408. mi.setUserAwarded(v.UserId)
  409. }
  410. /*gameHistory.MyMatch_Write(v.UserId, &gameHistory.MyMatchInfo{
  411. UserId: v.UserId, //int `json:"UserId, omitempty"` // 用户ID
  412. NickName: v.NickName, //string `json:"NickName, omitempty"` // 昵称
  413. MatchName: config.Name, //string // 比赛名称
  414. Rank: v.Rank, //int // 名次
  415. Items: prize, //int // 道具ID
  416. })*/
  417. }
  418. // 最后把所有报名记录都加上
  419. h.setAllEnrolledUsers(mi.enrolledUsers)
  420. getHistoryManager().addHistory(h)
  421. go writeRoomRecordToDB(&h)
  422. }
  423. func (mm *sngmatchMgr) OnUserEliminated(matchNo int, userId int, rank int) {
  424. log.Debug("sngmatchMgr.OnUserEliminated [%d] UserId[%d] Rank[%d]", matchNo, userId, rank)
  425. mi := mm.getMatchByMatchNo(matchNo)
  426. if mi == nil {
  427. log.Release("combomatchMgr.OnUserEliminated [%d] matchInfo not found", matchNo)
  428. return
  429. }
  430. config := mm.getConfig(mi.MatchId)
  431. if config == nil {
  432. log.Release("combomatchMgr.OnUserEliminated matchId[%d] not found", mi.MatchId)
  433. return
  434. }
  435. prize := config.getPrizes(rank)
  436. if len(prize) > 0 {
  437. inventory.AddItems(userId, prize, "配置赛奖励", common.LOGTYPE_COMBOMATCH_PRIZE)
  438. postUserRankNotification(userId, config.MatchId, matchNo, rank, prize)
  439. log.Debug("OnMatchEnd 发送通知和奖励 [%d] %v", userId, prize)
  440. }
  441. mi.setUserAwarded(userId)
  442. /*usr := mi.getUser(userId)
  443. var myInfo gameHistory.MyMatchInfo
  444. myInfo.UserId = userId
  445. myInfo.MatchName = config.Name
  446. myInfo.Rank = rank
  447. myInfo.Items = prize
  448. if usr != nil {
  449. myInfo.NickName = usr.NickName
  450. }
  451. gameHistory.MyMatch_Write(userId, &myInfo)*/
  452. }
  453. func (mm *sngmatchMgr) getMatchByMatchNo(matchNo int) *matchInfo {
  454. mm.lock.RLock()
  455. defer mm.lock.RUnlock()
  456. for _, v := range mm.matches {
  457. for _, v1 := range v {
  458. if v1.MatchNo == matchNo {
  459. return v1
  460. }
  461. }
  462. }
  463. return nil
  464. }
  465. func (mm *sngmatchMgr) getMatchList(userId int) string {
  466. var configs []matchconfig
  467. mm.lockConfig.RLock()
  468. for _, v := range mm.MatchConfigs {
  469. configs = append(configs, *v)
  470. }
  471. mm.lockConfig.RUnlock()
  472. for i := 0; i < len(configs); i++ {
  473. configs[i].setLeftFreeCount(userId)
  474. }
  475. d, _ := json.Marshal(configs)
  476. return string(d)
  477. }
  478. func (mm *sngmatchMgr) getUserMatchId(userId int) int {
  479. mm.lock.RLock()
  480. defer mm.lock.RUnlock()
  481. for _, v := range mm.matches {
  482. for _, v1 := range v {
  483. if v1.isUserPlaying(userId) {
  484. return v1.MatchId
  485. }
  486. }
  487. }
  488. return 0
  489. }
  490. func (mm *sngmatchMgr) addARobot(matchId int) bool {
  491. m := mm.getCurrentMatch(matchId)
  492. if m == nil {
  493. log.Release("sngmatchMgr.addARobot matchId[%d] has no live match", matchId)
  494. return false
  495. }
  496. loopCount := 0
  497. for {
  498. loopCount++
  499. if loopCount > 20 {
  500. log.Release("sngmatchMgr.addARobot matchId[%d] get a robot failed", matchId)
  501. return false
  502. }
  503. robotId := robot.GetARobot()
  504. if mm.isUserPlaying(robotId) {
  505. continue
  506. }
  507. usr := user.GetUserInfo(robotId)
  508. if usr == nil {
  509. continue
  510. }
  511. mm.enrollSngMatch(matchId, robotId, usr.NickName, usr.FaceId, usr.FaceUrl, 0)
  512. break
  513. }
  514. return true
  515. }
  516. func (mm *sngmatchMgr) isUserPlaying(userId int) bool {
  517. mm.lock.RLock()
  518. defer mm.lock.RUnlock()
  519. for _, m := range mm.matches {
  520. for _, mi := range m {
  521. if mi.isUserPlaying(userId) {
  522. return true
  523. }
  524. }
  525. }
  526. return false
  527. }