combomatchinfo.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  1. package combomatch
  2. import (
  3. "bet24.com/log"
  4. "bet24.com/servers/common"
  5. inventory "bet24.com/servers/micros/item_inventory/proto"
  6. item "bet24.com/servers/micros/item_inventory/proto"
  7. "bet24.com/servers/micros/matches/handler/matchbase"
  8. "bet24.com/servers/micros/matches/handler/pointmatch"
  9. "bet24.com/servers/micros/matches/handler/setsmatch"
  10. "bet24.com/servers/micros/matches/handler/simplematch"
  11. notification "bet24.com/servers/micros/notification/proto"
  12. task "bet24.com/servers/micros/task/proto"
  13. "encoding/json"
  14. "sync"
  15. "time"
  16. )
  17. type matchround struct {
  18. MatchType int
  19. MatchNo int
  20. matchInstance matchbase.MatchInstance
  21. EndTime int64
  22. StartTime int64
  23. createTime int64
  24. }
  25. func (mr *matchround) getStatus() int {
  26. if mr.matchInstance == nil {
  27. return matchbase.MatchStatus_Invalid
  28. }
  29. return mr.matchInstance.GetStatus()
  30. }
  31. type matchInfo struct {
  32. MatchId int
  33. Rounds []*matchround
  34. CurrentRound int // 当前轮次
  35. mm *combomatchMgr
  36. lock *sync.RWMutex
  37. userFee map[int]item.ItemPack
  38. robotConfig *matchbase.Robot_config
  39. robotCount int
  40. config *matchconfig
  41. allUsers []matchbase.MatchUser // 本次赛事所有用户,比赛开始后才有
  42. currentRoundWinners []matchbase.MatchUser // 本轮胜者,用于延迟进行下一轮缓存数据
  43. enrollUsers []combomatchuser // 预报名用户
  44. lockEnroll *sync.RWMutex
  45. userAwarded map[int]bool
  46. startFailed bool
  47. enrolledUsers []matchbase.EnrollUser
  48. }
  49. func newMatchInfo(matchId int, mm *combomatchMgr, robotConfig *matchbase.Robot_config, config *matchconfig) *matchInfo {
  50. ret := &matchInfo{
  51. MatchId: matchId,
  52. mm: mm,
  53. config: config,
  54. CurrentRound: -1, // 预报名
  55. }
  56. ret.userFee = make(map[int]item.ItemPack)
  57. ret.userAwarded = make(map[int]bool)
  58. ret.lock = &sync.RWMutex{}
  59. ret.lockEnroll = &sync.RWMutex{}
  60. ret.robotConfig = robotConfig
  61. if robotConfig != nil && robotConfig.Max > 0 {
  62. sec := robotConfig.GetWaitSec()
  63. time.AfterFunc(time.Second*time.Duration(sec), ret.checkRobot)
  64. }
  65. // 如果是预先报名,则启动定时器开始
  66. if config.IsPreEnroll() {
  67. nextGame := config.GetNextStartTime() - int(time.Now().Unix())
  68. log.Debug("combomatch.newMatchInfo MatchId[%d] [%d] seconds to start", matchId, nextGame)
  69. time.AfterFunc(time.Second*time.Duration(nextGame), ret.onMatchStart)
  70. // 最后一次
  71. if config.EnrollRange.End > 0 {
  72. go func(startTime int, beforeTime int) {
  73. delaySec := startTime - beforeTime
  74. if delaySec > 0 {
  75. time.Sleep(time.Second * time.Duration(delaySec))
  76. ret.onMatchLocked(beforeTime, true)
  77. }
  78. }(nextGame, config.EnrollRange.End)
  79. }
  80. // 额外的
  81. for _, v := range config.ExtraNotifySecs {
  82. go func(startTime int, beforeTime int) {
  83. delaySec := startTime - beforeTime
  84. if delaySec > 0 {
  85. time.Sleep(time.Second * time.Duration(delaySec))
  86. ret.onMatchLocked(beforeTime, false)
  87. }
  88. }(nextGame, v)
  89. }
  90. }
  91. return ret
  92. }
  93. func (mi *matchInfo) getMatchRound() *matchround {
  94. if mi.CurrentRound >= len(mi.Rounds) || mi.CurrentRound < 0 {
  95. return nil
  96. }
  97. return mi.Rounds[mi.CurrentRound]
  98. }
  99. func (mi *matchInfo) getCurrentInstance() matchbase.MatchInstance {
  100. mr := mi.getMatchRound()
  101. if mr == nil {
  102. return nil
  103. }
  104. return mr.matchInstance
  105. }
  106. func (mi *matchInfo) isEnded() bool {
  107. if mi.startFailed {
  108. return true
  109. }
  110. if mi.CurrentRound < mi.config.getTotalRound()-1 {
  111. return false
  112. }
  113. instance := mi.getCurrentInstance()
  114. if instance == nil {
  115. return false
  116. }
  117. return instance.GetStatus() == matchbase.MatchStatus_Ended
  118. }
  119. func (mi *matchInfo) getStatus() int {
  120. if mi.isEnded() {
  121. return matchbase.MatchStatus_Ended
  122. }
  123. // 报名
  124. if mi.CurrentRound < 0 {
  125. return matchbase.MatchStatus_Free
  126. }
  127. instance := mi.getCurrentInstance()
  128. if instance == nil {
  129. return matchbase.MatchStatus_Free
  130. }
  131. // 第一场如果未开,取第一场状态
  132. if mi.CurrentRound == 0 {
  133. return instance.GetStatus()
  134. }
  135. return matchbase.MatchStatus_Playing
  136. }
  137. func (mi *matchInfo) isFull() bool {
  138. if mi.config.IsPreEnroll() {
  139. //return len(mi.enrollUsers) >= mi.config.EnrollMax
  140. return false // 如果预报名,则不需要根据人满重开一个
  141. }
  142. if mi.CurrentRound == -1 {
  143. mi.lockEnroll.RLock()
  144. enrollCount := len(mi.enrollUsers)
  145. mi.lockEnroll.RUnlock()
  146. return enrollCount >= mi.config.TotalUser
  147. }
  148. instance := mi.getCurrentInstance()
  149. if instance == nil {
  150. return true
  151. }
  152. return instance.IsFull()
  153. }
  154. func (mi *matchInfo) canEnroll() bool {
  155. mi.lockEnroll.RLock()
  156. defer mi.lockEnroll.RUnlock()
  157. if mi.config.IsPreEnroll() {
  158. return len(mi.enrollUsers) < mi.config.EnrollMax
  159. }
  160. return len(mi.enrollUsers) < mi.config.TotalUser
  161. }
  162. func (mi *matchInfo) isUserEnrolled(userId int) bool {
  163. mi.lockEnroll.RLock()
  164. for _, v := range mi.enrollUsers {
  165. if v.UserId == userId {
  166. mi.lockEnroll.RUnlock()
  167. return true
  168. }
  169. }
  170. mi.lockEnroll.RUnlock()
  171. instance := mi.getCurrentInstance()
  172. if instance == nil {
  173. return false
  174. }
  175. return instance.IsUserEnrolled(userId)
  176. }
  177. func (mi *matchInfo) isUserPlaying(userId int) bool {
  178. if mi.getStatus() == matchbase.MatchStatus_Ended {
  179. return false
  180. }
  181. if !mi.isUserEnrolled(userId) {
  182. return false
  183. }
  184. instance := mi.getCurrentInstance()
  185. // 这里存在比赛还没开始的情况
  186. if instance == nil {
  187. // 超过一轮,看是否在上一轮晋级中
  188. if mi.CurrentRound < 0 {
  189. return true
  190. }
  191. // 在不在晋级列表
  192. for _, v := range mi.currentRoundWinners {
  193. if v.UserId == userId {
  194. return true
  195. }
  196. }
  197. return false
  198. }
  199. userlist := instance.GetUserList()
  200. for _, v := range userlist {
  201. if v == userId {
  202. return true
  203. }
  204. }
  205. return false
  206. }
  207. func (mi *matchInfo) isTimeout() bool {
  208. if !mi.isEnded() {
  209. return false
  210. }
  211. return time.Now().Unix()-mi.getEndTime() >= match_time_out_ended
  212. }
  213. func (mi *matchInfo) dump(isDetail bool) {
  214. log.Release(" MatchId[%d] CurrentRound[%d]", mi.MatchId, mi.CurrentRound)
  215. // 还没开始
  216. if len(mi.Rounds) == 0 {
  217. mi.lockEnroll.RLock()
  218. log.Release(" EnrollUserCount:%d", len(mi.enrollUsers))
  219. if isDetail {
  220. for _, v := range mi.enrollUsers {
  221. log.Release(" UserId:%d Nickname:%s", v.UserId, v.NickName)
  222. }
  223. }
  224. mi.lockEnroll.RUnlock()
  225. return
  226. } else {
  227. log.Release(" EnrollUserCount:%d", len(mi.allUsers))
  228. if isDetail {
  229. for _, v := range mi.allUsers {
  230. log.Release(" UserId:%d Nickname:%s,Score:%d,WinCount:%d,EnrollTime:%d", v.UserId, v.NickName, v.Score, v.WinCount, v.EnrollTime)
  231. }
  232. }
  233. }
  234. for i := 0; i < len(mi.Rounds); i++ {
  235. r := mi.Rounds[i]
  236. if i < len(mi.Rounds)-1 {
  237. log.Release(" Round[%d] MatchNo[%d] StartTime[%s] EndTime[%s]", i, r.MatchNo,
  238. common.TimeStampToString(r.StartTime), common.TimeStampToString(r.EndTime))
  239. continue
  240. }
  241. log.Release(" Round[%d] MatchNo[%d] Status[%s] StartTime[%s] EndTime[%s]", i, r.MatchNo, matchbase.GetMatchStatusDesc(r.getStatus()),
  242. common.TimeStampToString(r.StartTime), common.TimeStampToString(r.EndTime))
  243. }
  244. }
  245. func (mi *matchInfo) checkRobot() {
  246. if mi.getStatus() > matchbase.MatchStatus_Free || mi.robotCount >= mi.robotConfig.Max {
  247. return
  248. }
  249. if mi.isFull() {
  250. return
  251. }
  252. sec := mi.robotConfig.GetWaitSec()
  253. time.AfterFunc(time.Second*time.Duration(sec), mi.checkRobot)
  254. mi.robotCount += mi.mm.addARobot(mi.MatchId)
  255. }
  256. func (mi *matchInfo) sendNotification(userId int, data string) {
  257. if userId == -1 {
  258. // send all
  259. if mi.CurrentRound < 0 {
  260. mi.lockEnroll.RLock()
  261. for _, v := range mi.enrollUsers {
  262. notification.AddNotification(v.UserId, notification.Notification_Match, data)
  263. }
  264. mi.lockEnroll.RUnlock()
  265. } else {
  266. mi.lock.RLock()
  267. for _, v := range mi.allUsers {
  268. notification.AddNotification(v.UserId, notification.Notification_Match, data)
  269. }
  270. mi.lock.RUnlock()
  271. }
  272. return
  273. }
  274. notification.AddNotification(userId, notification.Notification_Match, data)
  275. }
  276. func (mi *matchInfo) setUserFee(userId int, fee item.ItemPack) {
  277. mi.lock.Lock()
  278. mi.userFee[userId] = fee
  279. mi.lock.Unlock()
  280. }
  281. func (mi *matchInfo) getAndRemoveUserFee(userId int) item.ItemPack {
  282. mi.lock.Lock()
  283. defer mi.lock.Unlock()
  284. ret, ok := mi.userFee[userId]
  285. if !ok {
  286. return item.ItemPack{}
  287. }
  288. delete(mi.userFee, userId)
  289. return ret
  290. }
  291. func (mi *matchInfo) isUserAwarded(userId int) bool {
  292. mi.lock.RLock()
  293. defer mi.lock.RUnlock()
  294. ret, ok := mi.userAwarded[userId]
  295. if !ok {
  296. return false
  297. }
  298. return ret
  299. }
  300. func (mi *matchInfo) setUserAwarded(userId int) {
  301. mi.lock.Lock()
  302. defer mi.lock.Unlock()
  303. mi.userAwarded[userId] = true
  304. }
  305. func (mi *matchInfo) getUserList() []int {
  306. instance := mi.getCurrentInstance()
  307. if instance == nil {
  308. return []int{}
  309. }
  310. return instance.GetUserList()
  311. }
  312. func (mi *matchInfo) getEndTime() int64 {
  313. if !mi.isEnded() {
  314. return 0
  315. }
  316. if len(mi.Rounds) <= 0 {
  317. return 0
  318. }
  319. return mi.Rounds[len(mi.Rounds)-1].EndTime
  320. }
  321. func (mi *matchInfo) getStartTime() int64 {
  322. if len(mi.Rounds) == 0 {
  323. return 0
  324. }
  325. return mi.Rounds[0].StartTime
  326. }
  327. func (mi *matchInfo) getMatchNo() int {
  328. if len(mi.Rounds) == 0 {
  329. return 0
  330. }
  331. return mi.Rounds[len(mi.Rounds)-1].MatchNo
  332. }
  333. func (mi *matchInfo) getStartSeconds() int {
  334. nextStartTime := mi.config.GetNextStartTime()
  335. if nextStartTime == 0 {
  336. return 0
  337. }
  338. return nextStartTime - int(time.Now().Unix())
  339. }
  340. func (mi *matchInfo) setStartTime(matchNo int) {
  341. if len(mi.Rounds) == 0 {
  342. log.Release("matchInfo.setStartTime no rounds")
  343. return
  344. }
  345. r := mi.Rounds[len(mi.Rounds)-1]
  346. if r.MatchNo != matchNo {
  347. log.Release("matchInfo.setStartTime MatchNo not match %d != %d", r.MatchNo, matchNo)
  348. return
  349. }
  350. r.StartTime = time.Now().Unix()
  351. if mi.CurrentRound == 0 {
  352. instance := mi.getCurrentInstance()
  353. if instance == nil {
  354. return
  355. }
  356. mi.allUsers = instance.GetAllMatchUsers()
  357. // 赋值enrollTime
  358. for k, v := range mi.allUsers {
  359. go task.DoTaskAction(v.UserId, task.TaskAction_playComboMatch, 1, task.TaskScope{})
  360. for _, v1 := range mi.enrollUsers {
  361. if v.UserId == v1.UserId {
  362. mi.allUsers[k].EnrollTime = v1.EnrollTime
  363. break
  364. }
  365. }
  366. }
  367. }
  368. }
  369. func (mi *matchInfo) setEndTime(matchNo int) {
  370. if len(mi.Rounds) == 0 {
  371. log.Release("matchInfo.setEndTime no rounds")
  372. return
  373. }
  374. r := mi.Rounds[len(mi.Rounds)-1]
  375. if r.MatchNo != matchNo {
  376. log.Release("matchInfo.setEndTime MatchNo not match %d != %d", r.MatchNo, matchNo)
  377. return
  378. }
  379. r.EndTime = time.Now().Unix()
  380. }
  381. func (mi *matchInfo) getAllMatchUsers() []matchbase.MatchUser {
  382. return mi.allUsers
  383. }
  384. func (mi *matchInfo) addMatchInfo(matchNo int, matchInstance matchbase.MatchInstance) {
  385. mi.CurrentRound++
  386. mi.Rounds = append(mi.Rounds, &matchround{
  387. MatchType: mi.config.getMatchType(mi.CurrentRound),
  388. MatchNo: matchNo,
  389. matchInstance: matchInstance,
  390. createTime: time.Now().Unix(),
  391. })
  392. //mi.currentRoundWinners = []int{}
  393. }
  394. func (mi *matchInfo) getMatchUser(userId int) *matchbase.MatchUser {
  395. for _, v := range mi.allUsers {
  396. if v.UserId == userId {
  397. return &v
  398. }
  399. }
  400. return nil
  401. }
  402. func (mi *matchInfo) getCurrentMatchType() int {
  403. if mi.CurrentRound == -1 {
  404. return matchbase.MatchType_Invalid
  405. }
  406. return mi.config.getMatchType(mi.CurrentRound)
  407. }
  408. func (mi *matchInfo) setRoundWinners(winners []int) {
  409. ni := ComboMatch_notificationInfo{Msg: ComboMatch_noti_promoted}
  410. d, _ := json.Marshal(ni)
  411. data := string(d)
  412. for _, v := range winners {
  413. mi.sendNotification(v, data)
  414. usr := mi.getMatchUser(v)
  415. if usr != nil {
  416. usr.Score = mi.config.getScorePercentToNext(mi.CurrentRound) * usr.Score / 100
  417. if usr.Score < 0 {
  418. usr.Score = 0
  419. }
  420. }
  421. mi.currentRoundWinners = append(mi.currentRoundWinners, *usr)
  422. }
  423. }
  424. func (mi *matchInfo) onMatchLocked(secsToStart int, finalCall bool) {
  425. if !mi.config.IsPreEnroll() {
  426. return
  427. }
  428. log.Debug("combomatch.matchInfo.onMatchLocked matchId[%d] secsToStart[%d]", mi.MatchId, secsToStart)
  429. mi.lockEnroll.Lock()
  430. totalUser := len(mi.enrollUsers)
  431. for i := 0; i < totalUser; i++ {
  432. mi.enrollUsers[i].confirmed = mi.enrollUsers[i].isRobot
  433. }
  434. mi.lockEnroll.Unlock()
  435. /*
  436. // 最后一次如果不满人,则不通知
  437. if totalUser < mi.config.EnrollMin && secsToStart <= mi.config.EnrollRange.End {
  438. mi.onMatchStart()
  439. return
  440. }
  441. */
  442. //postMatchLockNotification(mi.MatchId, secsToStart)
  443. callMsg := ComboMatch_noti_matchcall
  444. if finalCall {
  445. callMsg = ComboMatch_noti_matchlocked
  446. }
  447. ni := ComboMatch_notificationInfo{Msg: callMsg, MatchId: mi.MatchId, Seconds: secsToStart}
  448. d, _ := json.Marshal(ni)
  449. mi.sendNotification(-1, string(d))
  450. }
  451. func (mi *matchInfo) onMatchStart() {
  452. log.Debug("combomatch.matchInfo.onMatchStart matchId[%d]", mi.MatchId)
  453. // 把没有confirm的人清理
  454. var toRemove []int
  455. mi.lockEnroll.Lock()
  456. for i := 0; i < len(mi.enrollUsers); {
  457. if mi.enrollUsers[i].confirmed {
  458. i++
  459. } else {
  460. log.Debug(" removing unconfirmed user[%d]", mi.enrollUsers[i].UserId)
  461. toRemove = append(toRemove, mi.enrollUsers[i].UserId)
  462. mi.enrollUsers = append(mi.enrollUsers[:i], mi.enrollUsers[i+1:]...)
  463. }
  464. }
  465. mi.lockEnroll.Unlock()
  466. for _, v := range toRemove {
  467. // 退还报名费
  468. mi.quitUser(v, false)
  469. }
  470. // 看下人数够不够
  471. mi.lockEnroll.RLock()
  472. totalUser := len(mi.enrollUsers)
  473. mi.lockEnroll.RUnlock()
  474. // 如果是提前报名的,并且人数不够下限,则不开始比赛
  475. if totalUser < mi.config.EnrollMin && mi.config.IsPreEnroll() {
  476. mi.startFailed = true
  477. //postMatchFailedNotification(mi.MatchId)
  478. ni := ComboMatch_notificationInfo{Msg: ComboMatch_noti_matchfailed, MatchId: mi.MatchId}
  479. d, _ := json.Marshal(ni)
  480. mi.sendNotification(-1, string(d))
  481. mi.quitAllUsers()
  482. return
  483. }
  484. config := mi.config
  485. var matchNo int
  486. var err string
  487. var matchInstance matchbase.MatchInstance
  488. switch config.getMatchType(0) {
  489. case matchbase.MatchType_SimpleMatch:
  490. matchNo, err = simplematch.CreateMatch(-1, config.GameId, config.GameRule, totalUser, config.getTarget(0),
  491. config.TableUser, 0, 0, config.PlayTime, config.isEleminateByScore(mi.CurrentRound), config.getWinnerCount(0))
  492. if matchNo == 0 {
  493. log.Release("combomatchMgr.getOrCreateMatch simplematch.CreateMatch failed %s", err)
  494. return
  495. }
  496. matchInstance = simplematch.GetMatchInstance(matchNo)
  497. case matchbase.MatchType_PointMatch:
  498. e, ssec, sscore, w := config.getPointMatchParams(0)
  499. matchNo, err = pointmatch.CreateMatch(-1, config.GameId, config.GameRule, totalUser, config.TableUser, 0, 0, config.PlayTime,
  500. e, ssec, sscore, w)
  501. if matchNo == 0 {
  502. log.Release("combomatchMgr.getOrCreateMatch pointmatch.CreateMatch failed %s", err)
  503. return
  504. }
  505. matchInstance = pointmatch.GetMatchInstance(matchNo)
  506. case matchbase.MatchType_SetsMatch:
  507. matchNo, err = setsmatch.CreateMatch(-1, config.GameId, config.GameRule, totalUser, config.TableUser,
  508. 0, 0, config.PlayTime, config.getWinnerCount(0))
  509. if matchNo == 0 {
  510. log.Release("combomatchMgr.getOrCreateMatch setsmatch.CreateMatch failed %s", err)
  511. return
  512. }
  513. matchInstance = setsmatch.GetMatchInstance(matchNo)
  514. }
  515. if matchInstance == nil {
  516. log.Release("combomatchMgr.getOrCreateMatch GetMatchInstance == nil")
  517. return
  518. }
  519. matchInstance.RegisterReceiver(mi.mm)
  520. mi.CurrentRound = -1
  521. mi.addMatchInfo(matchNo, matchInstance)
  522. // 把报名玩家都拉进去
  523. mi.lockEnroll.RLock()
  524. for _, v := range mi.enrollUsers {
  525. switch mi.getCurrentMatchType() {
  526. case matchbase.MatchType_SimpleMatch:
  527. simplematch.EnrollMatch(v.UserId, v.NickName, v.FaceId, v.FaceUrl, matchNo)
  528. case matchbase.MatchType_PointMatch:
  529. pointmatch.EnrollMatch(v.UserId, v.NickName, v.FaceId, v.FaceUrl, matchNo)
  530. case matchbase.MatchType_SetsMatch:
  531. setsmatch.EnrollMatch(v.UserId, v.NickName, v.FaceId, v.FaceUrl, matchNo)
  532. }
  533. }
  534. mi.lockEnroll.RUnlock()
  535. }
  536. func (mi *matchInfo) quitAllUsers() {
  537. for _, v := range mi.enrollUsers {
  538. mi.quitUser(v.UserId, false)
  539. }
  540. mi.lockEnroll.Lock()
  541. mi.enrollUsers = []combomatchuser{}
  542. mi.lockEnroll.Unlock()
  543. }
  544. func (mi *matchInfo) quitUser(userId int, check bool) bool {
  545. config := mi.config
  546. if check {
  547. found := false
  548. mi.lockEnroll.Lock()
  549. for i := 0; i < len(mi.enrollUsers); {
  550. if mi.enrollUsers[i].UserId == userId {
  551. found = true
  552. mi.enrollUsers = append(mi.enrollUsers[:i], mi.enrollUsers[i+1:]...)
  553. } else {
  554. i++
  555. }
  556. }
  557. mi.lockEnroll.Unlock()
  558. if !found {
  559. log.Release("combomatch.matchInfo.quitUser[%d] not found in matchId[%d]", userId, mi.MatchId)
  560. return false
  561. }
  562. }
  563. mi.config.addOnline(-1)
  564. // 如果比赛已开始
  565. status := mi.getStatus()
  566. if status == matchbase.MatchStatus_Playing {
  567. // 按淘汰处理
  568. // ...
  569. }
  570. // 退钱
  571. if len(config.EnrollFee) > 0 && status < matchbase.MatchStatus_Playing {
  572. itm := mi.getAndRemoveUserFee(userId)
  573. if itm.Count > 0 {
  574. inventory.AddItems(userId, []item.ItemPack{itm},
  575. "combomatch fee return", common.LOGTYPE_COMBOMATCH_ENTER_RETURN)
  576. } else if config.DailyFreeCount > 0 {
  577. getFreeCountManager().reduceFreeCount(userId, config.MatchId)
  578. }
  579. }
  580. //postUserExitNotification(userId, mi.MatchId)
  581. ni := ComboMatch_notificationInfo{Msg: ComboMatch_noti_userexit, UserId: userId, MatchId: mi.MatchId}
  582. d, _ := json.Marshal(ni)
  583. mi.sendNotification(-1, string(d))
  584. return true
  585. }
  586. func (mi *matchInfo) addEnrollUser(userId int, nickname string, faceId int, faceUrl string, isRobot bool) {
  587. if mi.isUserEnrolled(userId) {
  588. log.Release("combomatch.addEnrollUser user[%d] already exist in matchId[%d]", userId, mi.MatchId)
  589. return
  590. }
  591. found := false
  592. for k, v := range mi.enrolledUsers {
  593. if v.UserId == userId {
  594. mi.enrolledUsers[k].EnrollTime = time.Now().Unix()
  595. found = true
  596. break
  597. }
  598. }
  599. if !found {
  600. mi.enrolledUsers = append(mi.enrolledUsers, matchbase.EnrollUser{UserId: userId, EnrollTime: time.Now().Unix()})
  601. }
  602. var usr combomatchuser
  603. usr.UserId = userId
  604. usr.NickName = nickname
  605. usr.FaceId = faceId
  606. usr.FaceUrl = faceUrl
  607. usr.EnrollTime = time.Now().Unix()
  608. usr.isRobot = isRobot
  609. usr.confirmed = true
  610. mi.lockEnroll.Lock()
  611. mi.enrollUsers = append(mi.enrollUsers, usr)
  612. userCount := len(mi.enrollUsers)
  613. mi.lockEnroll.Unlock()
  614. mi.config.addOnline(1)
  615. // 如果报名人满,则开始比赛,预报名除外
  616. if userCount == mi.config.TotalUser && !mi.config.IsPreEnroll() {
  617. mi.onMatchStart()
  618. }
  619. }
  620. func (mi *matchInfo) getMatchInfo() string {
  621. var ret struct {
  622. CurrentRound int
  623. Users []matchbase.MatchUser
  624. }
  625. ret.CurrentRound = mi.CurrentRound
  626. if mi.CurrentRound < 0 {
  627. for i := 0; i < len(mi.enrollUsers); i++ {
  628. ret.Users = append(ret.Users, mi.enrollUsers[i].MatchUser)
  629. }
  630. } else {
  631. ret.Users = mi.allUsers
  632. }
  633. d, _ := json.Marshal(ret)
  634. return string(d)
  635. }
  636. func (mi *matchInfo) getOnline() int {
  637. return len(mi.allUsers)
  638. }
  639. func (mi *matchInfo) updateRankAndScore(userId, rank, score int, winCount int) {
  640. for i := 0; i < len(mi.allUsers); i++ {
  641. if mi.allUsers[i].UserId == userId {
  642. mi.allUsers[i].Rank = rank
  643. mi.allUsers[i].Score = score
  644. mi.allUsers[i].WinCount += winCount
  645. return
  646. }
  647. }
  648. log.Release("combomatch.matchInfo.updateRankAndScore MatchNo[%d] UserId [%d] not found", mi.getMatchNo(), userId)
  649. }
  650. func (mi *matchInfo) getUser(userId int) *matchbase.MatchUser {
  651. ci := mi.getCurrentInstance()
  652. if ci != nil {
  653. return ci.GetUser(userId)
  654. }
  655. return nil
  656. }
  657. func (mi *matchInfo) postUserRankNotification(userId int, rank int, items []item.ItemPack) {
  658. ni := ComboMatch_notificationInfo{Msg: ComboMatch_noti_rank, UserId: userId, MatchId: mi.MatchId, Rank: rank, Prize: items}
  659. d, _ := json.Marshal(ni)
  660. mi.sendNotification(userId, string(d))
  661. }
  662. func (mi *matchInfo) getConfirmCount() int {
  663. ret := 0
  664. mi.lock.RLock()
  665. for _, v := range mi.enrollUsers {
  666. if v.confirmed {
  667. ret++
  668. }
  669. }
  670. mi.lock.RUnlock()
  671. return ret
  672. }
  673. func (mi *matchInfo) confirmMatch(userId int) bool {
  674. mi.lock.Lock()
  675. defer mi.lock.Unlock()
  676. for k, v := range mi.enrollUsers {
  677. if v.UserId == userId {
  678. mi.enrollUsers[k].confirmed = true
  679. return true
  680. }
  681. }
  682. return false
  683. }
  684. func (mi *matchInfo) tryRemoveOneRobot(forceRemove bool) bool {
  685. var robotId int
  686. realUserCount := 0
  687. totalUserCount := 0
  688. mi.lock.RLock()
  689. totalUserCount = len(mi.enrollUsers)
  690. for _, v := range mi.enrollUsers {
  691. if !v.isRobot {
  692. realUserCount++
  693. } else {
  694. if robotId == 0 {
  695. robotId = v.UserId
  696. }
  697. }
  698. }
  699. mi.lock.RUnlock()
  700. if !forceRemove {
  701. if totalUserCount < 5 || realUserCount > 0 {
  702. return false
  703. }
  704. }
  705. if robotId > 0 {
  706. return mi.quitUser(robotId, true)
  707. }
  708. return false
  709. }