//描述整个游戏,提供接口给外部使用,挂了,就全部挂 /** ## 游戏中的情况下,用户退出 使用延迟退出机制,游戏中的用户,不处理退出的事件,等这盘游戏结束后处理 如果没有游戏中的情况,则马上处理 */ package qznn import ( "errors" "fmt" "math/rand" "runtime/debug" "strconv" "strings" "time" "github.com/bwmarrin/snowflake" "github.com/patrickmn/go-cache" "github.com/sasha-s/go-deadlock" "github.com/shopspring/decimal" "github.com/sirupsen/logrus" "gogs.daxia.dev/huanan/pkg.daxia.dev/db" "gorm.io/gorm" "nn.daxia.dev/gameproto" "nn.daxia.dev/model" "nn.daxia.dev/nxd" "nn.daxia.dev/online" ) type kkMap map[string]interface{} type PlayerStatus int type RoomStatus int var ErrNotFound = errors.New("record not found") var ErrPwd = errors.New("password not correct") const ( RoomIDEmpty = int32(0) PlayerStatusWaitReady PlayerStatus = 1 //等待准备 PlayerStatusReady PlayerStatus = 2 //已经准备 PlayerStatusChooseMaster PlayerStatus = 3 //已经选择庄 PlayerStatusChooseMul PlayerStatus = 4 //已经选择倍数 PlayerStatusOpen PlayerStatus = 5 //已经开奖 RoomStatusWaitReady RoomStatus = 1 //等待准备 RoomStatusReady RoomStatus = 2 //已经有两个准备好,等待其他玩家准备 RoomStatusChooseMaster RoomStatus = 3 //正在选择庄家 RoomStatusChooseMul RoomStatus = 4 //正在选择倍数 RoomStatusOpen RoomStatus = 5 //正在开奖 ) type Chair struct { ID int32 //索引,1-5 PlayerID int32 //用户ID } type Room struct { ID int32 //房间ID Name string //名称 Passwd string //密码 RoomType int32 //房间类型 ChairList []Chair //桌子 BaseAmount decimal.Decimal //底金 Issue string //当前期号 Status RoomStatus //房间当前状态 StatusStartTime int64 //房间当前状态开始的时间 ReadyCh chan bool //当房间全部用户准备好,触发 ChooseMasterCh chan bool //全部选择完庄,触发 ChooseMulCh chan bool //全部选择完倍数,触发 OpenCh chan bool //全部开牌完毕,触发 } type Player struct { ID int32 Name string HeadImg string Balance decimal.Decimal //余额 Credits decimal.Decimal //游戏积分 RoomID int32 //当前所在房间ID JoinTime time.Time //加入时间 Status PlayerStatus IsMaster bool //是否为庄家 MasterMul int //抢庄的倍数 Mul int //倍数 CardList NNCardList //牌 } type Game struct { RoomMap map[int32]Room PlayerMap map[int32]Player IDGener *snowflake.Node LogID int64 //nextDelPlayerIDList *list.List //下一轮游戏开始会删,可能离线了 locker deadlock.Mutex } var gameInstance *Game func GetInstance() *Game { if gameInstance != nil { return gameInstance } node, err := snowflake.NewNode(1) if err != nil { logrus.Error(err) return nil } gameInstance = &Game{ RoomMap: map[int32]Room{}, PlayerMap: map[int32]Player{}, IDGener: node, //nextDelPlayerIDList: list.New(), locker: deadlock.Mutex{}, } gameInstance.Start() return gameInstance } func (p *Game) Start() { //初始化房间 chairList := make([]Chair, 0) for i := 0; i < 5; i++ { chairList = append(chairList, Chair{ ID: int32(i), PlayerID: 0, }) } roomID := 1 //10个免费房间,10个收费房间 for i := 0; i < 10; i++ { room := Room{ ID: int32(roomID), RoomType: int32(model.RoomTypeFree), ChairList: make([]Chair, 5), BaseAmount: decimal.NewFromInt(3), Status: RoomStatusWaitReady, ReadyCh: make(chan bool), ChooseMasterCh: make(chan bool), ChooseMulCh: make(chan bool), OpenCh: make(chan bool), } copy(room.ChairList, chairList) p.RoomMap[int32(roomID)] = room roomID++ } for i := 0; i < 10; i++ { room := Room{ ID: int32(roomID), RoomType: int32(model.RoomTypeNormal), ChairList: make([]Chair, 5), BaseAmount: decimal.NewFromInt(3), Status: RoomStatusWaitReady, ReadyCh: make(chan bool), ChooseMasterCh: make(chan bool), ChooseMulCh: make(chan bool), OpenCh: make(chan bool), } copy(room.ChairList, chairList) p.RoomMap[int32(roomID)] = room roomID++ } } func (p *Game) Connect(playerID int32) error { //处理房间 userModel := model.User{} err := userModel.GetUserByID(uint32(playerID)) if err != nil { logrus.Error(err) return err } p.locker.Lock() defer p.locker.Unlock() p.JoinNoLock(playerID) roomList, currPlayerList, logID := gameInstance.GetLogNoLock(int32(playerID)) roomListProto := make([]*gameproto.Room, 0) userIDList := make([]uint32, 0) atRoom := false var currRoom Room for _, roomItem := range roomList { baseAmountDeciaml := roomItem.BaseAmount.Mul(decimal.NewFromInt(100)) baseAmount := float64(baseAmountDeciaml.IntPart()) / 100 roomListProto = append(roomListProto, &gameproto.Room{ ID: uint32(roomItem.ID), Name: roomItem.Name, RoomType: gameproto.RoomType(roomItem.RoomType), NeedPwd: roomItem.Passwd != "", BaseAmount: baseAmount, ChairUserID1: uint32(roomItem.ChairList[0].PlayerID), ChairUserID2: uint32(roomItem.ChairList[1].PlayerID), ChairUserID3: uint32(roomItem.ChairList[2].PlayerID), ChairUserID4: uint32(roomItem.ChairList[3].PlayerID), ChairUserID5: uint32(roomItem.ChairList[4].PlayerID), }) for i := 0; i < 5; i++ { if roomItem.ChairList[i].PlayerID == 0 { continue } if roomItem.ChairList[i].PlayerID == int32(userModel.ID) { atRoom = true currRoom = roomItem } userIDList = append(userIDList, uint32(roomItem.ChairList[i].PlayerID)) } } var roomDetail gameproto.RoomDetail if atRoom { roomItem := currRoom baseAmountDeciaml := roomItem.BaseAmount.Mul(decimal.NewFromInt(100)) baseAmount := float64(baseAmountDeciaml.IntPart()) / 100 roomDetail = gameproto.RoomDetail{ ID: uint32(roomItem.ID), Name: roomItem.Name, RoomType: gameproto.RoomType(roomItem.RoomType), NeedPwd: roomItem.Passwd != "", BaseAmount: baseAmount, ChairUserID1: uint32(roomItem.ChairList[0].PlayerID), ChairUserID2: uint32(roomItem.ChairList[1].PlayerID), ChairUserID3: uint32(roomItem.ChairList[2].PlayerID), ChairUserID4: uint32(roomItem.ChairList[3].PlayerID), ChairUserID5: uint32(roomItem.ChairList[4].PlayerID), Status: uint32(currRoom.Status), StatusStartTime: roomItem.StatusStartTime, RoomUserList: make([]*gameproto.RoomUser, 0), TimeNow: time.Now().Unix(), } for _, player := range currPlayerList { balanceFloat, _ := player.Balance.Round(2).Float64() var cardList []uint32 if player.Status == PlayerStatusOpen { cardList = GetNNCardList(player.CardList) } if player.ID == int32(userModel.ID) { //如果准备好了,就显示3张牌 if player.Status != PlayerStatusWaitReady { cardList = GetNNCardListLimit(player.CardList, 3) } //如果选择了倍数,就返回5张 if player.Status == PlayerStatusChooseMul { cardList = GetNNCardList(player.CardList) } } roomDetail.RoomUserList = append(roomDetail.RoomUserList, &gameproto.RoomUser{ UserID: uint32(player.ID), CardList: cardList, IsMaster: player.IsMaster, MasterMul: uint32(player.MasterMul), Mul: uint32(player.Mul), Status: uint32(player.Status), Balance: balanceFloat, }) } } balance, _ := strconv.ParseFloat(userModel.Balance.StringFixed(2), 64) logrus.Debugf("join game user id:%d", userModel.ID) var userListProto []*gameproto.User if len(userIDList) != 0 { var err error userListModel, err := (model.User{}).GetUserByIDList(userIDList) if err != nil { logrus.Error("failed:", err) return err } userListProto = make([]*gameproto.User, 0) for _, userModel := range userListModel { userListProto = append(userListProto, &gameproto.User{ UserID: uint32(userModel.ID), Name: userModel.Name, Img: userModel.HeadImg, }) } } connectProto := gameproto.Connect{ UserID: uint32(userModel.ID), LogID: logID, Name: userModel.Nickname, Img: userModel.HeadImg, Balance: balance, RoomList: roomListProto, UserList: userListProto, RoomDetail: &roomDetail, } UserActive(userModel.ID) nxd.SendMsgToUserK(uint32(userModel.ID), gameproto.NotifyTypeEnum_NotifyTypeConnect, &connectProto) logrus.Debugf("send connect user id:%d", userModel.ID) return nil } func (p *Game) Join(playerID int32) error { p.locker.Lock() defer p.locker.Unlock() return p.JoinNoLock(playerID) } func (p *Game) JoinNoLock(playerID int32) error { userModel := model.User{} err := userModel.GetUserByID(uint32(playerID)) if err != nil { logrus.Error(err) return err } _, exists := p.PlayerMap[playerID] if exists { return nil } roomID := RoomIDEmpty room, _, err := p.getUserRoomID(playerID) if err == nil { roomID = room.ID } player := Player{ ID: playerID, Name: userModel.Name, HeadImg: userModel.HeadImg, Balance: userModel.Balance, Credits: decimal.NewFromInt(0), RoomID: roomID, Status: PlayerStatusWaitReady, JoinTime: time.Now(), } p.PlayerMap[playerID] = player p.EventInfo(playerID, "加入游戏", "加入游戏") return nil } func (p *Game) Leave(playerID int32) error { p.locker.Lock() defer p.locker.Unlock() playerItem, exists := p.PlayerMap[playerID] if !exists { p.EventInfo(playerID, "退出房间", "用户不存在") return nil } if playerItem.Status != PlayerStatusWaitReady { p.EventInfo(playerID, "退出房间", fmt.Sprintf("用户不是等待开始状态,无法退出,状态:%d", playerItem.Status)) return nil } delete(p.PlayerMap, playerID) for _, roomItem := range p.RoomMap { for index, chairItem := range roomItem.ChairList { if chairItem.PlayerID != playerID { continue } chairItem.PlayerID = 0 roomItem.ChairList[index] = chairItem p.EventInfo(playerID, "退出房间", "清理桌子信息") //发信息,表示离开 nxd.SendMsgToAllUserK(gameproto.NotifyTypeEnum_NotifyTypeLeaveRoom, &gameproto.LeaveRoom{ RoomID: uint32(roomItem.ID), ChairID: uint32(chairItem.ID), UserID: uint32(playerID), }) return nil } } p.EventInfo(playerID, "退出房间", "清理用户信息") return nil } func (p *Game) ChooseRndChair(playerID int32) error { p.locker.Lock() p.EventInfo(playerID, "随机选桌子", "开始") maxRoomID := -1 maxChairID := -1 maxUserCount := -1 for _, roomItem := range p.RoomMap { if roomItem.Passwd != "" { continue } userCount := 0 for _, chairItem := range roomItem.ChairList { if chairItem.PlayerID == 0 { continue } userCount++ } if userCount == 4 { continue } if userCount > maxUserCount { maxUserCount = userCount maxRoomID = int(roomItem.ID) for _, chairItem := range roomItem.ChairList { if chairItem.PlayerID != 0 { continue } maxChairID = int(chairItem.ID) break } } } if maxRoomID != -1 && maxChairID != -1 { p.EventInfo(playerID, "随机选桌子", fmt.Sprintf("找到桌子:%d-%d", maxRoomID, maxChairID)) p.locker.Unlock() return p.ChooseChair(playerID, int32(maxRoomID), int32(maxChairID), "") } p.EventInfo(playerID, "随机选桌子", "没有找到桌子") p.locker.Unlock() return ErrNotFound } func (p *Game) ChooseChair(playerID int32, roomID int32, chairID int32, passwd string) error { p.locker.Lock() defer p.locker.Unlock() p.EventInfo(playerID, "选桌子", "开始") err := p.ChooseChairNoLock(playerID, roomID, chairID, passwd) if err != nil { p.EventInfo(playerID, "选桌子", "选择失败:"+err.Error()) return err } p.EventInfo(playerID, "选桌子", "选取成功") return nil } func (p *Game) ChooseChairNoLock(playerID int32, roomID int32, chairID int32, passwd string) error { logrus.Info("ChooseChair...") playerItem, exists := p.PlayerMap[playerID] if !exists { p.EventInfo(playerID, "选桌子-inner", "用户不存在") return errors.New("用户不存在") } //离开房间 if roomID == 0 { p.EventInfo(playerID, "选桌子-inner", "退出房间") for rKey, room := range p.RoomMap { for cKey, chairItem := range room.ChairList { if chairItem.PlayerID != playerID { continue } //设置为0 room.ChairList[cKey].PlayerID = 0 p.RoomMap[rKey] = room playerItem.RoomID = 0 playerItem.Status = PlayerStatusWaitReady playerItem.IsMaster = false playerItem.MasterMul = 1 playerItem.Mul = 1 p.PlayerMap[playerID] = playerItem p.EventInfo(playerID, "选桌子-inner", "退出完成") //通知 nxd.SendMsgToAllUserK(gameproto.NotifyTypeEnum_NotifyTypeLeaveRoom, &gameproto.LeaveRoom{ RoomID: uint32(room.ID), ChairID: uint32(chairItem.ID), UserID: uint32(playerID), User: &gameproto.User{ UserID: uint32(playerItem.ID), Name: playerItem.Name, Img: playerItem.HeadImg, }, }) return nil } } return nil } p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("寻找桌子,房间ID:%d", roomID)) //同一个房间,切换,不影响 if playerItem.RoomID != 0 { if playerItem.RoomID != roomID && playerItem.RoomID != RoomIDEmpty { p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("没退出房间,就进入新的房间,不允许,之前房间:%d,进入房间:%d", playerItem.RoomID, roomID)) logrus.Error("没退出房间,就进入新的房间,不允许") return errors.New("没退出房间,就进入新的房间,不允许") } } roomItem, exists := p.RoomMap[roomID] if !exists { p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("房间不存,房间ID:%d", roomID)) return ErrNotFound } if roomItem.Passwd != passwd { p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("房间密码不对,输入密码:%s", passwd)) return errors.New("密码不正确") } //椅子不确定,选一个 if chairID == -1 { p.EventInfo(playerID, "选桌子-inner", "随机选取桌子") //选一次,判断是否已经在房间,是的话,直接返回之前的桌子 for _, chair := range roomItem.ChairList { if chair.PlayerID == playerItem.ID { chairID = chair.ID break } } if chairID == -1 { for _, chair := range roomItem.ChairList { if chair.PlayerID == 0 { chairID = chair.ID break } } } } p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("选取的桌子:%d", chairID)) if chairID == -1 { p.EventInfo(playerID, "选桌子-inner", "房间已经满人了") return errors.New("房间已经满人了") } if int(chairID) >= len(roomItem.ChairList) { p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("chair id 超过范围:%d", chairID)) return errors.New("chair id 超过范围") } //够底注才能进 if playerItem.Balance.LessThan(roomItem.BaseAmount) { p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("余额不足,无法进入房间,余额:%v,限额:%v", playerItem.Balance, roomItem.BaseAmount)) return errors.New("余额不足,无法进入房间") } chairItem := roomItem.ChairList[chairID] if chairItem.PlayerID == playerID { p.EventInfo(playerID, "选桌子-inner", "已经在桌子上") return nil } if chairItem.PlayerID != 0 { p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("桌子上有其他人:%d", chairItem.PlayerID)) return errors.New("有人了") } roomItem.ChairList[chairID] = Chair{ ID: chairID, PlayerID: playerID, } playerItem.RoomID = roomID playerItem.Status = PlayerStatusWaitReady playerItem.IsMaster = false playerItem.MasterMul = 1 playerItem.Mul = 1 p.PlayerMap[playerID] = playerItem p.EventInfo(playerID, "选桌子-inner", fmt.Sprintf("选完桌子:%d", chairID)) //通知,房间外的用户收到用户进入信息,房间内的,会带上房间详情 userIDList := online.GetAllUser() roomUserIDlist := make([]int32, 0) notInRoomUserIDlist := make([]int32, 0) for _, userID := range userIDList { currPlayer, exists := p.PlayerMap[int32(userID)] if !exists { continue } if currPlayer.RoomID == roomID { roomUserIDlist = append(roomUserIDlist, int32(userID)) } else { notInRoomUserIDlist = append(notInRoomUserIDlist, int32(userID)) } } p.EventInfo(playerID, "选桌子-inner", "通知房间外的用户,有人进入房间") nxd.SendMsgToUserList(notInRoomUserIDlist, gameproto.NotifyTypeEnum_NotifyTypeJoinRoom, &gameproto.JoinRoom{ RoomID: uint32(roomID), ChairID: uint32(chairID), UserID: uint32(playerID), GameStatus: uint32(roomItem.Status), User: &gameproto.User{UserID: uint32(playerItem.ID), Name: playerItem.Name, Img: playerItem.HeadImg}, }) baseAmountDeciaml := roomItem.BaseAmount.Mul(decimal.NewFromInt(100)) baseAmount := float64(baseAmountDeciaml.IntPart()) / 100 roomDetail := gameproto.RoomDetail{ ID: uint32(roomItem.ID), Name: roomItem.Name, RoomType: gameproto.RoomType(roomItem.RoomType), NeedPwd: roomItem.Passwd != "", BaseAmount: baseAmount, ChairUserID1: uint32(roomItem.ChairList[0].PlayerID), ChairUserID2: uint32(roomItem.ChairList[1].PlayerID), ChairUserID3: uint32(roomItem.ChairList[2].PlayerID), ChairUserID4: uint32(roomItem.ChairList[3].PlayerID), ChairUserID5: uint32(roomItem.ChairList[4].PlayerID), Status: uint32(roomItem.Status), StatusStartTime: roomItem.StatusStartTime, RoomUserList: make([]*gameproto.RoomUser, 0), TimeNow: time.Now().Unix(), } currPlayerList := make([]Player, 0) for _, chairItem := range roomItem.ChairList { if chairItem.PlayerID == 0 { continue } currPlayer, exists := p.PlayerMap[chairItem.PlayerID] if !exists { continue } currPlayerList = append(currPlayerList, currPlayer) } for _, player := range currPlayerList { balanceFloat, _ := player.Balance.Round(2).Float64() var cardList []uint32 if player.Status == PlayerStatusOpen { p.EventInfo(player.ID, "选桌子-inner", "玩家开牌状态,带上牌的信息") cardList = GetNNCardList(player.CardList) } roomDetail.RoomUserList = append(roomDetail.RoomUserList, &gameproto.RoomUser{ UserID: uint32(player.ID), CardList: cardList, IsMaster: player.IsMaster, MasterMul: uint32(player.MasterMul), Mul: uint32(player.Mul), Status: uint32(player.Status), Balance: balanceFloat, }) } p.EventInfo(playerID, "选桌子-inner", "通知房间内的用户,有人进入房间,带上房间详情") nxd.SendMsgToUserList(roomUserIDlist, gameproto.NotifyTypeEnum_NotifyTypeJoinRoom, &gameproto.JoinRoom{ RoomID: uint32(roomID), ChairID: uint32(chairID), UserID: uint32(playerID), GameStatus: uint32(roomItem.Status), User: &gameproto.User{UserID: uint32(playerItem.ID), Name: playerItem.Name, Img: playerItem.HeadImg}, RoomDetail: &roomDetail, }) return nil } func (p *Game) CreateRoom(playerID int32, roomType int32, passwd, name string, baseAmount int) (*Room, error) { if roomType != int32(model.RoomTypeFree) && roomType != int32(model.RoomTypeNormal) { return nil, errors.New("参数错误,房间类型不对") } if len(passwd) < 3 { return nil, errors.New("参数错误,密码长度不能少于3") } p.locker.Lock() defer p.locker.Unlock() p.EventInfo(playerID, "创建房间", "开始") baseAmountDecimal := decimal.NewFromInt(int64(baseAmount)) playerItem := p.PlayerMap[playerID] roomID := len(p.RoomMap) + 1 if playerItem.Balance.LessThan(baseAmountDecimal) { p.EventInfo(playerID, "创建房间", fmt.Sprintf("余额小于房间准入金额, %v-%v", playerItem.Balance, baseAmountDecimal)) return nil, errors.New("参数错误,余额小于房间准入金额") } room := Room{ ID: int32(roomID), RoomType: roomType, Name: name, Passwd: passwd, ChairList: make([]Chair, 5), BaseAmount: baseAmountDecimal, Status: RoomStatusWaitReady, StatusStartTime: 0, ReadyCh: make(chan bool), ChooseMasterCh: make(chan bool), ChooseMulCh: make(chan bool), OpenCh: make(chan bool), } chairList := make([]Chair, 0) for i := 0; i < 5; i++ { chairList = append(chairList, Chair{ ID: int32(i), PlayerID: 0, }) } copy(room.ChairList, chairList) p.RoomMap[int32(roomID)] = room p.EventInfo(playerID, "创建房间", "通知创建房间成功") //通知 nxd.SendMsgToAllUserK(gameproto.NotifyTypeEnum_NotifyTypeCreateRoom, &gameproto.CreateRoom{ ID: uint32(room.ID), CreatorID: uint32(playerID), Name: room.Name, NeedPwd: passwd != "", RoomType: gameproto.RoomType(room.RoomType), BaseAmount: float64(room.BaseAmount.Mul(decimal.NewFromInt(100)).IntPart()) / 100, ChairUserID1: 0, ChairUserID2: 0, ChairUserID3: 0, ChairUserID4: 0, ChairUserID5: 0, }) p.EventInfo(playerID, "创建房间", "选择桌子") err := p.ChooseChairNoLock(playerID, int32(roomID), 0, passwd) if err != nil { logrus.Error(err) return nil, err } return &room, nil } func (p *Game) Ready(playerID int32) error { p.locker.Lock() defer p.locker.Unlock() playerItem := p.PlayerMap[playerID] if playerItem.RoomID == RoomIDEmpty { p.EventInfo(playerID, "准备", "用户不在房间") return errors.New("没进入房间") } if playerItem.Status != PlayerStatusWaitReady { p.EventInfo(playerID, "准备", fmt.Sprintf("用户状态不对:%d", playerItem.Status)) return errors.New("状态不对") } roomItem := p.RoomMap[playerItem.RoomID] if roomItem.Status != RoomStatusWaitReady && roomItem.Status != RoomStatusReady { p.EventInfo(playerID, "准备", fmt.Sprintf("房间状态不对:%d", roomItem.Status)) return errors.New("房间状态不对") } playerItem.Status = PlayerStatusReady p.PlayerMap[playerID] = playerItem //通知 userIDList, err := p.getRoomUserIDList(roomItem.ID) if err != nil { logrus.Error(err) return err } p.EventInfo(playerID, "准备", "通知房间用户,已经准备好") nxd.SendMsgToUserList(userIDList, gameproto.NotifyTypeEnum_NotifyTypeReady, &gameproto.Ready{ RoomID: uint32(roomItem.ID), UserID: uint32(playerID), }) readyUserCount := 0 allReady := true for _, chairItem := range roomItem.ChairList { if chairItem.PlayerID == 0 { continue } chairPlayer := p.PlayerMap[chairItem.PlayerID] if chairPlayer.Status != PlayerStatusReady && chairPlayer.Status != PlayerStatusWaitReady { logrus.Panicf("-状态不对:%d", chairPlayer.Status) } if chairPlayer.Status != PlayerStatusReady { allReady = false continue } readyUserCount++ } if readyUserCount <= 1 { p.EventInfo(playerID, "准备", "已经准备的用户<=1") return nil } //两个人以上的情况,判断是否全部准备好 if allReady { p.EventInfo(playerID, "准备", "全部准备好,发送通知") go func() { defer func() { recover() }() roomItem.ReadyCh <- true }() } if roomItem.Status == RoomStatusReady { p.EventInfo(playerID, "准备", "房间已经是准备状态") return nil } //有两个玩家等待,房间状态切换为准备状态,不能取消了 issue := genIssue(playerItem.RoomID) chairID, err := p.getChairID(playerID, roomItem) if err != nil { logrus.Error(err) return err } roomItem.Issue = issue roomItem.Status = RoomStatusReady roomItem.StatusStartTime = time.Now().Unix() p.RoomMap[playerItem.RoomID] = roomItem p.EventInfo(playerID, "准备", "开始游戏,期号:"+issue) nxd.SendMsgToUserList(userIDList, gameproto.NotifyTypeEnum_NotifyTypeWaitReady, &gameproto.WaitReady{ RoomID: uint32(roomItem.ID), Issue: issue, }) go func() { defer func() { if err := recover(); err != nil { logrus.Error(err) fmt.Println("stacktrace from panic: \n" + string(debug.Stack())) } }() p.locker.Lock() { p.EventInfo(playerID, "准备", fmt.Sprintf("等待房间准备:%d", roomItem.ID)) } p.locker.Unlock() p.WaitReady(roomItem) func() { //TODO: 优化并发 p.locker.Lock() defer p.locker.Unlock() p.EventInfo(playerID, "准备", "等待房间完毕") //切换为准备完毕,限制进入了 roomItem.Status = RoomStatusChooseMaster roomItem.StatusStartTime = time.Now().Unix() p.RoomMap[roomItem.ID] = roomItem //已经准备好的用户,生成牌 userIDList, err = p.getRoomUserIDListWithStatus(roomItem.ID, PlayerStatusReady) if err != nil { logrus.Error(err) return } err = db.GetDB().Transaction(func(tx *gorm.DB) error { for _, userID := range userIDList { openNumCardList := getOpenNumList(issue, roomItem.ID, chairID) openNumCardStrList := make([]string, 0) for _, item := range openNumCardList { openNumCardStrList = append(openNumCardStrList, fmt.Sprintf("%d", item.Num)) } userItem := p.PlayerMap[userID] userItem.CardList = CardList2NNCardList(openNumCardList) p.PlayerMap[userID] = userItem gameOrderModel := model.GameOrder{ UserID: uint(userID), RoomID: uint(roomItem.ID), Issue: issue, OpenNumber: strings.Join(openNumCardStrList, ","), BaseAmount: roomItem.BaseAmount, IsDraw: 0, DrawTime: nil, IsMaster: 0, MasterMul: 0, Mul: 1, IsWin: 0, WinAmount: decimal.NewFromInt(0), BetIP: "", GameID: 1, } err = tx.Model(gameOrderModel).Create(&gameOrderModel).Error if err != nil { logrus.Error(err) return err } } p.EventInfo(playerID, "准备", "生成订单成功") return nil }) if err != nil { logrus.Error(err) return } }() p.Run(allReady, roomItem, issue) }() return nil } func (p *Game) UnReady(playerID int32) error { p.locker.Lock() defer p.locker.Unlock() playerItem := p.PlayerMap[playerID] if playerItem.RoomID == RoomIDEmpty { return errors.New("没进入房间") } if playerItem.Status != PlayerStatusReady { return errors.New("状态不对") } roomItem := p.RoomMap[playerItem.RoomID] if roomItem.Status != RoomStatusWaitReady { return errors.New("状态不对") } readyUserCount := 0 for _, chairItem := range roomItem.ChairList { if chairItem.PlayerID == 0 { continue } chairPlayer := p.PlayerMap[chairItem.PlayerID] if chairPlayer.Status != PlayerStatusReady { continue } readyUserCount++ } //设置用户 playerItem.Status = PlayerStatusWaitReady p.PlayerMap[playerID] = playerItem //通知 userIDList, err := p.getRoomUserIDList(roomItem.ID) if err != nil { logrus.Error(err) return err } nxd.SendMsgToUserList(userIDList, gameproto.NotifyTypeEnum_NotifyTypeUnReady, &gameproto.UnReady{ RoomID: uint32(roomItem.ID), UserID: uint32(playerID), }) return nil } func (p *Game) ChooseMaster(playerID int32, mul int32) error { p.locker.Lock() defer p.locker.Unlock() playerItem := p.PlayerMap[playerID] if playerItem.RoomID == RoomIDEmpty { return errors.New("没进入房间") } if playerItem.Status != PlayerStatusReady { return errors.New("状态不对") } roomItem := p.RoomMap[playerItem.RoomID] if roomItem.Status != RoomStatusChooseMaster { return errors.New("状态不对") } //更新用户信息 playerItem.Status = PlayerStatusChooseMaster playerItem.MasterMul = int(mul) p.PlayerMap[playerID] = playerItem allChoose := true for _, chairItem := range roomItem.ChairList { if chairItem.PlayerID == 0 { continue } chairPlayer := p.PlayerMap[chairItem.PlayerID] if chairPlayer.Status == PlayerStatusReady { allChoose = false break } } err := db.GetDB().Model(model.GameOrder{}).Where("user_id = ? and issue = ?", playerID, roomItem.Issue).Update("master_mul", mul).Error if err != nil { logrus.Error(err) return err } //通知 userIDList, err := p.getRoomUserIDList(roomItem.ID) if err != nil { logrus.Error(err) return err } for _, userID := range userIDList { nxd.SendMsgToUserK(uint32(userID), gameproto.NotifyTypeEnum_NotifyTypeChooseMaster, &gameproto.ChooseMaster{ UserID: uint32(playerID), Mul: uint32(mul), RoomID: uint32(roomItem.ID), }) } //全部选完,就进入下一阶段 if allChoose { go func() { defer func() { recover() }() roomItem.ChooseMasterCh <- true }() } return nil } func (p *Game) ChooseMul(playerID int32, mul int32) error { p.locker.Lock() defer p.locker.Unlock() playerItem := p.PlayerMap[playerID] if playerItem.RoomID == RoomIDEmpty { return errors.New("没进入房间") } if playerItem.Status != PlayerStatusChooseMaster { return errors.New("状态不对") } roomItem := p.RoomMap[playerItem.RoomID] if roomItem.Status != RoomStatusChooseMul { return errors.New("状态不对") } //更新用户 playerItem.Status = PlayerStatusChooseMul playerItem.Mul = int(mul) p.PlayerMap[playerID] = playerItem allChoose := true for _, chairItem := range roomItem.ChairList { if chairItem.PlayerID == 0 { continue } chairPlayer := p.PlayerMap[chairItem.PlayerID] if chairPlayer.Status == PlayerStatusChooseMaster { allChoose = false break } } err := db.GetDB().Model(model.GameOrder{}).Where("user_id = ? and issue = ?", playerID, roomItem.Issue).Update("mul", mul).Error if err != nil { logrus.Error(err) return err } //通知 userIDList, err := p.getRoomUserIDList(roomItem.ID) if err != nil { logrus.Error(err) return err } for _, sendToUserID := range userIDList { var cardList []uint32 if playerID == sendToUserID { cardList = GetNNCardList(playerItem.CardList) } nxd.SendMsgToUserK(uint32(sendToUserID), gameproto.NotifyTypeEnum_NotifyTypeChooseMul, &gameproto.ChooseMul{ UserID: uint32(playerID), Mul: uint32(mul), RoomID: uint32(roomItem.ID), CardList: cardList, }) } //全部选完,就进入下一阶段 if allChoose { go func() { defer func() { recover() }() roomItem.ChooseMulCh <- true }() } return nil } func (p *Game) OpenCard(playerID int32) error { p.locker.Lock() defer p.locker.Unlock() playerItem := p.PlayerMap[playerID] if playerItem.RoomID == RoomIDEmpty { return errors.New("没进入房间") } if playerItem.Status != PlayerStatusChooseMul { return errors.New("状态不对") } roomItem := p.RoomMap[playerItem.RoomID] if roomItem.Status != RoomStatusOpen { return errors.New("状态不对") } userIDList, err := p.getRoomUserIDList(roomItem.ID) if err != nil { logrus.Error(err) return err } nxd.SendMsgToUserList(userIDList, gameproto.NotifyTypeEnum_NotifyTypeOpen, &gameproto.Open{ RoomID: uint32(roomItem.ID), UserID: uint32(playerID), CardList: GetNNCardList(playerItem.CardList), }) //更新用户 playerItem.Status = PlayerStatusOpen p.PlayerMap[playerID] = playerItem allOpen := true for _, chairItem := range roomItem.ChairList { if chairItem.PlayerID == 0 { continue } chairPlayer := p.PlayerMap[chairItem.PlayerID] if chairPlayer.Status == PlayerStatusChooseMul { allOpen = false break } } //全部选完,就进入下一阶段 if allOpen { go func() { defer func() { recover() }() roomItem.OpenCh <- true }() } return nil } func (p *Game) getUserRoomID(playerID int32) (*Room, int32, error) { for _, roomItem := range p.RoomMap { for _, chairItem := range roomItem.ChairList { if chairItem.PlayerID != playerID { continue } return &roomItem, chairItem.ID, nil } } return nil, 0, ErrNotFound } func (p *Game) getChairID(playerID int32, room Room) (int32, error) { for i, chairID := range room.ChairList { if playerID == chairID.PlayerID { return int32(i), nil } } return 0, ErrNotFound } func (p *Game) GetLogNoLock(playerID int32) ([]Room, []Player, int64) { roomList := make([]Room, 0) playerList := make([]Player, 0) for _, roomItem := range p.RoomMap { roomList = append(roomList, roomItem) found := false for _, chairItem := range roomItem.ChairList { if chairItem.PlayerID != playerID { continue } found = true break } if !found { continue } for _, chairItem := range roomItem.ChairList { if chairItem.PlayerID == 0 { continue } player := p.PlayerMap[chairItem.PlayerID] playerList = append(playerList, player) } } return roomList, playerList, p.LogID } func genIssue(roomID int32) string { return fmt.Sprintf("%d-%d", roomID, time.Now().Unix()) } var cardCache = cache.New(10*time.Minute, 20*time.Minute) func getOpenNumList(issue string, roomID, chairID int32) []Card { currActiveCard, exists := cardCache.Get(issue) var cardMap map[int]int if !exists { cardMap = map[int]int{} for i := 1; i <= 52; i++ { cardMap[i] = 1 } cardCache.Set(issue, cardMap, cache.DefaultExpiration) } else { cardMap = currActiveCard.(map[int]int) } count := 0 cardList := make([]Card, 0) for { key := rand.Intn(52) + 1 if cardMap[key] == -1 { continue } cardList = append(cardList, Card{ Suit: key%13 + 1, Num: key/13 + 1, }) cardMap[key] = -1 count++ if count == 5 { break } } return cardList }