You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
568 lines
12 KiB
568 lines
12 KiB
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 |
|
}) |
|
}
|
|
|