tablesink.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. package gamelogic
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "math/rand"
  6. "strconv"
  7. "time"
  8. "bet24.com/servers/games/baloot/config"
  9. "bet24.com/servers/insecureframe/frame"
  10. "bet24.com/servers/insecureframe/gate"
  11. ladder "bet24.com/servers/micros/ladderservice/proto"
  12. waterpool "bet24.com/servers/micros/waterpool/proto"
  13. "bet24.com/utils"
  14. "github.com/google/uuid"
  15. )
  16. type tablesink struct {
  17. table frame.Table
  18. gameScene *GameScene
  19. simulator *simulatorScene
  20. roomInfo config.RoomInfo
  21. privateData string
  22. logic *cardlogic
  23. LastOpraTime time.Time //记录操作时间点
  24. uniqueId string // 唯一标识
  25. roomType string // "","PrivateRoom","Match"
  26. surrenderInfo *SurrenderInfo // 投降消息
  27. allPassCount int
  28. }
  29. func newTableSink(table frame.Table, data string) *tablesink {
  30. ts := new(tablesink)
  31. ts.table = table
  32. ts.privateData = data
  33. ts.logic = newCardLogic()
  34. found := false
  35. err := json.Unmarshal([]byte(data), &ts.roomInfo)
  36. if err != nil {
  37. for _, v := range config.Rooms.Rooms {
  38. if data == v.RoomName {
  39. ts.roomInfo = v
  40. found = true
  41. break
  42. }
  43. }
  44. if !found {
  45. ts.roomInfo = config.NewUserRoom.RoomInfo
  46. found = true
  47. }
  48. } else {
  49. found = true
  50. }
  51. if !found {
  52. ts.roomInfo = config.NewUserRoom.RoomInfo
  53. }
  54. ts.uniqueId = uuid.New().String()
  55. ts.allPassCount = 0
  56. ts.gameScene = newGameScene(table.GetTableID(), ts.roomInfo.ScoreToWin, ts.roomInfo.MaxSurrenderCount)
  57. ts.surrenderInfo = newSurrenderInfo()
  58. return ts
  59. }
  60. func (ts *tablesink) Destroy() {
  61. ts.table.LogWithTableId("------tablesink: Destroy-------")
  62. }
  63. func (ts *tablesink) OnGameMessage(userIndex int32, msg, data string) bool {
  64. switch msg {
  65. case CMD_TABLECHAT:
  66. ts.recvChatMsg(userIndex, data)
  67. case CMD_CANCLE_AUTO:
  68. ts.recvCancleAutoMsg(userIndex, data)
  69. case CMD_AUTO:
  70. ts.recvAutoMsg(userIndex, data)
  71. case CMD_ACTION:
  72. return ts.recvAction(userIndex, data)
  73. case CMD_SYNCDATA:
  74. return ts.recvSyncData(userIndex, data)
  75. case CMD_SURRENDER:
  76. return ts.recvSurrender(userIndex, data)
  77. case CMD_SURRENDER_RESULT:
  78. return ts.recvSurrenderResult(userIndex, data)
  79. default:
  80. ts.table.LogWithTableId("tablesink.OnGameMessage %s\n%s", msg, data)
  81. return false
  82. }
  83. return true
  84. }
  85. func (ts *tablesink) OnUserEnterTable(userIndex int32, chairId int) {
  86. usr := gate.GetUserInfo(userIndex)
  87. if usr == nil {
  88. ts.table.LogWithTableId("tablesink.OnUserEnterTable user not exist")
  89. return
  90. }
  91. ts.table.LogWithTableId("tablesink.OnUserEnterTable chair[%d]: %d:%s ", chairId, usr.GetUserId(), usr.GetUserNickName())
  92. //下发房间配置
  93. d, _ := json.Marshal(ts.roomInfo.RoomInfoBase)
  94. ts.table.SendGameData(userIndex, CMD_ROOMINFO, string(d))
  95. ts.table.SetTimer(TIMER_READY_0+chairId, SEC_READY)
  96. //有玩家进桌, 更新在线
  97. go func() {
  98. frame.UpdateRoomOnline(ts.roomInfo.RoomName, ts.roomInfo.RoomID-1)
  99. }()
  100. ts.gameScene.Players[chairId].isEndToStart = false
  101. ts.gameScene.Players[chairId].userID = usr.GetUserId()
  102. if usr.IsRobot() {
  103. ts.table.SetTimer(TIMER_READY_0+chairId, 100)
  104. }
  105. // 如果比赛场,可以查询玩家当前分数、底分和局数
  106. if ts.table.GetOwner() == -1 {
  107. ts.table.LogWithTableId("userScore:%d,baseScore:%d,setCount:%d", usr.GetScore(), usr.GetBaseScore(), usr.GetSetCount())
  108. ts.gameScene.Players[chairId].MatchScore = usr.GetScore()
  109. ts.gameScene.Players[chairId].IsValid = true
  110. }
  111. if config.Server.IsLadderRoom > 0 {
  112. data := ladder.GetUserLadderInfo(usr.GetUserId(), GAMEID)
  113. if data != nil {
  114. ts.gameScene.Players[chairId].LadderInfo = *data
  115. ts.gameScene.Players[chairId].LastWinCount = ladder.GetUserConsecutiveWinCount(usr.GetUserId(), GAMEID)
  116. info := ts.gameScene.getPlayerLadderInfo()
  117. ts.table.SendGameData(-1, CMD_LADDERINFO, info)
  118. } else {
  119. ts.table.LogWithTableId("User:%d, LadderInfo is nil", usr.GetUserId())
  120. }
  121. }
  122. if !usr.IsRobot() && ts.table.GetOwner() <= 0 {
  123. ts.table.NotifySceneChanged(chairId)
  124. }
  125. if usr.IsRobot() {
  126. ts.gameScene.Players[chairId].robotType = rand.Intn(3)
  127. friendId := ts.gameScene.Players[getFriendChair(chairId)].userID
  128. if friendId != 0 {
  129. friend := ts.table.GetPlayerByUserId(friendId)
  130. if friend != nil {
  131. ts.gameScene.Players[chairId].robotType = ts.gameScene.Players[getFriendChair(chairId)].robotType
  132. }
  133. }
  134. ts.gameScene.Players[chairId].robotActionProb = 100 - 50*ts.gameScene.Players[chairId].robotType
  135. ts.gameScene.Players[chairId].robotType = RobotType_Cautious
  136. ts.gameScene.Players[chairId].robotActionProb = 100
  137. }
  138. }
  139. func (ts *tablesink) OnUserExitTable(userIndex int32, chairId int) {
  140. usr := ts.table.GetUserByChair(chairId)
  141. if usr == nil {
  142. ts.table.LogWithTableId("tablesink.OnUserExitTable user not exist")
  143. return
  144. }
  145. ts.table.LogWithTableId("tablesink.OnUserExitTable chair[%d]: %d:%s", chairId, usr.GetUserId(), usr.GetUserNickName())
  146. ts.gameScene.Players[chairId].isEndToStart = false
  147. //有玩家离开, 更新在线
  148. go func() {
  149. frame.UpdateRoomOnline(ts.roomInfo.RoomName, ts.roomInfo.RoomID-1)
  150. }()
  151. if ts.gameScene.Phase == Phase_Free || ts.gameScene.Phase == Phase_End {
  152. ts.table.KillTimer(TIMER_READY_0 + chairId)
  153. if ts.gameScene.Phase != Phase_End && !usr.IsRobot() {
  154. ts.table.SetTimer(TIMER_ADD_ROBOT, 500)
  155. }
  156. go ts.checkAndStartGame()
  157. } else {
  158. // 没有参与游戏?
  159. p := &ts.gameScene.Players[chairId]
  160. if !p.IsValid {
  161. return
  162. }
  163. if ts.table.IsPrivate() {
  164. return
  165. }
  166. //游戏中离开,逃跑
  167. p.IsValid = false
  168. ts.writeScore(p.userID, p.EndScore, 0, 0, ScoreType_Flee, ts.roomInfo.RoomName, p.isRobot)
  169. go ts.table.WriteBetRecordWithSetcount(usr.GetUserId(), ts.gameScene.Players[chairId].bet, 0, 1.0, "fleeGold", "flee", ts.roomInfo.RoomName, 1)
  170. for i := 0; i < CHAIR_COUNT; i++ {
  171. p := &ts.gameScene.Players[i]
  172. if !p.IsValid {
  173. continue
  174. }
  175. p.EndScore = p.bet
  176. ts.writeScore(p.userID, p.bet, 0, 0, ScoreType_Return, ts.roomInfo.RoomName, p.isRobot)
  177. }
  178. ts.gameScene.Phase = Phase_End
  179. ts.endGame()
  180. ts.table.NotifySceneChanged(-1)
  181. ts.uniqueId = uuid.New().String()
  182. ts.gameScene.initData(ts.table.GetTableID(), ts.roomInfo.ScoreToWin, ts.roomInfo.MaxSurrenderCount)
  183. }
  184. }
  185. func (ts *tablesink) OnUserOffline(chairId int) {
  186. usr := ts.table.GetUserByChair(chairId)
  187. if usr == nil {
  188. ts.table.LogWithTableId("tablesink.OnUserOffline user not exist")
  189. return
  190. }
  191. ts.table.LogWithTableId("tablesink.OnUserOffline %d:%s", usr.GetUserId(), usr.GetUserNickName())
  192. }
  193. func (ts *tablesink) OnUserReplay(chairId int) {
  194. usr := ts.table.GetUserByChair(chairId)
  195. if usr == nil {
  196. ts.table.LogWithTableId("tablesink.OnUserOffline user not exist")
  197. return
  198. }
  199. ts.table.LogWithTableId("tablesink.OnUserReplay %d:%s", usr.GetUserId(), usr.GetUserNickName())
  200. //下发房间配置
  201. d, _ := json.Marshal(ts.roomInfo.RoomInfoBase)
  202. ts.table.SendGameData(usr.GetUserIndex(), CMD_ROOMINFO, string(d))
  203. ts.gameScene.Players[chairId].AutoOut = false
  204. if ts.surrenderInfo.canDoSurrender(chairId) {
  205. userIndex := ts.table.GetUserByChair(chairId).GetUserIndex()
  206. ts.table.SendGameData(userIndex, CMD_SURRENDER_RESULT, ts.surrenderInfo.getSurrenderInfo())
  207. }
  208. }
  209. func (ts *tablesink) OnUserReady(userIndex int32, chairId int) {
  210. defer utils.TimeCost(fmt.Sprintf("tablesink.OnUserReady %d", userIndex))()
  211. usr := gate.GetUserInfo(userIndex)
  212. if usr == nil {
  213. ts.table.LogWithTableId("tablesink.OnUserReady user not exist")
  214. return
  215. }
  216. userGold := ts.table.GetUserChipOrGoldByUser(usr)
  217. if userGold < ts.roomInfo.MinGold || (ts.roomInfo.MaxGold > 0 && userGold > ts.roomInfo.MaxGold) {
  218. ts.table.LogWithTableId("tablesink.OnUserReady %d 金币不足,站起", usr.GetUserId())
  219. if usr.IsRobot() {
  220. ts.table.KickUser(usr.GetUserIndex(), false)
  221. } else {
  222. ts.table.UserWatch(usr.GetUserIndex())
  223. }
  224. return
  225. }
  226. ts.table.LogWithTableId("tablesink.OnUserReady chair[%d]: %d:%s", chairId, usr.GetUserId(), usr.GetUserNickName())
  227. ts.table.KillTimer(TIMER_READY_0 + chairId)
  228. if ts.gameScene.Phase != Phase_GameEnd || ts.gameScene.Players[chairId].isEndToStart {
  229. ts.table.SetTimer(TIMER_ADD_ROBOT, 500)
  230. }
  231. if !usr.IsRobot() && ts.gameScene.Players[chairId].isEndToStart {
  232. if config.Server.IsLadderRoom > 0 {
  233. data := ladder.GetUserLadderInfo(usr.GetUserId(), GAMEID)
  234. if data != nil {
  235. ts.gameScene.Players[chairId].LadderInfo = *data
  236. info := ts.gameScene.getPlayerLadderInfo()
  237. ts.table.SendGameData(-1, CMD_LADDERINFO, info)
  238. } else {
  239. ts.table.LogWithTableId("User:%d, LadderInfo is nil", usr.GetUserId())
  240. }
  241. }
  242. ts.table.NotifySceneChanged(chairId)
  243. }
  244. ts.checkAndStartGame()
  245. }
  246. func (ts *tablesink) OnUserCancelReady(userIndex int32, chairId int) {
  247. usr := gate.GetUserInfo(userIndex)
  248. if usr == nil {
  249. ts.table.LogWithTableId("tablesink.OnUserCancelReady user not exist")
  250. return
  251. }
  252. ts.table.LogWithTableId("tablesink.OnUserCancelReady %d:%s", usr.GetUserId(), usr.GetUserNickName())
  253. }
  254. func (ts *tablesink) OnGetChairScene(chairId int, isPlayer bool) string {
  255. now := time.Now()
  256. ts.gameScene.LeftSec = ts.getSecond(ts.gameScene.Phase)/1000 - int(now.Sub(ts.LastOpraTime).Seconds())
  257. return ts.gameScene.getScene(chairId, isPlayer, false)
  258. }
  259. func (ts *tablesink) OnGetPrivateRoomScene(chairId int) string {
  260. now := time.Now()
  261. ts.gameScene.LeftSec = ts.getSecond(ts.gameScene.Phase)/1000 - int(now.Sub(ts.LastOpraTime).Seconds())
  262. str := ts.gameScene.getScene(chairId, true, true)
  263. return str
  264. }
  265. func (ts *tablesink) OnGetChairCount() int {
  266. return CHAIR_COUNT
  267. }
  268. func (ts *tablesink) OnTimer(timerId int) {
  269. switch timerId {
  270. case TIMER_READY_0:
  271. fallthrough
  272. case TIMER_READY_1:
  273. fallthrough
  274. case TIMER_READY_2:
  275. fallthrough
  276. case TIMER_READY_3:
  277. ts.checkUserReadyStatus(timerId - TIMER_READY_0)
  278. case TIMER_GAME:
  279. ts.dealGameTimeOut()
  280. case TIMER_REMOVE_ROBOT:
  281. ts.removeOneRobot()
  282. case TIMER_ADD_ROBOT:
  283. ts.checkIsNeedRobot()
  284. case TIMER_SEND_LEFT_CARD:
  285. ts.sendLeftCard()
  286. case TIMER_START_OUT_CARD:
  287. ts.onPlayerFirstOutCard()
  288. case TIMER_NEWROUND:
  289. ts.onTimerNewRound()
  290. case TIMER_ROBOT_ACTION_0:
  291. fallthrough
  292. case TIMER_ROBOT_ACTION_1:
  293. fallthrough
  294. case TIMER_ROBOT_ACTION_2:
  295. fallthrough
  296. case TIMER_ROBOT_ACTION_3:
  297. ts.onTimerRobotAction(timerId - TIMER_ROBOT_ACTION_0)
  298. case TIMER_AUTO_ACTION_0:
  299. fallthrough
  300. case TIMER_AUTO_ACTION_1:
  301. fallthrough
  302. case TIMER_AUTO_ACTION_2:
  303. fallthrough
  304. case TIMER_AUTO_ACTION_3:
  305. ts.onTimerAutoAction(timerId - TIMER_AUTO_ACTION_0)
  306. case TIMER_ALL_PASS:
  307. ts.onTimerBuyFailed()
  308. case TIMER_BUY_CHANGE:
  309. ts.gameScene.Index++
  310. ts.table.NotifySceneChanged(-1)
  311. case TIMER_DOUBLE_CHANGE:
  312. ts.gameScene.initAllPlayerLastAction()
  313. ts.gameScene.Index++
  314. ts.table.NotifySceneChanged(-1)
  315. ts.resetGameTimer()
  316. case TIMER_DELAY_END:
  317. ts.table.EndGame()
  318. case TIMER_START_GAME:
  319. ts.checkStartGame()
  320. case TIMER_SAWA:
  321. ts.onTimerSawa()
  322. case TIMER_CORRECTFINISH:
  323. ts.onTimerCorrectFinish()
  324. case TIMER_CORRECTDELAY:
  325. // 轮次结束也要清理收牌信息
  326. ts.gameScene.newOutCardRound()
  327. // 进入SetEnd阶段
  328. ts.enterGameEndPhase(true)
  329. case TIMER_SURRENDER:
  330. index := ts.table.GetUserByChair(getFriendChair(ts.surrenderInfo.WhoseSurrender)).GetUserIndex()
  331. data := Surrender_Failed
  332. ts.recvSurrenderResult(index, fmt.Sprintf("%d", data))
  333. case TIMER_ROBOT_SURRENDER:
  334. index := ts.table.GetUserByChair(getFriendChair(ts.surrenderInfo.WhoseSurrender)).GetUserIndex()
  335. data := Surrender_Success
  336. ts.recvSurrenderResult(index, fmt.Sprintf("%d", data))
  337. case TIMER_ROBOT_REPLY:
  338. start := rand.Intn(4)
  339. for i := 0; i < CHAIR_COUNT; i++ {
  340. c := (start + i) % CHAIR_COUNT
  341. r := rand.Intn(100)
  342. if r <= 60 && ts.gameScene.Players[c].isRobot {
  343. ts.sendRobotChatAction(RobotChatAction_Reply, ts.gameScene.Players[c].userID, -1, 0)
  344. break
  345. }
  346. }
  347. case TIMER_ROBOT_SEND_CHAT_0:
  348. fallthrough
  349. case TIMER_ROBOT_SEND_CHAT_1:
  350. fallthrough
  351. case TIMER_ROBOT_SEND_CHAT_2:
  352. fallthrough
  353. case TIMER_ROBOT_SEND_CHAT_3:
  354. chairId := timerId - TIMER_ROBOT_SEND_CHAT_0
  355. if ts.gameScene.Players[chairId].outTimeCount >= 2 {
  356. return
  357. }
  358. if ts.gameScene.WhoseTurn == chairId {
  359. start := rand.Intn(4)
  360. for i := 0; i < CHAIR_COUNT; i++ {
  361. c := (start + i) % CHAIR_COUNT
  362. if c == chairId {
  363. continue
  364. }
  365. if ts.gameScene.Players[c].isRobot {
  366. if ts.gameScene.Players[chairId].outTimeCount == 1 {
  367. ts.sendRobotChatAction(RobotChatAction_MuiltyTimeOut, ts.gameScene.Players[c].userID, ts.gameScene.Players[chairId].userID, 0)
  368. }
  369. break
  370. }
  371. }
  372. ts.gameScene.Players[chairId].outTimeCount++
  373. }
  374. }
  375. }
  376. func (ts *tablesink) checkStartGame() {
  377. if !ts.canStartGame(false) {
  378. if ts.table.GetOwner() <= 0 {
  379. ts.table.NotifySceneChanged(-1)
  380. }
  381. return
  382. }
  383. start := rand.Intn(4)
  384. for i := 0; i < CHAIR_COUNT; i++ {
  385. c := (start + i) % CHAIR_COUNT
  386. r := rand.Intn(100)
  387. if r <= 30 && ts.gameScene.Players[c].isRobot {
  388. ts.sendRobotChatAction(RobotChatAction_EnterRoom, ts.gameScene.Players[c].userID, -1, 0)
  389. ts.table.SetTimer(TIMER_ROBOT_REPLY, 1500)
  390. break
  391. }
  392. }
  393. ts.gameScene.setNextBanker()
  394. ts.gameScene.gameInit(getPreviousChair(ts.gameScene.Banker))
  395. controlType := ts.checkNeedControlPlayer()
  396. if controlType <= waterpool.PoolControl_Normal {
  397. ts.checkNeedControlRobot()
  398. }
  399. if !ts.table.IsPrivate() {
  400. ts.gameScene.initMaxDoubling(ts.roomInfo.IsDoublingMode)
  401. }
  402. if ts.roomInfo.RoomID == 0 && !ts.table.IsPrivate() {
  403. for i := 0; i < CHAIR_COUNT; i++ {
  404. usr := ts.table.GetUserByChair(i)
  405. if usr == nil {
  406. continue
  407. }
  408. if !usr.IsRobot() {
  409. ts.gameScene.Banker = getNextChair(i)
  410. ts.gameScene.WhoseTurn = i
  411. ts.gameScene.FirstActionChair = i
  412. break
  413. }
  414. }
  415. }
  416. ts.gameStart()
  417. ts.table.StartGame()
  418. if ts.roomInfo.IsDoublingMode {
  419. if getCardValue(ts.gameScene.PublicCard) == CardValueJ {
  420. ts.addDoublingData(Doubling_PublicCard_J)
  421. ts.sendShowDoublingMsg([]int{Doubling_PublicCard_J})
  422. }
  423. }
  424. ts.table.LogWithTableId("tablesink.startGame[%d]", ts.table.GetTableID())
  425. }
  426. func (ts *tablesink) DumpScene() {
  427. ts.gameScene.dump(true)
  428. }
  429. func (ts *tablesink) GetGoldLimit() (min, max int) {
  430. return ts.roomInfo.MinGold, ts.roomInfo.MaxGold
  431. }
  432. func (ts *tablesink) IsDual() bool {
  433. return ts.roomInfo.IsDual > 0
  434. }
  435. func (ts *tablesink) OnBaseScoreChanged(baseScore int) {
  436. ts.roomInfo.BaseScore = baseScore
  437. d, _ := json.Marshal(ts.roomInfo.RoomInfoBase)
  438. ts.table.SendGameData(-1, CMD_ROOMINFO, string(d))
  439. }
  440. func (ts *tablesink) SetPrivateRoomParam(param int, value string) {
  441. ts.table.LogWithTableId("tablesink.SetPrivateRoomParam %d:%s", param, value)
  442. switch param {
  443. case frame.Param_Target:
  444. t, err := strconv.Atoi(value)
  445. if err == nil {
  446. ts.roomInfo.ScoreToWin = t
  447. ts.uniqueId = uuid.New().String()
  448. ts.gameScene.initData(ts.table.GetTableID(), ts.roomInfo.ScoreToWin, ts.roomInfo.MaxSurrenderCount)
  449. }
  450. case frame.Param_PlayTimeout:
  451. t, err := strconv.Atoi(value)
  452. if err == nil {
  453. ts.roomInfo.SecPlay = t
  454. }
  455. }
  456. }
  457. func (ts *tablesink) OnPrivateRoomStatusChanged(oldStatus, newStatus int) {
  458. ts.table.LogWithTableId("OnPrivateRoomStatusChanged %d->%d", oldStatus, newStatus)
  459. ts.roomType = ts.table.PrivateRoomGetRoomType()
  460. ts.table.LogWithTableId("tablesink.OnPrivateRoomStatusChanged roomType:%s", ts.roomType)
  461. }
  462. func (ts *tablesink) OnPrivateRoomDismissed() {
  463. ts.table.LogWithTableId("OnPrivateRoomDismissed ")
  464. }
  465. func (ts *tablesink) IsAllRobot() bool {
  466. bExist := false
  467. for i := 0; i < CHAIR_COUNT; i++ {
  468. usr := ts.table.GetUserByChair(i)
  469. if usr == nil {
  470. continue
  471. }
  472. bExist = true
  473. if !usr.IsRobot() {
  474. return false
  475. }
  476. }
  477. return bExist
  478. }