singlematchinstance.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. package singlematch
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "math/rand"
  6. "sort"
  7. "time"
  8. "bet24.com/log"
  9. "bet24.com/servers/common"
  10. item "bet24.com/servers/micros/item_inventory/proto"
  11. "bet24.com/servers/micros/matches/handler/matchbase"
  12. pb "bet24.com/servers/micros/matches/proto"
  13. notification "bet24.com/servers/micros/notification/proto"
  14. privateroom "bet24.com/servers/micros/privateroom/proto"
  15. robot "bet24.com/servers/micros/userservices/proto"
  16. userservices "bet24.com/servers/micros/userservices/proto"
  17. )
  18. // 单个用户实例
  19. type singlematchinstance struct {
  20. pb.SingleMatchInfo
  21. matchConfig *SingleMatchConfig
  22. robotScores []int
  23. roomNo int
  24. tmpRobotIds []int
  25. reviveBeginTime int
  26. }
  27. func newMatchInstance(userId int, matchConfig *SingleMatchConfig) *singlematchinstance {
  28. ret := new(singlematchinstance)
  29. ret.MatchId = matchConfig.MatchId
  30. ret.UserId = userId
  31. ret.matchConfig = matchConfig
  32. ret.StartTime = time.Now().Unix()
  33. ret.tmpRobotIds = make([]int, matchConfig.TableUser-1)
  34. ret.Rank = 1
  35. for _, v := range matchConfig.Rounds {
  36. ret.RoundInfo = append(ret.RoundInfo, v.SingleMatchRoundInfo)
  37. }
  38. return ret
  39. }
  40. func (si *singlematchinstance) dump() {
  41. log.Release(" UserId[%d]MatchId[%d]Round[%d]Score[%d]Rank[%d]",
  42. si.UserId, si.MatchId, si.RoundIndex, si.UserScore, si.Rank)
  43. }
  44. func (si *singlematchinstance) isEnded() bool {
  45. return si.EndTime > 0
  46. }
  47. func (si *singlematchinstance) startRound() {
  48. // 读取配置
  49. round := si.matchConfig.getRound(si.RoundIndex)
  50. if round == nil {
  51. log.Release("singlematchinstance.startRound[%d] round not found,end", si.RoundIndex)
  52. // come to end
  53. si.endMatch()
  54. return
  55. }
  56. si.RoundIndex = round.Index
  57. if len(si.robotScores) == 0 {
  58. // 第一轮,创建虚拟分数
  59. for i := 0; i < round.TotalUser-1; i++ {
  60. si.robotScores = append(si.robotScores, si.matchConfig.InitScore)
  61. }
  62. si.UserScore = si.matchConfig.InitScore
  63. } else {
  64. // 先删除已淘汰的分数
  65. if round.TotalUser-1 < len(si.robotScores) {
  66. si.robotScores = si.robotScores[:round.TotalUser-1]
  67. }
  68. // 后面轮次,分数衰减
  69. for i := 0; i < len(si.robotScores); i++ {
  70. if si.robotScores[i] > 0 {
  71. si.robotScores[i] = si.robotScores[i] * (100 - si.matchConfig.ScoreShrinkPercent) / 100
  72. }
  73. }
  74. if si.UserScore > 0 {
  75. si.UserScore = si.UserScore * (100 - si.matchConfig.ScoreShrinkPercent) / 100
  76. }
  77. }
  78. // 打乱机器人分数
  79. si.shuffleRobotScores()
  80. // 计算所有机器人的分数
  81. si.calRobotScores()
  82. // 给玩家创建一个房间
  83. si.createPrivateRoom(round)
  84. }
  85. func (si *singlematchinstance) endMatch() {
  86. if si.UserId == 0 {
  87. log.Debug("singlematchinstance endMatch UserId cleared")
  88. return
  89. }
  90. // 比赛结束,可能是输也可能是赢
  91. // 发放奖励
  92. prize := si.matchConfig.getFinalPrize(si.Rank)
  93. if len(prize) > 0 {
  94. item.AddItems(si.UserId, prize,
  95. "singlematch prize", common.LOGTYPE_SINGLEMATCH_PRIZE)
  96. }
  97. if si.Rank == 1 {
  98. userservices.DoRecord(si.UserId, userservices.Record_LadderWinCount, 1)
  99. }
  100. si.postUserRankNotification(prize)
  101. // 处理完后,清理用户ID
  102. log.Debug("singlematchinstance endMatch clear UserId[%d] robotScores%v", si.UserId, si.robotScores)
  103. si.dump()
  104. si.UserId = 0
  105. si.EndTime = time.Now().Unix()
  106. }
  107. func (si *singlematchinstance) shuffleRobotScores() {
  108. rand.Shuffle(len(si.robotScores), func(i, j int) {
  109. si.robotScores[i], si.robotScores[j] = si.robotScores[j], si.robotScores[i]
  110. })
  111. }
  112. func (si *singlematchinstance) calRobotScores() {
  113. startIndex := si.matchConfig.TableUser - 1
  114. groupCount := (len(si.robotScores) - startIndex) / si.matchConfig.TableUser
  115. for i := 0; i < groupCount; i++ {
  116. // 随机一个分数
  117. score := si.matchConfig.getRandomScore(si.RoundIndex)
  118. si.robotScores[startIndex+i*si.matchConfig.TableUser] += score
  119. si.robotScores[startIndex+i*si.matchConfig.TableUser+1] += score
  120. si.robotScores[startIndex+i*si.matchConfig.TableUser+2] -= score
  121. si.robotScores[startIndex+i*si.matchConfig.TableUser+3] -= score
  122. }
  123. }
  124. func (si *singlematchinstance) createPrivateRoom(round *RoundInfo) {
  125. var err string
  126. si.roomNo, err = privateroom.CreatePrivateRoomByUser(-1, si.matchConfig.GameId, si.matchConfig.GameRule,
  127. -round.SetCount, si.matchConfig.TableUser,
  128. 0, 0, privateroom.RoomType_SimpleMatch, si.matchConfig.PlayTime, false, "")
  129. if si.roomNo == 0 {
  130. log.Release("singlematchinstance.createPrivateRoom failed UserId[%d],err[%s]", si.UserId, err)
  131. si.endMatch()
  132. return
  133. }
  134. // 把玩家拉入房间
  135. go si.arrangeUser(round)
  136. }
  137. func (si *singlematchinstance) arrangeUser(round *RoundInfo) {
  138. // 找出机器人
  139. userIds := make([]int, si.matchConfig.TableUser)
  140. userScores := make([]int, si.matchConfig.TableUser)
  141. for i := 0; i < len(userIds)-1; i++ {
  142. userIds[i] = robot.GetARobot()
  143. userScores[i] = si.robotScores[i]
  144. si.tmpRobotIds[i] = userIds[i]
  145. }
  146. userIds[si.matchConfig.TableUser-1] = si.UserId
  147. userScores[si.matchConfig.TableUser-1] = si.UserScore
  148. for k, v := range userIds {
  149. usr := userservices.GetUserInfo(v)
  150. if usr == nil {
  151. log.Release("singlematchinstance.arrangeUser userId[%d] not found", v)
  152. continue
  153. }
  154. var ret struct {
  155. ErrMsg string
  156. ServerAddr string
  157. TableId int
  158. ChairId int
  159. }
  160. retString := privateroom.UserRequestSit(si.roomNo,
  161. userIds[k], usr.NickName, usr.FaceId, usr.FaceUrl, k, userScores[k], round.BaseScore, si.RoundIndex)
  162. if err := json.Unmarshal([]byte(retString), &ret); err != nil {
  163. log.Release("singlematchinstance.arrangeUser try enter privateroom unmarshal failed %v", err)
  164. return
  165. }
  166. if ret.ServerAddr == "" {
  167. log.Release("singlematchinstance.arrangeUser try enter privateroom failed %s", ret.ErrMsg)
  168. return
  169. }
  170. log.Debug("singlematchinstance.arrangeUser user[%d] enter room %d chair %d",
  171. userIds[k], si.roomNo, ret.ChairId)
  172. sec := 60
  173. if k != len(userIds)-1 {
  174. sec = 5
  175. } else {
  176. go si.postUserMatchRoomNotification(usr.UserId, ret.ServerAddr, ret.TableId, ret.ChairId)
  177. }
  178. privateroom.ForceUserEnter(userIds[k], si.roomNo, ret.ChairId, sec)
  179. }
  180. }
  181. func (si *singlematchinstance) postUserMatchRoomNotification(userId int, serverAddr string, tableId, chairId int) {
  182. // 发送给所有人
  183. ni := matchbase.Match_notificationInfo{Msg: matchbase.Match_noti_matchroom, ServerAddr: serverAddr, TableId: tableId, ChairId: chairId}
  184. d, _ := json.Marshal(ni)
  185. notification.AddNotification(userId, notification.Notification_Match, string(d))
  186. }
  187. func (si *singlematchinstance) postUserRankNotification(prize []item.ItemPack) {
  188. ni := matchbase.Match_notificationInfo{Msg: matchbase.Match_noti_rank,
  189. UserId: si.UserId, Rank: si.Rank, PrizeItems: prize}
  190. d, _ := json.Marshal(ni)
  191. notification.AddNotification(si.UserId, notification.Notification_Match, string(d))
  192. }
  193. func (si *singlematchinstance) postUserPromotedNotification(prize []item.ItemPack) {
  194. matchData, _ := json.Marshal(si)
  195. ni := matchbase.Match_notificationInfo{
  196. Msg: matchbase.Match_noti_promoted,
  197. PrizeItems: prize,
  198. MatchData: string(matchData),
  199. }
  200. d, _ := json.Marshal(ni)
  201. notification.AddNotification(si.UserId, notification.Notification_Match, string(d))
  202. }
  203. func (si *singlematchinstance) postUserEliminatedNotification(reviveCost item.ItemPack) {
  204. ni := matchbase.Match_notificationInfo{Msg: matchbase.Match_noti_eliminated, ReviveCost: reviveCost, ReviveTimeoutSec: si.matchConfig.ReviveTimeoutSec}
  205. d, _ := json.Marshal(ni)
  206. notification.AddNotification(si.UserId, notification.Notification_Match, string(d))
  207. }
  208. func (si *singlematchinstance) onRoomEnd(roomNo int, winners []int) bool {
  209. if roomNo != si.roomNo {
  210. return false
  211. }
  212. log.Debug("singlematchinstance onRoomEnd %d", roomNo)
  213. round := si.matchConfig.getRound(si.RoundIndex)
  214. if round == nil {
  215. log.Release("singlematchinstance.onRoomEnd failed round of index[%d] not found", si.RoundIndex)
  216. return true
  217. }
  218. isWinner := false
  219. for i := 0; i < len(winners); i++ {
  220. if winners[i] == si.UserId {
  221. isWinner = true
  222. break
  223. }
  224. }
  225. if isWinner {
  226. si.WinCount++
  227. } else {
  228. si.LoseCount++
  229. }
  230. // 如果是按输赢淘汰
  231. if round.EliminatByLose {
  232. if !isWinner {
  233. si.onUserEleminated(round)
  234. } else {
  235. si.onUserPromoted(false)
  236. }
  237. return true
  238. }
  239. // 本轮结束了
  240. // 分数已经刷新,获取一下玩家的名次
  241. sort.Slice(si.robotScores, func(i, j int) bool {
  242. return si.robotScores[i] > si.robotScores[j]
  243. })
  244. // 看下我的名次
  245. var userRank int
  246. for i := 0; i < len(si.robotScores); i++ {
  247. if si.UserScore >= si.robotScores[i] {
  248. userRank = i + 1
  249. break
  250. }
  251. }
  252. if userRank == 0 {
  253. userRank = len(si.robotScores)
  254. }
  255. si.Rank = userRank
  256. log.Debug("UserId[%d]Score[%d]Rank[%d]robotScores%v", si.UserId, si.UserScore, userRank, si.robotScores)
  257. if si.matchConfig.isFinalRound(si.RoundIndex) {
  258. si.endMatch()
  259. return true
  260. }
  261. // 是否被淘汰?
  262. if userRank >= round.TotalUser-round.EliminatUser {
  263. si.onUserEleminated(round)
  264. } else {
  265. si.onUserPromoted(false)
  266. }
  267. return true
  268. }
  269. func (si *singlematchinstance) onRoomStart(roomNo int) {
  270. if roomNo != si.roomNo {
  271. return
  272. }
  273. }
  274. // 游戏结束后先同步分数,再调用onRoomEnd
  275. func (si *singlematchinstance) onRoomUserScoreChanged(roomNo int, userId int, score int) bool {
  276. if roomNo != si.roomNo {
  277. return false
  278. }
  279. log.Debug("singlematchinstance onRoomUserScoreChanged %d userId[%d]score[%d]", roomNo, userId, score)
  280. if userId == si.UserId {
  281. si.UserScore += score
  282. return true
  283. }
  284. // 机器人
  285. for i := 0; i < len(si.tmpRobotIds); i++ {
  286. if userId == si.tmpRobotIds[i] {
  287. si.robotScores[i] += score
  288. return true
  289. }
  290. }
  291. log.Release("singlematchinstance.onRoomUserScoreChanged roomNo[%d],userId[%d],score[%d] not handled", roomNo, userId, score)
  292. return true
  293. }
  294. func (si *singlematchinstance) onUserEleminated(round *RoundInfo) {
  295. if round.ReviveCost.Count == 0 || si.Revived {
  296. si.endMatch()
  297. return
  298. }
  299. // 等待玩家购买晋级
  300. time.AfterFunc(time.Second*time.Duration(si.matchConfig.ReviveTimeoutSec), si.onEleminatedTimeout)
  301. si.reviveBeginTime = int(time.Now().Unix())
  302. si.postUserEliminatedNotification(round.ReviveCost)
  303. si.ReviveCost = round.ReviveCost
  304. }
  305. func (si *singlematchinstance) onEleminatedTimeout() {
  306. si.ReviveCost.ItemId = 0
  307. si.ReviveCost.Count = 0
  308. si.ReviveSecs = 0
  309. // 已经选择复活或者已经结束
  310. if si.Revived || si.UserId == 0 {
  311. return
  312. }
  313. si.endMatch()
  314. }
  315. func (si *singlematchinstance) onUserPromoted(revive bool) {
  316. round := si.matchConfig.getRound(si.RoundIndex + 1)
  317. // 是否最后一轮
  318. if si.matchConfig.isFinalRound(si.RoundIndex) || round == nil {
  319. si.endMatch()
  320. return
  321. }
  322. si.RoundIndex++
  323. prize := round.Prize
  324. if revive {
  325. prize = nil
  326. }
  327. // 发送奖品
  328. if len(prize) > 0 {
  329. item.AddItems(si.UserId, prize,
  330. "singlematch prize", common.LOGTYPE_SINGLEMATCH_PRIZE)
  331. }
  332. si.postUserPromotedNotification(prize)
  333. time.AfterFunc(10*time.Second, si.startRound)
  334. }
  335. func (si *singlematchinstance) isTimeout() bool {
  336. now := time.Now().Unix()
  337. if si.EndTime > 0 {
  338. return now-si.EndTime > 600
  339. }
  340. if now-si.StartTime > 3600 {
  341. log.Release("singlematchinstance isTimeout 异常超时 ")
  342. si.dump()
  343. return true
  344. }
  345. return false
  346. }
  347. func (si *singlematchinstance) noRevive() bool {
  348. if si.UserId == 0 || si.EndTime > 0 {
  349. log.Release("singlematchinstance.noRevive userId[%d] ended", si.UserId)
  350. return false
  351. }
  352. si.onEleminatedTimeout()
  353. return true
  354. }
  355. func (si *singlematchinstance) revive() bool {
  356. if si.UserId == 0 || si.EndTime > 0 {
  357. log.Release("singlematchinstance.revive userId[%d] ended", si.UserId)
  358. return false
  359. }
  360. if si.Revived {
  361. log.Release("singlematchinstance.revive userId[%d] Revived", si.UserId)
  362. return false
  363. }
  364. si.ReviveCost.ItemId = 0
  365. si.ReviveCost.Count = 0
  366. si.ReviveSecs = 0
  367. round := si.matchConfig.getRound(si.RoundIndex)
  368. if round == nil || round.ReviveCost.Count == 0 {
  369. log.Release("singlematchinstance.revive userId[%d] Unable to revive", si.UserId)
  370. return false
  371. }
  372. ok, err := item.Consume(si.UserId, round.ReviveCost.ItemId,
  373. common.LOGTYPE_SINGLEMATCH_REVIVE, round.ReviveCost.Count, 0)
  374. if !ok {
  375. errMsg := fmt.Sprintf("not enough fee[%v] for revive err = %s", round.ReviveCost, err)
  376. log.Release("singlematchmanager.revive failed %s", errMsg)
  377. return false
  378. }
  379. // 分数
  380. si.UserScore = si.getAverageScore()
  381. si.Revived = true
  382. si.onUserPromoted(true)
  383. return true
  384. }
  385. func (si *singlematchinstance) getAverageScore() int {
  386. if len(si.robotScores) == 0 {
  387. return 0
  388. }
  389. total := 0
  390. for _, v := range si.robotScores {
  391. total += v
  392. }
  393. return total / len(si.robotScores)
  394. }
  395. func (si *singlematchinstance) calReviveTimeout() int {
  396. if si.reviveBeginTime == 0 {
  397. return 0
  398. }
  399. ret := si.matchConfig.ReviveTimeoutSec - (int(time.Now().Unix()) - si.reviveBeginTime)
  400. if ret < 0 {
  401. ret = 0
  402. }
  403. return ret
  404. }