package game import ( "container/list" "fmt" "math/rand" "server/call" "server/common" "server/pb" "server/util" "strconv" "sync/atomic" "time" "github.com/gogo/protobuf/proto" "github.com/liangdas/mqant-modules/room" "github.com/liangdas/mqant/gate" "github.com/liangdas/mqant/log" "github.com/liangdas/mqant/module" timewheel "github.com/liangdas/mqant/module/modules/timer" ) // 百人游戏主桌子 var ( MainTable *Table ) type SubTable interface { Init(t *Table) Reset() Enter(p *Player) StartGame() Settle() Destroy() } type Table struct { app module.App SubTable SubTable IsDestroy bool Players map[string]room.BasePlayer Room *GameRoom List *TableList Lists []*TableList Ele *list.Element Timer string RobotTimer string UUID string room.QTable Status pb.GameStatus OperateTimeout int64 SeatPlayers []*Player // 有座位的玩家 SlicePlayers []*Player // 所有玩家 BetPlayers []*pb.BetPlayers // 本局下注玩家按下注量排序 StartTime int64 // 开局时间 EndTime int64 // 结束时间 ConfigChange bool // 配置修改后,该桌子等玩家打完回收 IsSingle bool // 是否是独立房间 Ran *rand.Rand Config common.ConfigGameRoom *MillionSubTable // *RoomSubTable } func (t *Table) Init() { t.Ran = rand.New(rand.NewSource(time.Now().UnixNano())) t.SeatPlayers = make([]*Player, t.Config.MaxSeats) } func (t *Table) GetSeats() map[string]room.BasePlayer { return t.Players } func (t *Table) GetApp() module.App { return t.app } func (t *Table) OnCreate() { //可以加载数据 //一定要调用QTable.OnCreate() t.QTable.OnCreate() } func (t *Table) Update(ds time.Duration) { } func (t *Table) OnTimeOut() { if ThisGameType == GameTypeRoom { t.Debug("OnTimeOut") t.Finish() } } func (t *Table) Reset() { t.UUID = "" t.Status = pb.GameStatus_GameStatusNormal t.StartTime = 0 t.EndTime = 0 t.BetPlayers = []*pb.BetPlayers{} t.StopTimer() // if t.RoomSubTable != nil { // t.RoomSubTable.Reset() // } if t.MillionSubTable != nil { t.MillionSubTable.Reset() } t.SubTable.Reset() } func (t *Table) Enter(uid int, s gate.Session, ct common.CurrencyType) { t.Debug("player %v enter", uid) // 如果桌子已经销毁,重新分配一张桌子给玩家 if t.IsDestroy { rid := findRoom(t.Room.RoomId()) if rid == 0 { rid = findRoom(0) } gameRoom := GetRoom(rid) if gameRoom != nil { gameRoom.Enter(uid, s, ct) } return } // 如果玩家已在游戏中,直接返回 p := t.GetPlayer(uid) if p != nil { delete(t.Players, p.Session().GetSessionID()) t.Players[s.GetSessionID()] = p p.Bind(s) p.IsLeave = false AllPlayers.Store(uid, p) util.Go(func() { p.UpdatePlayerStatus() }) p.SubPlayer.OnEnter() return } p = NewPlayer(uid, t) p.GameCurrencyType = ct p.Bind(s) t.AddOnePlayer(p) p.T.SubTable.Enter(p) p.Enter() } func (t *Table) AddOnePlayer(p *Player) { atomic.AddInt64(t.Room.PlayerCount, 1) // if !p.IsRobot() { atomic.AddInt64(PlayerCount, 1) t.Players[p.Session().GetSessionID()] = p // } else { // t.Robots = append(t.Robots, p.Robot) // t.Players[p.Robot.RobotSessionID] = p // } t.SlicePlayers = append(t.SlicePlayers, p) } func (t *Table) RemoveOnePlayer(p *Player) { atomic.AddInt64(t.Room.PlayerCount, -1) // if !p.IsRobot() { atomic.AddInt64(PlayerCount, -1) p.DeleteSeats() // } else { // p.Robot.StopTimer() // if p.Robot.ID > 0 { // p.Robot.DelRobotPlaying() // AllRobots.Delete(p.Robot.ID) // } // for i := 0; i < len(t.Robots); i++ { // if t.Robots[i] == p.Robot { // t.Robots = append(t.Robots[:i], t.Robots[i+1:]...) // break // } // } // delete(t.GetSeats(), p.Robot.RobotSessionID) // } for i := 0; i < len(t.SlicePlayers); i++ { if t.SlicePlayers[i] == p { t.SlicePlayers = append(t.SlicePlayers[:i], t.SlicePlayers[i+1:]...) break } } } // Broadcast 桌子广播 func (t *Table) Broadcast(pid int, send proto.Message, uid ...int) { topic := fmt.Sprintf("%v:%v", call.GetTopicName(), pid) sendP := []string{} reset := false for _, v := range t.Players { p := v.(*Player) if p.IsLeave || p.IsRobot() { continue } except := false for _, k := range uid { if p.UID == k { except = true break } } if !except { reset = true if p.MillionSubPlayer != nil { sendP = append(sendP, v.Session().GetSessionID()) } else { p.Send(pid, send) } } } if reset { t.ResetTimeOut() } if len(sendP) == 0 { return } body, _ := proto.Marshal(send) err := t.SendCallBackMsgNR(sendP, topic, body) if err != nil { t.Error("err:%v", err) } } func (t *Table) StartGame() { if t.Status != pb.GameStatus_GameStatusNormal || t.IsDestroy { return } now := time.Now().Unix() t.StopTimer() t.Status = pb.GameStatus_GameStatusPlaying t.StartTime = now t.UUID = call.SnowNode().Generate().String() t.Debug("start game") t.SubTable.StartGame() for _, v := range t.GetSeats() { p := v.(*Player) if p.IsRobot() { continue } p.StartTime = now p.UUID = t.UUID } if len(t.Room.TableList) > 1 { t.Room.ChangeOne(t) } } func (t *Table) Settle() { t.Debug("settle,status:%v", t.Status) if t.Status == pb.GameStatus_GameStatusSettle { return } t.StopTimer() t.EndTime = time.Now().Unix() t.Status = pb.GameStatus_GameStatusSettle t.SubTable.Settle() if len(t.Room.TableList) > 1 { t.Room.ChangeOne(t) } if t.Room.RoomType == RoomTypePractice { return } if ThisGameType == GameTypeMillion { t.MillionSubTable.Settle() } } func (t *Table) NF(f func()) { f() } func (t *Table) OnDestroy() { t.Debug("Destroy") t.QTable.OnDestroy() t.IsDestroy = true t.StopTimer() for _, v := range t.GetSeats() { p := v.(*Player) if !p.IsRobot() && p.TotalBet > 0 && p.Status == pb.PlayerStatus_PlayerStatusPlaying { p.Debug("destroy return,total:%v", p.TotalBet) call.UpdateCurrencyPro(&common.UpdateCurrency{ CurrencyBalance: &common.CurrencyBalance{ UID: p.UID, Event: common.CurrencyEventGameCancelBet, Type: p.GameCurrencyType, Value: p.TotalBet, }, }) } p.Status = pb.PlayerStatus_PlayerStatusNormal if p.MillionSubPlayer != nil { p.TotalBet = 0 } p.Leave() } t.Room.RemoveOne(t) t.Room.Tables.Delete(t.TableIDInt()) t.SubTable.Destroy() } func (t *Table) Leave(session gate.Session) { player := t.FindPlayer(session) if player == nil { return } p := player.(*Player) p.Leave() } // 计算当前还在游戏中的玩家 func (t *Table) CountPlayingPlayers() int { count := 0 for _, v := range t.GetSeats() { if v.(*Player).Status == pb.PlayerStatus_PlayerStatusPlaying { count++ } } return count } func (t *Table) CountRealPlayings() int { count := 0 for _, v := range t.Players { if !v.(*Player).IsRobot() && v.(*Player).Status == pb.PlayerStatus_PlayerStatusPlaying { count++ } } return count } // 计算参与了游戏的玩家 func (t *Table) CountPlayedPlayers() int { count := 0 for _, v := range t.GetSeats() { if v.(*Player).Status != pb.PlayerStatus_PlayerStatusNormal { count++ } } return count } func (t *Table) StopTimer() { t.OperateTimeout = 0 if t.Timer == "" { return } timewheel.GetTimeWheel().RemoveTimer(t.Timer) t.Timer = "" } func (t *Table) GetPlayer(uid int) *Player { for _, v := range t.GetSeats() { p := v.(*Player) if p.UID == uid { return p } } return nil } func (t *Table) TableIDInt() int { id, _ := strconv.Atoi(t.TableId()) return id } // 计算当前还在游戏中的玩家 func (t *Table) GetOnePlaying() *Player { for _, v := range t.GetSeats() { p := v.(*Player) if p.Status == pb.PlayerStatus_PlayerStatusPlaying { return p } } return nil } // 获取一名真实玩家 func (t *Table) GetRealOne() *Player { for _, v := range t.GetSeats() { p := v.(*Player) if !p.IsRobot() { return p } } return nil } // 获取一名玩牌中真实玩家 func (t *Table) GetRealOnePlaying() *Player { for _, v := range t.GetSeats() { p := v.(*Player) if p.Role == common.PlayerRoleNormal && p.Status == pb.PlayerStatus_PlayerStatusPlaying { return p } } return nil } // 获取一名真实玩家 func (t *Table) GetRealPlayings() []*Player { ret := []*Player{} for _, v := range t.GetSeats() { p := v.(*Player) if p.IsRobot() || p.Status != pb.PlayerStatus_PlayerStatusPlaying { continue } ret = append(ret, p) } return ret } func (t *Table) CountEmptySeat() int { count := 0 for i := 0; i < len(t.SeatPlayers); i++ { if t.SeatPlayers[i] == nil { count++ } } return count } func (t *Table) FindEmptySeat() int { for i := 0; i < len(t.SeatPlayers); i++ { if t.SeatPlayers[i] == nil { return i } } return -1 } func (t *Table) GetLastPlayingPlayer(id int) *Player { max := len(t.SeatPlayers) seat := id + max for i := 0; i < max-1; i++ { seat-- seatID := seat % max other := t.SeatPlayers[seatID] if other != nil && other.Status == pb.PlayerStatus_PlayerStatusPlaying { return other } } return nil } func (t *Table) GetNextPlayingPlayer(seatID int) *Player { for i := 0; i < len(t.SeatPlayers)-1; i++ { seatID = (seatID + 1) % len(t.SeatPlayers) one := t.SeatPlayers[seatID] if one != nil && one.Status == pb.PlayerStatus_PlayerStatusPlaying { return one } } return nil } // 随机获取一个玩家 func (t *Table) GetRandomPlayer() *Player { Players := []*Player{} for _, v := range t.SeatPlayers { if v == nil || v.Status != pb.PlayerStatus_PlayerStatusPlaying { continue } Players = append(Players, v) } return Players[t.Ran.Intn(len(Players))] } func (t *Table) GetSeatPlayer(seat int) *Player { for i, v := range t.SeatPlayers { if v == nil { continue } if v.SeatID == seat { return t.SeatPlayers[i] } } return nil } func (t *Table) GetOtherPlayer(seat int) *Player { for i, v := range t.SeatPlayers { if v == nil || v.SeatID == seat || v.Status != pb.PlayerStatus_PlayerStatusPlaying { continue } return t.SeatPlayers[i] } return nil } func (t *Table) StartGameTimer(dur time.Duration) { if t.IsDestroy { return } t.Timer = fmt.Sprintf("startGame_%v", t.TableId()) timewheel.GetTimeWheel().AddTimerCustom(dur, t.Timer, nil, func(arge interface{}) { t.Debug("startGame timer callback") t.Put("nf", t.Settle) }) } func (t *Table) SettleGameTimer(dur time.Duration) { if t.IsDestroy { return } t.Timer = fmt.Sprintf("settleGame_%v", t.TableId()) timewheel.GetTimeWheel().AddTimerCustom(dur, t.Timer, nil, func(arge interface{}) { t.Debug("settleGame timer callback") t.PutQueue("nf", func() { t.Reset() }) }) } // GetPlayingPlayers 获取本局所有正在游戏中的玩家 func (t *Table) GetPlayingPlayers() []*Player { ret := []*Player{} for _, v := range t.GetSeats() { p := v.(*Player) if p.Status != pb.PlayerStatus_PlayerStatusPlaying { continue } ret = append(ret, p) } return ret } // Debug 封装桌子打印日志方法 func (t *Table) Debug(fomat string, a ...interface{}) { log.Debug("uuid:%v,roomID:%v,tableID:%v,"+fomat, append([]interface{}{t.UUID, t.Room.RoomId(), t.TableId()}, a...)...) } // Error 封装桌子打印日志方法 func (t *Table) Error(fomat string, a ...interface{}) { log.Error("uuid:%v,roomID:%v,tableID:%v,"+fomat, append([]interface{}{t.UUID, t.Room.RoomId(), t.TableId()}, a...)...) } func (t *Table) Put(_func string, params ...interface{}) { for i := 0; i < 3; i++ { err := t.PutQueue(_func, params...) if err == nil { return } log.Error("err:%v", err) } } func (t *Table) GetRealBet() int64 { var realBet int64 for _, v := range t.Players { p := v.(*Player) if p.IsRobot() { continue } realBet += p.TotalBet } return realBet } // ChangeConfig 配置改变 func (t *Table) ChangeConfig() { t.Debug("config change") t.PutQueue("nf", func() { t.ConfigChange = true }) }