tablesink_impl.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. package gamelogic
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "time"
  6. "bet24.com/log"
  7. "bet24.com/servers/games/greedy/common"
  8. "bet24.com/servers/games/greedy/config"
  9. userservices "bet24.com/servers/micros/userservices/proto"
  10. waterpool "bet24.com/servers/micros/waterpool/proto"
  11. "bet24.com/servers/user"
  12. "bet24.com/utils"
  13. )
  14. func (ts *tablesink) startPeriod() {
  15. delaySecond := 0
  16. switch ts.roomInfo.State {
  17. case common.GameState_Open:
  18. delaySecond = ts.roomInfo.BetTime
  19. ts.roomInfo.TotalBet = 0
  20. ts.roomInfo.State = common.GameState_Bet
  21. case common.GameState_Bet:
  22. common.ResetWinningOdds(ts.roomInfo.Odds)
  23. common.ResetBetProbability(ts.roomInfo.Probability)
  24. delaySecond = ts.roomInfo.OpenTime
  25. ts.roomInfo.State = common.GameState_Open
  26. default:
  27. log.Release("startPeriod failed state = %d", ts.roomInfo.State)
  28. return
  29. }
  30. ts.LastStateTick = time.Now()
  31. ts.onStateChanged()
  32. if ts.closeGame && ts.roomInfo.State == common.GameState_Open {
  33. //开奖后关闭房间
  34. log.Debug("tablesink.startPeriod Close the room after the open draw %v:%d,State = %d noPlayerCount= %d ", ts.roomInfo.RoomName, delaySecond, ts.roomInfo.State, ts.noPlayerCount)
  35. // 关闭游戏
  36. ts.stopPeriodPoll()
  37. return
  38. }
  39. ts.timer = time.AfterFunc(time.Duration(delaySecond)*time.Second, ts.startPeriod)
  40. log.Debug("tablesink.startPeriod %v:%d,State = %d", ts.roomInfo.RoomName, delaySecond, ts.roomInfo.State)
  41. }
  42. // 停止定时器的
  43. func (ts *tablesink) stopPeriodPoll() {
  44. if ts.timer != nil {
  45. ts.timer.Stop()
  46. }
  47. if ts.noPlayerCount >= 6 {
  48. ts.table.PrivateRoomSetWinners(nil)
  49. }
  50. }
  51. func (ts *tablesink) getStateSecond() int {
  52. return int(time.Since(ts.LastStateTick).Seconds())
  53. }
  54. func (ts *tablesink) onStateChanged() {
  55. if ts.roomInfo.State == common.GameState_Bet {
  56. ts.lock.Lock()
  57. ts.userBetList = make(map[int][]common.Bet)
  58. ts.roomInfo.SerialNumber++
  59. //初始化8个区域
  60. max := int(common.BidTypeMax)
  61. betCounts := make([]int, max)
  62. diamondHots := make([]int, max)
  63. areaPopular := common.AreaPopular{
  64. MostPopular: -1,
  65. BetCount: betCounts,
  66. DiamondHot: diamondHots,
  67. }
  68. ts.areaPopular = &areaPopular
  69. ts.hotDogAmount = 0
  70. ts.kebabAmount = 0
  71. ts.chickenLegAmount = 0
  72. ts.meatSliceAmount = 0
  73. ts.radishAmount = 0
  74. ts.cornAmount = 0
  75. ts.spinachAmount = 0
  76. ts.tomatoAmount = 0
  77. ts.lock.Unlock()
  78. log.Debug("onStateChanged SerialNumber = %d", ts.roomInfo.SerialNumber)
  79. go ts.arrangeRobotActions()
  80. }
  81. // 如果是开奖阶段
  82. if ts.roomInfo.State == common.GameState_Open {
  83. ts.enterOpenState(false)
  84. go ts.stopRobotPoll()
  85. }
  86. ts.table.NotifySceneChanged(-1)
  87. //ts.refreshRoomInfo()
  88. }
  89. func (ts *tablesink) getStateData() string {
  90. var state common.GameState
  91. state.Sec = ts.getStateSecond()
  92. ts.lock.RLock()
  93. state.State = ts.roomInfo.State
  94. ts.roomInfo.StateSec = state.Sec
  95. state.SpinResult = ts.spinResult
  96. state.Historys = ts.roomInfo.Historys
  97. state.TableUser = *ts.score_users
  98. state.AreaPopular = *ts.areaPopular
  99. state.SerialNumber = ts.roomInfo.SerialNumber
  100. if state.State == common.GameState_Bet {
  101. for _, v := range ts.userBetList {
  102. state.BetCmds = append(state.BetCmds, v...)
  103. }
  104. }
  105. state.CloseGame = ts.closeGame
  106. ts.lock.RUnlock()
  107. data, _ := json.Marshal(state)
  108. return string(data)
  109. }
  110. func (ts *tablesink) isCanBet(bet common.Bet) (bool, string) {
  111. amount := bet.Amount
  112. userId := bet.UserId
  113. betId := bet.BetId
  114. if ts.roomInfo.State != common.GameState_Bet {
  115. return false, "Bet failed,Please wait for next round"
  116. }
  117. if amount < ts.roomInfo.MinBet || amount > ts.roomInfo.MaxBet {
  118. return false, "Bet amount exceeded"
  119. }
  120. bidType := common.BetOption(betId)
  121. if bidType >= common.BidTypeMax || bidType < common.HotDog {
  122. log.Release("isCanBet invalid betting area %d,%v", bidType, common.HotDog)
  123. return false, "invalid bet area"
  124. }
  125. // 检查个人下注
  126. myAmount := 0
  127. ts.lock.RLock()
  128. betList, ok := ts.userBetList[userId]
  129. if ok {
  130. for _, v := range betList {
  131. myAmount += v.Amount
  132. }
  133. }
  134. ts.lock.RUnlock()
  135. maxBet := ts.roomInfo.MaxBet
  136. totalMaxBet := ts.roomInfo.TotalMax
  137. totalBet := ts.roomInfo.TotalBet
  138. if myAmount+amount > maxBet {
  139. return false, "Bet amount user exceeded"
  140. }
  141. // 检查全体下注
  142. if amount+totalBet > totalMaxBet {
  143. return false, "Bet amount section exceeded"
  144. }
  145. return true, ""
  146. }
  147. func (ts *tablesink) addBet(bet common.Bet) (bool, string) {
  148. ok, errMsg := ts.isCanBet(bet)
  149. if !ok {
  150. return ok, errMsg
  151. }
  152. ts.lock.Lock()
  153. ts.roomInfo.TotalBet += bet.Amount
  154. betList, ok := ts.userBetList[bet.UserId]
  155. // 合并之前下注
  156. found := false
  157. if ok {
  158. for i := 0; i < len(betList); i++ {
  159. if !betList[i].IsSameBet(&bet) {
  160. continue
  161. }
  162. betList[i].Amount += bet.Amount
  163. // 合并下注
  164. found = true
  165. break
  166. }
  167. }
  168. //log.Debug("tablesink.addBet %v", ts.userBetList)
  169. if !found {
  170. ts.userBetList[bet.UserId] = append(ts.userBetList[bet.UserId], bet)
  171. ts.areaPopular.BetCount[bet.BetId]++
  172. }
  173. ts.lock.Unlock()
  174. betAmount := bet.Amount
  175. ts.userList.addBet(bet.UserId, betAmount, bet.BetId)
  176. return true, ""
  177. }
  178. func (ts *tablesink) enterOpenState(isReset bool) {
  179. spinResult := common.Spin()
  180. // 钻石场 避免 连续大奖 重新摇奖
  181. if gs.IsChipRoom() && spinResult == common.MeatSlice && ts.drawMeatSliceWinAmount > 0 {
  182. spinResult = common.Spin()
  183. }
  184. ts.drawMeatSliceWinAmount = 0
  185. // 开奖
  186. ts.lock.Lock()
  187. ts.spinResult = spinResult
  188. ts.lock.Unlock()
  189. //针对主区域差额确定,特殊区域不考虑
  190. controlType := 0 //controlType 1表示扣减,2表示放水
  191. //找到投注额的最大值和最小值
  192. maxAmount := ts.hotDogAmount
  193. minAmount := ts.hotDogAmount
  194. //最大值和最小值对应的下注区域
  195. var maxBet, minBet common.BetOption
  196. betsAmount := []int{ts.hotDogAmount, ts.kebabAmount, ts.chickenLegAmount, ts.meatSliceAmount, ts.radishAmount, ts.cornAmount, ts.spinachAmount, ts.tomatoAmount}
  197. //只重置一次
  198. if !isReset {
  199. for i, f := range betsAmount {
  200. if f > maxAmount {
  201. maxAmount = f
  202. //因为默认值是0,所以这里要加1
  203. maxBet = common.BetOption(i + 1)
  204. }
  205. if f < minAmount {
  206. minAmount = f
  207. minBet = common.BetOption(i + 1)
  208. }
  209. }
  210. if maxAmount > 0 || minAmount > 0 {
  211. controlType = waterpool.GetControlType(0, false, common.GAMEID)
  212. }
  213. //根据情况判断是否需要重新开奖
  214. if controlType == 1 {
  215. if spinResult == maxBet {
  216. ts.enterOpenState(true)
  217. return
  218. }
  219. } else if controlType == 2 {
  220. if spinResult != minBet {
  221. ts.enterOpenState(true)
  222. return
  223. }
  224. }
  225. }
  226. ts.addHistory(int(spinResult))
  227. ts.lock.Lock()
  228. ts.scores = []common.UserSetScore{}
  229. if ts.userList == nil {
  230. // 如果没有玩家,将 noPlayerCount 的值加 1
  231. ts.noPlayerCount++
  232. if ts.noPlayerCount >= 6 {
  233. // 如果连续 6 期没有玩家,开奖后关闭游戏
  234. ts.closeGame = true
  235. }
  236. } else {
  237. ts.noPlayerCount = 0
  238. }
  239. // 纪录每个玩家总输赢
  240. for userId, betList := range ts.userBetList {
  241. usr := ts.table.GetUserByUserId(userId)
  242. var total struct {
  243. UserId int
  244. BetAmount int
  245. WinAmount int
  246. WinAmountOnly int
  247. DayWinAmount int
  248. }
  249. total.UserId = userId
  250. betDesc := fmt.Sprintf("%v:", ts.roomInfo.SerialNumber)
  251. betIdDesc := ""
  252. betAmountDesc := ""
  253. resultDesc := ""
  254. totalTax := 0
  255. //开奖赔率
  256. winOdds := common.GetOdds(spinResult)
  257. for i, v := range betList {
  258. // 根据每个人计算结果
  259. var result common.Result
  260. result.UserId = v.UserId
  261. result.Bet = v
  262. result.SpinResult = spinResult
  263. result.Amount = v.Amount
  264. betAmount := v.Amount
  265. total.BetAmount += betAmount
  266. odds := common.GetResultOdds(result.BetId, spinResult)
  267. //log.Debug("GetResultOdds odds= %v, BetId = %v", odds, result.BetId)
  268. // 看下有没有中的
  269. if odds > 0 {
  270. result.WinAmount = int(odds * float64(result.Amount))
  271. //免费的也要扣本金
  272. realWin := result.WinAmount - result.Amount
  273. if realWin > 0 {
  274. result.Tax = realWin * ts.roomInfo.TaxRate / 100
  275. }
  276. total.WinAmountOnly += result.WinAmount
  277. result.WinAmount -= result.Tax
  278. total.WinAmount += result.WinAmount
  279. }
  280. betIdDesc += fmt.Sprintf("%d", result.BetId)
  281. if i != len(betList)-1 {
  282. betIdDesc += ","
  283. }
  284. betAmountDesc = fmt.Sprintf("%s [%s:%s]", betAmountDesc, ts.getBetDesc(result.Bet), utils.FormatScore(v.Amount))
  285. resultDesc = ts.getResultDesc(result)
  286. totalTax += result.Tax
  287. //ts.handleResult(result, usr)
  288. }
  289. betDesc = fmt.Sprintf("%s%s&%s", betDesc, betIdDesc, betAmountDesc)
  290. if total.WinAmountOnly > 0 {
  291. ts.winRank.addWinPool(userId, total.WinAmountOnly)
  292. }
  293. go ts.handleResult(userId, total.BetAmount, total.WinAmount, totalTax, usr)
  294. // 纪录输赢
  295. go ts.table.WriteBetRecordWithPlayTime(userId, total.BetAmount, total.WinAmount, winOdds,
  296. betDesc, resultDesc, ts.roomInfo.RoomName, ts.roomInfo.BetTime)
  297. ts.userList.addResult(userId, total.WinAmount-total.BetAmount, total.WinAmountOnly)
  298. ts.scores = append(ts.scores, common.UserSetScore{UserId: userId, Score: total.WinAmount - total.BetAmount})
  299. if usr == nil {
  300. continue
  301. }
  302. _, _, winAmount := ts.winRank.getRankList(userId, -1)
  303. total.DayWinAmount = winAmount
  304. // 发送我的总输赢
  305. data, _ := json.Marshal(total)
  306. ts.table.SendGameData(usr.GetUserIndex(), TotalResult, string(data))
  307. }
  308. ts.lock.Unlock()
  309. ts.winRank.winPoolToRedis()
  310. //ts.userList.dump()
  311. // 结算完毕,计算连胜榜
  312. ts.lock.Lock()
  313. //因为前端只需要显示前三名的NickName和金币所以这里只取前三名
  314. ts.score_users.WinGoldRankingUsers = ts.userList.getWinGoldRankingUsers(3)
  315. ts.score_users.DayWinGoldRankingUsers = ts.GetWinRank(1)
  316. ts.lock.Unlock()
  317. ts.userList.clearBetStatus()
  318. }
  319. func (ts *tablesink) addHistory(spinResult int) {
  320. Historys := config.OpenHistory{SpinResult: spinResult, SerialNumber: ts.roomInfo.SerialNumber}
  321. ts.lock.Lock()
  322. defer ts.lock.Unlock()
  323. log.Debug("addHistory %v,%v", Historys, ts.roomInfo)
  324. ts.roomInfo.Historys = append(ts.roomInfo.Historys, Historys)
  325. if len(ts.roomInfo.Historys) > ts.roomInfo.HistoryCount {
  326. ts.roomInfo.Historys = ts.roomInfo.Historys[1:]
  327. }
  328. }
  329. // 处理结算
  330. func (ts *tablesink) handleResult(userId int, betAmount, winAmount, tax int, usr *user.UserInfo) {
  331. // data, _ := json.Marshal(result)
  332. // if usr != nil {
  333. // go ts.table.SendGameData(usr.GetUserIndex(), Result, string(data))
  334. // }
  335. ts.table.WriteUserMoney(userId, winAmount, tax, 2, 2+common.GAMEID*100, ts.roomInfo.RoomName)
  336. // 写用户纪录
  337. // var record common.RecordInfo
  338. // record.SpinResult = result.SpinResult
  339. // record.SerialNumber = ts.roomInfo.SerialNumber
  340. // d, _ := json.Marshal(record)
  341. // go transaction.WriteGameRecord(userId, common.GAMEID, ts.roomInfo.RoomName, string(d))
  342. }
  343. func (ts *tablesink) getHistory() string {
  344. ts.lock.RLock()
  345. defer ts.lock.RUnlock()
  346. data, _ := json.Marshal(ts.roomInfo.Historys)
  347. return string(data)
  348. }
  349. func (ts *tablesink) getBetDesc(bet common.Bet) string {
  350. return common.GetBetDesc(bet.BetId)
  351. }
  352. func (ts *tablesink) getResultDesc(result common.Result) string {
  353. return common.GetResultDesc(result.SpinResult)
  354. }
  355. func (ts *tablesink) currentGameNumber() int {
  356. now := time.Now()
  357. startOfDay := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
  358. difference := now.Sub(startOfDay)
  359. secondsSinceMidnight := int(difference.Seconds())
  360. gameDuration := ts.roomInfo.BetTime + ts.roomInfo.OpenTime
  361. currentGame := secondsSinceMidnight/gameDuration + 1
  362. return currentGame
  363. }
  364. func (ts *tablesink) GetWinRank(count int) []common.ScoreUser {
  365. //用户id
  366. var userIds []int
  367. //获取winRank前10名
  368. rankList, _, _ := ts.winRank.getRankList(0, count)
  369. log.Debug("tablesink.GetWinRank rankList:%v", rankList)
  370. //不为空
  371. if len(rankList) > 0 {
  372. scoreUserList := make([]common.ScoreUser, 0, len(rankList))
  373. //如果rankList昵称为空则补齐
  374. userIds = make([]int, len(rankList))
  375. for i, u := range rankList {
  376. userIds[i] = u.UserId
  377. }
  378. users := userservices.GetUserInfoInBulk(userIds)
  379. for i, v := range rankList {
  380. info := make([]int, 0, 1)
  381. info = append(info, v.WinAmount)
  382. scoreUser := common.ScoreUser{
  383. UserId: v.UserId,
  384. NickName: "",
  385. FaceId: 0,
  386. FaceUrl: "",
  387. VipLevel: 0,
  388. VipExpire: 0,
  389. Decorations: nil,
  390. Info: info,
  391. }
  392. user := users[i]
  393. if user.UserId == v.UserId {
  394. scoreUser.NickName = user.NickName
  395. scoreUser.FaceId = user.FaceId
  396. scoreUser.FaceUrl = user.FaceUrl
  397. scoreUser.VipLevel = user.Vip
  398. scoreUser.VipExpire = user.VipExpire
  399. scoreUser.Decorations = user.Decorations
  400. }
  401. scoreUserList = append(scoreUserList, scoreUser)
  402. }
  403. return scoreUserList
  404. }
  405. return []common.ScoreUser{}
  406. }