qznn.go 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329
  1. //描述整个游戏,提供接口给外部使用,挂了,就全部挂
  2. /**
  3. ## 游戏中的情况下,用户退出
  4. 使用延迟退出机制,游戏中的用户,不处理退出的事件,等这盘游戏结束后处理
  5. 如果没有游戏中的情况,则马上处理
  6. */
  7. package qznn
  8. import (
  9. "errors"
  10. "fmt"
  11. "math/rand"
  12. "runtime/debug"
  13. "strconv"
  14. "strings"
  15. "time"
  16. "github.com/bwmarrin/snowflake"
  17. "github.com/patrickmn/go-cache"
  18. "github.com/sasha-s/go-deadlock"
  19. "github.com/shopspring/decimal"
  20. "github.com/sirupsen/logrus"
  21. "gogs.daxia.dev/huanan/pkg.daxia.dev/db"
  22. "gorm.io/gorm"
  23. "nn.daxia.dev/gameproto"
  24. "nn.daxia.dev/model"
  25. "nn.daxia.dev/nxd"
  26. "nn.daxia.dev/online"
  27. )
  28. type kkMap map[string]interface{}
  29. type PlayerStatus int
  30. type RoomStatus int
  31. var ErrNotFound = errors.New("record not found")
  32. var ErrPwd = errors.New("password not correct")
  33. const (
  34. RoomIDEmpty = int32(0)
  35. PlayerStatusWaitReady PlayerStatus = 1 //等待准备
  36. PlayerStatusReady PlayerStatus = 2 //已经准备
  37. PlayerStatusChooseMaster PlayerStatus = 3 //已经选择庄
  38. PlayerStatusChooseMul PlayerStatus = 4 //已经选择倍数
  39. PlayerStatusOpen PlayerStatus = 5 //已经开奖
  40. RoomStatusWaitReady RoomStatus = 1 //等待准备
  41. RoomStatusReady RoomStatus = 2 //已经有两个准备好,等待其他玩家准备
  42. RoomStatusChooseMaster RoomStatus = 3 //正在选择庄家
  43. RoomStatusChooseMul RoomStatus = 4 //正在选择倍数
  44. RoomStatusOpen RoomStatus = 5 //正在开奖
  45. )
  46. type Chair struct {
  47. ID int32 //索引,1-5
  48. PlayerID int32 //用户ID
  49. }
  50. type Room struct {
  51. ID int32 //房间ID
  52. Name string //名称
  53. Passwd string //密码
  54. RoomType int32 //房间类型
  55. ChairList []Chair //桌子
  56. BaseAmount decimal.Decimal //底金
  57. Issue string //当前期号
  58. Status RoomStatus //房间当前状态
  59. StatusStartTime int64 //房间当前状态开始的时间
  60. ReadyCh chan bool //当房间全部用户准备好,触发
  61. ChooseMasterCh chan bool //全部选择完庄,触发
  62. ChooseMulCh chan bool //全部选择完倍数,触发
  63. OpenCh chan bool //全部开牌完毕,触发
  64. }
  65. type Player struct {
  66. ID int32
  67. Name string
  68. HeadImg string
  69. Balance decimal.Decimal //余额
  70. Credits decimal.Decimal //游戏积分
  71. RoomID int32 //当前所在房间ID
  72. JoinTime time.Time //加入时间
  73. Status PlayerStatus
  74. IsMaster bool //是否为庄家
  75. MasterMul int //抢庄的倍数
  76. Mul int //倍数
  77. CardList NNCardList //牌
  78. }
  79. type Game struct {
  80. RoomMap map[int32]Room
  81. PlayerMap map[int32]Player
  82. IDGener *snowflake.Node
  83. LogID int64
  84. //nextDelPlayerIDList *list.List //下一轮游戏开始会删,可能离线了
  85. locker deadlock.Mutex
  86. }
  87. var gameInstance *Game
  88. func GetInstance() *Game {
  89. if gameInstance != nil {
  90. return gameInstance
  91. }
  92. node, err := snowflake.NewNode(1)
  93. if err != nil {
  94. logrus.Error(err)
  95. return nil
  96. }
  97. gameInstance = &Game{
  98. RoomMap: map[int32]Room{},
  99. PlayerMap: map[int32]Player{},
  100. IDGener: node,
  101. //nextDelPlayerIDList: list.New(),
  102. locker: deadlock.Mutex{},
  103. }
  104. gameInstance.Start()
  105. return gameInstance
  106. }
  107. func (p *Game) Start() {
  108. //初始化房间
  109. chairList := make([]Chair, 0)
  110. for i := 0; i < 5; i++ {
  111. chairList = append(chairList, Chair{
  112. ID: int32(i),
  113. PlayerID: 0,
  114. })
  115. }
  116. roomID := 1
  117. //10个免费房间,10个收费房间
  118. for i := 0; i < 10; i++ {
  119. room := Room{
  120. ID: int32(roomID),
  121. RoomType: int32(model.RoomTypeFree),
  122. ChairList: make([]Chair, 5),
  123. BaseAmount: decimal.NewFromInt(3),
  124. Status: RoomStatusWaitReady,
  125. ReadyCh: make(chan bool),
  126. ChooseMasterCh: make(chan bool),
  127. ChooseMulCh: make(chan bool),
  128. OpenCh: make(chan bool),
  129. }
  130. copy(room.ChairList, chairList)
  131. p.RoomMap[int32(roomID)] = room
  132. roomID++
  133. }
  134. for i := 0; i < 10; i++ {
  135. room := Room{
  136. ID: int32(roomID),
  137. RoomType: int32(model.RoomTypeNormal),
  138. ChairList: make([]Chair, 5),
  139. BaseAmount: decimal.NewFromInt(3),
  140. Status: RoomStatusWaitReady,
  141. ReadyCh: make(chan bool),
  142. ChooseMasterCh: make(chan bool),
  143. ChooseMulCh: make(chan bool),
  144. OpenCh: make(chan bool),
  145. }
  146. copy(room.ChairList, chairList)
  147. p.RoomMap[int32(roomID)] = room
  148. roomID++
  149. }
  150. }
  151. func (p *Game) Connect(playerID int32) error {
  152. //处理房间
  153. userModel := model.User{}
  154. err := userModel.GetUserByID(uint32(playerID))
  155. if err != nil {
  156. logrus.Error(err)
  157. return err
  158. }
  159. p.locker.Lock()
  160. defer p.locker.Unlock()
  161. p.JoinNoLock(playerID)
  162. roomList, currPlayerList, logID := gameInstance.GetLogNoLock(int32(playerID))
  163. roomListProto := make([]*gameproto.Room, 0)
  164. userIDList := make([]uint32, 0)
  165. atRoom := false
  166. var currRoom Room
  167. for _, roomItem := range roomList {
  168. baseAmountDeciaml := roomItem.BaseAmount.Mul(decimal.NewFromInt(100))
  169. baseAmount := float64(baseAmountDeciaml.IntPart()) / 100
  170. roomListProto = append(roomListProto, &gameproto.Room{
  171. ID: uint32(roomItem.ID),
  172. Name: roomItem.Name,
  173. RoomType: gameproto.RoomType(roomItem.RoomType),
  174. NeedPwd: roomItem.Passwd != "",
  175. BaseAmount: baseAmount,
  176. ChairUserID1: uint32(roomItem.ChairList[0].PlayerID),
  177. ChairUserID2: uint32(roomItem.ChairList[1].PlayerID),
  178. ChairUserID3: uint32(roomItem.ChairList[2].PlayerID),
  179. ChairUserID4: uint32(roomItem.ChairList[3].PlayerID),
  180. ChairUserID5: uint32(roomItem.ChairList[4].PlayerID),
  181. })
  182. for i := 0; i < 5; i++ {
  183. if roomItem.ChairList[i].PlayerID == 0 {
  184. continue
  185. }
  186. if roomItem.ChairList[i].PlayerID == int32(userModel.ID) {
  187. atRoom = true
  188. currRoom = roomItem
  189. }
  190. userIDList = append(userIDList, uint32(roomItem.ChairList[i].PlayerID))
  191. }
  192. }
  193. var roomDetail gameproto.RoomDetail
  194. if atRoom {
  195. roomItem := currRoom
  196. baseAmountDeciaml := roomItem.BaseAmount.Mul(decimal.NewFromInt(100))
  197. baseAmount := float64(baseAmountDeciaml.IntPart()) / 100
  198. roomDetail = gameproto.RoomDetail{
  199. ID: uint32(roomItem.ID),
  200. Name: roomItem.Name,
  201. RoomType: gameproto.RoomType(roomItem.RoomType),
  202. NeedPwd: roomItem.Passwd != "",
  203. BaseAmount: baseAmount,
  204. ChairUserID1: uint32(roomItem.ChairList[0].PlayerID),
  205. ChairUserID2: uint32(roomItem.ChairList[1].PlayerID),
  206. ChairUserID3: uint32(roomItem.ChairList[2].PlayerID),
  207. ChairUserID4: uint32(roomItem.ChairList[3].PlayerID),
  208. ChairUserID5: uint32(roomItem.ChairList[4].PlayerID),
  209. Status: uint32(currRoom.Status),
  210. StatusStartTime: roomItem.StatusStartTime,
  211. RoomUserList: make([]*gameproto.RoomUser, 0),
  212. TimeNow: time.Now().Unix(),
  213. }
  214. for _, player := range currPlayerList {
  215. balanceFloat, _ := player.Balance.Round(2).Float64()
  216. var cardList []uint32
  217. if player.Status == PlayerStatusOpen {
  218. cardList = GetNNCardList(player.CardList)
  219. }
  220. if player.ID == int32(userModel.ID) {
  221. //如果准备好了,就显示3张牌
  222. if player.Status != PlayerStatusWaitReady {
  223. cardList = GetNNCardListLimit(player.CardList, 3)
  224. }
  225. //如果选择了倍数,就返回5张
  226. if player.Status == PlayerStatusChooseMul {
  227. cardList = GetNNCardList(player.CardList)
  228. }
  229. }
  230. roomDetail.RoomUserList = append(roomDetail.RoomUserList, &gameproto.RoomUser{
  231. UserID: uint32(player.ID),
  232. CardList: cardList,
  233. IsMaster: player.IsMaster,
  234. MasterMul: uint32(player.MasterMul),
  235. Mul: uint32(player.Mul),
  236. Status: uint32(player.Status),
  237. Balance: balanceFloat,
  238. })
  239. }
  240. }
  241. balance, _ := strconv.ParseFloat(userModel.Balance.StringFixed(2), 64)
  242. logrus.Debugf("join game user id:%d", userModel.ID)
  243. var userListProto []*gameproto.User
  244. if len(userIDList) != 0 {
  245. var err error
  246. userListModel, err := (model.User{}).GetUserByIDList(userIDList)
  247. if err != nil {
  248. logrus.Error("failed:", err)
  249. return err
  250. }
  251. userListProto = make([]*gameproto.User, 0)
  252. for _, userModel := range userListModel {
  253. userListProto = append(userListProto, &gameproto.User{
  254. UserID: uint32(userModel.ID),
  255. Name: userModel.Name,
  256. Img: userModel.HeadImg,
  257. })
  258. }
  259. }
  260. connectProto := gameproto.Connect{
  261. UserID: uint32(userModel.ID),
  262. LogID: logID,
  263. Name: userModel.Nickname,
  264. Img: userModel.HeadImg,
  265. Balance: balance,
  266. RoomList: roomListProto,
  267. UserList: userListProto,
  268. RoomDetail: &roomDetail,
  269. }
  270. UserActive(userModel.ID)
  271. nxd.SendMsgToUserK(uint32(userModel.ID), gameproto.NotifyTypeEnum_NotifyTypeConnect, &connectProto)
  272. logrus.Debugf("send connect user id:%d", userModel.ID)
  273. return nil
  274. }
  275. func (p *Game) Join(playerID int32) error {
  276. p.locker.Lock()
  277. defer p.locker.Unlock()
  278. return p.JoinNoLock(playerID)
  279. }
  280. func (p *Game) JoinNoLock(playerID int32) error {
  281. userModel := model.User{}
  282. err := userModel.GetUserByID(uint32(playerID))
  283. if err != nil {
  284. logrus.Error(err)
  285. return err
  286. }
  287. _, exists := p.PlayerMap[playerID]
  288. if exists {
  289. return nil
  290. }
  291. roomID := RoomIDEmpty
  292. room, _, err := p.getUserRoomID(playerID)
  293. if err == nil {
  294. roomID = room.ID
  295. }
  296. player := Player{
  297. ID: playerID,
  298. Name: userModel.Name,
  299. HeadImg: userModel.HeadImg,
  300. Balance: userModel.Balance,
  301. Credits: decimal.NewFromInt(0),
  302. RoomID: roomID,
  303. Status: PlayerStatusWaitReady,
  304. JoinTime: time.Now(),
  305. }
  306. p.PlayerMap[playerID] = player
  307. p.EventInfo(playerID, "加入游戏", "加入游戏")
  308. return nil
  309. }
  310. func (p *Game) Leave(playerID int32) error {
  311. p.locker.Lock()
  312. defer p.locker.Unlock()
  313. playerItem, exists := p.PlayerMap[playerID]
  314. if !exists {
  315. p.EventInfo(playerID, "退出房间", "用户不存在")
  316. return nil
  317. }
  318. if playerItem.Status != PlayerStatusWaitReady {
  319. p.EventInfo(playerID, "退出房间", fmt.Sprintf("用户不是等待开始状态,无法退出,状态:%d", playerItem.Status))
  320. return nil
  321. }
  322. delete(p.PlayerMap, playerID)
  323. for _, roomItem := range p.RoomMap {
  324. for index, chairItem := range roomItem.ChairList {
  325. if chairItem.PlayerID != playerID {
  326. continue
  327. }
  328. chairItem.PlayerID = 0
  329. roomItem.ChairList[index] = chairItem
  330. p.EventInfo(playerID, "退出房间", "清理桌子信息")
  331. //发信息,表示离开
  332. nxd.SendMsgToAllUserK(gameproto.NotifyTypeEnum_NotifyTypeLeaveRoom, &gameproto.LeaveRoom{
  333. RoomID: uint32(roomItem.ID),
  334. ChairID: uint32(chairItem.ID),
  335. UserID: uint32(playerID),
  336. })
  337. return nil
  338. }
  339. }
  340. p.EventInfo(playerID, "退出房间", "清理用户信息")
  341. return nil
  342. }
  343. func (p *Game) ChooseRndChair(playerID int32) error {
  344. p.locker.Lock()
  345. p.EventInfo(playerID, "随机选桌子", "开始")
  346. maxRoomID := -1
  347. maxChairID := -1
  348. maxUserCount := -1
  349. for _, roomItem := range p.RoomMap {
  350. if roomItem.Passwd != "" {
  351. continue
  352. }
  353. userCount := 0
  354. for _, chairItem := range roomItem.ChairList {
  355. if chairItem.PlayerID == 0 {
  356. continue
  357. }
  358. userCount++
  359. }
  360. if userCount == 4 {
  361. continue
  362. }
  363. if userCount > maxUserCount {
  364. maxUserCount = userCount
  365. maxRoomID = int(roomItem.ID)
  366. for _, chairItem := range roomItem.ChairList {
  367. if chairItem.PlayerID != 0 {
  368. continue
  369. }
  370. maxChairID = int(chairItem.ID)
  371. break
  372. }
  373. }
  374. }
  375. if maxRoomID != -1 && maxChairID != -1 {
  376. p.EventInfo(playerID, "随机选桌子", fmt.Sprintf("找到桌子:%d-%d", maxRoomID, maxChairID))
  377. p.locker.Unlock()
  378. return p.ChooseChair(playerID, int32(maxRoomID), int32(maxChairID), "")
  379. }
  380. p.EventInfo(playerID, "随机选桌子", "没有找到桌子")
  381. p.locker.Unlock()
  382. return ErrNotFound
  383. }
  384. func (p *Game) ChooseChair(playerID int32, roomID int32, chairID int32, passwd string) error {
  385. p.locker.Lock()
  386. defer p.locker.Unlock()
  387. p.EventInfo(playerID, "选桌子", "开始")
  388. err := p.ChooseChairNoLock(playerID, roomID, chairID, passwd)
  389. if err != nil {
  390. p.EventInfo(playerID, "选桌子", "选择失败:"+err.Error())
  391. return err
  392. }
  393. p.EventInfo(playerID, "选桌子", "选取成功")
  394. return nil
  395. }
  396. func (p *Game) ChooseChairNoLock(playerID int32, roomID int32, chairID int32, passwd string) error {
  397. logrus.Info("ChooseChair...")
  398. playerItem, exists := p.PlayerMap[playerID]
  399. if !exists {
  400. p.EventInfo(playerID, "选桌子-inner", "用户不存在")
  401. return errors.New("用户不存在")
  402. }
  403. //离开房间
  404. if roomID == 0 {
  405. p.EventInfo(playerID, "选桌子-inner", "退出房间")
  406. for rKey, room := range p.RoomMap {
  407. for cKey, chairItem := range room.ChairList {
  408. if chairItem.PlayerID != playerID {
  409. continue
  410. }
  411. //设置为0
  412. room.ChairList[cKey].PlayerID = 0
  413. p.RoomMap[rKey] = room
  414. playerItem.RoomID = 0
  415. playerItem.Status = PlayerStatusWaitReady
  416. playerItem.IsMaster = false
  417. playerItem.MasterMul = 1
  418. playerItem.Mul = 1
  419. p.PlayerMap[playerID] = playerItem
  420. p.EventInfo(playerID, "选桌子-inner", "退出完成")
  421. //通知
  422. nxd.SendMsgToAllUserK(gameproto.NotifyTypeEnum_NotifyTypeLeaveRoom, &gameproto.LeaveRoom{
  423. RoomID: uint32(room.ID),
  424. ChairID: uint32(chairItem.ID),
  425. UserID: uint32(playerID),
  426. User: &gameproto.User{
  427. UserID: uint32(playerItem.ID),
  428. Name: playerItem.Name,
  429. Img: playerItem.HeadImg,
  430. },
  431. })
  432. return nil
  433. }
  434. }
  435. return nil
  436. }
  437. p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("寻找桌子,房间ID:%d", roomID))
  438. //同一个房间,切换,不影响
  439. if playerItem.RoomID != 0 {
  440. if playerItem.RoomID != roomID && playerItem.RoomID != RoomIDEmpty {
  441. p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("没退出房间,就进入新的房间,不允许,之前房间:%d,进入房间:%d", playerItem.RoomID, roomID))
  442. logrus.Error("没退出房间,就进入新的房间,不允许")
  443. return errors.New("没退出房间,就进入新的房间,不允许")
  444. }
  445. }
  446. roomItem, exists := p.RoomMap[roomID]
  447. if !exists {
  448. p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("房间不存,房间ID:%d", roomID))
  449. return ErrNotFound
  450. }
  451. if roomItem.Passwd != passwd {
  452. p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("房间密码不对,输入密码:%s", passwd))
  453. return errors.New("密码不正确")
  454. }
  455. //椅子不确定,选一个
  456. if chairID == -1 {
  457. p.EventInfo(playerID, "选桌子-inner", "随机选取桌子")
  458. //选一次,判断是否已经在房间,是的话,直接返回之前的桌子
  459. for _, chair := range roomItem.ChairList {
  460. if chair.PlayerID == playerItem.ID {
  461. chairID = chair.ID
  462. break
  463. }
  464. }
  465. if chairID == -1 {
  466. for _, chair := range roomItem.ChairList {
  467. if chair.PlayerID == 0 {
  468. chairID = chair.ID
  469. break
  470. }
  471. }
  472. }
  473. }
  474. p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("选取的桌子:%d", chairID))
  475. if chairID == -1 {
  476. p.EventInfo(playerID, "选桌子-inner", "房间已经满人了")
  477. return errors.New("房间已经满人了")
  478. }
  479. if int(chairID) >= len(roomItem.ChairList) {
  480. p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("chair id 超过范围:%d", chairID))
  481. return errors.New("chair id 超过范围")
  482. }
  483. //够底注才能进
  484. if playerItem.Balance.LessThan(roomItem.BaseAmount) {
  485. p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("余额不足,无法进入房间,余额:%v,限额:%v", playerItem.Balance, roomItem.BaseAmount))
  486. return errors.New("余额不足,无法进入房间")
  487. }
  488. chairItem := roomItem.ChairList[chairID]
  489. if chairItem.PlayerID == playerID {
  490. p.EventInfo(playerID, "选桌子-inner", "已经在桌子上")
  491. return nil
  492. }
  493. if chairItem.PlayerID != 0 {
  494. p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("桌子上有其他人:%d", chairItem.PlayerID))
  495. return errors.New("有人了")
  496. }
  497. roomItem.ChairList[chairID] = Chair{
  498. ID: chairID,
  499. PlayerID: playerID,
  500. }
  501. playerItem.RoomID = roomID
  502. playerItem.Status = PlayerStatusWaitReady
  503. playerItem.IsMaster = false
  504. playerItem.MasterMul = 1
  505. playerItem.Mul = 1
  506. p.PlayerMap[playerID] = playerItem
  507. p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("选完桌子:%d", chairID))
  508. //通知,房间外的用户收到用户进入信息,房间内的,会带上房间详情
  509. userIDList := online.GetAllUser()
  510. roomUserIDlist := make([]int32, 0)
  511. notInRoomUserIDlist := make([]int32, 0)
  512. for _, userID := range userIDList {
  513. currPlayer, exists := p.PlayerMap[int32(userID)]
  514. if !exists {
  515. continue
  516. }
  517. if currPlayer.RoomID == roomID {
  518. roomUserIDlist = append(roomUserIDlist, int32(userID))
  519. } else {
  520. notInRoomUserIDlist = append(notInRoomUserIDlist, int32(userID))
  521. }
  522. }
  523. p.EventInfo(playerID, "选桌子-inner", "通知房间外的用户,有人进入房间")
  524. nxd.SendMsgToUserList(notInRoomUserIDlist, gameproto.NotifyTypeEnum_NotifyTypeJoinRoom, &gameproto.JoinRoom{
  525. RoomID: uint32(roomID),
  526. ChairID: uint32(chairID),
  527. UserID: uint32(playerID),
  528. GameStatus: uint32(roomItem.Status),
  529. User: &gameproto.User{UserID: uint32(playerItem.ID), Name: playerItem.Name, Img: playerItem.HeadImg},
  530. })
  531. baseAmountDeciaml := roomItem.BaseAmount.Mul(decimal.NewFromInt(100))
  532. baseAmount := float64(baseAmountDeciaml.IntPart()) / 100
  533. roomDetail := gameproto.RoomDetail{
  534. ID: uint32(roomItem.ID),
  535. Name: roomItem.Name,
  536. RoomType: gameproto.RoomType(roomItem.RoomType),
  537. NeedPwd: roomItem.Passwd != "",
  538. BaseAmount: baseAmount,
  539. ChairUserID1: uint32(roomItem.ChairList[0].PlayerID),
  540. ChairUserID2: uint32(roomItem.ChairList[1].PlayerID),
  541. ChairUserID3: uint32(roomItem.ChairList[2].PlayerID),
  542. ChairUserID4: uint32(roomItem.ChairList[3].PlayerID),
  543. ChairUserID5: uint32(roomItem.ChairList[4].PlayerID),
  544. Status: uint32(roomItem.Status),
  545. StatusStartTime: roomItem.StatusStartTime,
  546. RoomUserList: make([]*gameproto.RoomUser, 0),
  547. TimeNow: time.Now().Unix(),
  548. }
  549. currPlayerList := make([]Player, 0)
  550. for _, chairItem := range roomItem.ChairList {
  551. if chairItem.PlayerID == 0 {
  552. continue
  553. }
  554. currPlayer, exists := p.PlayerMap[chairItem.PlayerID]
  555. if !exists {
  556. continue
  557. }
  558. currPlayerList = append(currPlayerList, currPlayer)
  559. }
  560. for _, player := range currPlayerList {
  561. balanceFloat, _ := player.Balance.Round(2).Float64()
  562. var cardList []uint32
  563. if player.Status == PlayerStatusOpen {
  564. p.EventInfo(player.ID, "选桌子-inner", "玩家开牌状态,带上牌的信息")
  565. cardList = GetNNCardList(player.CardList)
  566. }
  567. roomDetail.RoomUserList = append(roomDetail.RoomUserList, &gameproto.RoomUser{
  568. UserID: uint32(player.ID),
  569. CardList: cardList,
  570. IsMaster: player.IsMaster,
  571. MasterMul: uint32(player.MasterMul),
  572. Mul: uint32(player.Mul),
  573. Status: uint32(player.Status),
  574. Balance: balanceFloat,
  575. })
  576. }
  577. p.EventInfo(playerID, "选桌子-inner", "通知房间内的用户,有人进入房间,带上房间详情")
  578. nxd.SendMsgToUserList(roomUserIDlist, gameproto.NotifyTypeEnum_NotifyTypeJoinRoom, &gameproto.JoinRoom{
  579. RoomID: uint32(roomID),
  580. ChairID: uint32(chairID),
  581. UserID: uint32(playerID),
  582. GameStatus: uint32(roomItem.Status),
  583. User: &gameproto.User{UserID: uint32(playerItem.ID), Name: playerItem.Name, Img: playerItem.HeadImg},
  584. RoomDetail: &roomDetail,
  585. })
  586. return nil
  587. }
  588. func (p *Game) CreateRoom(playerID int32, roomType int32, passwd, name string, baseAmount int) (*Room, error) {
  589. if roomType != int32(model.RoomTypeFree) && roomType != int32(model.RoomTypeNormal) {
  590. return nil, errors.New("参数错误,房间类型不对")
  591. }
  592. if len(passwd) < 3 {
  593. return nil, errors.New("参数错误,密码长度不能少于3")
  594. }
  595. p.locker.Lock()
  596. defer p.locker.Unlock()
  597. p.EventInfo(playerID, "创建房间", "开始")
  598. baseAmountDecimal := decimal.NewFromInt(int64(baseAmount))
  599. playerItem := p.PlayerMap[playerID]
  600. roomID := len(p.RoomMap) + 1
  601. if playerItem.Balance.LessThan(baseAmountDecimal) {
  602. p.EventInfo(playerID, "创建房间", fmt.Sprintf("余额小于房间准入金额, %v-%v", playerItem.Balance, baseAmountDecimal))
  603. return nil, errors.New("参数错误,余额小于房间准入金额")
  604. }
  605. room := Room{
  606. ID: int32(roomID),
  607. RoomType: roomType,
  608. Name: name,
  609. Passwd: passwd,
  610. ChairList: make([]Chair, 5),
  611. BaseAmount: baseAmountDecimal,
  612. Status: RoomStatusWaitReady,
  613. StatusStartTime: 0,
  614. ReadyCh: make(chan bool),
  615. ChooseMasterCh: make(chan bool),
  616. ChooseMulCh: make(chan bool),
  617. OpenCh: make(chan bool),
  618. }
  619. chairList := make([]Chair, 0)
  620. for i := 0; i < 5; i++ {
  621. chairList = append(chairList, Chair{
  622. ID: int32(i),
  623. PlayerID: 0,
  624. })
  625. }
  626. copy(room.ChairList, chairList)
  627. p.RoomMap[int32(roomID)] = room
  628. p.EventInfo(playerID, "创建房间", "通知创建房间成功")
  629. //通知
  630. nxd.SendMsgToAllUserK(gameproto.NotifyTypeEnum_NotifyTypeCreateRoom, &gameproto.CreateRoom{
  631. ID: uint32(room.ID),
  632. CreatorID: uint32(playerID),
  633. Name: room.Name,
  634. NeedPwd: passwd != "",
  635. RoomType: gameproto.RoomType(room.RoomType),
  636. BaseAmount: float64(room.BaseAmount.Mul(decimal.NewFromInt(100)).IntPart()) / 100,
  637. ChairUserID1: 0,
  638. ChairUserID2: 0,
  639. ChairUserID3: 0,
  640. ChairUserID4: 0,
  641. ChairUserID5: 0,
  642. })
  643. p.EventInfo(playerID, "创建房间", "选择桌子")
  644. err := p.ChooseChairNoLock(playerID, int32(roomID), 0, passwd)
  645. if err != nil {
  646. logrus.Error(err)
  647. return nil, err
  648. }
  649. return &room, nil
  650. }
  651. func (p *Game) Ready(playerID int32) error {
  652. p.locker.Lock()
  653. defer p.locker.Unlock()
  654. playerItem := p.PlayerMap[playerID]
  655. if playerItem.RoomID == RoomIDEmpty {
  656. p.EventInfo(playerID, "准备", "用户不在房间")
  657. return errors.New("没进入房间")
  658. }
  659. if playerItem.Status != PlayerStatusWaitReady {
  660. p.EventInfo(playerID, "准备", fmt.Sprintf("用户状态不对:%d", playerItem.Status))
  661. return errors.New("状态不对")
  662. }
  663. roomItem := p.RoomMap[playerItem.RoomID]
  664. if roomItem.Status != RoomStatusWaitReady && roomItem.Status != RoomStatusReady {
  665. p.EventInfo(playerID, "准备", fmt.Sprintf("房间状态不对:%d", roomItem.Status))
  666. return errors.New("房间状态不对")
  667. }
  668. playerItem.Status = PlayerStatusReady
  669. p.PlayerMap[playerID] = playerItem
  670. //通知
  671. userIDList, err := p.getRoomUserIDList(roomItem.ID)
  672. if err != nil {
  673. logrus.Error(err)
  674. return err
  675. }
  676. p.EventInfo(playerID, "准备", "通知房间用户,已经准备好")
  677. nxd.SendMsgToUserList(userIDList, gameproto.NotifyTypeEnum_NotifyTypeReady, &gameproto.Ready{
  678. RoomID: uint32(roomItem.ID),
  679. UserID: uint32(playerID),
  680. })
  681. readyUserCount := 0
  682. allReady := true
  683. for _, chairItem := range roomItem.ChairList {
  684. if chairItem.PlayerID == 0 {
  685. continue
  686. }
  687. chairPlayer := p.PlayerMap[chairItem.PlayerID]
  688. if chairPlayer.Status != PlayerStatusReady && chairPlayer.Status != PlayerStatusWaitReady {
  689. logrus.Panicf("-状态不对:%d", chairPlayer.Status)
  690. }
  691. if chairPlayer.Status != PlayerStatusReady {
  692. allReady = false
  693. continue
  694. }
  695. readyUserCount++
  696. }
  697. if readyUserCount <= 1 {
  698. p.EventInfo(playerID, "准备", "已经准备的用户<=1")
  699. return nil
  700. }
  701. //两个人以上的情况,判断是否全部准备好
  702. if allReady {
  703. p.EventInfo(playerID, "准备", "全部准备好,发送通知")
  704. go func() {
  705. defer func() {
  706. recover()
  707. }()
  708. roomItem.ReadyCh <- true
  709. }()
  710. }
  711. if roomItem.Status == RoomStatusReady {
  712. p.EventInfo(playerID, "准备", "房间已经是准备状态")
  713. return nil
  714. }
  715. //有两个玩家等待,房间状态切换为准备状态,不能取消了
  716. issue := genIssue(playerItem.RoomID)
  717. chairID, err := p.getChairID(playerID, roomItem)
  718. if err != nil {
  719. logrus.Error(err)
  720. return err
  721. }
  722. roomItem.Issue = issue
  723. roomItem.Status = RoomStatusReady
  724. roomItem.StatusStartTime = time.Now().Unix()
  725. p.RoomMap[playerItem.RoomID] = roomItem
  726. p.EventInfo(playerID, "准备", "开始游戏,期号:"+issue)
  727. nxd.SendMsgToUserList(userIDList, gameproto.NotifyTypeEnum_NotifyTypeWaitReady, &gameproto.WaitReady{
  728. RoomID: uint32(roomItem.ID),
  729. Issue: issue,
  730. })
  731. go func() {
  732. defer func() {
  733. if err := recover(); err != nil {
  734. logrus.Error(err)
  735. fmt.Println("stacktrace from panic: \n" + string(debug.Stack()))
  736. }
  737. }()
  738. p.locker.Lock()
  739. {
  740. p.EventInfo(playerID, "准备", fmt.Sprintf("等待房间准备:%d", roomItem.ID))
  741. }
  742. p.locker.Unlock()
  743. p.WaitReady(roomItem)
  744. func() {
  745. //TODO: 优化并发
  746. p.locker.Lock()
  747. defer p.locker.Unlock()
  748. p.EventInfo(playerID, "准备", "等待房间完毕")
  749. //切换为准备完毕,限制进入了
  750. roomItem.Status = RoomStatusChooseMaster
  751. roomItem.StatusStartTime = time.Now().Unix()
  752. p.RoomMap[roomItem.ID] = roomItem
  753. //已经准备好的用户,生成牌
  754. userIDList, err = p.getRoomUserIDListWithStatus(roomItem.ID, PlayerStatusReady)
  755. if err != nil {
  756. logrus.Error(err)
  757. return
  758. }
  759. err = db.GetDB().Transaction(func(tx *gorm.DB) error {
  760. for _, userID := range userIDList {
  761. openNumCardList := getOpenNumList(issue, roomItem.ID, chairID)
  762. openNumCardStrList := make([]string, 0)
  763. for _, item := range openNumCardList {
  764. openNumCardStrList = append(openNumCardStrList, fmt.Sprintf("%d", item.Num))
  765. }
  766. userItem := p.PlayerMap[userID]
  767. userItem.CardList = CardList2NNCardList(openNumCardList)
  768. p.PlayerMap[userID] = userItem
  769. gameOrderModel := model.GameOrder{
  770. UserID: uint(userID),
  771. RoomID: uint(roomItem.ID),
  772. Issue: issue,
  773. OpenNumber: strings.Join(openNumCardStrList, ","),
  774. BaseAmount: roomItem.BaseAmount,
  775. IsDraw: 0,
  776. DrawTime: nil,
  777. IsMaster: 0,
  778. MasterMul: 0,
  779. Mul: 1,
  780. IsWin: 0,
  781. WinAmount: decimal.NewFromInt(0),
  782. BetIP: "",
  783. GameID: 1,
  784. }
  785. err = tx.Model(gameOrderModel).Create(&gameOrderModel).Error
  786. if err != nil {
  787. logrus.Error(err)
  788. return err
  789. }
  790. }
  791. p.EventInfo(playerID, "准备", "生成订单成功")
  792. return nil
  793. })
  794. if err != nil {
  795. logrus.Error(err)
  796. return
  797. }
  798. }()
  799. p.Run(allReady, roomItem, issue)
  800. }()
  801. return nil
  802. }
  803. func (p *Game) UnReady(playerID int32) error {
  804. p.locker.Lock()
  805. defer p.locker.Unlock()
  806. playerItem := p.PlayerMap[playerID]
  807. if playerItem.RoomID == RoomIDEmpty {
  808. return errors.New("没进入房间")
  809. }
  810. if playerItem.Status != PlayerStatusReady {
  811. return errors.New("状态不对")
  812. }
  813. roomItem := p.RoomMap[playerItem.RoomID]
  814. if roomItem.Status != RoomStatusWaitReady {
  815. return errors.New("状态不对")
  816. }
  817. readyUserCount := 0
  818. for _, chairItem := range roomItem.ChairList {
  819. if chairItem.PlayerID == 0 {
  820. continue
  821. }
  822. chairPlayer := p.PlayerMap[chairItem.PlayerID]
  823. if chairPlayer.Status != PlayerStatusReady {
  824. continue
  825. }
  826. readyUserCount++
  827. }
  828. //设置用户
  829. playerItem.Status = PlayerStatusWaitReady
  830. p.PlayerMap[playerID] = playerItem
  831. //通知
  832. userIDList, err := p.getRoomUserIDList(roomItem.ID)
  833. if err != nil {
  834. logrus.Error(err)
  835. return err
  836. }
  837. nxd.SendMsgToUserList(userIDList, gameproto.NotifyTypeEnum_NotifyTypeUnReady, &gameproto.UnReady{
  838. RoomID: uint32(roomItem.ID),
  839. UserID: uint32(playerID),
  840. })
  841. return nil
  842. }
  843. func (p *Game) ChooseMaster(playerID int32, mul int32) error {
  844. p.locker.Lock()
  845. defer p.locker.Unlock()
  846. playerItem := p.PlayerMap[playerID]
  847. if playerItem.RoomID == RoomIDEmpty {
  848. return errors.New("没进入房间")
  849. }
  850. if playerItem.Status != PlayerStatusReady {
  851. return errors.New("状态不对")
  852. }
  853. roomItem := p.RoomMap[playerItem.RoomID]
  854. if roomItem.Status != RoomStatusChooseMaster {
  855. return errors.New("状态不对")
  856. }
  857. //更新用户信息
  858. playerItem.Status = PlayerStatusChooseMaster
  859. playerItem.MasterMul = int(mul)
  860. p.PlayerMap[playerID] = playerItem
  861. allChoose := true
  862. for _, chairItem := range roomItem.ChairList {
  863. if chairItem.PlayerID == 0 {
  864. continue
  865. }
  866. chairPlayer := p.PlayerMap[chairItem.PlayerID]
  867. if chairPlayer.Status == PlayerStatusReady {
  868. allChoose = false
  869. break
  870. }
  871. }
  872. err := db.GetDB().Model(model.GameOrder{}).Where("user_id = ? and issue = ?", playerID, roomItem.Issue).Update("master_mul", mul).Error
  873. if err != nil {
  874. logrus.Error(err)
  875. return err
  876. }
  877. //通知
  878. userIDList, err := p.getRoomUserIDList(roomItem.ID)
  879. if err != nil {
  880. logrus.Error(err)
  881. return err
  882. }
  883. for _, userID := range userIDList {
  884. nxd.SendMsgToUserK(uint32(userID), gameproto.NotifyTypeEnum_NotifyTypeChooseMaster, &gameproto.ChooseMaster{
  885. UserID: uint32(playerID),
  886. Mul: uint32(mul),
  887. RoomID: uint32(roomItem.ID),
  888. })
  889. }
  890. //全部选完,就进入下一阶段
  891. if allChoose {
  892. go func() {
  893. defer func() {
  894. recover()
  895. }()
  896. roomItem.ChooseMasterCh <- true
  897. }()
  898. }
  899. return nil
  900. }
  901. func (p *Game) ChooseMul(playerID int32, mul int32) error {
  902. p.locker.Lock()
  903. defer p.locker.Unlock()
  904. playerItem := p.PlayerMap[playerID]
  905. if playerItem.RoomID == RoomIDEmpty {
  906. return errors.New("没进入房间")
  907. }
  908. if playerItem.Status != PlayerStatusChooseMaster {
  909. return errors.New("状态不对")
  910. }
  911. roomItem := p.RoomMap[playerItem.RoomID]
  912. if roomItem.Status != RoomStatusChooseMul {
  913. return errors.New("状态不对")
  914. }
  915. //更新用户
  916. playerItem.Status = PlayerStatusChooseMul
  917. playerItem.Mul = int(mul)
  918. p.PlayerMap[playerID] = playerItem
  919. allChoose := true
  920. for _, chairItem := range roomItem.ChairList {
  921. if chairItem.PlayerID == 0 {
  922. continue
  923. }
  924. chairPlayer := p.PlayerMap[chairItem.PlayerID]
  925. if chairPlayer.Status == PlayerStatusChooseMaster {
  926. allChoose = false
  927. break
  928. }
  929. }
  930. err := db.GetDB().Model(model.GameOrder{}).Where("user_id = ? and issue = ?", playerID, roomItem.Issue).Update("mul", mul).Error
  931. if err != nil {
  932. logrus.Error(err)
  933. return err
  934. }
  935. //通知
  936. userIDList, err := p.getRoomUserIDList(roomItem.ID)
  937. if err != nil {
  938. logrus.Error(err)
  939. return err
  940. }
  941. for _, sendToUserID := range userIDList {
  942. var cardList []uint32
  943. if playerID == sendToUserID {
  944. cardList = GetNNCardList(playerItem.CardList)
  945. }
  946. nxd.SendMsgToUserK(uint32(sendToUserID), gameproto.NotifyTypeEnum_NotifyTypeChooseMul, &gameproto.ChooseMul{
  947. UserID: uint32(playerID),
  948. Mul: uint32(mul),
  949. RoomID: uint32(roomItem.ID),
  950. CardList: cardList,
  951. })
  952. }
  953. //全部选完,就进入下一阶段
  954. if allChoose {
  955. go func() {
  956. defer func() {
  957. recover()
  958. }()
  959. roomItem.ChooseMulCh <- true
  960. }()
  961. }
  962. return nil
  963. }
  964. func (p *Game) OpenCard(playerID int32) error {
  965. p.locker.Lock()
  966. defer p.locker.Unlock()
  967. playerItem := p.PlayerMap[playerID]
  968. if playerItem.RoomID == RoomIDEmpty {
  969. return errors.New("没进入房间")
  970. }
  971. if playerItem.Status != PlayerStatusChooseMul {
  972. return errors.New("状态不对")
  973. }
  974. roomItem := p.RoomMap[playerItem.RoomID]
  975. if roomItem.Status != RoomStatusOpen {
  976. return errors.New("状态不对")
  977. }
  978. userIDList, err := p.getRoomUserIDList(roomItem.ID)
  979. if err != nil {
  980. logrus.Error(err)
  981. return err
  982. }
  983. nxd.SendMsgToUserList(userIDList, gameproto.NotifyTypeEnum_NotifyTypeOpen, &gameproto.Open{
  984. RoomID: uint32(roomItem.ID),
  985. UserID: uint32(playerID),
  986. CardList: GetNNCardList(playerItem.CardList),
  987. })
  988. //更新用户
  989. playerItem.Status = PlayerStatusOpen
  990. p.PlayerMap[playerID] = playerItem
  991. allOpen := true
  992. for _, chairItem := range roomItem.ChairList {
  993. if chairItem.PlayerID == 0 {
  994. continue
  995. }
  996. chairPlayer := p.PlayerMap[chairItem.PlayerID]
  997. if chairPlayer.Status == PlayerStatusChooseMul {
  998. allOpen = false
  999. break
  1000. }
  1001. }
  1002. //全部选完,就进入下一阶段
  1003. if allOpen {
  1004. go func() {
  1005. defer func() {
  1006. recover()
  1007. }()
  1008. roomItem.OpenCh <- true
  1009. }()
  1010. }
  1011. return nil
  1012. }
  1013. func (p *Game) getUserRoomID(playerID int32) (*Room, int32, error) {
  1014. for _, roomItem := range p.RoomMap {
  1015. for _, chairItem := range roomItem.ChairList {
  1016. if chairItem.PlayerID != playerID {
  1017. continue
  1018. }
  1019. return &roomItem, chairItem.ID, nil
  1020. }
  1021. }
  1022. return nil, 0, ErrNotFound
  1023. }
  1024. func (p *Game) getChairID(playerID int32, room Room) (int32, error) {
  1025. for i, chairID := range room.ChairList {
  1026. if playerID == chairID.PlayerID {
  1027. return int32(i), nil
  1028. }
  1029. }
  1030. return 0, ErrNotFound
  1031. }
  1032. func (p *Game) GetLogNoLock(playerID int32) ([]Room, []Player, int64) {
  1033. roomList := make([]Room, 0)
  1034. playerList := make([]Player, 0)
  1035. for _, roomItem := range p.RoomMap {
  1036. roomList = append(roomList, roomItem)
  1037. found := false
  1038. for _, chairItem := range roomItem.ChairList {
  1039. if chairItem.PlayerID != playerID {
  1040. continue
  1041. }
  1042. found = true
  1043. break
  1044. }
  1045. if !found {
  1046. continue
  1047. }
  1048. for _, chairItem := range roomItem.ChairList {
  1049. if chairItem.PlayerID == 0 {
  1050. continue
  1051. }
  1052. player := p.PlayerMap[chairItem.PlayerID]
  1053. playerList = append(playerList, player)
  1054. }
  1055. }
  1056. return roomList, playerList, p.LogID
  1057. }
  1058. func genIssue(roomID int32) string {
  1059. return fmt.Sprintf("%d-%d", roomID, time.Now().Unix())
  1060. }
  1061. var cardCache = cache.New(1*time.Minute, 2*time.Minute)
  1062. func getOpenNumList(issue string, roomID, chairID int32) []Card {
  1063. cardCache.Get(issue)
  1064. cardList := make([]Card, 0)
  1065. for c := 0; c < 5; c++ {
  1066. cardList = append(cardList, Card{
  1067. Suit: rand.Intn(4) + 1,
  1068. Num: rand.Intn(13) + 1,
  1069. })
  1070. }
  1071. return cardList
  1072. }