room.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. package game
  2. import (
  3. "fmt"
  4. "github.com/davy66666/poker-go/src/server/protocol"
  5. "log"
  6. "microGame/app/usercenter/cmd/api/internal/algorithm"
  7. "microGame/app/usercenter/model/room"
  8. "microGame/pkg/common"
  9. "microGame/pkg/result"
  10. "sync"
  11. "time"
  12. )
  13. var once sync.Once
  14. const (
  15. RUNNING uint8 = 1
  16. GAMEOVER uint8 = 0
  17. SB uint32 = 0
  18. BB uint32 = 0
  19. CHIPS uint32 = 1000
  20. )
  21. type Room struct {
  22. *room.Room
  23. Rid int64
  24. Mutex sync.Mutex // 并发安全锁
  25. Players []*Player
  26. observes []*Player // 站起的玩家
  27. AutoSitDown []*Player // 自动坐下队列
  28. Phase string // 当前阶段(pre-flop, flop, turn, river, showdown)
  29. remain int
  30. allin int
  31. n uint8
  32. status uint8 //游戏是否进行中0:game over, 1:running
  33. isRunning bool //判断goroutine是否已经启动
  34. SB uint32 // 小盲注
  35. BB uint32 // 大盲注
  36. Cards algorithm.Cards // 公共牌
  37. Pot []uint32 // 奖池筹码数, 第一项为主池,其他项(若存在)为边池
  38. Timeout time.Duration // 倒计时超时时间(秒)
  39. Button uint8 // 当前庄家座位号,从1开始
  40. Chips []uint32 // 玩家本局下注的总筹码数,与Players一一对应
  41. Bet uint32 // 当前回合 上一玩家下注额
  42. Max uint8 // 房间最大玩家人数
  43. MaxChips uint32
  44. MinChips uint32
  45. Done chan struct{} //销毁room 退出goroutine
  46. NextRound chan struct{} // 游戏自动开始
  47. }
  48. func NewRoom(max uint8, sb, bb uint32, chips uint32, timeout uint8) *Room {
  49. if max <= 0 || max > 9 {
  50. max = 9 // default 9 Players
  51. }
  52. r := &Room{
  53. Chips: make([]uint32, max),
  54. Players: make([]*Player, max),
  55. Pot: make([]uint32, 0, max),
  56. Timeout: time.Second * time.Duration(timeout),
  57. SB: sb,
  58. BB: bb,
  59. Max: max,
  60. Done: make(chan struct{}),
  61. }
  62. //room入库记录room 生成room id
  63. return r
  64. }
  65. type startDelay struct {
  66. kind uint8
  67. }
  68. func (r *Room) New(m interface{}) *Room {
  69. if msg, ok := m.(*protocol.JoinRoom); ok {
  70. if len(msg.RoomNumber) == 0 {
  71. //r := room.FindRoom()
  72. //return r
  73. }
  74. //数据库查询room 如果存在直接返回
  75. //r := room.GetRoom(msg.RoomNumber)
  76. //if r != nil {
  77. // //return r
  78. //}
  79. //room.Insert()
  80. return NewRoom(9, SB, BB, CHIPS, Timeout)
  81. }
  82. return nil
  83. }
  84. func (r *Room) isEmpty() bool {
  85. return len(r.Players) == 0
  86. }
  87. func (r *Room) WriteMsg(msg interface{}, exc ...int64) {
  88. // 将 exc 列表转换成 map,提高排除用户的查询效率
  89. excMap := make(map[int64]struct{}, len(exc))
  90. for _, uid := range exc {
  91. excMap[uid] = struct{}{}
  92. }
  93. // 遍历玩家并广播消息
  94. for _, v := range r.Players {
  95. if v == nil {
  96. continue
  97. }
  98. // 如果玩家的 UID 在排除列表中,则跳过该玩家
  99. if _, excluded := excMap[v.GetUid()]; excluded {
  100. continue
  101. }
  102. v.WriteMsg(msg)
  103. }
  104. }
  105. func (r *Room) handleGameLogic() {
  106. fmt.Printf("房间 %d 处理游戏逻辑\n", r.Rid)
  107. // 示例:检查是否需要下一轮发牌,或者执行其他游戏逻辑
  108. r.nextRound()
  109. }
  110. func (r *Room) startGame() {
  111. r.Mutex.Lock()
  112. // 如果已经有 Goroutine 运行,不重复启动
  113. if r.isRunning {
  114. log.Printf("房间 %d 已经在运行\n", r.Rid)
  115. r.Mutex.Unlock()
  116. return
  117. }
  118. // 标记房间正在运行
  119. r.isRunning = true
  120. r.Mutex.Unlock()
  121. // 启动新的 Goroutine 来管理房间
  122. go func() {
  123. go common.RunWithRecover(func() {
  124. r.modifyRoomStatus()
  125. })
  126. r.nextRound()
  127. // 监听房间状态并执行游戏逻辑
  128. for {
  129. select {
  130. case <-r.NextRound:
  131. // 检查游戏状态并进行下一轮
  132. r.nextRound()
  133. case <-r.Done: // 监听房间关闭信号
  134. close(r.NextRound)
  135. log.Printf("房间 %d 关闭,退出 Goroutine\n", r.Rid)
  136. return
  137. }
  138. }
  139. }()
  140. }
  141. // **下一轮**
  142. func (r *Room) nextRound() bool {
  143. r.Mutex.Lock()
  144. log.Println("进入下一轮")
  145. // 将符合条件的观察者转为玩家
  146. for k := 0; k < len(r.observes); k++ {
  147. observer := r.observes[k]
  148. if observer.chips > 0 && uint8(len(r.Players)) < r.Max {
  149. observer.SetSitDown()
  150. r.Players = append(r.Players, observer)
  151. // 删除观察者
  152. r.observes = append(r.observes[:k], r.observes[k+1:]...)
  153. k-- // 调整索引,确保不会跳过下一个观察者
  154. log.Printf("观察者 %d 转为玩家", observer.Uid)
  155. }
  156. }
  157. // 检查是否继续游戏
  158. if len(r.Players) < 2 {
  159. log.Println("游戏结束")
  160. r.Mutex.Unlock()
  161. r.goNextGame()
  162. return false
  163. }
  164. //游戏正在进行
  165. r.status = 1
  166. r.Mutex.Unlock()
  167. //发牌 下注
  168. fmt.Println("开始发牌!")
  169. startDelay := &startDelay{
  170. kind: 0,
  171. }
  172. //处理游戏下注
  173. r.startDelay(startDelay)
  174. //// 进入新的一轮
  175. //// 5 秒后自动进入下一轮
  176. go common.RunWithRecover(func() {
  177. r.Broadcast(result.WSSuccess("start", startDelay), true)
  178. r.goNextGame()
  179. })
  180. return true
  181. }
  182. func (r *Room) goNextGame() {
  183. go func() {
  184. time.Sleep(5 * time.Second)
  185. r.NextRound <- struct{}{}
  186. }()
  187. }
  188. func (r *Room) modifyRoomStatus() {
  189. //r.Started = false
  190. r.status = 0
  191. }
  192. func (r *Room) Broadcast(msg interface{}, all bool, exc ...int64) {
  193. // 将 exc 列表转换成 map,提高排除用户的查询效率
  194. var excMap map[int64]struct{}
  195. if len(exc) > 0 {
  196. excMap = make(map[int64]struct{}, len(exc))
  197. for _, uid := range exc {
  198. excMap[uid] = struct{}{}
  199. }
  200. }
  201. // 遍历玩家并广播消息
  202. for _, v := range r.Players {
  203. if v == nil || (!all && v.IsGaming()) {
  204. continue
  205. }
  206. if _, excluded := excMap[v.GetUid()]; excluded {
  207. continue
  208. }
  209. v.WriteMsg(msg)
  210. }
  211. // 遍历观察者并广播消息
  212. for _, v := range r.observes {
  213. if v == nil {
  214. continue
  215. }
  216. if _, excluded := excMap[v.Uid]; excluded {
  217. continue
  218. }
  219. v.WriteMsg(msg)
  220. }
  221. }
  222. func (r *Room) addPlayer(o *Player) uint8 {
  223. for _, v := range r.Players {
  224. if v != nil && v.GetUid() == o.Uid {
  225. return 0
  226. }
  227. }
  228. for k, v := range r.Players {
  229. if v == nil {
  230. r.Players[k] = o
  231. o.Pos = uint8(k + 1)
  232. o.SetSitDown()
  233. return o.Pos
  234. }
  235. }
  236. return 0
  237. }
  238. func (r *Room) removePlayer(o *Player) uint8 {
  239. for k, v := range r.Players {
  240. if v != nil && v.GetUid() == o.Uid {
  241. v.SetPos(0)
  242. r.Players[k] = nil
  243. return uint8(k + 1)
  244. }
  245. }
  246. return 0
  247. }
  248. func (r *Room) addObserve(o *Player) uint8 {
  249. for _, v := range r.observes {
  250. if v != nil && v.Uid == o.Uid {
  251. return 0
  252. }
  253. }
  254. o.SetObserve()
  255. r.observes = append(r.observes, o)
  256. return 0
  257. }
  258. func (r *Room) removeObserve(o *Player) {
  259. for k, v := range r.observes {
  260. if v != nil && v.Uid == o.Uid {
  261. r.observes = append(r.observes[:k], r.observes[k+1:]...)
  262. return
  263. }
  264. }
  265. }
  266. func (r *Room) CloseRoomChannel() {
  267. once.Do(func() {
  268. close(r.Done)
  269. fmt.Println("通道被关闭")
  270. })
  271. }
  272. // Each 从指定索引 `start` 开始遍历玩家列表,并对满足条件的玩家执行回调函数 `f`
  273. func (r *Room) Each(start uint8, f func(o *Player) bool) {
  274. volume := r.Cap()
  275. if volume == 0 { // 避免除零错误
  276. return
  277. }
  278. end := (volume + start - 1) % volume
  279. i := start
  280. for {
  281. player := r.Players[i]
  282. if player != nil && player.IsGaming() {
  283. if !f(player) { // 如果回调返回 false,则终止循环
  284. return
  285. }
  286. }
  287. if i == end { // 处理最后一个元素,防止额外调用
  288. break
  289. }
  290. i = (i + 1) % volume
  291. }
  292. }
  293. func (r *Room) Cap() uint8 {
  294. return r.Max
  295. }
  296. func (r *Room) Len() uint8 {
  297. var num uint8
  298. for _, v := range r.Players {
  299. if v != nil {
  300. num++
  301. }
  302. }
  303. return num
  304. }
  305. func (r *Room) GetNumber() string {
  306. return r.RoomNumber
  307. }
  308. func (r *Room) SetNumber(value string) {
  309. r.RoomNumber = value
  310. }
  311. func (r *Room) Data() interface{} { return r.Room }
  312. func (r *Room) FindRoom() bool {
  313. r.Mutex.Lock()
  314. if r.Len() < r.Cap() {
  315. return true
  316. }
  317. r.Mutex.Unlock()
  318. return false
  319. }