Browse Source

1 推筒子 初版本上传

dev
MoQingYu 3 months ago
parent
commit
7a008e87f0
  1. 118
      game-server/game/TTZ/TTZ/config/blood.json
  2. 126
      game-server/game/TTZ/TTZ/etc/etc.toml
  3. 41
      game-server/game/TTZ/TTZ/etc/game.toml
  4. 13
      game-server/game/TTZ/app/app.go
  5. 67
      game-server/game/TTZ/app/config/config.go
  6. 35
      game-server/game/TTZ/app/core/blood/blood.go
  7. 7
      game-server/game/TTZ/app/core/comm/comm.go
  8. 95
      game-server/game/TTZ/app/core/core.go
  9. 297
      game-server/game/TTZ/app/core/game/game.go
  10. 110
      game-server/game/TTZ/app/core/game/game_bet.go
  11. 24
      game-server/game/TTZ/app/core/game/game_check_room.go
  12. 103
      game-server/game/TTZ/app/core/game/game_comm.go
  13. 72
      game-server/game/TTZ/app/core/game/game_config.go
  14. 121
      game-server/game/TTZ/app/core/game/game_copy.go
  15. 46
      game-server/game/TTZ/app/core/game/game_ctrl_blood.go
  16. 36
      game-server/game/TTZ/app/core/game/game_disconnect.go
  17. 10
      game-server/game/TTZ/app/core/game/game_dissolution.go
  18. 64
      game-server/game/TTZ/app/core/game/game_get_banker.go
  19. 128
      game-server/game/TTZ/app/core/game/game_get_room_info.go
  20. 138
      game-server/game/TTZ/app/core/game/game_init.go
  21. 130
      game-server/game/TTZ/app/core/game/game_join_room.go
  22. 362
      game-server/game/TTZ/app/core/game/game_logic.go
  23. 47
      game-server/game/TTZ/app/core/game/game_new.go
  24. 232
      game-server/game/TTZ/app/core/game/game_out_room.go
  25. 687
      game-server/game/TTZ/app/core/game/game_over.go
  26. 48
      game-server/game/TTZ/app/core/game/game_player.go
  27. 30
      game-server/game/TTZ/app/core/game/game_reconnect.go
  28. 46
      game-server/game/TTZ/app/core/game/game_repeat_room.go
  29. 8
      game-server/game/TTZ/app/core/game/game_resume_gameinfo.go
  30. 495
      game-server/game/TTZ/app/core/game/game_robot.go
  31. 190
      game-server/game/TTZ/app/core/game/game_robot_timer.go
  32. 214
      game-server/game/TTZ/app/core/game/game_select_banker.go
  33. 44
      game-server/game/TTZ/app/core/game/game_service.go
  34. 62
      game-server/game/TTZ/app/core/game/game_update_gold.go
  35. 111
      game-server/game/TTZ/app/core/game/game_watch.go
  36. 227
      game-server/game/TTZ/app/core/protocol/protocol_msg.go
  37. 104
      game-server/game/TTZ/app/core/protocol/protocol_struct.go
  38. 48
      game-server/game/TTZ/app/core/room/base.go
  39. 362
      game-server/game/TTZ/app/core/room/roommgr.go
  40. 142
      game-server/game/TTZ/app/core/types/poker.go
  41. 451
      game-server/game/TTZ/app/gamemanage/gamemanage.go
  42. 39
      game-server/game/TTZ/app/route/route.go
  43. 100
      game-server/game/TTZ/config/blood.json
  44. 126
      game-server/game/TTZ/etc/etc.toml
  45. 41
      game-server/game/TTZ/etc/game.toml
  46. 161
      game-server/game/TTZ/game_test.go
  47. 48
      game-server/game/TTZ/main.go

118
game-server/game/TTZ/TTZ/config/blood.json

@ -0,0 +1,118 @@
{
"Comment": "WinPro 庄稼赢得概率 RoomLimit 房间限红 RechargeLimit 弹充值限制 AreaID 区域ID(1-10) AreaName 区域名字(9-16) AreaPro 区域赔付比例 AreaLimit 区域限红",
"Comment2": "TimeSelectPoker 二阶段多久翻一次牌",
"RoomLimit" : [200000000,200000000],
"PlayerLimit" : [20000000,20000000],
"RechargeLimit" : 10000,
"ChipList": [10000,20000,50000,100000,200000,500000],
"AreaConfig": [
{
"AreaID": 1,
"AreaName": "ANDAR",
"AreaPro": 0.95,
"AreaLimit": 50000000
},
{
"AreaID": 2,
"AreaName": "BAHAR",
"AreaPro": 1.05,
"AreaLimit": 50000000
},
{
"AreaID": 3,
"AreaName": "♥️",
"AreaPro": 1,
"AreaLimit": 50000000
},
{
"AreaID": 4,
"AreaName": "♠️",
"AreaPro": 1,
"AreaLimit": 50000000
},
{
"AreaID": 5,
"AreaName": "♣️",
"AreaPro": 1,
"AreaLimit": 50000000
},
{
"AreaID": 6,
"AreaName": "♦️",
"AreaPro": 1,
"AreaLimit": 50000000
},
{
"AreaID": 7,
"AreaName": "1~8",
"AreaPro": 1,
"AreaLimit": 50000000
},
{
"AreaID": 8,
"AreaName": "9~16",
"AreaPro": 2.5,
"AreaLimit": 50000000
},
{
"AreaID": 9,
"AreaName": "17~24",
"AreaPro": 4,
"AreaLimit": 30000000
},
{
"AreaID": 10,
"AreaName": "25",
"AreaPro": 7,
"AreaLimit": 20000000
}
],
"GearConfigComment": "正数血池抽中控制 控系统赢 负数血池控系统输",
"GearConfig": [
{
"GearID": 3000,
"WinPro": 6000
},{
"GearID": 2000,
"WinPro": 3000
},{
"GearID": 1000,
"WinPro": 500
},{
"GearID": -1000,
"WinPro": 0
},{
"GearID": -2000,
"WinPro": 1500
},{
"GearID": -3000,
"WinPro": 3000
}
],
"AndroidOprateConfig": {
"CountMin": 2,
"CountMax": 3,
"FlushHZ": 300,
"LeavePro": [[1,500],[4,2000],[6,3000],[9,5000]],
"LeastMin": 2
},
"AndroidBetConfigComment1": "ColorXXX 花色区域配置 NormalXXX AB区域配置 SpecialXXX 次数区域配置",
"AndroidBetConfigComment2": "区域说明 【A区域 1】【B区域2】【红3】【黑4】【梅5】【方6】【1~8 7】【9~16 8】【17~24 9】【25~more 10】",
"AndroidBetConfig": {
"ColorPro": [[3,2500],[4,2500],[5,2500],[6,2500]],
"ColorBet": [[3,100000,5000000],[4,100000,5000000],[5,100000,5000000],[6,100000,5000000]],
"MaxCha": 1500000,
"NormalArea": [[1,5000],[2,5000]],
"NormalBet": [[1,100000,3000000],[2,500000,3000000]],
"SpecialPro": 3000,
"SpecialArea": [[7,4500],[8,2500],[9,1500],[10,500]],
"SpecialBet": [[7,500000,3000000],[8,500000,2000000],[9,300000,2000000],[10,200000,2000000]]
},
"TimeOutBegin" : 3000,
"TimeOutBetFirst" : 15000,
"TimeOutFirstResult" : 5000,
"TimeOutBetFinal" : 15000,
"TimeOutFinalResult" : 5000,
"TimeSelectPoker" : 100,
"TimeKickTimes" :5
}

126
game-server/game/TTZ/TTZ/etc/etc.toml

@ -0,0 +1,126 @@
# 进程号
pid = "./run/game.pid"
# 统一时区设置。项目中的时间获取请使用xtime.Now()
timezone = "Local"
[cluster]
[cluster.node]
# 实例名称
name = "TTZ"
# 编解码器。可选:json | proto
codec = "json"
[config]
[config.file]
# 配置文件或配置目录路径
path = "./config"
# 读写模式。可选:read-only | read-write,默认为read-only
mode = "read-write"
[config.etcd]
# 客户端连接地址+
addrs = ["127.0.01:2379"]
# 客户端拨号超时时间,支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认为5s
dialTimeout = "5s"
# 路径。默认为/config
path = "/config"
# 读写模式。可选:read-only | read-write,默认为read-only
mode = "read-only"
[locate]
[locate.redis]
# 客户端连接地址
addrs = ["127.0.01:62331"]
# 数据库号
db = 0
# 用户名
username = ""
# 密码
password = "zetdCehWe5t3z9"
# 最大重试次数
maxRetries = 3
# key前缀
prefix = "xgame"
[registry]
[registry.etcd]
# 客户端连接地址
addrs = ["127.0.01:2379"]
# 客户端拨号超时时间(秒)
dialTimeout = 5
# 命名空间
namespace = "services"
# 超时时间(秒)
timeout = 3
# 心跳重试次数
retryTimes = 3
# 心跳重试间隔(秒)
retryInterval = 10
[registry.consul]
# 客户端连接地址
addr = "127.0.01:8500"
# 是否启用健康检查
healthCheck = true
# 健康检查时间间隔(秒),仅在启用健康检查后生效
healthCheckInterval = 10
# 健康检查超时时间(秒),仅在启用健康检查后生效
healthCheckTimeout = 5
# 是否启用心跳检查
heartbeatCheck = true
# 心跳检查时间间隔(秒),仅在启用心跳检查后生效
heartbeatCheckInterval = 10
# 健康检测失败后自动注销服务时间(秒)
deregisterCriticalServiceAfter = 30
[transport]
[transport.rpcx]
# RPCX服务器相关配置
[transport.rpcx.server]
# 服务器监听地址,空或:0时系统将会随机端口号
addr = ":0"
# 秘钥文件
keyFile = ""
# 证书文件
certFile = ""
# RPCX服务器相关配置
[transport.rpcx.client]
# 证书文件
certFile = ""
# 证书域名
serverName = ""
# 连接池大小,默认为10
poolSize = 10
[packet]
# 字节序,默认为big。可选:little | big
byteOrder = "big"
# 路由字节数,默认为2字节
routeBytes = 4
# 序列号字节数,默认为2字节
seqBytes = 4
# 消息字节数,默认为5000字节
bufferBytes = 100000
[log]
[log.zap]
# 日志输出文件
file = "./log/xgame.log"
# 日志输出级别,可选:debug | info | warn | error | fatal | panic
level = "debug"
# 日志输出格式,可选:text | json
format = "text"
# 是否输出到终端
stdout = true
# 时间格式,标准库时间格式
timeFormat = "2006/01/02 15:04:05.000000"
# 堆栈的最低输出级别,可选:debug | info | warn | error | fatal | panic
stackLevel = "error"
# 文件最大留存时间,d:天、h:时、m:分、s:秒
fileMaxAge = "7d"
# 文件最大尺寸限制,单位(MB)
fileMaxSize = 100
# 文件切割方式
fileCutRule = "day"
# 是否启用调用文件全路径
callerFullPath = false
# 是否启用分级存储
classifiedStorage = false

41
game-server/game/TTZ/TTZ/etc/game.toml

@ -0,0 +1,41 @@
# 以下配置为游戏固定配置,修改必需重启游戏生效
# 游戏配置(游戏模型基础配置,不可随意变动配置名)
[game]
# 游戏ID
id = 20013
# 游戏类型
type = 1
# 游戏名称
name = "TTZ"
# 最小牌桌数
minTableNum = 1
# 最大牌桌数
maxTableNum = 1
# 是否动态牌桌
isDynamicTable = false
# 最小座位数
minSeatNum = 0
# 最大座位数
maxSeatNum = 0
# 是否动态座位
isDynamicSeat = true
# 是否回合制游戏,例如:(回合制游戏:麻将)(非回合制游戏:slots)
isTurnBasedGame = false
# 是否需要底池接口
isNeedPot = false
# 是否需要奖池接口
isNeedPrizePool = true
# 是否需要血池接口
isNeedBloodPool = false
# 自定义配置(由开发人员自行定义)
[custom]
# 押注时长(秒)
BaseLineCount = 25
# 准备起飞时长(秒)
readyingDuration = 4
# 结算时长(秒)
settlementDuration = 5

13
game-server/game/TTZ/app/app.go

@ -0,0 +1,13 @@
package app
import (
"base/cluster/node"
"xgame/game/TTZ/app/core"
"xgame/game/TTZ/app/gamemanage"
)
func Init(proxy *node.Proxy) {
// 初始化游戏
gamemanage.GameProxy = proxy
core.NewCore(proxy).Init()
}

67
game-server/game/TTZ/app/config/config.go

@ -0,0 +1,67 @@
package conf
import (
"base/config"
"base/log"
)
//var (
// prizePoolCfg *prizePoolConfig
//)
const (
ConfigBloodName = "blood"
fileType = ".json"
)
type BloodCtrl struct {
GameRound int `json:"GameRound"`
RechargeLimit int64 `json:"RechargeLimit"`
MinGameScore int64 `json:"MinGameScore"`
ChipList []int64 `json:"ChipList"`
GearConfig []struct {
GearID int `json:"GearID"`
WinPro int `json:"WinPro"`
BankerPro int `json:"BankerPro"`
GetBankerPro int `json:"GetBankerPro"`
WinBetPro [][]int `json:"WinBetPro"`
LoseBetPro [][]int `json:"LoseBetPro"`
BankerWinPro int `json:"BankerWinPro"`
} `json:"GearConfig"`
AndroidOprateConfig struct {
MustBanker int `json:"MustBanker"`
InGold [][]int `json:"InGold"`
CreateTableHZ int64 `json:"CreateTableHZ"`
TableCreatePro [][]int32 `json:"TableCreatePro"`
CountMax0 [][]int `json:"CountMax0"`
CountMax1 [][]int `json:"CountMax1"`
CountMax2 [][]int `json:"CountMax2"`
CountMax3 [][]int `json:"CountMax3"`
CountMax4 [][]int `json:"CountMax4"`
CountMax5 [][]int `json:"CountMax5"`
JoinHZ []int `json:"JoinHZ"`
FlushHZ int `json:"FlushHZ"`
} `json:"AndroidOprateConfig"`
TimeoutDestroy int64 `json:"TimeoutDestroy"`
TimeOutBegin int64 `json:"TimeOutBegin"`
TimeOutGetBanker int64 `json:"TimeOutGetBanker"`
TimeOutBB int64 `json:"TimeOutBB"`
TimeOutDT int64 `json:"TimeOutDT"`
TimeOutBet int64 `json:"TimeOutBet"`
TimeOutCP int64 `json:"TimeOutCP"`
TimeOutResult int64 `json:"TimeOutResult"`
TimeKickTimes int64 `json:"TimeKickTimes"`
}
func ReadBloodConfig() BloodCtrl {
log.Debug("获取配置 表名", ConfigBloodName)
var readData BloodCtrl
err := config.Get(ConfigBloodName).Scan(&readData)
if err != nil {
log.Fatalf("Read AB BloodConfig err:%v", err)
}
log.Debug("获取配置成功 ", readData.RechargeLimit)
return readData
}

35
game-server/game/TTZ/app/core/blood/blood.go

@ -0,0 +1,35 @@
package blood
import (
"base/cluster/node"
"base/log"
"xgame/game/service/rpc_client"
)
type BoolePool struct {
BoolPoolSockNum int64 // 血池库存
BoolPoolId int64 // 血池ID
StockConf string //房间取库存配置
RoomGears string //房间当前挡位值
NowTurnPoint int64 //房间当前拐点值
}
// SetBoole 设置血池
func (b *BoolePool) SetBoole(Proxy *node.Proxy, BloodID int64) bool {
reply, err := rpc_client.GetGameBloodByGameIdAndRoomId(Proxy, BloodID)
if err != nil {
log.Error("获取血池失败", err)
return false
}
b.BoolPoolSockNum = reply.StockNum
b.BoolPoolId = reply.Id
b.StockConf = reply.StockConf
b.RoomGears = reply.RoomGears
b.NowTurnPoint = reply.NowTurnPoint
log.Debug("获取血池 ", reply)
return true
}

7
game-server/game/TTZ/app/core/comm/comm.go

@ -0,0 +1,7 @@
package comm
//销毁房间回调
type DestoryCB func(int64)
// 大厅通知回调
type NotifyDTUser func(int32, interface{})

95
game-server/game/TTZ/app/core/core.go

@ -0,0 +1,95 @@
package core
import (
"base/cluster"
"base/cluster/node"
"base/log"
"base/task"
"fmt"
"xgame/game/TTZ/app/core/room"
"xgame/game/TTZ/app/gamemanage"
"xgame/game/TTZ/app/route"
"xgame/internal/entity"
userevt "xgame/internal/event/user"
"xgame/internal/middleware"
)
type Core struct {
Proxy *node.Proxy
playerMgr *entity.PlayerMgr
}
func NewCore(proxy *node.Proxy) *Core {
return &Core{
Proxy: proxy,
playerMgr: entity.NewPlayerMgr(proxy),
}
}
func (c *Core) Init() {
//go agent.StartAgent()
c.Proxy.Router().Group(func(group *node.RouterGroup) {
// 注册中间件
group.Middleware(middleware.Auth)
group.AddRouteHandler(route.Enter, false, gamemanage.EnterGame) // 进入游戏
group.AddRouteHandler(route.JoinTable, false, gamemanage.JoinTable) // 进入游戏
group.AddRouteHandler(route.Bet, false, gamemanage.UserBet) // 下注请求
group.AddRouteHandler(route.LogoutTable, false, gamemanage.LogoutTable) // 退出游戏请求
group.AddRouteHandler(route.AllTable, false, gamemanage.AllTable) // 进入游戏
group.AddRouteHandler(route.GetBanker, false, gamemanage.GetBanker) // 抢庄
group.AddRouteHandler(route.WatchToDown, false, gamemanage.WatchToDown) // 主动观战转坐下
})
// 断线重连-好像没用
c.Proxy.AddEventHandler(cluster.Reconnect, gamemanage.Reconnect)
// 断开连接-好像没用
c.Proxy.AddEventHandler(cluster.Disconnect, gamemanage.Disconnect)
room.Init(c.Proxy)
userevt.SubscribeGoldChange(func(uid int64) {
gamemanage.UpdateUserGold(uid)
})
userevt.SubscribeTableConfigChange(func(gameID int64) {
gamemanage.UpdateTableConfig(gameID)
})
}
// Reconnect 掉线重连
func Reconnect(event *node.Event) {
task.AddTask(func() {
if event.UID <= 0 {
return
}
//Ctx.Context()
/*
* 只能配合客户端想办法了,实在没办法,这服务器架构设计的有缺陷
*/
uid := event.UID
r := room.Gh.GetRoomObject(uid)
if r != nil {
r.OnReconnect(uid)
fmt.Println(uid, "断线重连")
}
})
}
// Disconnect 掉线
func Disconnect(event *node.Event) {
task.AddTask(func() {
if event.UID <= 0 {
return
}
uid := event.UID
//r := room.GetRoomByUid(uid)
r := room.Gh.GetRoomObject(uid)
if r != nil {
r.OnDisconnect(uid)
log.Debug(uid, "玩家掉线,设置为离线状态")
}
})
}

297
game-server/game/TTZ/app/core/game/game.go

@ -0,0 +1,297 @@
package game
import (
"base/cluster/node"
"base/log"
"context"
"math/rand"
"sort"
"sync"
"time"
conf "xgame/game/TTZ/app/config"
"xgame/game/TTZ/app/core/blood"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/core/types"
"xgame/internal/option/room"
gamepb "xgame/internal/service/game/pb"
"github.com/roylee0704/gron"
"github.com/shopspring/decimal"
)
const (
NORMAL_END = int32(1) //正常结算
)
const (
INVALID_VALUE = 0xFFFF //无效值
GAME_NAME = "推筒子" //游戏名字
GAME_ID = 20013 //游戏ID
MAX_USER_NUM = 5 //房间最大人数
MAX_ROBOT_BUN = 3 //房间最多机器人
NEXT_GAME_TIME_OUT = 3000 //开始倒计时
GET_BANKER_TIME_OUT = 10000 //抢庄倒计时
BB_TIME_OUT = 3000 //抢庄后到开始下注的等待时间
DT_TIME_OUT = 3000 //丢骰子动画展示倒计时
BET_TIME_OUT = 15000 //下注倒计时
CP_TIME_OUT = 12000 //搓牌倒计时
RESULT_TIME_OUT = 5000 //结算倒计时
GIVEUP_COUNT = 5 //多少轮不下注踢出去
DESTROY_TIME_OUT = 60000 //房间销毁的时间
ROBOT_ENTER_TIME_MIN = 2000 //机器人最低入场时间
ROBOT_ENTER_TIME_MAX = 6000 //机器人最高入场时间
MIN_GAME_SCORE = 50000 // 最低能玩的金额 限制50
)
const (
USER_OFF_LINE = 0 // 离线
USER_ON_LINE = 1 // 在线
USER_LEAVE = 2 // 离开
)
const (
//桌子状态
EN_TABLE_STATE_UNKNOWN int32 = 0 // 缺省
EN_TABLE_STATE_READY_TO_START int32 = 1 // 准备开始
EN_TABLE_STATE_PLAYING int32 = 2 // 游戏中
EN_TABLE_STATE_FINISH int32 = 3 // 结算中
)
type PokerUserType struct { // 用户牌型排序
ChairId int32
Bei int
HandPoker types.PokerSlice // 玩家手牌
}
type Room struct {
RoomProxy *node.Proxy
Ctx context.Context
Opt *room.Options //房间的参数
TableConfig *gamepb.GameTableTotal
DestroyRoomFunc func(int64) //销毁房间回调
QuitRoomFunc func(int64) //退出房间回调
NotifyFunc func(int32, interface{}) //大厅通知回调
RId int64 //房间ID
RoomID int32 //
GameID int32 //
ReasonString string //原因字符串
AgentID int32 //
OffsetHours float64 //时区差
MaxPlayerCount int32 //最大玩家数
MaxRobotPlayerCount int32 //最大允许机器玩家数
PersonalRate int64 // 个人倍率
RoomRate int64 // 房间倍率
RoomLimit decimal.Decimal //int64// 房间进入条件
PlayerMap map[int32]*Player //
PlayerWatchMap []*Player //
MaxPlayerNum int32 //
State int32 //房间状态 0空闲,1准备开始,2花色下注,3花色结算,4二轮下注,5二轮结算
PokerIndex int32 //
PokersCount int32 //
CurrOperationPlayer int32 //
CurrentGameState int32 //游戏阶段
CreatorId int64 //创建者 0 主房间 其它表示是为哪个点控玩家生成的房间
StartTimer int64 //
EndTimer int64 //
RobotMgr *RobotManager //
Round int32 //轮数
GameMaxRound int32 //总轮数
CurrBanker int32 //当前庄家
CurrBeginUser int32 //当前开始玩家
BankerLimitMax int64 //庄家最大限红
WinPlayer int32 //上一把赢家
EndReason int32 //上一把结束原因
BoolePool *blood.BoolePool //
StockValue int64 //库存值[在初始化房间时候获取,在解散房间时候落地]
GronTimer *gron.Cron //
CurrentOperationTimer int64 //当前操作时间(单位:毫秒)
roomRWLock sync.RWMutex //
IsP2pControls bool //
shuffleRandPointer *rand.Rand //
BetList []int64 //筹码列表
UserScore map[int32]decimal.Decimal //本局总输赢
BaoDiBanker bool //是否保底过
GetBankerList []int32 //抢庄的列表
NoGetBankerList []int32 //未抢庄的列表
CardList types.PokerSlice // 牌堆
DropCardList types.PokerSlice // 弃牌堆
Record []protocol.HistoryRecord //历史记录
BloodCtrl conf.BloodCtrl //血池控制
isUserCtl bool //本桌是否人物点控
UserCtlGear int64 //人物点控档位
GuoScore int64 // 锅的钱
MinGameScore int64 //最低能玩的金额
TimeOutBegin int64 //开始倒计时DT
TimeOutGetBanker int64 //抢庄倒计时
TimeOutBB int64 //抢庄动画展示倒计时
TimeOutDT int64 //丢骰子动画展示倒计时
TimeOutBet int64 //下注倒计时
TimeOutCP int64 //搓牌倒计时
TimeOutResult int64 //结算倒计时
TimeKickTimes int64 //多少轮不下注踢出去
TimeoutDestroy int64 //不开场房间销毁的时间
}
// GetRoomId 获取房间ID
func (rr *Room) GetRoomId() int64 {
return int64(rr.RoomID)
}
func (rr *Room) GetRId() int64 { // 获取房间RId
return rr.RId
}
func (rr *Room) GetState() int32 { // 获取房间状态
return rr.CurrentGameState
}
func (rr *Room) GetRoomState() int32 { // GetRoomState 房间状态
if rr.GetRoomPlayerCount() > rr.MaxPlayerCount {
return 1 //房间满了
}
return 0
}
// OnPlayerReady 玩家准备
func (rr *Room) OnPlayerReady(userId int64, p interface{}) {
}
// GetRoomPlayer 获取玩家-chairId==椅子号
func (rr *Room) GetRoomPlayer(chairId int32) *Player {
return rr.PlayerMap[chairId]
}
func (rr *Room) GetRoomRealCanBetPlayerCount() int32 {
count := int32(0)
for _, v := range rr.PlayerMap {
if !v.IsRobot && v.CanBet {
count++
}
}
return count
}
// GetRoomPlayerCount 房间人数
func (rr *Room) GetRoomPlayerCount() int32 {
return int32(len(rr.PlayerMap))
}
func (rr *Room) GetRoomCanBetPlayerCount() int32 { // 房间可以玩的人
count := int32(0)
for _, v := range rr.PlayerMap {
if v.CanBet {
count++
}
}
return count
}
func (rr *Room) GetRoomRealPlayerCount() int32 {
count := int32(0)
for _, v := range rr.PlayerMap {
if !v.IsRobot {
count++
}
}
return count
}
// GetRoomPlayerByUid 通过uid取player_obj
func (rr *Room) GetRoomPlayerByUid(uid int64) *Player {
for _, p := range rr.PlayerMap {
if p.Uid == uid {
return p
}
}
for _, p := range rr.PlayerWatchMap {
if p.Uid == uid {
return p
}
}
// for i := rr.players.Front(); i != nil; i = i.Next() {
// pPlayer := i.Value.(*Player)
// if pPlayer.Uid == uid {
// return pPlayer
// }
// }
return nil
}
func (rr *Room) SetPlayerState(uid int64, state int32) bool {
p := rr.GetRoomPlayerByUid(uid)
if p != nil {
p.OnlineState = state
return true
}
return false
}
// OnGameOperate 操作
func (rr *Room) OnGameOperate(uid int64, state int32) bool {
return true
}
func (rr *Room) OnGetGameId() int32 {
return rr.GameID
}
// 乱序函数
func (rr *Room) shuffleDeck(deck *[]types.Poker) {
rand.NewSource(time.Now().UnixNano()) // 用当前时间作为随机种子
for i := len(*deck) - 1; i > 0; i-- {
j := rand.Intn(i + 1) // 生成随机索引
(*deck)[i], (*deck)[j] = (*deck)[j], (*deck)[i] // 交换
}
}
// 乱序函数
func (rr *Room) shuffleDeck2(deck *[]types.PokerSlice) {
for i := len(*deck) - 1; i > 0; i-- {
j := rand.Intn(i + 1) // 生成随机索引
(*deck)[i], (*deck)[j] = (*deck)[j], (*deck)[i] // 交换
}
}
// 初始化牌组
func (rr *Room) InitCardList(CardCount int) {
rr.CardList = rr.CardList[:0]
rr.DropCardList = rr.DropCardList[:0]
var TempPoker []types.Poker
for i := 0; i < CardCount; i++ {
for _, poker := range types.OnePokers {
TempPoker = append(TempPoker, poker)
}
}
rr.shuffleDeck(&TempPoker)
for PokerIndex := 0; PokerIndex < len(TempPoker); PokerIndex++ {
rr.CardList = append(rr.CardList, TempPoker[PokerIndex])
}
}
func (rr *Room) SearchTypeHandCard() []types.PokerSlice { // 计算一个获胜类型的牌组出来
var ResultPokerList []types.PokerSlice
// 先选好五套牌
for i := 0; i < MAX_USER_NUM; i++ {
ResultPokerList[i] = append(ResultPokerList[i], rr.CardList[len(rr.CardList)-2:]...)
rr.DropCardList = append(rr.DropCardList, ResultPokerList[i]...)
rr.CardList = rr.CardList[:len(rr.CardList)-2]
}
log.Debug(" 桌子号 ", rr.RId, " 选好的五套牌 ", ResultPokerList)
// 排序
sort.Slice(ResultPokerList, func(i, j int) bool {
return ResultPokerList[i].Compare(ResultPokerList[j]) == 1
})
log.Debug(" 桌子号 ", rr.RId, " 排序后 选好的五套牌 ", ResultPokerList)
return ResultPokerList
}

110
game-server/game/TTZ/app/core/game/game_bet.go

@ -0,0 +1,110 @@
package game
import (
"base/log"
"github.com/shopspring/decimal"
"strconv"
"time"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/route"
)
func (rr *Room) OnUserBet(uid int64, msg interface{}, Auto bool) { // 用户下注
message := msg.(*protocol.BetReq)
BetRes := &protocol.BetRes{}
NotifyBet := &protocol.NotifyBet{}
ChairId := int32(-1)
for _, player := range rr.PlayerMap {
if player.Uid == uid {
ChairId = player.ChairId
break
}
}
if ChairId == -1 {
log.Debug("ChairId == -1")
return
}
if rr.CurrentGameState != protocol.STATE_BET {
log.Debug("玩家", ChairId, "非下注时间不能下注 当前阶段 ", rr.CurrentGameState)
BetRes.Code = protocol.ErrorCodeBetError
BetRes.CodeMsg = "非下注时间不能下注 当前阶段 " + strconv.Itoa(int(rr.CurrentGameState))
rr.Send(rr.PlayerMap[ChairId], route.BetRes, BetRes)
return
}
if message.BetAmount%rr.Opt.MinBetAsset != 0 {
log.Debug("玩家", ChairId, "必须是最小下注的整数倍 ", rr.Opt.MinBetAsset)
BetRes.Code = protocol.ErrorCodeBetError
BetRes.CodeMsg = "必须是最小下注的整数倍 当前最小下注 " + strconv.Itoa(int(rr.Opt.MinBetAsset))
rr.Send(rr.PlayerMap[ChairId], route.BetRes, BetRes)
return
}
// 下注区域判断
player := rr.PlayerMap[ChairId]
if rr.CurrBanker == player.ChairId {
log.Debug("玩家", ChairId, "是庄家不能下注 ")
BetRes.Code = protocol.ErrorCodeBetError
BetRes.CodeMsg = "庄家不能下注 "
rr.Send(rr.PlayerMap[ChairId], route.BetRes, BetRes)
return
}
if !player.IsRobot && message.BetAmount > rr.PlayerMap[ChairId].Gold.IntPart() {
log.Debug("玩家", ChairId, "个人下注总值", message.BetAmount, " 拥有的钱: ", rr.PlayerMap[ChairId].Gold.IntPart())
BetRes.Code = protocol.ErrorCodeBetError
BetRes.CodeMsg = "玩家最多再下注 " + strconv.Itoa(int(rr.PlayerMap[ChairId].Gold.IntPart()))
rr.Send(player, route.BetRes, BetRes)
return
}
rr.UserScore[ChairId] = rr.UserScore[ChairId].Sub(decimal.NewFromInt(message.BetAmount))
player.Gold = player.Gold.Sub(decimal.NewFromInt(message.BetAmount))
if !player.IsRobot && (player.Ctx != nil) {
AfterGold, BloodPool, StockNum := rr.WriteUserScore(player, 1, message.BetAmount, 0, 0, rr.ReasonString, "", "", 0)
log.Debug("WriteUserScore 后 AfterGold=", AfterGold, " BloodPool=", BloodPool, " StockNum=", StockNum)
}
// 下过注
rr.PlayerMap[ChairId].CurrentRoundBet = true
rr.PlayerMap[ChairId].BetScore = message.BetAmount
BetRes.ChairID = ChairId
BetRes.TableTotalWin = rr.PlayerMap[ChairId].TableWin
BetRes.UserScore = rr.PlayerMap[ChairId].Gold.IntPart()
NotifyBet.ChairID = BetRes.ChairID
NotifyBet.TableTotalWin = BetRes.TableTotalWin
for _, TempPlayer := range rr.PlayerMap {
if TempPlayer.ChairId == ChairId {
rr.Send(TempPlayer, route.BetRes, BetRes)
} else {
rr.Send(TempPlayer, route.NotifyBet, NotifyBet)
}
}
for _, TempPlayer := range rr.PlayerWatchMap {
rr.Send(TempPlayer, route.NotifyBet, NotifyBet)
}
// 判断是否提前结束
bEnd := true
for _, TempPlayer := range rr.PlayerMap {
if TempPlayer.CanBet && !TempPlayer.CurrentRoundBet {
bEnd = false
break
}
}
if !Auto && bEnd { // 不是自动才能提前结算
log.Debug("OnUserBet 桌子号 ", rr.RId, " 提前结束 ")
milliseconds := time.Now().UnixMilli()
rr.CurrentOperationTimer = milliseconds
}
}

24
game-server/game/TTZ/app/core/game/game_check_room.go

@ -0,0 +1,24 @@
package game
// check检查[]
func (rr *Room) check() bool {
//1.检查人数[机器人加真玩家]
if rr.GetRoomPlayerCount() < 1 {
return false
}
if rr.GetInRoomRobotCount() <= 0 {
return false
}
return true
}
func (rr *Room) IsJoinRoom() bool {
if rr.GetRoomPlayerCount() >= MAX_USER_NUM {
return false
}
return true
}

103
game-server/game/TTZ/app/core/game/game_comm.go

@ -0,0 +1,103 @@
package game
import (
"math/rand"
"sort"
"time"
)
func (rr *Room) ColorGroup(arr []int) map[int][]int {
cardMap := map[int][]int{}
for _, c := range arr {
clo := c >> 4
cardMap[clo] = append(cardMap[clo], c)
}
temphandCards := map[int][]int{}
for i, items := range cardMap {
var tempItem []int
for _, item := range items {
tempItem = append(tempItem, item)
}
sort.Ints(tempItem)
temphandCards[i] = tempItem
}
return temphandCards
}
// GetInRoomRobotCount 获取房间机器人数量
func (rr *Room) GetInRoomRobotCount() int32 {
unm := int32(0)
for _, player := range rr.PlayerMap {
if player.IsRobot == true {
unm++
}
}
return unm
}
// MakeChairId 分配椅子号 如果分配到-1表示房间满了
func (rr *Room) MakeChairId() int32 {
chairId := int32(-1)
for i := int32(0); i < rr.MaxPlayerCount; i++ {
if rr.GetRoomPlayer(i) == nil {
chairId = i
break
}
}
return chairId
}
// ArrayToMap arr装map
func (rr *Room) ArrayToMap(arr []int) map[int]int {
var tempMap map[int]int
for _, v := range arr {
tempMap[v]++
}
return tempMap
}
// MapToArray map装arr
func (rr *Room) MapToArray(m map[int]int) []int {
var tempArr = make([]int, 0)
for k, v := range m {
for i := 0; i < v; i++ {
tempArr = append(tempArr, k)
}
}
return tempArr
}
// BoolPoolConfigToRoomConfig 数据库中的配置形式转成自己的形式
func (rr *Room) BoolPoolConfigToRoomConfig() {
//rr.boolePool.BoolPoolConfig.ControValue
}
// CalWeight 权重计算
func (rr *Room) CalWeight(weightVal []int32) int32 {
l := len(weightVal)
sum := int32(0)
for _, val := range weightVal {
sum += val
}
R := rand.New(rand.NewSource(int64(uint64(time.Now().UnixNano()))))
randVal := R.Int31n(sum)
grade := int32(0)
for i := int32(0); i < int32(l); i++ {
if randVal <= weightVal[i] {
grade = i
break
}
randVal -= weightVal[i]
}
return grade
}

72
game-server/game/TTZ/app/core/game/game_config.go

@ -0,0 +1,72 @@
package game
import (
"base/log"
conf "xgame/game/TTZ/app/config"
)
func (rr *Room) InitBloodControl() {
rr.BloodCtrl = conf.ReadBloodConfig()
log.Debug("InitBloodControl: GameRound=", rr.BloodCtrl.GameRound)
if rr.BloodCtrl.TimeOutBegin == 0 {
rr.TimeOutBegin = NEXT_GAME_TIME_OUT
} else {
rr.TimeOutBegin = rr.BloodCtrl.TimeOutBegin
}
if rr.BloodCtrl.TimeOutGetBanker == 0 {
rr.TimeOutGetBanker = GET_BANKER_TIME_OUT
} else {
rr.TimeOutGetBanker = rr.BloodCtrl.TimeOutGetBanker
}
if rr.BloodCtrl.TimeOutBB == 0 {
rr.TimeOutBB = BB_TIME_OUT
} else {
rr.TimeOutBB = rr.BloodCtrl.TimeOutBB
}
if rr.BloodCtrl.TimeOutDT == 0 {
rr.TimeOutDT = DT_TIME_OUT
} else {
rr.TimeOutDT = rr.BloodCtrl.TimeOutDT
}
if rr.BloodCtrl.TimeOutBet == 0 {
rr.TimeOutBet = BET_TIME_OUT
} else {
rr.TimeOutBet = rr.BloodCtrl.TimeOutBet
}
if rr.BloodCtrl.TimeOutCP == 0 {
rr.TimeOutCP = CP_TIME_OUT
} else {
rr.TimeOutCP = rr.BloodCtrl.TimeOutCP
}
if rr.BloodCtrl.TimeOutResult == 0 {
rr.TimeOutResult = RESULT_TIME_OUT
} else {
rr.TimeOutResult = rr.BloodCtrl.TimeOutResult
}
if rr.BloodCtrl.TimeKickTimes == 0 {
rr.TimeKickTimes = GIVEUP_COUNT
} else {
rr.TimeKickTimes = rr.BloodCtrl.TimeKickTimes
}
if rr.BloodCtrl.TimeoutDestroy == 0 {
rr.TimeoutDestroy = DESTROY_TIME_OUT
} else {
rr.TimeoutDestroy = rr.BloodCtrl.TimeoutDestroy
}
if rr.BloodCtrl.MinGameScore == 0 {
rr.MinGameScore = MIN_GAME_SCORE
} else {
rr.MinGameScore = rr.BloodCtrl.MinGameScore
}
}

121
game-server/game/TTZ/app/core/game/game_copy.go

@ -0,0 +1,121 @@
package game
import (
"base/encoding/json"
"base/log"
"github.com/roylee0704/gron"
"github.com/shopspring/decimal"
"time"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/service/rpc_client"
optionRoom "xgame/internal/option/room"
"xgame/internal/service/game/pb"
)
func (rr *Room) Copy(SrcRoom *Room, Uid int64) {
// 获取当前时间
now := time.Now()
// 获取当前时区的偏移量
_, offset := now.Zone()
// 将偏移量转换为小时数
offsetHours := float64(offset) / 3600
rr.OffsetHours = offsetHours
log.Debug("当前时间 ", now, " 当前时区偏差值", rr.OffsetHours)
// 读取配置文件
rr.InitBloodControl()
//配置库存[房间的库存]
rr.StockValue = rr.BoolePool.BoolPoolSockNum
//2.初始化配置设置房间 opts
rr.Opt = &optionRoom.Options{}
var replyOpt *pb.FetchRoomsReply
if rr.RoomID < 6 {
replyOpt = rpc_client.GetSrcRoomByLevel(rr.RoomProxy, rr.GameID, rr.AgentID, rr.RoomID)
} else {
replyOpt = rpc_client.GetSrcRoom(rr.RoomProxy, rr.GameID, rr.AgentID, rr.RoomID)
}
if replyOpt == nil {
log.Debug("初始化房间失败")
} else {
err1 := json.Unmarshal([]byte(replyOpt.RoomOpts), rr.Opt)
if err1 != nil {
log.Debug(err1)
}
}
log.Debug("房间配置: ", rr.Opt)
//1.初始化血池
if rr.BoolePool.SetBoole(rr.RoomProxy, rr.Opt.BloodId) == false {
log.Debug("初始化血池失败")
}
//机器人数
rr.MaxRobotPlayerCount = MAX_ROBOT_BUN
//房间最大人数
rr.MaxPlayerCount = MAX_USER_NUM
// 个人倍率
rr.PersonalRate = rr.Opt.PersonalRate
// 房间倍率
rr.RoomRate = rr.Opt.RoomRate
// 最大轮数
rr.GameMaxRound = 1000 // todo 这里需要从配置获取
// 房间进入条件
rr.RoomLimit = decimal.NewFromFloat(float64(rr.Opt.MinEnterAsset))
// 底注
rr.BetList = rr.Opt.BetAssetOptions
//new一个定时器
if rr.GronTimer != nil {
rr.GronTimer.Stop()
rr.GronTimer = nil
}
rr.GronTimer = gron.New()
rr.GronTimer.AddFunc(gron.Every(50*time.Millisecond), func() {
rr.run()
})
rr.GronTimer.Start()
//初始化机器人配置
rr.RobotMgr.OnInit("", rr)
// 复制桌子上的人过来
for i, i2 := range SrcRoom.PlayerMap {
if i2.IsRobot {
rr.PlayerMap[i] = i2
rr.RobotMgr.CopyRobotsEnterGameRoom(i2)
}
if Uid == i2.Uid {
rr.PlayerMap[i] = i2
}
}
// 删除挪过来的人
for i, i2 := range SrcRoom.PlayerMap {
if Uid == i2.Uid {
SrcRoom.PlayerMap[i] = nil
delete(SrcRoom.PlayerMap, i)
break
}
}
// 复制记录
for _, record := range SrcRoom.Record {
rr.Record = append(rr.Record, record)
}
rr.State = EN_TABLE_STATE_READY_TO_START
milliseconds := time.Now().UnixMilli()
rr.CurrentGameState = protocol.STATE_BEGIN
rr.CurrentOperationTimer = milliseconds + rr.TimeOutBegin
return
}

46
game-server/game/TTZ/app/core/game/game_ctrl_blood.go

@ -0,0 +1,46 @@
package game
import (
"base/log"
"math/rand"
"strconv"
)
func (rr *Room) GetCtrlBlood() int { // 0 不控 1 系统赢 2 系统输
//return 0 // 测试添加
TemPro := 10000 // todo 测试后删除
//log.Debug(" 桌子号", rr.RId, "房间当前血池 :", rr.BoolePool)
CurrentGears := 0
TempRoomGears, err := strconv.Atoi(rr.BoolePool.RoomGears)
if err == nil {
CurrentGears = TempRoomGears
//log.Debug(" 桌子号", rr.RId, "房间当前血池档位1 :", TempRoomGears, " CurrentGears :", CurrentGears)
}
//判断点控
if rr.CreatorId != 0 && rr.isUserCtl {
CurrentGears = int(rr.UserCtlGear)
//log.Debug(" 桌子号", rr.RId, "房间当前血池档位2 :", rr.UserCtlGear, " CurrentGears :", CurrentGears)
}
for _, TempGearConfig := range rr.BloodCtrl.GearConfig {
if TempGearConfig.GearID == CurrentGears {
TemPro = TempGearConfig.WinPro
break
}
}
log.Debug(" 桌子号", rr.RId, "房间当前血池 :", rr.BoolePool, "是否个人点控 :", rr.isUserCtl, "点控档位 :", CurrentGears, " 房间点控", TempRoomGears, " 当前的概率", TemPro)
// 正数血池抽中控制 控系统赢 负数血池控系统输
if rr.GetInRoomRobotCount() > 0 && rand.Intn(10000) < TemPro {
if CurrentGears >= 0 {
return 1
} else {
return 2
}
} else {
return 0
}
}

36
game-server/game/TTZ/app/core/game/game_disconnect.go

@ -0,0 +1,36 @@
package game
import "base/log"
/*
OnReconnect(userId int64, proxy *node.Proxy) // 断线重连
OnDisconnect(userId int64, proxy *node.Proxy) // 玩家离线
*/
// OnDisconnect 玩家离线 保存正在游戏的进度
func (rr *Room) OnDisconnect(userId int64) {
rr.roomRWLock.Lock()
log.Debug(" 桌子号", rr.RId, "玩家", userId, "离线")
for _, player := range rr.PlayerMap {
if player != nil {
if player.Uid == userId {
player.OnlineState = USER_OFF_LINE
break
}
}
}
for _, player := range rr.PlayerWatchMap {
if player != nil {
if player.Uid == userId {
player.OnlineState = USER_OFF_LINE
rr.roomRWLock.Unlock()
rr.OnGetOutRoom(player.Uid)
rr.roomRWLock.Lock()
break
}
}
}
rr.roomRWLock.Unlock()
}

10
game-server/game/TTZ/app/core/game/game_dissolution.go

@ -0,0 +1,10 @@
package game
func (rr *Room) IsCanDissolution() bool { // IsCanDissolution是否能解散房间
return false
}
func (rr *Room) OnDestroy(uid int64) {
rr.DestroyRoomFunc(uid)
}

64
game-server/game/TTZ/app/core/game/game_get_banker.go

@ -0,0 +1,64 @@
package game
import (
"base/log"
"time"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/route"
)
func (rr *Room) OnUserGetBanker(uid int64, msg interface{}) { // 用户抢庄
message := msg.(*protocol.GetBankerReq)
BankerRes := &protocol.GetBankerRes{}
NotifyBanker := &protocol.NotifyBanker{}
ChairId := int32(-1)
for _, player := range rr.PlayerMap {
if player.Uid == uid {
ChairId = player.ChairId
break
}
}
if ChairId == -1 {
log.Error("致命错误")
return
}
if rr.CurrentGameState != protocol.STATE_GET_BANKER {
log.Debug("玩家", ChairId, "非等待时间不能抢庄 ", rr.CurrentGameState)
BankerRes.Code = protocol.ErrorGetBanker
BankerRes.CodeMsg = "非抢庄时间不能抢庄"
rr.Send(rr.PlayerMap[ChairId], route.GetBankerRes, BankerRes)
return
}
NotifyBanker.ChairID = ChairId
NotifyBanker.State = message.State
for _, TempPlayer := range rr.PlayerMap {
if TempPlayer.ChairId == ChairId {
rr.Send(TempPlayer, route.GetBankerRes, BankerRes)
} else {
rr.Send(TempPlayer, route.NotifyBanker, NotifyBanker)
}
}
for _, TempPlayer := range rr.PlayerWatchMap {
rr.Send(TempPlayer, route.NotifyBanker, NotifyBanker)
}
if message.State == 0 {
rr.BaoDiBanker = true
rr.GetBankerList = append(rr.GetBankerList, ChairId)
//log.Debug(" 桌子号", rr.RId, "玩家", ChairId, " 抢庄 是否机器人", rr.PlayerMap[ChairId].IsRobot)
} else {
rr.NoGetBankerList = append(rr.NoGetBankerList, ChairId)
//log.Debug(" 桌子号", rr.RId, "玩家", ChairId, " 不抢 是否机器人", rr.PlayerMap[ChairId].IsRobot)
}
// 判断是否提前结束
if len(rr.PlayerMap) == (len(rr.GetBankerList) + len(rr.NoGetBankerList)) { // 提前开始
//log.Debug("OnUserBet 桌子号 ", rr.RId, " 抢庄提前结束 ")
milliseconds := time.Now().UnixMilli()
rr.CurrentOperationTimer = milliseconds
}
}

128
game-server/game/TTZ/app/core/game/game_get_room_info.go

@ -0,0 +1,128 @@
package game
import (
"base/cluster/node"
"base/log"
"time"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/route"
)
func (rr *Room) OnGetRoomInfo(uid int64, ctx *node.Context) interface{} {
var pPlayer *Player
TableInfo := &protocol.RoomInfoRes{}
now := time.Now()
TableInfo.RoomId = int32(rr.RId)
TableInfo.ServerLocation = rr.OffsetHours
TableInfo.ServerTimestamp = now.UnixMilli()
TableInfo.RoomStatus = rr.CurrentGameState
TableInfo.StatusChangeTimestamp = rr.CurrentOperationTimer
TableInfo.SingleMaxBet = rr.BankerLimitMax
TableInfo.RoomConfig = &protocol.TableConfig{
AlwaysBet: rr.Opt.AlwaysBet,
RechargeLimit: rr.BloodCtrl.RechargeLimit,
TaxMing: rr.Opt.BrightRate,
}
for _, player := range rr.PlayerMap {
if player.Uid == uid {
pPlayer = player
}
TableSeat := &protocol.RoomPlayer{}
TableSeat.Uid = player.Uid
TableSeat.ChairID = player.ChairId
TableSeat.State = rr.State
TableSeat.Avatar = player.Avatar
TableSeat.Gold = player.Gold.IntPart()
TableSeat.Nickname = player.Nickname
TableSeat.Sex = player.Sex
TableSeat.Vip = 0
TableSeat.Level = player.Level
TableSeat.Exp = 0
TableSeat.BetScore = player.BetScore
TableSeat.EndScore = player.WinScore
TableSeat.CanBet = player.CanBet
if player.ChairId == rr.CurrBanker {
TableSeat.IsBanker = 1
}
for _, poker := range player.HandPoker {
TableSeat.HandPoker = append(TableSeat.HandPoker, int32(poker))
}
TableSeat.PokerType, TableSeat.Point = player.HandPoker.GetCardType()
TableSeat.BetScore = player.BetScore
TableInfo.PlayerList = append(TableInfo.PlayerList, TableSeat)
}
for _, player := range rr.PlayerWatchMap {
if player.Uid == uid {
pPlayer = player
}
TableSeat := &protocol.RoomPlayer{}
TableSeat.Uid = player.Uid
TableSeat.ChairID = player.ChairId
TableSeat.State = rr.State
TableSeat.Avatar = player.Avatar
TableSeat.Gold = player.Gold.IntPart()
TableSeat.Nickname = player.Nickname
TableSeat.Sex = player.Sex
TableSeat.Vip = 0
TableSeat.Level = player.Level
TableSeat.Exp = 0
TableSeat.EndScore = player.WinScore
TableSeat.CanBet = player.CanBet
if player.ChairId == rr.CurrBanker {
TableSeat.IsBanker = 1
}
for _, poker := range player.HandPoker {
TableSeat.HandPoker = append(TableSeat.HandPoker, int32(poker))
}
TableSeat.PokerType, TableSeat.Point = player.HandPoker.GetCardType()
TableSeat.BetScore = player.BetScore
TableInfo.PlayerWatchMap = append(TableInfo.PlayerWatchMap, TableSeat)
}
TableInfo.Record = rr.Record
log.Debug("TableInfo==>", TableInfo)
if pPlayer != nil && !pPlayer.IsRobot {
for index, record := range rr.Record {
log.Debug("现在有 ", len(rr.Record), " 个记录 当前第 ", index, " 个 值:", record)
}
}
////加分测试
//if !pPlayer.IsRobot && (pPlayer.Ctx != nil) {
// rpc_client.IncrAsset(pPlayer.Ctx, int64(10000000),int32(model.UserGoldChangeReasonRummy))
//}
pPlayer.Ctx = ctx
rr.SendTableInfo(pPlayer, route.RoomInfoRes, TableInfo)
// 发送完桌子消息 再激活用户状态
pPlayer.OnlineState = USER_ON_LINE
//判断开场
if rr.CurrentGameState < protocol.STATE_GET_BANKER && rr.GetRoomPlayerCount() > 1 {
rr.State = EN_TABLE_STATE_PLAYING
milliseconds := time.Now().UnixMilli()
rr.CurrentGameState = protocol.STATE_GET_BANKER
rr.CurrentOperationTimer = milliseconds + rr.TimeOutGetBanker
rr.RepeatRoomUser()
rr.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: protocol.STATE_GET_BANKER,
NextTimestamp: rr.CurrentOperationTimer,
})
// 抢庄做了加一秒的容错判断
rr.CurrentOperationTimer += 1000
}
return TableInfo
}

138
game-server/game/TTZ/app/core/game/game_init.go

@ -0,0 +1,138 @@
package game
import (
"base/encoding/json"
"base/log"
"strconv"
"time"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/service/rpc_client"
optionRoom "xgame/internal/option/room"
"xgame/internal/service/game/pb"
"github.com/roylee0704/gron"
"github.com/shopspring/decimal"
)
type ReadData struct {
Pokers []int `json:"pokers"`
CardType int `json:"card_type"`
Code int `json:"code"`
}
type ConstantDelaySchedule struct {
Delay time.Duration
}
// Every returns a crontab Schedule that activates once every duration.
// Delays of less than a second are not supported (will round up to 1 second).
// Any fields less than a Second are truncated.
func Every(duration time.Duration) ConstantDelaySchedule {
MinTime := 100 * time.Millisecond
if duration < MinTime {
duration = MinTime
}
return ConstantDelaySchedule{
Delay: duration,
}
}
func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time {
return t.Add(schedule.Delay)
}
// OnInit 初始化房间
func (rr *Room) OnInit() bool {
// 获取当前时间
now := time.Now()
// 获取当前时区的偏移量
_, offset := now.Zone()
// 将偏移量转换为小时数
offsetHours := float64(offset) / 3600
rr.OffsetHours = offsetHours
log.Debug("当前时间 ", now, " 当前时区偏差值", rr.OffsetHours)
// 读取配置文件
rr.InitBloodControl()
var gameTableConfig *pb.FetchGameTableTotalsReply
TableType := int64(1) //1 系统 2玩家
if rr.CreatorId != 0 {
TableType = 2
}
gameTableConfig = rpc_client.GetTableCtrlConfig(rr.RoomProxy, int64(rr.GameID), strconv.Itoa(int(rr.RoomID)), TableType)
if len(gameTableConfig.List) == 0 {
log.Error(" 21点后台获取桌子配置 无配置 房间等级 ", rr.RoomID)
} else {
rr.TableConfig = gameTableConfig.List[0]
log.Debug(" 21点后台获取桌子配置 房间等级 ", rr.RoomID, " 配置 ", rr.TableConfig)
}
//配置库存[房间的库存]
rr.StockValue = rr.BoolePool.BoolPoolSockNum
//2.初始化配置设置房间 opts
rr.Opt = &optionRoom.Options{}
var replyOpt *pb.FetchRoomsReply
if rr.RoomID < 6 {
replyOpt = rpc_client.GetSrcRoomByLevel(rr.RoomProxy, rr.GameID, rr.AgentID, rr.RoomID)
} else {
replyOpt = rpc_client.GetSrcRoom(rr.RoomProxy, rr.GameID, rr.AgentID, rr.RoomID)
}
if replyOpt == nil {
log.Debug("初始化房间失败")
} else {
err1 := json.Unmarshal([]byte(replyOpt.RoomOpts), rr.Opt)
if err1 != nil {
log.Debug(err1)
}
}
log.Debug("房间配置: ", rr.Opt)
//1.初始化血池
if rr.BoolePool.SetBoole(rr.RoomProxy, rr.Opt.BloodId) == false {
log.Debug("初始化血池失败")
}
//机器人数
rr.MaxRobotPlayerCount = MAX_ROBOT_BUN
//房间最大人数
rr.MaxPlayerCount = MAX_USER_NUM
// 个人倍率
rr.PersonalRate = rr.Opt.PersonalRate
// 房间倍率
rr.RoomRate = rr.Opt.RoomRate
// 最大轮数
rr.GameMaxRound = 1000 // todo 这里需要从配置获取
// 房间进入条件
rr.RoomLimit = decimal.NewFromFloat(float64(rr.Opt.MinEnterAsset))
// 底注
rr.BetList = rr.Opt.BetAssetOptions
//new一个定时器
if rr.GronTimer != nil {
rr.GronTimer.Stop()
rr.GronTimer = nil
}
rr.GronTimer = gron.New()
rr.GronTimer.AddFunc(Every(50*time.Millisecond), func() {
rr.run()
})
rr.GronTimer.Start()
//初始化机器人配置
rr.RobotMgr.OnInit("", rr)
rr.State = EN_TABLE_STATE_READY_TO_START
milliseconds := time.Now().UnixMilli()
rr.CurrentGameState = protocol.STATE_WAIT
rr.CurrentOperationTimer = milliseconds + rr.TimeoutDestroy
return true
}

130
game-server/game/TTZ/app/core/game/game_join_room.go

@ -0,0 +1,130 @@
package game
import (
"base/log"
"github.com/shopspring/decimal"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/route"
)
// JoinRoom 玩家加入房间 0==加入房间成功,-1==满员了
func (rr *Room) JoinRoom(pPlayer *Player, isWatching int32) int32 {
rr.roomRWLock.Lock()
defer rr.roomRWLock.Unlock()
//res := &protocol.SitDownRes{}
chairId := rr.MakeChairId()
//// 测试添加
//if pPlayer.Uid == 75 {
// chairId = -1
//}
//if -1 == chairId {
// //说明满员了
// //rr.Send(pPlayer.Uid, 0, res)
// res.Code = -1
// res.CodeMsg = "满员了"
// if pPlayer.Ctx != nil && !pPlayer.IsRobot {
// pPlayer.Ctx.Request.Message.Route = route.Enter
// err := pPlayer.Ctx.Response(res)
// if err != nil {
// log.Errorf("response message failed: %v", err)
// }
// }
// return -1
//}
//钱不够 不能进
if pPlayer.Gold.LessThan(rr.RoomLimit) {
return -1
}
//钱不够 直接观战
if pPlayer.Gold.LessThan(decimal.NewFromInt(rr.MinGameScore)) {
chairId = -1
}
//要求观战
if isWatching == 1 {
chairId = -1
}
//rr.RoomLimit
pPlayer.ChairId = chairId
pPlayer.OnlineState = USER_LEAVE
pPlayer.IsCheat = false
pPlayer.CanBet = false
rr.isUserCtl = false
pPlayer.CheatType = 0
pPlayer.CheatValue = 0
if !pPlayer.IsRobot {
Reply := rr.GetUserGameControl(rr.RoomProxy, pPlayer.Uid, int64(rr.GameID), 0)
if Reply != nil && Reply.CheatValue != 0 {
log.Debug(" 桌子号", rr.RId, "房间:", rr.RoomID, "真玩家uid==", pPlayer.Uid, " 更新点控:", Reply)
rr.isUserCtl = true
pPlayer.IsCheat = true
pPlayer.CheatType = Reply.CheatType
pPlayer.CheatValue = Reply.CheatValue
}
log.Debug(" 桌子号", rr.RId, "房间:", rr.RoomID, "真玩家uid==", pPlayer.Uid, " 点控类型:", pPlayer.CheatType, " 点控值:", pPlayer.CheatValue, " 是否点控", pPlayer.IsCheat)
}
log.Debug(" 桌子号", rr.RId, "玩家名字:", pPlayer.Nickname, " 桌位号:", chairId, "加入房间==>")
SitDownRes := &protocol.SitDownRes{
Code: 0,
CodeMsg: "sit success",
PlayerInfo: &protocol.RoomPlayer{},
}
NotifySitDown := &protocol.NoticeSitDown{
PlayerInfo: &protocol.RoomPlayer{},
}
TableSeat := &protocol.RoomPlayer{}
TableSeat.Uid = pPlayer.Uid
TableSeat.ChairID = pPlayer.ChairId
TableSeat.State = rr.State
TableSeat.Avatar = pPlayer.Avatar
TableSeat.Gold = pPlayer.Gold.IntPart()
TableSeat.Nickname = pPlayer.Nickname
TableSeat.Sex = pPlayer.Sex
TableSeat.Vip = 0
TableSeat.Level = pPlayer.Level
TableSeat.Exp = 0
TableSeat.BetScore = pPlayer.BetScore
TableSeat.EndScore = pPlayer.WinScore
SitDownRes.PlayerInfo = TableSeat
NotifySitDown.PlayerInfo = TableSeat
if chairId == -1 {
// 观战
rr.PlayerWatchMap = append(rr.PlayerWatchMap, pPlayer)
} else {
rr.PlayerMap[pPlayer.ChairId] = pPlayer
}
for temChairID, temPlayer := range rr.PlayerMap {
if temChairID != pPlayer.ChairId {
rr.Send(temPlayer, route.NotifySitDown, NotifySitDown)
}
}
for _, temPlayer := range rr.PlayerWatchMap {
rr.Send(temPlayer, route.NotifySitDown, NotifySitDown)
}
rr.SendTableInfo(pPlayer, route.SitDownRes, SitDownRes)
return 0
}
func (rr *Room) LeaveRoom(uid int64) int32 {
rr.roomRWLock.Lock()
defer rr.roomRWLock.Unlock()
log.Debug("玩家", uid, "申请离开游戏")
return 0
}

362
game-server/game/TTZ/app/core/game/game_logic.go

@ -0,0 +1,362 @@
package game
import (
"base/cluster/node"
"base/log"
"time"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/route"
)
// UpdateCtx 这里是无奈之举
func (rr *Room) UpdateCtx(uid int64, ctx *node.Context) {
rr.roomRWLock.Lock()
defer rr.roomRWLock.Unlock()
for Index, p := range rr.PlayerMap {
if p.Uid == uid {
log.Debug("桌子ID", rr.RId, "PlayerMap User ", uid, " 用户名字 ", p.Nickname, "原来的 ", p.Ctx, "新的 Ctx", ctx)
rr.PlayerMap[Index].OnlineState = USER_ON_LINE
rr.PlayerMap[Index].Ctx = ctx
break
}
}
for Index, p := range rr.PlayerWatchMap {
if p.Uid == uid {
log.Debug("桌子ID", rr.RId, "PlayerWatchMap User ", uid, " 用户名字 ", p.Nickname, "原来的 ", p.Ctx, "新的 Ctx", ctx)
rr.PlayerWatchMap[Index].OnlineState = USER_ON_LINE
rr.PlayerWatchMap[Index].Ctx = ctx
break
}
}
}
func (rr *Room) UpdateUserOut(uid int64) { // 更新用户退出状态
rr.roomRWLock.Lock()
defer rr.roomRWLock.Unlock()
for Index, p := range rr.PlayerMap {
if p.Uid == uid {
rr.PlayerMap[Index].UserNeedOut = true
break
}
}
}
func (rr *Room) run() {
milliseconds := time.Now().UnixMilli()
if rr.State == EN_TABLE_STATE_UNKNOWN {
return
}
switch rr.CurrentGameState {
//等待开场
case protocol.STATE_WAIT:
{
if milliseconds >= rr.CurrentOperationTimer {
for _, player := range rr.PlayerMap {
// 没人就解散房间
log.Debug("PlayerMap 用户 ", player.Nickname, " 没人就解散房间 ", rr.RId)
rr.OnGetOutRoom(player.Uid)
}
for _, player := range rr.PlayerWatchMap {
// 没人就解散房间
log.Debug("PlayerWatchMap 用户 ", player.Nickname, " 没人就解散房间 ", rr.RId)
rr.OnGetOutRoom(player.Uid)
}
}
}
//下一把
case protocol.STATE_BEGIN:
{
// 等待过程中人数又不足了 重新等待开场
if rr.GetRoomPlayerCount() <= 4 {
log.Debug(" 桌子号 ", rr.RId, " 等待过程中人数又不足了 重新等待开场 ")
rr.State = EN_TABLE_STATE_READY_TO_START
rr.CurrentGameState = protocol.STATE_WAIT
rr.CurrentOperationTimer = milliseconds + rr.TimeoutDestroy
rr.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: protocol.STATE_WAIT,
NextTimestamp: rr.CurrentOperationTimer,
})
break
}
if milliseconds >= rr.CurrentOperationTimer {
log.Debug(" 桌子号 ", rr.RId, " 开始抢庄 ")
//设置游戏状态
rr.State = EN_TABLE_STATE_PLAYING
rr.Round++ //轮数+1
rr.CurrentOperationTimer = milliseconds + rr.TimeOutGetBanker
rr.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: protocol.STATE_GET_BANKER,
NextTimestamp: rr.CurrentOperationTimer,
})
// 抢庄做了加一秒的容错判断
rr.CurrentOperationTimer += 1000
}
return
}
//等待抢庄
case protocol.STATE_GET_BANKER:
{
// 保底抢庄
if !rr.BaoDiBanker && milliseconds >= rr.CurrentOperationTimer-1000 {
rr.BaoDiBanker = true
for _, player := range rr.PlayerMap {
if player.IsRobot {
CanGetBanker := true
for _, UserChairID := range rr.NoGetBankerList {
if UserChairID == player.ChairId {
CanGetBanker = false
break
}
}
if CanGetBanker {
rr.OnUserGetBanker(player.Uid, &protocol.GetBankerReq{
State: 0,
})
break
}
}
}
}
if milliseconds >= rr.CurrentOperationTimer {
log.Debug(" 桌子号 ", rr.RId, " 等待开始下注 ")
rr.CurrentGameState = protocol.STATE_BB
rr.CurrentOperationTimer = milliseconds + rr.TimeOutBB
rr.OnGameSelectBanker()
rr.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: rr.CurrentGameState,
NextTimestamp: rr.CurrentOperationTimer,
})
}
}
case protocol.STATE_BB:
{
if milliseconds >= rr.CurrentOperationTimer {
log.Debug(" 桌子号 ", rr.RId, " 等待丢骰子 ")
rr.OnGameBeginUser()
rr.CurrentGameState = protocol.STATE_DT
rr.CurrentOperationTimer = milliseconds + rr.TimeOutDT
rr.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: rr.CurrentGameState,
NextTimestamp: rr.CurrentOperationTimer,
})
}
}
case protocol.STATE_DT:
{
if milliseconds >= rr.CurrentOperationTimer {
log.Debug(" 桌子号 ", rr.RId, " 开始下注 ")
rr.CurrentGameState = protocol.STATE_BET
rr.CurrentOperationTimer = milliseconds + rr.TimeOutBet
rr.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: rr.CurrentGameState,
NextTimestamp: rr.CurrentOperationTimer,
})
}
}
case protocol.STATE_BET:
{
if milliseconds >= rr.CurrentOperationTimer {
rr.CurrentOperationTimer = milliseconds + 60*1000
// 未下注的自动下最低注
for index, tempPlayer := range rr.PlayerMap {
if !tempPlayer.CanBet || tempPlayer.ChairId == rr.CurrBanker || rr.PlayerMap[index].CurrentRoundBet {
continue
}
rr.OnUserBet(tempPlayer.Uid, &protocol.BetReq{
BetAmount: rr.Opt.MinBetAsset,
}, true)
}
log.Debug(" 桌子号 ", rr.RId, " 开始搓牌 ")
rr.CurrentGameState = protocol.STATE_CP
rr.CurrentOperationTimer = milliseconds + rr.TimeOutCP
rr.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: rr.CurrentGameState,
NextTimestamp: 0,
})
}
}
case protocol.STATE_CP:
{
if milliseconds >= rr.CurrentOperationTimer {
log.Debug(" 桌子号 ", rr.RId, " 开始结算 ")
// 先弄个长点的时间防止出问题
rr.CurrentOperationTimer = milliseconds + 30000
rr.OnEventGameConclude()
}
}
case protocol.STATE_RESULT:
{
if milliseconds >= rr.CurrentOperationTimer {
log.Debug(" 桌子号 ", rr.RId, " 等待下一局开始 ")
// 先弄个长点的时间防止出问题
rr.CurrentOperationTimer = milliseconds + 30000
for _, player := range rr.PlayerWatchMap {
if player.IsRobot {
continue
}
if player.OnlineState != USER_ON_LINE {
log.Debug("观战用户 ", player.Nickname, " 不在线 ", rr.RId)
rr.OnGetOutRoom(player.Uid)
} else if !player.IsRobot && player.PlayWatchCount >= int(3) {
log.Debug("用户 ", player.Nickname, " 连续观战了3局 ", rr.RId)
rr.OnGetOutRoom(player.Uid)
}
}
// 钱不够转为观战
for _, player := range rr.PlayerMap {
if player.IsRobot {
continue
}
if player.Gold.IntPart() <= rr.MinGameScore {
log.Debug("用户 ", player.Nickname, " 钱不够转为观战 拥有钱:", player.Gold.IntPart(), " 需要的钱:", rr.MinGameScore)
TempChairID := player.ChairId
player.ChairId = -1
rr.SendAllMessage(route.NotifyDownToWatch, &protocol.NotifyDownToWatch{
ChairID: TempChairID,
})
// 通知大厅有人离开或观战
NotifySitDown := &protocol.NotifyDTSitDown{
Uid: player.Uid,
RoomID: rr.RoomID,
TableID: rr.RId,
ChairID: TempChairID,
Avatar: player.Avatar,
State: rr.CurrentGameState,
}
rr.NotifyFunc(route.NotifyDTStandUp, NotifySitDown)
delete(rr.PlayerMap, TempChairID)
rr.PlayerWatchMap = append(rr.PlayerWatchMap, player)
}
}
//设置游戏状态
rr.State = EN_TABLE_STATE_READY_TO_START
rr.CurrentGameState = protocol.STATE_BEGIN
rr.CurrentOperationTimer = milliseconds + rr.TimeOutBegin
//0.复位房间一些相关数据
rr.RepeatRoom()
rr.RepeatRoomUser()
//人数不足了 重新等待开场
if rr.GetRoomPlayerCount() <= 1 || rr.GetRoomRealPlayerCount() < 1 {
rr.State = EN_TABLE_STATE_READY_TO_START
rr.CurrentGameState = protocol.STATE_WAIT
rr.CurrentOperationTimer = milliseconds + rr.TimeoutDestroy
rr.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: protocol.STATE_WAIT,
NextTimestamp: rr.CurrentOperationTimer,
})
break
} else {
if rr.GuoScore <= 10 {
log.Debug(" 桌子号 ", rr.RId, " 等待下一大局开始 ")
rr.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: protocol.STATE_BEGIN,
NextTimestamp: rr.CurrentOperationTimer,
})
} else {
log.Debug(" 桌子号 ", rr.RId, " 下一小局开始 等待丢骰子 ")
rr.OnGameBeginUser()
rr.CurrentGameState = protocol.STATE_DT
rr.CurrentOperationTimer = milliseconds + rr.TimeOutDT
rr.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: rr.CurrentGameState,
NextTimestamp: rr.CurrentOperationTimer,
})
}
}
}
}
}
}
func (rr *Room) Send(player *Player, msgId int32, message interface{}) {
//if msgId != route.NotifyBet {
// log.Info("Nickname==", player.Nickname, " MsgId==", msgId, " :", message)
// log.Printf(1, "内容: %+v\n", message)
//}
if !player.IsRobot {
if player.OnlineState == USER_ON_LINE && player.Ctx != nil {
if msgId > 0 {
player.Ctx.Request.Message.Route = msgId
}
if err := player.Ctx.Response(message); err != nil {
log.Debug("response message failed: ", err, "uid====>", player.Uid)
}
}
} else {
rr.RobotMgr.OnRobotMessage(msgId, player, message)
}
}
func (rr *Room) SendTableInfo(player *Player, msgId int32, message interface{}) {
//log.Info("Nickname==", player.Nickname, " MsgId==", msgId, " :", message)
//log.Printf(1, "内容: %+v\n", message)
if !player.IsRobot {
if player.Ctx != nil {
if msgId > 0 {
player.Ctx.Request.Message.Route = msgId
}
if err := player.Ctx.Response(message); err != nil {
log.Debug("response message failed: ", err, "uid====>", player.Uid)
}
}
} else {
rr.RobotMgr.OnRobotMessage(msgId, player, message)
}
}
func (rr *Room) SendAllMessage(msgId int32, message interface{}) {
//log.Info("桌子号", rr.RId, " MsgId==", msgId, " :", message)
go func() {
for _, pPlayer := range rr.PlayerMap {
if pPlayer != nil {
rr.Send(pPlayer, msgId, message)
}
}
for _, pPlayer := range rr.PlayerWatchMap {
if pPlayer != nil {
rr.Send(pPlayer, msgId, message)
}
}
}()
}

47
game-server/game/TTZ/app/core/game/game_new.go

@ -0,0 +1,47 @@
package game
import (
"base/cluster/node"
"context"
"github.com/shopspring/decimal"
"strconv"
"sync"
"xgame/game/TTZ/app/core/blood"
"xgame/game/TTZ/app/core/comm"
)
func New(Uid, rid int64, RoomID, GameID, AgentID int32, destory comm.DestoryCB, qiut comm.DestoryCB, Notify comm.NotifyDTUser, RoomProxy *node.Proxy) *Room {
r := &Room{
RoomProxy: RoomProxy,
Ctx: context.Background(),
RId: rid,
RoomID: RoomID,
GameID: GameID,
ReasonString: strconv.Itoa(int(GameID)) + "|" + strconv.Itoa(int(RoomID)),
AgentID: AgentID,
PlayerMap: make(map[int32]*Player),
PlayerWatchMap: make([]*Player, 0),
RobotMgr: NewRobotManager(),
Round: 0,
WinPlayer: INVALID_VALUE,
EndReason: 0,
CreatorId: Uid,
roomRWLock: sync.RWMutex{},
BoolePool: &blood.BoolePool{},
StockValue: 0,
DestroyRoomFunc: destory,
QuitRoomFunc: qiut,
NotifyFunc: Notify,
CurrentOperationTimer: 0,
RoomLimit: decimal.Zero,
IsP2pControls: false,
BetList: make([]int64, 0),
UserScore: make(map[int32]decimal.Decimal),
//ControValueMap: make(map[int64]decimal.Decimal),
//MessageQueue: mq,
//players: list.New(),
//autoTimer: time.NewTimer(1 * time.Second),
}
return r
}

232
game-server/game/TTZ/app/core/game/game_out_room.go

@ -0,0 +1,232 @@
package game
import (
"base/log"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/route"
)
func (rr *Room) UpdateSeat() {
l := rr.GetRoomPlayerCount()
for i := int32(0); i < l; i++ {
p := rr.PlayerMap[i]
if p == nil {
ChairId := rr.MakeChairId()
rr.PlayerMap[ChairId].ChairId = ChairId
}
}
for _, pPlayer := range rr.PlayerMap {
if pPlayer == nil {
continue
}
}
}
// OnGetOutRoom 退出房间
func (rr *Room) OnGetOutRoom(uid int64) bool {
rr.roomRWLock.Lock()
defer rr.roomRWLock.Unlock()
LeaveTableRes := &protocol.LeaveTableRes{}
NotifyLeaveRoomRes := &protocol.NotifyLeaveRoomRes{}
ret := false
var tempPlayerMap = map[int32]*Player{}
var OutUser *Player
for seatId, pPlayer := range rr.PlayerMap {
if pPlayer == nil {
continue
}
tempPlayerMap[seatId] = pPlayer
if pPlayer.Uid == uid {
OutUser = pPlayer
}
}
NotifyLeaveRoomRes.ChairID = 0xFF
if OutUser == nil {
for TempIndex, pPlayer := range rr.PlayerWatchMap {
if pPlayer == nil {
continue
}
if pPlayer.Uid == uid {
rr.Send(pPlayer, route.LogoutTableRes, LeaveTableRes)
NotifyLeaveRoomRes.ChairID = -1
OutUser = pPlayer
if !pPlayer.IsRobot {
pPlayer.OnlineState = USER_LEAVE
pPlayer.Ctx = nil
rr.QuitRoomFunc(pPlayer.Uid)
if TempIndex == len(rr.PlayerWatchMap)-1 {
rr.PlayerWatchMap = rr.PlayerWatchMap[:TempIndex]
} else {
rr.PlayerWatchMap = append(rr.PlayerWatchMap[:TempIndex], rr.PlayerWatchMap[TempIndex+1:]...)
}
} else {
//针对机器人的删除
if TempIndex == len(rr.PlayerWatchMap)-1 {
rr.PlayerWatchMap = rr.PlayerWatchMap[:TempIndex]
} else {
rr.PlayerWatchMap = append(rr.PlayerWatchMap[:TempIndex], rr.PlayerWatchMap[TempIndex+1:]...)
}
}
break
}
}
}
for _, pPlayer := range rr.PlayerMap {
if pPlayer == nil {
continue
}
if pPlayer.Uid == uid {
rr.Send(pPlayer, route.LogoutTableRes, LeaveTableRes)
if !pPlayer.IsRobot /*&& !tempPlayerMap[pPlayer.ChairId].IsRobot*/ {
pPlayer.OnlineState = USER_LEAVE
pPlayer.Ctx = nil
// 如果抢庄了 需要从庄家列表删除
for TempIndex, TempChairID := range rr.GetBankerList {
if TempChairID == pPlayer.ChairId {
if TempIndex == len(rr.GetBankerList)-1 {
rr.GetBankerList = rr.GetBankerList[:TempIndex]
} else {
rr.GetBankerList = append(rr.GetBankerList[:TempIndex], rr.GetBankerList[TempIndex+1:]...)
}
break
}
}
//为了下面的rr.PlayerMap = tempPlayerMap这个表达式
tempPlayerMap[pPlayer.ChairId].OnlineState = USER_LEAVE
tempPlayerMap[pPlayer.ChairId].Ctx = nil
rr.QuitRoomFunc(pPlayer.Uid)
// 通知大厅有人离开
NotifySitDown := &protocol.NotifyDTSitDown{
Uid: pPlayer.Uid,
RoomID: rr.RoomID,
TableID: rr.RId,
ChairID: pPlayer.ChairId,
Avatar: pPlayer.Avatar,
State: rr.CurrentGameState,
}
rr.NotifyFunc(route.NotifyDTStandUp, NotifySitDown)
delete(tempPlayerMap, pPlayer.ChairId)
} else {
//针对机器人的删除
delete(tempPlayerMap, pPlayer.ChairId)
}
ret = true
} else {
if OutUser != nil {
NotifyLeaveRoomRes.ChairID = OutUser.ChairId
} else {
NotifyLeaveRoomRes.ChairID = 0xFF
}
NotifyLeaveRoomRes.LeaveReason = 0
rr.Send(pPlayer, route.NotifyGetOutGame, NotifyLeaveRoomRes)
}
}
for _, pPlayer := range rr.PlayerWatchMap {
if pPlayer == nil {
continue
}
rr.Send(pPlayer, route.NotifyGetOutGame, NotifyLeaveRoomRes)
}
rr.PlayerMap = tempPlayerMap
//for i, v := range rr.PlayerMap {
// log.Debug(" 桌子号", rr.RId, "退出房间后,房间中的玩家", i, "的信息", v.Uid, ":", v)
//}
//// 观战转坐下
//TempChairID := int32(0)
//for TempChairID = 0; TempChairID < MAX_USER_NUM; TempChairID++ {
// if _, exists := rr.PlayerMap[TempChairID]; !exists {
// for TempIndex, TempWatchPlayer := range rr.PlayerWatchMap {
// // 金币足才可以自动坐下
// if TempWatchPlayer.Gold.IntPart() > rr.MinGameScore {
// WatchUser := rr.PlayerWatchMap[TempIndex]
// WatchUser.ChairId = TempChairID
// rr.SendAllMessage(route.NotifyWatchToDown, &protocol.NotifyWatchToDown{
// Uid: WatchUser.Uid,
// ChairID: TempChairID,
// })
//
// // 通知大厅有人坐下
// NotifySitDown := &protocol.NotifyDTSitDown{
// Uid: WatchUser.Uid,
// RoomID: rr.RoomID,
// TableID: rr.RId,
// ChairID: TempChairID,
// Avatar: WatchUser.Avatar,
// State: rr.CurrentGameState,
// }
// rr.NotifyFunc(route.NotifyDTSitDown, NotifySitDown)
//
// rr.PlayerMap[TempWatchPlayer.ChairId] = TempWatchPlayer
//
// if TempIndex == len(rr.PlayerWatchMap)-1 {
// rr.PlayerWatchMap = rr.PlayerWatchMap[:TempIndex]
// } else {
// rr.PlayerWatchMap = append(rr.PlayerWatchMap[:TempIndex], rr.PlayerWatchMap[TempIndex+1:]...)
// }
// break
// }
// }
// }
//}
if ret {
log.Debug(" 桌子号", rr.RId, "玩家", uid, "退出房间成功")
playerCount := rr.GetRoomPlayerCount()
robotCount := rr.GetInRoomRobotCount()
log.Debug(" 桌子号", rr.RId, "玩家人数", playerCount)
if playerCount == 0 || (rr.CreatorId != 0 && playerCount == robotCount) {
//退出所有观战玩家
for _, pPlayer := range rr.PlayerWatchMap {
if pPlayer == nil {
continue
}
rr.Send(pPlayer, route.LogoutTableRes, LeaveTableRes)
}
rr.PlayerWatchMap = rr.PlayerWatchMap[:0]
//关闭定时器
rr.GronTimer.Stop()
rr.RobotMgr.GronTimer.Stop()
for i := int32(0); i < playerCount; i++ {
p := rr.PlayerMap[i]
if p == nil {
continue
}
rr.PlayerMap[i] = nil
delete(rr.PlayerMap, i)
//这里面来全是机器人的,所有不需要调用 rr.QuitRoomFunc
}
log.Debug(" 桌子号", rr.RId, "点控房间 全是机器人,房间没意义,销毁")
rr.DestroyRoomFunc(rr.RId)
}
}
return true
}

687
game-server/game/TTZ/app/core/game/game_over.go

@ -0,0 +1,687 @@
package game
import (
"base/encoding/json"
"base/log"
"fmt"
"github.com/shopspring/decimal"
"math/rand"
"sort"
"strconv"
"time"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/core/types"
"xgame/game/TTZ/app/route"
"xgame/game/service/rpc_client"
writeUserScorepb "xgame/internal/service/writeUserScore/pb"
"xgame/tools"
)
func (rr *Room) OnBuildOtherResultPoker() { //计算扑克
log.Debug(" 桌子号", rr.RId, " 计算扑克 ")
// 洗牌
if len(rr.CardList) == 0 {
rr.InitCardList(1)
}
rr.PokersCount = int32(len(rr.CardList))
//点控
rr.isUserCtl = false
rr.UserCtlGear = 0
PlayerCount := rr.GetRoomPlayerCount()
for i := int32(0); i < PlayerCount; i++ {
p := rr.PlayerMap[i]
if p == nil || p.IsRobot {
continue
}
//取点控
p.CheatValue = 0
p.CheatType = 0
p.CheatValueBefore = 0
p.IsCheat = false
playerControl := rpc_client.GetUserGameControl(p.Ctx, int64(rr.GameID), int64(rr.RoomID))
if playerControl != nil {
log.Debug("获取到的玩家点控 玩家 ", p.Uid, " 名字:", p.Nickname, " 点控", playerControl)
if playerControl.CheatType != 0 {
rr.isUserCtl = true
rr.UserCtlGear = playerControl.CheatType
p.IsCheat = true
p.CheatValue = playerControl.CheatValue
p.CheatType = playerControl.CheatType
p.CheatValueBefore = p.CheatValue
log.Debug("设置点控成功 点控类型 ", p.CheatType, " 点控值:", p.CheatValueBefore)
}
}
}
WinType := 0
CurrentGears := 0
TempRoomGears, err := strconv.Atoi(rr.BoolePool.RoomGears)
if err == nil {
CurrentGears = TempRoomGears
//log.Debug(" 桌子号", r.GameRoom.RId, "房间当前血池档位1 :", TempRoomGears, " CurrentGears :", CurrentGears)
}
if rr.isUserCtl {
CurrentGears = int(rr.UserCtlGear)
}
log.Debug(" 桌子号", rr.RId, "房间当前血池档位 :", TempRoomGears, " 人物控制后实际血池:", CurrentGears)
CurrentGearConfig := rr.BloodCtrl.GearConfig[0]
for _, GearConfig := range rr.BloodCtrl.GearConfig {
if GearConfig.GearID == CurrentGears {
CurrentGearConfig = GearConfig
break
}
}
if rr.GetRoomRealCanBetPlayerCount() == 0 && rr.PlayerMap[rr.CurrBanker].IsRobot {
// 没有真人玩 随机
WinType = 0
} else {
WinType = 2
// 随机机器人的输赢
for i := int32(0); i < MAX_USER_NUM; i++ {
if _, exists := rr.PlayerMap[i]; exists {
tempPlayer := rr.PlayerMap[i]
tempPlayer.RobotWin = false
if !tempPlayer.CanBet && tempPlayer.ChairId != rr.CurrBanker {
continue
}
if tempPlayer.IsRobot && rand.Intn(10000) < CurrentGearConfig.WinPro {
tempPlayer.RobotWin = true
log.Debug(" 用户 ", tempPlayer.ChairId, " 是否机器人 ", tempPlayer.IsRobot, " 赢")
}
}
}
// 机器人庄的输赢
if rr.PlayerMap[rr.CurrBanker].IsRobot {
if rand.Intn(10000) < CurrentGearConfig.BankerWinPro {
rr.PlayerMap[rr.CurrBanker].RobotWin = true
} else {
{
rr.PlayerMap[rr.CurrBanker].RobotWin = false
}
}
}
//判断输赢
tempHandCard := rr.SearchTypeHandCard()
switch WinType {
case 0:
// 不控制
log.Debug(" 桌子号", rr.RId, " 不控制 :")
rr.shuffleDeck2(&tempHandCard)
for tempIndex, _ := range rr.PlayerMap {
rr.PlayerMap[tempIndex].HandPoker = rr.PlayerMap[tempIndex].HandPoker[:0]
rr.PlayerMap[tempIndex].HandPoker = append(rr.PlayerMap[tempIndex].HandPoker, tempHandCard[tempIndex]...)
}
case 2:
log.Debug(" 桌子号", rr.RId, " 牌是 :", tempHandCard)
if rr.PlayerMap[rr.CurrBanker].IsRobot && rr.PlayerMap[rr.CurrBanker].RobotWin {
// 如果庄家是机器人 并且需要赢
RealCanBetUserCount := rr.GetRoomRealCanBetPlayerCount()
// 随机牌的起始位置
BeginIndex := rand.Intn(MAX_USER_NUM - int(RealCanBetUserCount))
rr.PlayerMap[rr.CurrBanker].HandPoker = rr.PlayerMap[rr.CurrBanker].HandPoker[:0]
rr.PlayerMap[rr.CurrBanker].HandPoker = append(rr.PlayerMap[rr.CurrBanker].HandPoker, tempHandCard[BeginIndex]...)
WinPoker := tempHandCard[:BeginIndex]
var LosePoker []types.PokerSlice
if BeginIndex < 4 {
LosePoker = tempHandCard[BeginIndex+1:]
}
rr.shuffleDeck2(&WinPoker)
rr.shuffleDeck2(&LosePoker)
// 先分配要输的真人
for tempIndex, tempPlayer := range rr.PlayerMap {
if len(LosePoker) == 0 {
break
}
if tempPlayer.CanBet && !tempPlayer.IsRobot {
rr.PlayerMap[tempIndex].HandPoker = rr.PlayerMap[tempIndex].HandPoker[:0]
rr.PlayerMap[tempIndex].HandPoker = append(rr.PlayerMap[tempIndex].HandPoker, LosePoker[0]...)
if len(LosePoker) > 1 {
LosePoker = LosePoker[1:]
} else {
LosePoker = LosePoker[:0]
}
}
}
// 再分配其它机器人
for tempIndex, tempPlayer := range rr.PlayerMap {
if len(LosePoker) == 0 {
break
}
if tempPlayer.CanBet && tempPlayer.IsRobot {
rr.PlayerMap[tempIndex].HandPoker = rr.PlayerMap[tempIndex].HandPoker[:0]
rr.PlayerMap[tempIndex].HandPoker = append(rr.PlayerMap[tempIndex].HandPoker, LosePoker[0]...)
if len(LosePoker) > 1 {
LosePoker = LosePoker[1:]
} else {
LosePoker = LosePoker[:0]
}
}
}
for tempIndex, tempPlayer := range rr.PlayerMap {
if len(WinPoker) == 0 {
break
}
if tempPlayer.CanBet && tempPlayer.IsRobot && len(rr.PlayerMap[tempIndex].HandPoker) == 0 {
rr.PlayerMap[tempIndex].HandPoker = rr.PlayerMap[tempIndex].HandPoker[:0]
rr.PlayerMap[tempIndex].HandPoker = append(rr.PlayerMap[tempIndex].HandPoker, WinPoker[0]...)
if len(WinPoker) > 1 {
WinPoker = WinPoker[1:]
} else {
WinPoker = WinPoker[:0]
}
}
}
for _, tempPlayer := range rr.PlayerMap {
log.Debug(" 桌子号 ", rr.RId, " 庄家 ", rr.CurrBanker, " 当前 ", tempPlayer.ChairId, " 当前是否机器人 ", tempPlayer.IsRobot, " 是否需要赢 ", tempPlayer.RobotWin, " 手牌 ", tempPlayer.HandPoker)
}
} else if rr.PlayerMap[rr.CurrBanker].IsRobot && !rr.PlayerMap[rr.CurrBanker].RobotWin {
// 如果庄家是机器人 并且需要输
RealCanBetUserCount := rr.GetRoomRealCanBetPlayerCount()
// 随机牌的起始位置
BeginIndex := int(RealCanBetUserCount) + rand.Intn(MAX_USER_NUM-int(RealCanBetUserCount))
rr.PlayerMap[rr.CurrBanker].HandPoker = rr.PlayerMap[rr.CurrBanker].HandPoker[:0]
rr.PlayerMap[rr.CurrBanker].HandPoker = append(rr.PlayerMap[rr.CurrBanker].HandPoker, tempHandCard[BeginIndex]...)
WinPoker := tempHandCard[:BeginIndex]
var LosePoker []types.PokerSlice
if BeginIndex < 4 {
LosePoker = tempHandCard[BeginIndex+1:]
}
rr.shuffleDeck2(&WinPoker)
rr.shuffleDeck2(&LosePoker)
// 先分配要赢的真人
for tempIndex, tempPlayer := range rr.PlayerMap {
if len(WinPoker) == 0 {
break
}
if tempPlayer.CanBet && !tempPlayer.IsRobot {
rr.PlayerMap[tempIndex].HandPoker = rr.PlayerMap[tempIndex].HandPoker[:0]
rr.PlayerMap[tempIndex].HandPoker = append(rr.PlayerMap[tempIndex].HandPoker, WinPoker[0]...)
if len(WinPoker) > 1 {
WinPoker = WinPoker[1:]
} else {
WinPoker = WinPoker[:0]
}
}
}
// 再分配其它机器人
for tempIndex, tempPlayer := range rr.PlayerMap {
if len(LosePoker) == 0 {
break
}
if tempPlayer.CanBet && tempPlayer.IsRobot {
rr.PlayerMap[tempIndex].HandPoker = rr.PlayerMap[tempIndex].HandPoker[:0]
rr.PlayerMap[tempIndex].HandPoker = append(rr.PlayerMap[tempIndex].HandPoker, LosePoker[0]...)
if len(LosePoker) > 1 {
LosePoker = LosePoker[1:]
} else {
LosePoker = LosePoker[:0]
}
}
}
for tempIndex, tempPlayer := range rr.PlayerMap {
if len(WinPoker) == 0 {
break
}
if tempPlayer.CanBet && tempPlayer.IsRobot && len(rr.PlayerMap[tempIndex].HandPoker) == 0 {
rr.PlayerMap[tempIndex].HandPoker = rr.PlayerMap[tempIndex].HandPoker[:0]
rr.PlayerMap[tempIndex].HandPoker = append(rr.PlayerMap[tempIndex].HandPoker, WinPoker[0]...)
if len(WinPoker) > 1 {
WinPoker = WinPoker[1:]
} else {
WinPoker = WinPoker[:0]
}
}
}
for _, tempPlayer := range rr.PlayerMap {
log.Debug(" 桌子号 ", rr.RId, " 庄家 ", rr.CurrBanker, " 当前 ", tempPlayer.ChairId, " 当前是否机器人 ", tempPlayer.IsRobot, " 是否需要赢 ", tempPlayer.RobotWin, " 手牌 ", tempPlayer.HandPoker)
}
} else {
// 玩家是庄家
NeedWinRobotCount := 0
for _, tempPlayer := range rr.PlayerMap {
if tempPlayer.IsRobot && tempPlayer.CanBet && tempPlayer.RobotWin {
NeedWinRobotCount++
}
}
// 随机牌的起始位置
WinPoker := tempHandCard[:NeedWinRobotCount]
LosePoker := tempHandCard[NeedWinRobotCount:]
rr.shuffleDeck2(&WinPoker)
rr.shuffleDeck2(&LosePoker)
// 先分配要赢的机器人
for tempIndex, tempPlayer := range rr.PlayerMap {
if len(WinPoker) == 0 {
break
}
if tempPlayer.IsRobot && tempPlayer.CanBet && tempPlayer.RobotWin {
rr.PlayerMap[tempIndex].HandPoker = rr.PlayerMap[tempIndex].HandPoker[:0]
rr.PlayerMap[tempIndex].HandPoker = append(rr.PlayerMap[tempIndex].HandPoker, WinPoker[0]...)
if len(WinPoker) > 1 {
WinPoker = WinPoker[1:]
} else {
WinPoker = WinPoker[:0]
}
}
}
// 再分配其它人
for tempIndex, tempPlayer := range rr.PlayerMap {
if len(LosePoker) == 0 {
break
}
if tempPlayer.CanBet && len(rr.PlayerMap[tempIndex].HandPoker) == 0 {
rr.PlayerMap[tempIndex].HandPoker = rr.PlayerMap[tempIndex].HandPoker[:0]
rr.PlayerMap[tempIndex].HandPoker = append(rr.PlayerMap[tempIndex].HandPoker, LosePoker[0]...)
if len(LosePoker) > 1 {
LosePoker = LosePoker[1:]
} else {
LosePoker = LosePoker[:0]
}
}
}
for _, tempPlayer := range rr.PlayerMap {
log.Debug(" 桌子号 ", rr.RId, " 庄家 ", rr.CurrBanker, " 当前 ", tempPlayer.ChairId, " 当前是否机器人 ", tempPlayer.IsRobot, " 是否需要赢 ", tempPlayer.RobotWin, " 手牌 ", tempPlayer.HandPoker)
}
}
}
}
}
func (rr *Room) insertIntoSlice(slice types.PokerSlice, index int, value types.Poker) types.PokerSlice {
// 确保 index 在合适的范围内
if index < 0 || index > len(slice) {
return slice // 返回原slice,或处理错误
}
// 创建一个新切片,长度为原切片长度 + 1
newSlice := make(types.PokerSlice, len(slice)+1)
// 将切片的前部分复制到新切片
copy(newSlice, slice[:index])
// 插入新元素
newSlice[index] = value
// 将后面的部分复制到新切片
copy(newSlice[index+1:], slice[index:])
return newSlice
}
func (rr *Room) OnEventGameConclude() { // 结束-结算
milliseconds := time.Now().UnixMilli()
// 结算展示时间
rr.CurrentGameState = protocol.STATE_RESULT
rr.CurrentOperationTimer = milliseconds + rr.TimeOutResult
rr.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: rr.CurrentGameState,
NextTimestamp: rr.CurrentOperationTimer,
})
//结算
GameOver := &protocol.NotifyGameConclude{}
// 计算玩家赢得区域
var sArea string
var sPlayers string
for _, player := range rr.PlayerMap {
if !player.IsRobot {
if len(sPlayers) == 0 {
sPlayers = sPlayers + player.UserNo
} else {
sPlayers = sPlayers + "," + player.UserNo
}
}
}
BankerWinScore := int64(0) // 庄家赢得钱
for _, player := range rr.PlayerMap {
player.PlayWatchCount = 0
player.PlayCount++
if player.ChairId != rr.CurrBanker && !player.CurrentRoundBet {
player.NoBetCount++
} else {
player.NoBetCount = 0
}
}
sArea = ""
var RecordInfo []protocol.RecordInfoPlayer
// 先放庄家
BankerPlayer := rr.PlayerMap[rr.CurrBanker]
RecordInfo = append(RecordInfo, protocol.RecordInfoPlayer{
ChairID: rr.CurrBanker,
UserNo: BankerPlayer.UserNo,
})
for _, poker := range BankerPlayer.HandPoker {
RecordInfo[0].HandPoker = append(RecordInfo[0].HandPoker, int32(poker))
}
RecordInfo[0].PokerType, RecordInfo[0].PokerPoint = BankerPlayer.HandPoker.GetCardType()
// 再放其它人
for _, player := range rr.PlayerMap {
if !player.CanBet {
continue
}
// 庄家先拿所有下注
BankerWinScore += player.BetScore
var TempRecordInfoPlayer protocol.RecordInfoPlayer
TempRecordInfoPlayer.ChairID = player.ChairId
TempRecordInfoPlayer.UserNo = player.UserNo
for _, poker := range player.HandPoker {
TempRecordInfoPlayer.HandPoker = append(TempRecordInfoPlayer.HandPoker, int32(poker))
}
TempRecordInfoPlayer.PokerType, TempRecordInfoPlayer.PokerPoint = BankerPlayer.HandPoker.GetCardType()
if BankerPlayer.HandPoker.Compare(player.HandPoker) == 1 {
TempRecordInfoPlayer.IsWin = false
} else {
TempRecordInfoPlayer.IsWin = true
}
RecordInfo = append(RecordInfo, TempRecordInfoPlayer)
}
options, err := json.Marshal(RecordInfo)
if err != nil {
log.Debug("RecordInfo 序列化失败")
}
sArea = string(options)
// 先收所有输给庄家的钱
for _, player := range rr.PlayerMap {
if !player.CanBet {
continue
}
if rr.PlayerMap[rr.CurrBanker].HandPoker.Compare(player.HandPoker) == 2 {
continue
}
TableSeat := &protocol.RoomPlayer{}
//输赢
TempUserWinScore := -int64(player.HandPoker.GetCardTypeBei()) * player.BetScore
if -TempUserWinScore > player.Gold.IntPart() {
TempUserWinScore = -player.Gold.IntPart()
}
BankerWinScore += -TempUserWinScore
player.WinBetScore = 0
//记录输赢并写分
UserRealBet := player.BetScore // 计算打码量
if !player.IsRobot && (player.BetScore != 0) && (player.Ctx != nil) {
AfterGold, BloodPool, StockNum := rr.WriteUserScore(player, 2, player.BetScore, player.WinBetScore, TempUserWinScore, rr.ReasonString, sArea, sPlayers, UserRealBet)
log.Debug("WriteUserScore 后 AfterGold=", AfterGold, " BloodPool=", BloodPool, " StockNum=", StockNum, " GameID", rr.GameID, " RoomID", rr.RoomID)
rr.UserScore[player.ChairId] = decimal.NewFromInt(AfterGold)
player.Gold = decimal.NewFromInt(AfterGold)
}
TableSeat.Uid = player.Uid
TableSeat.ChairID = player.ChairId
if rr.CurrBanker == player.ChairId {
TableSeat.IsBanker = 1
}
TableSeat.State = rr.State
TableSeat.Avatar = player.Avatar
TableSeat.Gold = player.Gold.IntPart()
TableSeat.Nickname = player.Nickname
TableSeat.Sex = player.Sex
TableSeat.Vip = 0
TableSeat.Level = player.Level
TableSeat.Exp = 0
TableSeat.EndScore = TempUserWinScore
TableSeat.WinLose = 2
for _, poker := range player.HandPoker {
TableSeat.HandPoker = append(TableSeat.HandPoker, int32(poker))
}
TableSeat.PokerType, TableSeat.Point = player.HandPoker.GetCardType()
TableSeat.BetScore = player.BetScore
GameOver.PlayList = append(GameOver.PlayList, TableSeat)
}
// 然后从最大的开始赔
var WinPlayer []PokerUserType // 找到赢家
for _, player := range rr.PlayerMap {
if !player.CanBet {
continue
}
if rr.PlayerMap[rr.CurrBanker].HandPoker.Compare(player.HandPoker) == 1 {
continue
}
var TempWinPlayer PokerUserType
TempWinPlayer.ChairId = player.ChairId
TempWinPlayer.Bei = player.HandPoker.GetCardTypeBei()
TempWinPlayer.HandPoker = player.HandPoker
WinPlayer = append(WinPlayer, TempWinPlayer)
}
sort.Slice(WinPlayer, func(i, j int) bool {
return WinPlayer[i].Bei >= WinPlayer[j].Bei
})
for _, tempWinPlayer := range WinPlayer {
TableSeat := &protocol.RoomPlayer{}
player := rr.PlayerMap[tempWinPlayer.ChairId]
//输赢
TempUserWinScore := int64(tempWinPlayer.Bei) * player.BetScore
if TempUserWinScore > BankerWinScore+rr.GuoScore {
TempUserWinScore = BankerWinScore + rr.GuoScore
}
BankerWinScore -= TempUserWinScore
player.WinBetScore = player.BetScore
//记录输赢并写分
UserRealBet := player.BetScore // 计算打码量
if TempUserWinScore > 0 {
TempRateAfter := int64(float64(TempUserWinScore) * (1 - rr.Opt.BrightRate))
rr.UserScore[player.ChairId] = rr.UserScore[player.ChairId].Add(decimal.NewFromInt(TempRateAfter))
player.Gold = rr.UserScore[player.ChairId].Add(decimal.NewFromInt(TempRateAfter))
TableSeat.EndScore = TempRateAfter
}
if !player.IsRobot && (player.BetScore != 0) && (player.Ctx != nil) {
AfterGold, BloodPool, StockNum := rr.WriteUserScore(player, 2, player.BetScore, player.WinBetScore, TempUserWinScore, rr.ReasonString, sArea, sPlayers, UserRealBet)
log.Debug("WriteUserScore 后 AfterGold=", AfterGold, " BloodPool=", BloodPool, " StockNum=", StockNum, " GameID", rr.GameID, " RoomID", rr.RoomID)
rr.UserScore[player.ChairId] = decimal.NewFromInt(AfterGold)
player.Gold = decimal.NewFromInt(AfterGold)
}
TableSeat.Uid = player.Uid
TableSeat.ChairID = player.ChairId
if rr.CurrBanker == player.ChairId {
TableSeat.IsBanker = 1
}
TableSeat.State = rr.State
TableSeat.Avatar = player.Avatar
TableSeat.Gold = player.Gold.IntPart()
TableSeat.Nickname = player.Nickname
TableSeat.Sex = player.Sex
TableSeat.Vip = 0
TableSeat.Level = player.Level
TableSeat.Exp = 0
TableSeat.WinLose = 1
for _, poker := range player.HandPoker {
TableSeat.HandPoker = append(TableSeat.HandPoker, int32(poker))
}
TableSeat.PokerType, TableSeat.Point = player.HandPoker.GetCardType()
TableSeat.BetScore = player.BetScore
GameOver.PlayList = append(GameOver.PlayList, TableSeat)
}
// 剩余的是庄家赢得
TempRateAfter := BankerWinScore
var UserRealBet int64
var WinBetGold int64
var WinGold int64
if BankerWinScore > 0 {
TempRateAfter = int64(float64(BankerWinScore) * (1 - rr.Opt.BrightRate))
WinBetGold = BankerWinScore
WinGold = 0
} else {
WinBetGold = 0
WinGold = BankerWinScore
}
BankerPlayer.TableWin += TempRateAfter
BankerPlayer.WinScore = TempRateAfter
rr.UserScore[BankerPlayer.ChairId] = rr.UserScore[BankerPlayer.ChairId].Add(decimal.NewFromInt(TempRateAfter))
if TempRateAfter < 0 {
UserRealBet = -TempRateAfter
}
if !BankerPlayer.IsRobot && (BankerPlayer.WinScore != 0) && (BankerPlayer.Ctx != nil) {
AfterGold, BloodPool, StockNum := rr.WriteUserScore(BankerPlayer, 3, 0, WinBetGold, WinGold, rr.ReasonString, "", sPlayers, UserRealBet)
log.Debug("WriteUserScore 后 AfterGold=", AfterGold, " BloodPool=", BloodPool, " StockNum=", StockNum, " GameID", rr.GameID, " RoomID", rr.RoomID)
rr.UserScore[BankerPlayer.ChairId] = decimal.NewFromInt(AfterGold)
BankerPlayer.Gold = decimal.NewFromInt(AfterGold)
}
// 庄家数据
TableSeat := &protocol.RoomPlayer{}
TableSeat.Uid = BankerPlayer.Uid
TableSeat.ChairID = BankerPlayer.ChairId
if rr.CurrBanker == BankerPlayer.ChairId {
TableSeat.IsBanker = 1
}
TableSeat.State = rr.State
TableSeat.Avatar = BankerPlayer.Avatar
TableSeat.Gold = BankerPlayer.Gold.IntPart()
TableSeat.Nickname = BankerPlayer.Nickname
TableSeat.Sex = BankerPlayer.Sex
TableSeat.Vip = 0
TableSeat.Level = BankerPlayer.Level
TableSeat.Exp = 0
TableSeat.WinLose = 2
if BankerPlayer.WinScore > 0 {
TableSeat.WinLose = 1
}
for _, poker := range BankerPlayer.HandPoker {
TableSeat.HandPoker = append(TableSeat.HandPoker, int32(poker))
}
TableSeat.PokerType, TableSeat.Point = BankerPlayer.HandPoker.GetCardType()
TableSeat.BetScore = BankerPlayer.BetScore
GameOver.PlayList = append(GameOver.PlayList, TableSeat)
sort.Slice(GameOver.PlayList, func(i, j int) bool {
return GameOver.PlayList[i].ChairID < GameOver.PlayList[j].ChairID
})
GameOver.Record = rr.Record
//for index, record := range rr.Record {
// log.Debug("结束 现在有 ", len(rr.Record), " 个记录 当前第 ", index, " 个 值:", record)
//}
for _, player := range rr.PlayerWatchMap {
player.PlayWatchCount++
}
rr.State = EN_TABLE_STATE_FINISH //结算状态
rr.SendAllMessage(route.NotifyGameConclude, GameOver)
log.Debug(rr.TimeOutResult/1000, ":秒后开始下一局倒计时")
}
func (rr *Room) WriteUserScore(User *Player, BetType int32, BetGold int64, WinBetGold int64, WinGold int64, reason string, Area string, Players string, UserRealBet int64) (int64, float64, int64) {
currentDate := time.Now().Format("20060102")
roundNo := fmt.Sprintf(fmt.Sprintf("%s%s%s", currentDate, strconv.Itoa(int(rr.RoomID)), rpc_client.GenerateRoundNo()))
Req := writeUserScorepb.WriteUserScoreArgs{
UID: User.Uid,
AgentID: User.AgentID,
ChannelID: 0,
Reason: reason,
GameType: 2,
GameName: rr.Opt.Name,
GameID: GAME_ID,
RoomID: int64(rr.RoomID),
SeatID: User.ChairId,
RoundNO: roundNo,
MingTax: rr.Opt.BrightRate,
AnTax: rr.Opt.DarkRate,
CurrentRoomNum: rr.GetRoomRealPlayerCount(),
RoomGears: rr.BoolePool.RoomGears,
BloodID: rr.BoolePool.BoolPoolId,
Stock: rr.BoolePool.BoolPoolSockNum,
StockConfig: rr.BoolePool.StockConf,
StockThresholdValue: tools.Int64ToStr(rr.BoolePool.NowTurnPoint),
IsCtrl: User.IsCheat,
AssetKind: 1,
BetAmount: BetGold,
WinAmount: WinGold,
BeforeBalance: 0,
AfterBalance: 0,
CheatType: User.CheatType,
CheatValueBefore: User.CheatValueBefore,
CheatValueAfter: 0,
UserAmount: BetGold,
BetType: int64(BetType),
UserWinBetAmount: WinBetGold,
Area: Area,
Players: Players,
UserRealBet: UserRealBet,
TableId: rr.RId,
RobotCount: int64(rr.GetRoomPlayerCount() - rr.GetRoomRealPlayerCount()),
}
if BetType == 3 {
Req.UserAmount = 0
}
log.Debug("用户", User.Uid, "名字", User.Nickname, "写分 ", Req)
Res := rpc_client.WriteUserScore(rr.RoomProxy, Req)
log.Debug("写分返回 ", Res)
return Res.Gold, Res.BloodPool, Res.StockNum
}

48
game-server/game/TTZ/app/core/game/game_player.go

@ -0,0 +1,48 @@
package game
import (
"base/cluster/node"
"github.com/shopspring/decimal"
"xgame/game/TTZ/app/core/types"
)
const (
MAX_UNM = 100 //房间最大人数
)
type Player struct {
Ctx *node.Context
Uid int64 //userId
UserNo string //
AgentID int32 //代理ID
ChairId int32 //椅子号
IsRobot bool //是否是机器人
Gold decimal.Decimal // 玩家金币
Diamond decimal.Decimal // 玩家钻石
Ticket decimal.Decimal // 玩家点券
Nickname string // 姓名
Avatar string // 玩家头像
OnlineState int32 // USER_OFF_LINE
Sex int32 //
Level int32 //
IsCheat bool //是否有控制
CheatType int64 //控制类型
RoomGears string //玩家拐点值
CheatValueBefore int64 //库存 变化前数据
CheatValue int64 //控制值[CheatValue这个是数据库取出来的,石总那边配置]
CurrCheatValue decimal.Decimal //当前控制值[CheatValue这个是数据库取出来的,石总那边配置,CurrCheatValue这个是每局输赢计算,游戏开始之前都要与CheatValue比较用于确定是否点控该玩家]
RobotWin bool // true 赢 false 输
HandPoker types.PokerSlice // 玩家手牌
PlayCount int //在桌子上呆了几局
PlayWatchCount int //在桌子连续观战了几局
CurrentRoundBet bool //本局是否下注了
CanBet bool //玩家是否可以下注
NoBetCount int //在桌子上未下注的局数
TableWin int64 // 桌子上的总输赢
BetScore int64 // 结算时的总下注
WinScore int64 // 结算时的总输赢
WinBetScore int64 // 赢的区域的总下注
UserNeedOut bool //退出状态
}

30
game-server/game/TTZ/app/core/game/game_reconnect.go

@ -0,0 +1,30 @@
package game
import (
"base/cluster"
"base/session"
"xgame/game/rummy/app/core/protocol"
"xgame/internal/route"
)
// OnReconnect 断线重连
func (rr *Room) OnReconnect(userId int64) {
rr.roomRWLock.Lock()
defer rr.roomRWLock.Unlock()
//本身这里是可以:检查到该玩的游戏还没结束,在玩家登录了成功后服务器该本身主动推送房间和玩家的信息的
//但是由于服务器的缺陷,拿不到Ctx,只能先通知客户端你有个游戏还在继续,让客户端主动拉取房间信息
_ = rr.RoomProxy.Push(rr.Ctx, &cluster.PushArgs{
Kind: session.User,
Target: userId,
//Target: uid,
Message: &cluster.Message{
Route: route.ResumeGame,
Data: &protocol.ResumeGameNotice{
RoomId: rr.RoomID,
GameId: rr.GameID,
},
},
})
}

46
game-server/game/TTZ/app/core/game/game_repeat_room.go

@ -0,0 +1,46 @@
package game
import (
"base/log"
"xgame/game/TTZ/app/core/protocol"
)
// RepeatRoom 复位桌子
func (rr *Room) RepeatRoom() {
rr.BaoDiBanker = false
rr.PokerIndex = 0 //
rr.PokersCount = 0 //
rr.CurrOperationPlayer = -1 //
rr.CurrBanker = INVALID_VALUE //
rr.IsP2pControls = false //点控
// 重新获得血池 测试关闭
if rr.BoolePool.SetBoole(rr.RoomProxy, rr.Opt.BloodId) == false {
log.Debug(" 桌子号", rr.RId, "复位桌子获取血池失败")
}
}
func (rr *Room) RepeatRoomUser() { //复位玩家的数据
if rr.CurrentGameState == protocol.STATE_GET_BANKER {
for index, _ := range rr.PlayerMap {
rr.PlayerMap[index].CurrentRoundBet = false
rr.PlayerMap[index].CanBet = true
rr.PlayerMap[index].BetScore = 0
rr.PlayerMap[index].WinScore = 0
rr.PlayerMap[index].RobotWin = false
rr.PlayerMap[index].HandPoker = rr.PlayerMap[index].HandPoker[:0]
}
for index, _ := range rr.PlayerWatchMap {
rr.PlayerWatchMap[index].CurrentRoundBet = false
rr.PlayerWatchMap[index].CanBet = true
rr.PlayerWatchMap[index].BetScore = 0
rr.PlayerWatchMap[index].WinScore = 0
rr.PlayerWatchMap[index].RobotWin = false
rr.PlayerWatchMap[index].HandPoker = rr.PlayerWatchMap[index].HandPoker[:0]
}
}
}

8
game-server/game/TTZ/app/core/game/game_resume_gameinfo.go

@ -0,0 +1,8 @@
package game
// 恢复游戏信息
func (rr *Room) OnResumeGameData(uid int64) {
rr.roomRWLock.Lock()
defer rr.roomRWLock.Unlock()
}

495
game-server/game/TTZ/app/core/game/game_robot.go

@ -0,0 +1,495 @@
package game
import (
"base/codes"
"base/log"
"math/rand"
"strconv"
"sync"
"time"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/route"
"github.com/cognusion/randomnames"
"github.com/roylee0704/gron"
"github.com/shopspring/decimal"
)
const (
RobotUid = 900000000
BetTimer = 0x1000
)
var (
CurRobotUserCount = int64(0)
RobotBet = int32(1000)
)
type RobotBetCtrlInfo struct {
BetAmount int64 //下注金额
MinBet int64 // 最低下注
MaxBet int64 // 最高下注
}
type RobotManager struct {
GronTimer *gron.Cron
Lock sync.RWMutex
GameRoom *Room
FirstEnterTimer int64 //首个机器人进入时间
NextEnterTimer int64 //下个机器人进入时间
FlushCountTimer int64 //下次刷新机器人数量的时间
MaxCount int //机器人数量
PlayerMaps map[int32]*Player //当前有的机器人
playerBetCtrl map[int32]*RobotBetCtrlInfo //当前有的机器人
playerStat map[int32]int32 //机器人的状态
RobotTimer map[int32]int64 //机器人的操作时间
}
func NewRobotManager() *RobotManager {
rm := &RobotManager{
Lock: sync.RWMutex{},
PlayerMaps: make(map[int32]*Player),
playerStat: make(map[int32]int32),
playerBetCtrl: make(map[int32]*RobotBetCtrlInfo),
RobotTimer: make(map[int32]int64),
}
return rm
}
func (r *RobotManager) OnInit(conf string, GameRoom *Room) {
r.GameRoom = GameRoom
if r.GronTimer != nil {
r.GronTimer.Stop()
r.GronTimer = nil
}
r.GronTimer = gron.New()
r.GronTimer.AddFunc(gron.Every(time.Duration(50)*time.Millisecond), func() {
r.run()
})
nowUnixNano := time.Now().UnixNano()
nowMilliSeconds := nowUnixNano / 1e6
// 第一个进入的机器人 只判断一次
r.FirstEnterTimer = 0
if r.GameRoom.CreatorId != 0 {
FirstMinTime := r.GameRoom.TableConfig.FirstRobotCheckMinFrequency * 1000
FirstMaxTime := r.GameRoom.TableConfig.FirstRobotCheckMaxFrequency * 1000
if FirstMaxTime <= FirstMinTime {
r.FirstEnterTimer = nowMilliSeconds + FirstMaxTime
} else {
r.FirstEnterTimer = nowMilliSeconds + rand.Int63n(FirstMaxTime-FirstMinTime) + FirstMinTime
}
}
// 间隔多少时间正常进个机器人
MinInterTime := 0
MaxInterTime := 0
if r.GameRoom.TableConfig != nil {
MinInterTime = int(r.GameRoom.TableConfig.RobotCheckMinFrequency) * 1000
MaxInterTime = int(r.GameRoom.TableConfig.RobotCheckMaxFrequency) * 1000
log.Debug(" 后台配置 机器人进入频率 最小 ", MinInterTime, " 最大 ", MaxInterTime)
} else {
MinInterTime = r.GameRoom.BloodCtrl.AndroidOprateConfig.JoinHZ[0] * 1000
MaxInterTime = r.GameRoom.BloodCtrl.AndroidOprateConfig.JoinHZ[1] * 1000
}
rTime := int64(0)
if MaxInterTime <= MinInterTime {
rTime = int64(MinInterTime)
} else {
rTime = int64(rand.Intn(MaxInterTime-MinInterTime) + MinInterTime)
}
//处理个意外
if rTime == 0 {
rTime = 2000
log.Error(" 出现0了 是不行的 ", r.GameRoom.RoomID)
}
r.NextEnterTimer = nowMilliSeconds + rTime
if GameRoom.TableConfig != nil {
r.MaxCount = 0
if GameRoom.TableConfig.TableType == 2 {
if GameRoom.TableConfig.OneMaxNum <= GameRoom.TableConfig.OneMinNum {
r.MaxCount = int(GameRoom.TableConfig.OneMinNum)
} else {
r.MaxCount = int(rand.Int63n(GameRoom.TableConfig.OneMaxNum-GameRoom.TableConfig.OneMinNum+1) + GameRoom.TableConfig.OneMinNum)
}
} else {
if GameRoom.TableConfig.ZeroMaxNum <= GameRoom.TableConfig.ZeroMinNum {
r.MaxCount = int(GameRoom.TableConfig.ZeroMinNum)
} else {
r.MaxCount = int(rand.Int63n(GameRoom.TableConfig.ZeroMaxNum-GameRoom.TableConfig.ZeroMinNum+1) + GameRoom.TableConfig.ZeroMinNum)
}
}
r.FlushCountTimer = nowMilliSeconds + GameRoom.TableConfig.CheckFrequency*1000
log.Debug("从后台配置 最大机器人数 ", r.MaxCount, " 刷新时间 ", GameRoom.TableConfig.CheckFrequency)
} else {
ProMax := 0
for _, tempCount := range GameRoom.BloodCtrl.AndroidOprateConfig.CountMax0 {
ProMax += tempCount[1]
}
ProNow := rand.Intn(ProMax)
r.MaxCount = 0
for _, tempCount := range GameRoom.BloodCtrl.AndroidOprateConfig.CountMax0 {
if ProNow < tempCount[1] {
r.MaxCount = tempCount[0]
break
} else {
ProNow -= tempCount[1]
}
}
r.FlushCountTimer = nowMilliSeconds + int64(GameRoom.BloodCtrl.AndroidOprateConfig.FlushHZ*1000)
}
r.GronTimer.Start()
}
// NewGameRobot new个机器人
func (r *RobotManager) NewGameRobot() *Player {
//待定先从数据库user表中获取,没有就new一个注册到user中去
MinGold := r.GameRoom.BloodCtrl.AndroidOprateConfig.InGold[r.GameRoom.RoomID][0]
ManGold := r.GameRoom.BloodCtrl.AndroidOprateConfig.InGold[r.GameRoom.RoomID][1]
gold := rand.Intn(ManGold-MinGold) + MinGold
diamond := rand.Int63n(1000) + 1000
ticket := rand.Int63n(1000) + 1000
CurRobotUserCount++
if CurRobotUserCount > 10000 {
CurRobotUserCount = 1
}
//随机头像
avatar := rand.Intn(180) + 1
return &Player{
Uid: CurRobotUserCount + RobotUid,
Gold: decimal.NewFromFloat(float64(gold)), // 玩家金币
Diamond: decimal.NewFromFloat(float64(diamond)), // 玩家钻石
Ticket: decimal.NewFromFloat(float64(ticket)), // 玩家点券
IsRobot: true,
Avatar: strconv.Itoa(avatar),
Nickname: randomnames.RandomName(),
}
}
// RobotsEnterGameRoom 机器人进入房间
func (r *RobotManager) RobotsEnterGameRoom() {
pPlayer := r.NewGameRobot()
if 0 != r.GameRoom.JoinRoom(pPlayer, 0) {
log.Debug(" 桌子号", r.GameRoom.RId, "机器人加入房间失败")
return
}
// 发送完桌子消息 再激活用户状态
pPlayer.OnlineState = USER_ON_LINE
// 通知大厅有机器人坐下
NotifySitDown := &protocol.NotifyDTSitDown{
Uid: pPlayer.Uid,
RoomID: r.GameRoom.RoomID,
TableID: r.GameRoom.RId,
ChairID: pPlayer.ChairId,
Avatar: pPlayer.Avatar,
State: r.GameRoom.CurrentGameState,
}
r.GameRoom.NotifyFunc(route.NotifyDTSitDown, NotifySitDown)
//判断开场
if r.GameRoom.State < EN_TABLE_STATE_PLAYING && r.GameRoom.GetRoomPlayerCount() > 4 {
log.Debug(" 桌子号", r.GameRoom.RId, " 可以开场当前人数", r.GameRoom.GetRoomPlayerCount())
r.GameRoom.State = EN_TABLE_STATE_PLAYING
milliseconds := time.Now().UnixMilli()
r.GameRoom.CurrentGameState = protocol.STATE_BEGIN
r.GameRoom.CurrentOperationTimer = milliseconds + r.GameRoom.TimeOutBegin
r.GameRoom.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: protocol.STATE_BEGIN,
NextTimestamp: r.GameRoom.CurrentOperationTimer,
})
}
r.Lock.Lock()
defer r.Lock.Unlock()
r.PlayerMaps[pPlayer.ChairId] = pPlayer
r.playerStat[pPlayer.ChairId] = 1
r.playerBetCtrl[pPlayer.ChairId] = &RobotBetCtrlInfo{}
//随机一个下次判断行为时间
milliseconds := time.Now().UnixMilli()
//rTime := rand.Int63n(5) + 2
r.RobotTimer[pPlayer.ChairId] = milliseconds + 60*1000
log.Debug(" 桌子号", r.GameRoom.RId, " UID:", pPlayer.Uid, " 名字 ", pPlayer.Nickname, "机器人加入房间Ok:", codes.OK.Code(), " 当前机器人个数", r.GameRoom.GetInRoomRobotCount())
}
func (r *RobotManager) CopyRobotsEnterGameRoom(pPlayer *Player) { // 复制的机器人进入房间
r.Lock.Lock()
defer r.Lock.Unlock()
r.PlayerMaps[pPlayer.ChairId] = pPlayer
r.playerStat[pPlayer.ChairId] = 1
r.playerBetCtrl[pPlayer.ChairId] = &RobotBetCtrlInfo{}
//随机一个下次判断行为时间
milliseconds := time.Now().UnixMilli()
//rTime := rand.Int63n(5) + 2
r.RobotTimer[pPlayer.ChairId] = milliseconds + 60*1000
log.Debug(" 桌子号", r.GameRoom.RId, " UID:", pPlayer.Uid, " 名字 ", pPlayer.Nickname, "机器人加入房间Ok:", codes.OK.Code(), " 当前机器人个数", r.GameRoom.GetInRoomRobotCount())
}
func (r *RobotManager) AddLeaveRobot(chairId int32) { // 机器人离开
r.Lock.Lock()
defer r.Lock.Unlock()
robot := r.PlayerMaps[chairId]
if robot == nil {
return
}
delete(r.PlayerMaps, chairId)
delete(r.RobotTimer, chairId)
delete(r.playerStat, chairId)
delete(r.playerBetCtrl, chairId)
}
func (r *RobotManager) UpdateRobot() { // 更新机器人
}
func (r *RobotManager) OnRobotMessage(msgId int32, player *Player, msg interface{}) { // 消息相应
//if msgId != route.NotifyBet {
// log.Debug(" 桌子号", r.GameRoom.RId, "机器人收到消息", msgId)
// log.Printf(1, "内容: %+v\n", msg)
//}
switch msgId {
case route.NotifySitDown: // 加入游戏
{
if player == nil {
log.Debug("加入游戏==>机器人不能为空")
return
}
if !player.IsRobot {
log.Debug("加入游戏==>这不是一个机器人")
return
}
break
}
case route.NotifyStateTime: // 通知阶段倒计时
{
if player == nil {
log.Debug("开始游戏==>机器人不能为空")
return
}
if !player.IsRobot {
log.Debug("开始游戏==>这不是一个机器人")
return
}
if _, exists := r.playerBetCtrl[player.ChairId]; !exists {
log.Debug("机器人还没进来")
return
}
message := msg.(*protocol.NotifyStateTime)
if message == nil {
log.Debug("通知退出==>解析 NotifyLeaveRoomRes 结构体失败")
return
}
//随机一个下次判断行为时间
CurrentGears := 0
TempRoomGears, err := strconv.Atoi(r.GameRoom.BoolePool.RoomGears)
if err == nil {
CurrentGears = TempRoomGears
//log.Debug(" 桌子号", r.GameRoom.RId, "房间当前血池档位1 :", TempRoomGears, " CurrentGears :", CurrentGears)
}
CurrentGearConfig := r.GameRoom.BloodCtrl.GearConfig[0]
for _, GearConfig := range r.GameRoom.BloodCtrl.GearConfig {
if GearConfig.GearID == CurrentGears {
CurrentGearConfig = GearConfig
break
}
}
// 判断下注的档位
milliseconds := time.Now().UnixMilli()
if message.NextState == protocol.STATE_BET {
if r.playerBetCtrl[player.ChairId].MinBet > 0 {
tempRand := rand.Intn(100)
tempBetAmount := r.playerBetCtrl[player.ChairId].MinBet
if tempRand < 10 && r.playerBetCtrl[player.ChairId].MinBet*5 < r.playerBetCtrl[player.ChairId].MaxBet {
tempBetAmount *= 5
} else if tempRand < 20 && r.playerBetCtrl[player.ChairId].MinBet*4 < r.playerBetCtrl[player.ChairId].MaxBet {
tempBetAmount *= 4
} else if tempRand < 40 && r.playerBetCtrl[player.ChairId].MinBet*3 < r.playerBetCtrl[player.ChairId].MaxBet {
tempBetAmount *= 3
} else if tempRand < 60 && r.playerBetCtrl[player.ChairId].MinBet*2 < r.playerBetCtrl[player.ChairId].MaxBet {
tempBetAmount *= 2
}
r.playerBetCtrl[player.ChairId].BetAmount = tempBetAmount
} else {
r.playerBetCtrl[player.ChairId].BetAmount = r.GameRoom.Opt.MinBetAsset
}
//随机一个下次判断行为时间 第一轮要加7秒动动画
rTime := rand.Int63n(8000) + 2000
r.Lock.Lock()
r.RobotTimer[player.ChairId] = milliseconds + rTime
r.Lock.Unlock()
//log.Debug(r.GameRoom.RId, " 机器人 ", player.Uid, "随机的金额", r.playerBetCtrl[player.ChairId].BetAmount, " ", rTime, "秒后下注")
} else {
r.playerBetCtrl[player.ChairId].BetAmount = 0
}
// 判断抢庄
if message.NextState == protocol.STATE_GET_BANKER {
if rand.Intn(10000) < CurrentGearConfig.BankerPro {
go func() {
SelectTime := rand.Int63n(r.GameRoom.BloodCtrl.TimeOutGetBanker-2000) + 2000
time.Sleep(time.Duration(SelectTime) * time.Millisecond)
r.GameRoom.OnUserGetBanker(player.Uid, &protocol.GetBankerReq{
State: 0,
})
}()
} else {
go func() {
SelectTime := rand.Int63n(r.GameRoom.BloodCtrl.TimeOutGetBanker-2000) + 2000
time.Sleep(time.Duration(SelectTime) * time.Millisecond)
r.GameRoom.OnUserGetBanker(player.Uid, &protocol.GetBankerReq{
State: 1,
})
}()
}
}
break
}
case route.NotifyCurrentBeginUser: // 通知开始骰子玩家
{
if player == nil {
log.Debug("开始游戏==>机器人不能为空")
return
}
if !player.IsRobot {
log.Debug("开始游戏==>这不是一个机器人")
return
}
message := msg.(*protocol.NotifyCurrentBeginUser)
if message == nil {
log.Debug("通知退出==>解析 NotifyLeaveRoomRes 结构体失败")
return
}
if _, exists := r.playerBetCtrl[player.ChairId]; exists {
r.playerBetCtrl[player.ChairId].MinBet = message.MinBet
r.playerBetCtrl[player.ChairId].MaxBet = message.MaxBet
}
break
}
case route.NotifyPoker: // 通知翻牌
{
if player == nil {
log.Debug("开始游戏==>机器人不能为空")
return
}
if !player.IsRobot {
log.Debug("开始游戏==>这不是一个机器人")
return
}
break
}
case route.NotifyGetOutGame: // 退出房间
{
if player == nil {
log.Debug("通知退出==>机器人不能为空")
return
}
if !player.IsRobot {
log.Debug("通知退出==>这不是一个机器人")
return
}
message := msg.(*protocol.NotifyLeaveRoomRes)
if message == nil {
log.Debug("通知退出==>解析 NotifyLeaveRoomRes 结构体失败")
return
}
break
}
case route.NotifyBet: //出牌
{
if player == nil {
log.Debug("出牌==>机器人不能为空")
return
}
if !player.IsRobot {
log.Debug("出牌==>这不是一个机器人")
return
}
break
}
case route.NotifyGameConclude:
{
if player == nil {
log.Debug("结算==>机器人不能为空")
return
}
if !player.IsRobot {
log.Debug("结算==>这不是一个机器人")
return
}
message := msg.(*protocol.NotifyGameConclude)
if message == nil {
log.Debug("结算==>解析 NotifyGameConclude 结构体失败")
return
}
//结束时候判断是否机器人要离开
//if message.GameState == 1 {
if r.GameRoom.GetInRoomRobotCount() > int32(r.MaxCount) {
r.GameRoom.OnGetOutRoom(player.Uid)
log.Debug(r.GameRoom.RId, " 机器人 ", player.Uid, " 需要离开 当前机器人个数", r.GameRoom.GetInRoomRobotCount(), "需要的人数", r.MaxCount)
}
//}
}
case route.LogoutTableRes:
if player == nil {
log.Debug("结算==>机器人不能为空")
return
}
if !player.IsRobot {
log.Debug("结算==>这不是一个机器人")
return
}
r.AddLeaveRobot(player.ChairId)
log.Debug(r.GameRoom.RId, " 机器人 ", player.Uid, " 已经离开 当前机器人个数", r.GameRoom.GetInRoomRobotCount())
default:
{
break
}
}
}

190
game-server/game/TTZ/app/core/game/game_robot_timer.go

@ -0,0 +1,190 @@
package game
import (
"base/log"
"math/rand"
"time"
"xgame/game/TTZ/app/core/protocol"
)
// KickRobotTimer 踢人定时器
func (r *RobotManager) KickRobotTimer() {
}
func (r *RobotManager) run() {
milliseconds := time.Now().UnixMilli()
if r.FirstEnterTimer != 0 && milliseconds >= r.FirstEnterTimer {
r.FirstEnterTimer = 0
if r.GameRoom.GetInRoomRobotCount() < int32(r.MaxCount) {
//r.Lock.Lock()
r.RobotsEnterGameRoom()
}
}
if milliseconds >= r.NextEnterTimer {
if r.GameRoom.GetInRoomRobotCount() < int32(r.MaxCount) {
//r.Lock.Lock()
r.RobotsEnterGameRoom()
MinInterTime := 0
MaxInterTime := 0
if r.GameRoom.TableConfig != nil {
MinInterTime = int(r.GameRoom.TableConfig.RobotCheckMinFrequency) * 1000
MaxInterTime = int(r.GameRoom.TableConfig.RobotCheckMaxFrequency) * 1000
log.Debug(" 后台配置 机器人进入频率 最小 ", MinInterTime, " 最大 ", MaxInterTime)
} else {
MinInterTime = r.GameRoom.BloodCtrl.AndroidOprateConfig.JoinHZ[0] * 1000
MaxInterTime = r.GameRoom.BloodCtrl.AndroidOprateConfig.JoinHZ[1] * 1000
}
rTime := int64(0)
if MaxInterTime <= MinInterTime {
rTime = int64(MinInterTime)
} else {
rTime = int64(rand.Intn(MaxInterTime-MinInterTime) + MinInterTime)
}
//处理个意外
if rTime == 0 {
rTime = 2000
log.Error(" 出现0了 是不行的 ", r.GameRoom.RoomID)
}
r.NextEnterTimer = milliseconds + rTime
//r.Lock.Unlock()
}
}
if r.GameRoom.TableConfig != nil {
if milliseconds >= r.FlushCountTimer {
switch r.GameRoom.GetRoomRealPlayerCount() {
case 0:
r.MaxCount = 0
if r.GameRoom.TableConfig.ZeroMaxNum <= r.GameRoom.TableConfig.ZeroMinNum {
r.MaxCount = int(r.GameRoom.TableConfig.ZeroMinNum)
} else {
r.MaxCount = int(rand.Int63n(r.GameRoom.TableConfig.ZeroMaxNum-r.GameRoom.TableConfig.ZeroMinNum+1) + r.GameRoom.TableConfig.ZeroMinNum)
}
case 1:
r.MaxCount = 0
if r.GameRoom.TableConfig.OneMaxNum <= r.GameRoom.TableConfig.OneMinNum {
r.MaxCount = int(r.GameRoom.TableConfig.OneMinNum)
} else {
r.MaxCount = int(rand.Int63n(r.GameRoom.TableConfig.OneMaxNum-r.GameRoom.TableConfig.OneMinNum+1) + r.GameRoom.TableConfig.OneMinNum)
}
case 2:
r.MaxCount = 0
if r.GameRoom.TableConfig.TwoMaxNum <= r.GameRoom.TableConfig.TwoMinNum {
r.MaxCount = int(r.GameRoom.TableConfig.TwoMinNum)
} else {
r.MaxCount = int(rand.Int63n(r.GameRoom.TableConfig.TwoMaxNum-r.GameRoom.TableConfig.TwoMinNum+1) + r.GameRoom.TableConfig.TwoMinNum)
}
case 3:
r.MaxCount = 0
if r.GameRoom.TableConfig.ThreeMaxNum <= r.GameRoom.TableConfig.ThreeMinNum {
r.MaxCount = int(r.GameRoom.TableConfig.ThreeMinNum)
} else {
r.MaxCount = int(rand.Int63n(r.GameRoom.TableConfig.ThreeMaxNum-r.GameRoom.TableConfig.ThreeMinNum+1) + r.GameRoom.TableConfig.ThreeMinNum)
}
case 4:
r.MaxCount = 0
if r.GameRoom.TableConfig.FourMaxNum <= r.GameRoom.TableConfig.FourMinNum {
r.MaxCount = int(r.GameRoom.TableConfig.FourMinNum)
} else {
r.MaxCount = int(rand.Int63n(r.GameRoom.TableConfig.FourMaxNum-r.GameRoom.TableConfig.FourMinNum+1) + r.GameRoom.TableConfig.FourMinNum)
}
case 5:
r.MaxCount = 0
if r.GameRoom.TableConfig.FiveMaxNum <= r.GameRoom.TableConfig.FiveMinNum {
r.MaxCount = int(r.GameRoom.TableConfig.FiveMinNum)
} else {
r.MaxCount = int(rand.Int63n(r.GameRoom.TableConfig.FiveMaxNum-r.GameRoom.TableConfig.FiveMinNum+1) + r.GameRoom.TableConfig.FiveMinNum)
}
}
r.FlushCountTimer = milliseconds + r.GameRoom.TableConfig.CheckFrequency*1000
log.Debug("从后台配置 最大机器人数 ", r.MaxCount, " 刷新时间 ", r.GameRoom.TableConfig.CheckFrequency)
}
} else {
if milliseconds >= r.FlushCountTimer {
var TempCountMax [][]int
switch r.GameRoom.GetRoomRealPlayerCount() {
case 0:
TempCountMax = r.GameRoom.BloodCtrl.AndroidOprateConfig.CountMax0
case 1:
TempCountMax = r.GameRoom.BloodCtrl.AndroidOprateConfig.CountMax1
case 2:
TempCountMax = r.GameRoom.BloodCtrl.AndroidOprateConfig.CountMax2
case 3:
TempCountMax = r.GameRoom.BloodCtrl.AndroidOprateConfig.CountMax3
case 4:
TempCountMax = r.GameRoom.BloodCtrl.AndroidOprateConfig.CountMax4
case 5:
TempCountMax = r.GameRoom.BloodCtrl.AndroidOprateConfig.CountMax5
}
ProMax := 0
for _, tempCount := range TempCountMax {
ProMax += tempCount[1]
}
ProNow := 0
if ProMax != 0 {
ProNow = rand.Intn(ProMax)
}
r.MaxCount = 0
for _, tempCount := range TempCountMax {
if ProNow < tempCount[1] {
r.MaxCount = tempCount[0]
break
} else {
ProNow -= tempCount[1]
}
}
r.FlushCountTimer = milliseconds + int64(r.GameRoom.BloodCtrl.AndroidOprateConfig.FlushHZ*1000)
}
}
for Index, player := range r.PlayerMaps {
r.Lock.Lock()
func() {
defer r.Lock.Unlock()
}()
if _, exists := r.RobotTimer[Index]; !exists {
log.Debug("机器人 ", player.Uid, " RobotTimer状态不存在 ", r.PlayerMaps, r.playerStat, r.RobotTimer)
} else {
if _, exists2 := r.playerStat[Index]; !exists2 {
log.Debug("机器人 ", player.Uid, " 状态不存在 ", r.PlayerMaps, r.playerStat, r.RobotTimer)
} else {
if r.playerStat[Index] != 0 && milliseconds >= r.RobotTimer[Index] {
// 可以下注并且到了下注时间
if r.GameRoom.CurrBanker == player.ChairId {
//log.Debug("机器人 ", player.Uid, " 坐庄的的机器人 不能下注")
continue
}
if !player.CanBet {
//log.Debug("机器人 ", player.Uid, " 第一轮不能下注")
continue
}
if r.playerBetCtrl[Index].BetAmount < 0 {
log.Debug("机器人 ", player.Uid, " 没有可下注额度了")
continue
}
// 随机下注金额
req := &protocol.BetReq{
BetAmount: r.playerBetCtrl[Index].BetAmount,
}
r.GameRoom.OnUserBet(player.Uid, req, false)
// 下次操作时间
r.RobotTimer[Index] = milliseconds + 60*1000
}
}
}
}
}

214
game-server/game/TTZ/app/core/game/game_select_banker.go

@ -0,0 +1,214 @@
package game
import (
"base/log"
"math/rand"
"strconv"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/core/types"
"xgame/game/TTZ/app/route"
)
func (rr *Room) OnGameSelectBanker() { // 定庄
NotifyCurrentBanker := &protocol.NotifyCurrentBanker{}
//log.Debug(" 桌子号", rr.RId, "定庄 庄家列表 :", rr.GetBankerList)
// 抢庄时候重置一下所有可下注的状态
for index, TempPlayer := range rr.PlayerMap {
rr.PlayerMap[index].CanBet = true
NoHave := true
for _, i2 := range rr.GetBankerList {
if i2 == TempPlayer.ChairId {
NoHave = false
break
}
}
if NoHave {
for _, i2 := range rr.NoGetBankerList {
if i2 == TempPlayer.ChairId {
NoHave = false
break
}
}
if NoHave {
rr.GetBankerList = append(rr.GetBankerList, TempPlayer.ChairId)
}
}
}
//log.Debug(" 桌子号", rr.RId, "定庄 庄家列表 2 :", rr.GetBankerList)
if len(rr.GetBankerList) == 0 {
NotifyCurrentBanker.Reason = 1
for _, player := range rr.PlayerMap {
rr.GetBankerList = append(rr.GetBankerList, player.ChairId)
}
NotifyCurrentBanker.ChairID = rr.GetBankerList[rand.Intn(len(rr.GetBankerList))]
rr.CurrBanker = NotifyCurrentBanker.ChairID
rr.GetBankerList = rr.GetBankerList[:0]
rr.NoGetBankerList = rr.NoGetBankerList[:0]
} else {
NotifyCurrentBanker.Reason = 0
NotifyCurrentBanker.ChairID = rr.GetBankerList[rand.Intn(len(rr.GetBankerList))]
CurrentGears := 0
TempRoomGears, err := strconv.Atoi(rr.BoolePool.RoomGears)
if err == nil {
CurrentGears = TempRoomGears
//log.Debug(" 桌子号", rr.RId, "房间当前血池档位1 :", TempRoomGears, " CurrentGears :", CurrentGears)
}
CurrentGearConfig := rr.BloodCtrl.GearConfig[0]
for _, GearConfig := range rr.BloodCtrl.GearConfig {
if GearConfig.GearID == CurrentGears {
CurrentGearConfig = GearConfig
break
}
}
// 判断是不是优先机器人
if rand.Intn(10000) < CurrentGearConfig.GetBankerPro {
for _, GetBankerList := range rr.GetBankerList {
if rr.PlayerMap[GetBankerList].IsRobot {
log.Debug(" 桌子号", rr.RId, "必须机器人当庄 档位:", TempRoomGears, " 上庄列表 :", GetBankerList)
NotifyCurrentBanker.ChairID = GetBankerList
break
}
}
}
rr.CurrBanker = NotifyCurrentBanker.ChairID
rr.GetBankerList = rr.GetBankerList[:0]
rr.NoGetBankerList = rr.NoGetBankerList[:0]
}
// TODO 这里从哪儿获取
rr.GuoScore = 100000
rr.SendAllMessage(route.NotifyCurrentBanker, NotifyCurrentBanker)
log.Debug(" 桌子号", rr.RId, "确定庄家 :", rr.CurrBanker)
// 庄家不能下注
rr.PlayerMap[rr.CurrBanker].CanBet = false
}
func (rr *Room) OnGameBeginUser() { // 开始发牌玩家
// 计算出扑克
rr.OnBuildOtherResultPoker()
var AllUser []int32
for _, player := range rr.PlayerMap {
if player.CanBet || player.ChairId == rr.CurrBanker {
AllUser = append(AllUser, player.ChairId)
}
}
CurrentChairID := rr.GetBankerList[rand.Intn(len(rr.GetBankerList))]
rr.CurrBeginUser = CurrentChairID
//计算锅导致的最大下注
bHaveBB := false
MaxBet := int64(0)
for _, poker := range rr.DropCardList {
if poker == 10 {
bHaveBB = true
break
}
}
if bHaveBB {
MaxBet = rr.GuoScore / 3
MaxBet = MaxBet / rr.Opt.MinBetAsset
MaxBet = MaxBet * rr.Opt.MinBetAsset
} else {
MaxBet = rr.GuoScore / 4
MaxBet = MaxBet / rr.Opt.MinBetAsset
MaxBet = MaxBet * rr.Opt.MinBetAsset
}
for _, pPlayer := range rr.PlayerMap {
if pPlayer != nil {
NewMaxBet := int64(0)
if bHaveBB {
NewMaxBet = pPlayer.Gold.IntPart() / 3
NewMaxBet = NewMaxBet / rr.Opt.MinBetAsset
NewMaxBet = NewMaxBet * rr.Opt.MinBetAsset
} else {
NewMaxBet = pPlayer.Gold.IntPart() / 4
NewMaxBet = NewMaxBet / rr.Opt.MinBetAsset
NewMaxBet = NewMaxBet * rr.Opt.MinBetAsset
}
if NewMaxBet > MaxBet {
NewMaxBet = MaxBet
}
NotifyCurrentBeginUser := protocol.NotifyCurrentBeginUser{
ChairID: rr.CurrBeginUser,
MinBet: rr.Opt.MinBetAsset,
MaxBet: NewMaxBet,
}
for _, poker := range pPlayer.HandPoker {
NotifyCurrentBeginUser.HandPoker = append(NotifyCurrentBeginUser.HandPoker, int32(poker))
}
NotifyCurrentBeginUser.PokerType, NotifyCurrentBeginUser.Point = pPlayer.HandPoker.GetCardType()
rr.Send(pPlayer, route.NotifyCurrentBeginUser, NotifyCurrentBeginUser)
}
}
// 计算骰子需要多少
var TzIDList []types.Poker
var UserList []int32
CurrentTzID := int32(1)
for i := rr.CurrBanker; i < rr.CurrBanker+MAX_USER_NUM; i++ {
TempChairID := i
if TempChairID >= MAX_USER_NUM {
TempChairID -= MAX_USER_NUM
}
if _, exist := rr.PlayerMap[TempChairID]; exist {
UserList = append(UserList, TempChairID)
}
}
UserList = append(UserList, UserList...)
for index, chairID := range UserList {
if index == 0 {
continue
}
if chairID == rr.CurrBeginUser {
if index < 7 {
TzIDList = append(TzIDList, types.Poker(index))
} else {
break
}
}
}
rr.shuffleDeck(&TzIDList)
if len(TzIDList) > 0 {
CurrentTzID = int32(TzIDList[0])
}
for _, pPlayer := range rr.PlayerWatchMap {
if pPlayer != nil {
rr.Send(pPlayer, route.NotifyCurrentBeginUser, protocol.NotifyCurrentBeginUser{
ChairID: rr.CurrBeginUser,
TzID: CurrentTzID,
})
}
}
log.Debug(" 桌子号", rr.RId, "开始发牌玩家 :", rr.CurrBeginUser)
}

44
game-server/game/TTZ/app/core/game/game_service.go

@ -0,0 +1,44 @@
package game
import (
"base/cluster/node"
"base/log"
"context"
"xgame/internal/service/basic"
pb2 "xgame/internal/service/basic/pb"
gameservice "xgame/internal/service/game"
gamepb "xgame/internal/service/game/pb"
)
// GetSrcRoomByCtx 获取原始房间 通过ctx
func (rr *Room) GetSrcRoomByCtx(Proxy *node.Proxy, gameID, agentID, srcRoomId int32) *gamepb.FetchRoomsReply {
client, err := gameservice.NewClient(Proxy.NewServiceClient)
if err != nil {
return nil
}
reply, err := client.FetchRoomByID(context.Background(), &gamepb.FetchRoomByIdArgs{
GameID: gameID,
AgentID: agentID,
RoomID: srcRoomId,
})
return reply
}
// GetUserGameControl 获取用户游戏控制 通过Ctx上下文
func (rr *Room) GetUserGameControl(Proxy *node.Proxy, uId, gameId, roomId int64) *pb2.GetUserGameControlInfoReply {
client, err := basic.NewClient(Proxy.NewServiceClient)
if err != nil {
log.Debug("error .获取用户游戏控制=>", err)
return nil
}
userControl, err := client.GetUserGameControlInfo(context.Background(), &pb2.GetUserGameControlInfoArgs{
UID: uId,
GameId: gameId,
RoomId: roomId,
})
return userControl
}

62
game-server/game/TTZ/app/core/game/game_update_gold.go

@ -0,0 +1,62 @@
package game
import (
"base/log"
"github.com/shopspring/decimal"
assetservice "xgame/internal/service/asset"
assetspb "xgame/internal/service/asset/pb"
)
func (rr *Room) OnUpdateGold(userId int64) { // 更新用户的钱
rr.roomRWLock.Lock()
defer rr.roomRWLock.Unlock()
for i, player := range rr.PlayerMap {
if player.Uid == userId {
client2, err := assetservice.NewClient(player.Ctx.Proxy.NewServiceClient)
if err != nil {
log.Errorf("assetservice NewClient failed: %v", err)
return
}
reply3, err := client2.GetAsset(player.Ctx.Context(), &assetspb.GetAssetArgs{
UID: player.Uid,
})
if err != nil {
return
}
rr.PlayerMap[i].Gold = decimal.NewFromFloat(float64(reply3.Gold))
log.Debug("更新玩家 ", player.Uid, " 名字:", player.Nickname, " 金币:", rr.PlayerMap[i].Gold)
break
}
}
for i, player := range rr.PlayerWatchMap {
if player.Uid == userId {
client2, err := assetservice.NewClient(player.Ctx.Proxy.NewServiceClient)
if err != nil {
log.Errorf("assetservice NewClient failed: %v", err)
return
}
reply3, err := client2.GetAsset(player.Ctx.Context(), &assetspb.GetAssetArgs{
UID: player.Uid,
})
if err != nil {
return
}
rr.PlayerWatchMap[i].Gold = decimal.NewFromFloat(float64(reply3.Gold))
log.Debug("更新玩家 ", player.Uid, " 名字:", player.Nickname, " 金币:", rr.PlayerWatchMap[i].Gold)
break
}
}
}

111
game-server/game/TTZ/app/core/game/game_watch.go

@ -0,0 +1,111 @@
package game
import (
"base/log"
"time"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/route"
)
func (rr *Room) OnUserWatchToDown(uid int64, msg interface{}) { // 主动观战转坐下
message := msg.(*protocol.WatchToDownReq)
WatchToDownRes := &protocol.WatchToDownRes{}
//判断用户是否在观战
var WToDUser *Player
for _, TempWatchPlayer := range rr.PlayerWatchMap {
if TempWatchPlayer.Uid == uid {
WToDUser = TempWatchPlayer
}
}
if WToDUser == nil {
log.Debug("玩家", uid, "不在观战席")
return
}
////判断座位是否合法
//if message.ChairID < 0 || message.ChairID >= MAX_USER_NUM {
// log.Debug("玩家", uid, "要坐的座位 ", message.ChairID, " 不存在 ")
// WatchToDownRes.Code = protocol.ErrorWatchToDownNoExist
// WatchToDownRes.CodeMsg = "要坐的座位不存在"
// rr.Send(WToDUser, route.WatchToDownRes, WatchToDownRes)
//}
//
////判断座位是否空着
//if TempPlayer, exist := rr.PlayerMap[message.ChairID]; !exist {
// log.Debug("玩家", uid, "座位 ", message.ChairID, " 不空 ", " 上面坐着:", TempPlayer.Uid)
// WatchToDownRes.Code = protocol.ErrorWatchToDownPlayerExist
// WatchToDownRes.CodeMsg = "座位不空 不能坐下"
// rr.Send(WToDUser, route.WatchToDownRes, WatchToDownRes)
//}
//临时修改为找一个空位
TempChairId := rr.MakeChairId()
if TempChairId == -1 {
log.Debug("没有空座位")
WatchToDownRes.Code = protocol.ErrorWatchToDownPlayerExist
WatchToDownRes.CodeMsg = "没有空座位"
rr.Send(WToDUser, route.WatchToDownRes, WatchToDownRes)
return
} else {
message.ChairID = TempChairId
}
for TempIndex, TempWatchPlayer := range rr.PlayerWatchMap {
if TempWatchPlayer.Uid == uid {
//判断钱是否够
if TempWatchPlayer.Gold.IntPart() <= rr.MinGameScore {
log.Debug("玩家", uid, "钱不够 不能坐下 ", TempWatchPlayer.Gold.IntPart(), " 需要的钱:", rr.MinGameScore)
WatchToDownRes.Code = protocol.ErrorWatchToDownGoldLess
WatchToDownRes.CodeMsg = "钱不够 不能坐下"
rr.Send(TempWatchPlayer, route.WatchToDownRes, WatchToDownRes)
break
}
WatchUser := rr.PlayerWatchMap[TempIndex]
WatchUser.ChairId = message.ChairID
rr.SendAllMessage(route.NotifyWatchToDown, &protocol.NotifyWatchToDown{
Uid: WatchUser.Uid,
ChairID: message.ChairID,
})
// 通知大厅有人坐下
NotifySitDown := &protocol.NotifyDTSitDown{
Uid: WatchUser.Uid,
RoomID: rr.RoomID,
TableID: rr.RId,
ChairID: message.ChairID,
Avatar: WatchUser.Avatar,
State: rr.CurrentGameState,
}
rr.NotifyFunc(route.NotifyDTSitDown, NotifySitDown)
rr.PlayerMap[WatchUser.ChairId] = WatchUser
if TempIndex == len(rr.PlayerWatchMap)-1 {
rr.PlayerWatchMap = rr.PlayerWatchMap[:TempIndex]
} else {
rr.PlayerWatchMap = append(rr.PlayerWatchMap[:TempIndex], rr.PlayerWatchMap[TempIndex+1:]...)
}
break
}
}
//判断开场
if rr.CurrentGameState < protocol.STATE_GET_BANKER && rr.GetRoomPlayerCount() > 1 {
rr.State = EN_TABLE_STATE_PLAYING
milliseconds := time.Now().UnixMilli()
rr.CurrentGameState = protocol.STATE_GET_BANKER
rr.CurrentOperationTimer = milliseconds + rr.TimeOutGetBanker
rr.RepeatRoomUser()
rr.SendAllMessage(route.NotifyStateTime, &protocol.NotifyStateTime{
NextState: protocol.STATE_GET_BANKER,
NextTimestamp: rr.CurrentOperationTimer,
})
// 抢庄做了加一秒的容错判断
rr.CurrentOperationTimer += 1000
}
}

227
game-server/game/TTZ/app/core/protocol/protocol_msg.go

@ -0,0 +1,227 @@
package protocol
type JoinRoomReq struct { // 加入房间的请求
RoomID int32 `json:"roomID"` // 房间ID 0初级场 1中级场 2高级场
TableID int64 `json:"tableID"` // 桌子ID 快速加入时桌子ID用0 创建桌子用1 不然用TableInfo里的TableID
GameID int32 `json:"gameID"` // 游戏ID
AgentID int32 `json:"agentID"` // 代理ID
IsWatching int32 `json:"isWatching"` // 是否观战身份加入游戏
}
type AllTableReq struct { // 获取所有桌子
RoomID int32 `json:"roomID"` // 房间ID 0初级场 1中级场 2高级场
GameID int32 `json:"gameID"` // 游戏ID
AgentID int32 `json:"agentID"` // 代理ID
}
type SitDownRes struct { //玩家坐下返回
Code int32 `json:"code"` //错误码
CodeMsg string `json:"codeMsg"` //错误内容
PlayerInfo *RoomPlayer `json:"playerList"` //用户信息
}
type AllTableRes struct { //所有桌子的信息返回
Code int32 `json:"code"` //错误码
CodeMsg string `json:"codeMsg"` //错误内容
TableInfo []TableInfo `json:"tableInfo"` //桌子信息
}
type NoticeSitDown struct { //通知玩家坐下
PlayerInfo *RoomPlayer `json:"playerList"` //用户信息
}
type NotifyDTSitDown struct { //通知大厅玩家坐下
Uid int64 `json:"uid"` // userId
RoomID int32 `json:"roomID"` // 房间ID 0初级场 1中级场 2高级场
TableID int64 `json:"tableID"` // 桌子ID 快速加入时桌子ID用0 不然用TableInfo里的TableID
ChairID int32 `json:"chairId"` // 座位号
Avatar string `json:"avatar"` // 玩家头像
State int32 `json:"state"` // 房间状态
}
type RoomInfoRes struct {
RoomId int32 `json:"roomId"` //房间ID
RoomConfig *TableConfig `json:"roomConfig,omitempty"` //房间配置
ServerLocation float64 `json:"serverLocation"` //同步服务器时区
ServerTimestamp int64 `json:"serverTimestamp"` //同步服务器时间戳
SingleMaxBet int64 `json:"singleMaxBet"` //单人最大下注额度
PlayerList []*RoomPlayer `json:"playerList,omitempty"` //用户列表
PlayerWatchMap []*RoomPlayer `json:"playerWatchMap,omitempty"` //观战用户列表
RoomStatus int32 `json:"roomStatus"` //房间的状态
StatusChangeTimestamp int64 `json:"statusChangeTimestamp"` //下一次变更状态的时间戳
Record []HistoryRecord `json:"record,omitempty"` //历史记录
}
// ResumeGameNotice 通知恢复游戏
type ResumeGameNotice struct {
RoomId int32 `json:"roomId"` //房间ID
GameId int32 `json:"gameId"` //游戏ID
}
type BetReq struct { //下注请求
BetAmount int64 `json:"betAmount,omitempty"` //下注
}
type BetRes struct { //下注返回
Code int32 `json:"code"` //错误码
CodeMsg string `json:"codeMsg"` //错误内容
ChairID int32 `json:"chairId"` //座位ID
TableTotalWin int64 `json:"tableTotalWin"` //在桌子上的总输赢
UserScore int64 `json:"userScore"` //用户剩余分数
}
type NotifyBet struct { //通知下注
ChairID int32 `json:"chairId"` //座位ID
TableTotalWin int64 `json:"tableTotalWin"` //在桌子上的总输赢
}
type LeaveTable struct { //退出桌子请求
}
type LeaveTableRes struct { // 退出桌子返回
Code int32 `json:"code"` //错误码
CodeMsg string `json:"codeMsg"` //错误内容
}
type GetBankerReq struct { //抢庄请求
State int32 `json:"state"` //0 抢庄 1 不抢
}
type GetBankerRes struct { // 抢庄返回
Code int32 `json:"code"` //错误码
CodeMsg string `json:"codeMsg"` //错误内容
}
type WatchToDownReq struct { //主动观战转坐下请求
ChairID int32 `json:"chairId"` //座位ID
}
type WatchToDownRes struct { //主动观战转坐下返回
Code int32 `json:"code"` //错误码
CodeMsg string `json:"codeMsg"` //错误内容
}
type NotifyBanker struct { // 通知抢庄
ChairID int32 `json:"chairId"` //座位ID
State int32 `json:"state"` //0 抢庄 1 不抢
}
type NotifyCurrentBanker struct { // 通知庄家
ChairID int32 `json:"chairId"` //座位ID
Reason int32 `json:"reason"` //定庄原因 0 正常随机 1 所有人未抢庄所有人随机
}
type NotifyCurrentBeginUser struct { // 通知开始玩家
ChairID int32 `json:"chairId"` // 开始玩家的座位ID
TzID int32 `json:"tzID"` // 骰子大小
MinBet int64 `json:"minBet"` // 最低下注
MaxBet int64 `json:"maxBet"` // 最高下注
HandPoker []int32 `json:"handPoker,omitempty"` // 用户当前手牌
PokerType byte `json:"pokerType" ` // 牌类型
Point int `json:"point"` // 点数
}
type NotifyLeaveRoomRes struct { // 通知退出桌子
Uid int64 `json:"uid"` // userId
ChairID int32 `json:"chairId"` // 座位号
LeaveReason int32 `json:"leaveReason"` // 离开原因 0主动离开 1踢出房间
}
type NotifyStateTime struct { // 通知阶段倒计时
NextState int32 `json:"nextState"` // 下阶段类型
NextTimestamp int64 `json:"nextTimestamp"` // 下阶段开始时间戳
}
type NotifyPoker struct { // 通知翻牌
PokerArea int32 `json:"pokerArea"` // 扑克区域 0 中间区域 1 A区域 2 B区域
Poker int32 `json:"poker"` // 扑克
}
type NotifyGameConclude struct { // 通知游戏结束结算
PlayList []*RoomPlayer `json:"playList,omitempty"` //用户列表更新
Record []HistoryRecord `json:"record,omitempty"` //历史记录
}
type NotifyDownToWatch struct { // 通知玩家坐下转观战
ChairID int32 `json:"chairId"` // 座位号
}
type NotifyWatchToDown struct { // 通知玩家观战转坐下
Uid int64 `json:"uid"` // userId
ChairID int32 `json:"chairId"` // 座位号
}
type NotifyRoomDestroy struct { // 通知房间销毁
RoomID int32 `json:"roomID"` // 房间ID 0初级场 1中级场 2高级场
TableID int64 `json:"tableID"` // 桌子ID
}
type Marquee struct {
Enable bool `json:"enable"` // 是否启用
MinWinMultiple float64 `json:"minWinMultiple"` // 最低赢钱倍数
MinWinAsset int64 `json:"minWinAsset"` // 最低赢钱数额
}
type Jackpot struct {
Enable bool `json:"enable"` // 是否启用
MinWinMultiple float64 `json:"minWinMultiple"` // 最低赢钱倍数
MinWinAsset int64 `json:"minWinAsset"` // 最低赢钱数额
}
type Control struct {
Enable bool `json:"enable"` // 是否启用
Stock int64 `json:"stock"` // 库存值
Lines []*ControlLine `json:"lines"` // 控制线
}
type ControlLine struct {
MaxRate float64 `json:"maxRate"` // 最大库存率
MaxMultiple float64 `json:"maxMultiple"` // 最大限制倍数
LoseProbability float64 `json:"loseProbability"` // 输的概率
}
type Options struct {
ID int `json:"id"` // 房间ID
Name string `json:"name"` // 房间名称
IsAllowWatch bool `json:"isAllowWatch"` // 是否允许观战
TaxRate float64 `json:"taxRate"` // 房间税率
AssetKind int `json:"assetKind"` // 使用的资产类型(1:金币 2:钻石)
MinEnterAsset int64 `json:"minEnterAsset"` // 最小进入限制
MaxEnterAsset int64 `json:"maxEnterAsset"` // 最大进入限制
MinBetAsset int64 `json:"minBetAsset"` // 最小押注限制
MaxBetAsset int64 `json:"maxBetAsset"` // 最大押注限制
MinBetMultiple float64 `json:"minBetMultiple"` // 最小押注倍率
MaxBetMultiple float64 `json:"maxBetMultiple"` // 最大押注倍率
BetAssetOptions []int64 `json:"betAssetOptions"` // 押注资产选项
BetMultipleOptions []float64 `json:"betMultipleOptions"` // 押注的倍率选项
Rtp int `json:"rtp"` // 押注的倍率选项
BootValue int64 `json:"bootValue"` // 下注金额
RoomLimit int64 `json:"roomLimit"` // 房间进入条件
PersonalRate int64 `json:"personalRate"` // 个人倍率
RoomRate int64 `json:"roomRate"` // 房间倍率
MaxRound int32 `json:"maxRound"` // 最大轮数
AiRangeMin int64 `json:"aiRangeMin"` // AI随机范围下限
AiRangeMax int64 `json:"aiRangeMax"` // AI随机范围上限
AiVipRangeMin int64 `json:"aiVipRangeMin"` // AI VIP随机范围下限
AiVipRangeMax int64 `json:"aiVipRangeMax"` // AI VIP随机范围上限
Marquee *Marquee `json:"marquee"` // 跑马灯触发项
Jackpot *Jackpot `json:"jackpot"` // 大奖信息触发项
Control *Control `json:"control"` // 控制信息项
Country string `json:"country"` // 国家
BalanceLine int64 `json:"balanceLine"` // 平衡线
DefaultBet int64 `json:"defaultBet"` // 默认下注
RoundTime int64 `json:"roundTime"` // 每轮回合的轮次时间
}
type RecordInfoPlayer struct { // 存储的结构显示
ChairID int32 `json:"chairId"` //座位ID
UserNo string `json:"userNo"` //用户ID
HandPoker []int32 `json:"handPoker,omitempty"` // 用户当前手牌
PokerType byte `json:"pokerType" ` // 牌类型
PokerPoint int `json:"pokerPoint" ` // 牌点数
IsWin bool `json:"isWin"`
}
type RecordInfo struct { // 存储的结构显示
Player []RecordInfoPlayer `json:"player,omitempty"` // 单独用户信息
}

104
game-server/game/TTZ/app/core/protocol/protocol_struct.go

@ -0,0 +1,104 @@
package protocol
import "xgame/game/TTZ/app/core/types"
const (
ErrorCodeLessThanMinEnter = 20013001 //低于最小进入条件
ErrorCodeGameNoStart = 20013002 //游戏未开启
ErrorCodeUnknown = 20013003 //未知错误
ErrorCodeNoBetState = 20013004 //非下注阶段
ErrorCodeBetError = 20013005 //下注失败
ErrorLongTimeKickUser = 20013006 //长时间未下注踢人 连续5局未下注
ErrorGetBanker = 20013007 //抢庄失败
ErrorWatchToDownGoldLess = 20013008 //观战转坐下 钱不够
ErrorWatchToDownNoExist = 20013009 //观战转坐下 座位不存在
ErrorWatchToDownPlayerExist = 20013010 //观战转坐下 座位已经有人
ErrorLeaveTableBanker = 20013011 //庄家不能离开房间
ErrorWatchToDownNotInWatch = 20013012 //观战转坐下 人不在观战席
)
const (
Win = 1 //赢
Lose = 2 //输
He = 3 //和
)
const (
PokerTypeBiShi = 1 // 闭十 相加为10 1倍
PokerTypeBanQiDown = 2 // 7.5以下 1倍
PokerTypeBanQiUp = 3 // 7.5以上 2倍
PokerTypeDui = 4 // 对子 3倍
PokerTypeEBG = 5 // 二八杠 3倍
PokerTypeSB = 6 // 双百至尊 4倍
)
type RoomPlayer struct { //房间的用户信息
Uid int64 `json:"uid"` // userId
ChairID int32 `json:"chairId"` // 座位号
IsBanker int32 `json:"isBanker"` // 0 非庄 1 庄
State int32 `json:"state"` // 状态
Avatar string `json:"avatar"` // 玩家头像
Gold int64 `json:"gold"` // 玩家金币
Nickname string `json:"nickname"` // 姓名
Sex int32 `json:"sex"` // 性别
Vip int32 `json:"vip"` // VIP
Level int32 `json:"level"` // 等级
Exp int32 `json:"exp"` // 经验
EndScore int64 `json:"endScore"` // 本次结算分数 这个只有赢的值
BetScore int64 `json:"betScore"` // 结算时的总下注
WinLose int32 `json:"winLose"` // 本轮输赢 0 无效 1 赢 2 输 3 和
CanBet bool `json:"canBet"` // 玩家是否需要等待下一局开始
HandPoker []int32 `json:"handPoker,omitempty"` // 用户当前手牌
PokerType byte `json:"pokerType" ` // 牌类型
Point int `json:"point"` // 点数
}
type TablePlayer struct { //桌子的用户信息
ChairID int32 `json:"chairId"` // 座位号
Avatar string `json:"avatar"` // 玩家头像
}
type HistoryRecordSub struct { //历史记录信息
ChairID []int32 `json:"chairId"` // 座位号
Avatar []string `json:"avatar"` // 玩家头像
HandPoker []types.PokerSlice `json:"handPoker"` // 手牌
PokerType []int32 `json:"pokerType"` // 类型
Point []int32 `json:"point"` // 点数
}
type HistoryRecord struct { //历史记录信息
List []HistoryRecordSub `json:"list"` // 记录列表
}
const (
AREA_INVALUE int32 = 0
AREA_ANDAR int32 = 1
AREA_BANHAR int32 = 2
)
const (
STATE_INVALUE int32 = 0 // 无效阶段
STATE_WAIT int32 = 1 // 等人阶段等不到就结束房间
STATE_BEGIN int32 = 2 // 开始
STATE_GET_BANKER int32 = 3 // 抢庄
STATE_BB int32 = 4 // 抢庄动画过渡
STATE_DT int32 = 5 // 丢骰子定谁开牌的动画时间
STATE_BET int32 = 6 // 下注
STATE_CP int32 = 7 // 搓牌
STATE_RESULT int32 = 8 // 结算
)
type TableConfig struct { // 桌子配置
AlwaysBet int64 `json:"alwaysBet"` // 房费
RechargeLimit int64 `json:"rechargeLimit"` // 弹充值限制
TaxMing float64 `json:"taxMing"` // 明税
}
type TableInfo struct { // 桌子信息
RoomID int32 `json:"roomID"` // 房间ID 0初级场 1中级场 2高级场
TableID int64 `json:"tableId"` // 桌子ID
GameID int32 `json:"gameID"` // 游戏ID
AgentID int32 `json:"agentID"` // 代理ID
State int32 `json:"state"` // 房间状态
TablePlayer []TablePlayer `json:"playerList"` // 桌子上的用户信息
}

48
game-server/game/TTZ/app/core/room/base.go

@ -0,0 +1,48 @@
package room
import (
"base/cluster/node"
"flag"
"golang.org/x/exp/rand"
"time"
"xgame/game/TTZ/app/core/game"
)
type HomeBase interface {
OnInit() bool // 初始化房间
OnPlayerReady(userId int64, p interface{}) // 准备
OnReconnect(userId int64) // 断线重连
OnDisconnect(userId int64) // 玩家离线
JoinRoom(pPlayer *game.Player, isWatching int32) int32 // 加入房间
LeaveRoom(uid int64) int32 // 离开房间
GetRoomId() int64 // 获取房间Id
GetRId() int64 // 获取房间RId
GetState() int32 // 获取房间状态
OnGetGameId() int32 // 游戏ID
GetRoomState() int32 // 获取房间的状态
GetRoomPlayerCount() int32 // 获取房间里的人数
GetRoomPlayer(chairId int32) *game.Player // 通过椅子号获取玩家对象
GetRoomPlayerByUid(uid int64) *game.Player // 通过uid获取玩家对象
SetPlayerState(uid int64, state int32) bool // 设置玩家状态
OnGetOutRoom(uid int64) bool // 退出房间
IsCanDissolution() bool // 是否能解散房间
UpdateCtx(uid int64, ctx *node.Context) // 无奈加的,更新node.Context
UpdateUserOut(uid int64) // 更新用户退出状态
OnUserBet(uid int64, msg interface{}, Auto bool) // 用户下注
OnUserGetBanker(uid int64, msg interface{}) // 用户抢庄
OnUserWatchToDown(uid int64, msg interface{}) // 用户观战转坐下
OnResumeGameData(uid int64) //
OnDestroy(uid int64) // 销毁房间
OnGetRoomInfo(uid int64, ctx *node.Context) interface{} //
IsJoinRoom() bool //
OnUpdateGold(userId int64) // 更新用户的钱
}
var (
GsId = flag.Int64("i", 1, "gs_id")
R *rand.Rand
)
func init() {
R = rand.New(rand.NewSource(uint64(time.Now().UnixNano())))
}

362
game-server/game/TTZ/app/core/room/roommgr.go

@ -0,0 +1,362 @@
package room
import (
"base/cluster/node"
"base/log"
"context"
"github.com/roylee0704/gron"
"math/rand"
"sort"
"strconv"
"sync"
"time"
conf "xgame/game/TTZ/app/config"
"xgame/game/TTZ/app/core/game"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/route"
"xgame/game/service/rpc_client"
gameservice "xgame/internal/service/game"
gamepb "xgame/internal/service/game/pb"
)
const (
RoomFree = 0
RoomStared = 1
RoomFull = 2
)
type Home struct {
GronTimer *gron.Cron
GameProxy *node.Proxy
BloodCtrl conf.BloodCtrl //血池控制
NextCreateTable int64 //下个机器人创建房间的时间
TableCount []int32 //各等级的房间个数
TableNeedCount []int32 //各等级需要的房间个数
Rhb map[int64]HomeBase
RhbEx map[int64]int64
RhbRoom map[int64]*game.Room
EnterUser map[int64]*node.Context
NextId int64
lock sync.Mutex
RoomsReply *gamepb.FetchRoomsReply
TableConfig []*gamepb.GameTableTotal
}
var Gh *Home
func Init(GameProxy *node.Proxy) {
Gh = &Home{
Rhb: make(map[int64]HomeBase),
RhbEx: make(map[int64]int64),
RhbRoom: make(map[int64]*game.Room),
EnterUser: make(map[int64]*node.Context),
TableCount: []int32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
TableNeedCount: []int32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
}
Gh.GameProxy = GameProxy
Gh.InitBloodControl()
Gh.InitBloodControlByBackend()
milliseconds := time.Now().UnixMilli()
Gh.NextCreateTable = milliseconds + 300
if Gh.GronTimer != nil {
Gh.GronTimer.Stop()
Gh.GronTimer = nil
}
Gh.GronTimer = gron.New()
Gh.GronTimer.AddFunc(gron.Every(time.Duration(50)*time.Millisecond), func() {
Gh.run()
})
Gh.GronTimer.Start()
}
func (h *Home) InitBloodControl() {
h.BloodCtrl = conf.ReadBloodConfig()
//log.Debug("roommgr InitBloodControl:", h.BloodCtrl)
log.Debug("h.BloodCtrl.AndroidOprateConfig.CreateTableHZ = ", h.BloodCtrl.AndroidOprateConfig.CreateTableHZ)
if h.BloodCtrl.AndroidOprateConfig.CreateTableHZ == 0 {
h.BloodCtrl.AndroidOprateConfig.CreateTableHZ = 30000
}
}
func (h *Home) InitBloodControlByBackend() {
// 按等级从后台获取到配置
var replyOpt *gamepb.FetchGameTableTotalsReply
replyOpt = rpc_client.GetTableCtrlConfig(h.GameProxy, game.GAME_ID, "", 1)
h.TableConfig = replyOpt.List
if len(h.TableConfig) == 0 {
log.Error(game.GAME_ID, " 21点后台获取桌子配置 无配置")
} else {
log.Debug(" 21点获取后台桌子配置 ", h.TableConfig)
}
}
func (h *Home) run() {
milliseconds := time.Now().UnixMilli()
if milliseconds >= h.NextCreateTable {
for _, SingleTableConfig := range h.TableConfig {
tempRoomID, err := strconv.Atoi(SingleTableConfig.RoomId)
if err == nil {
if SingleTableConfig.TableMaxNum <= SingleTableConfig.TableMinNum {
h.TableNeedCount[tempRoomID] = int32(SingleTableConfig.TableMinNum)
} else {
h.TableNeedCount[tempRoomID] = int32(rand.Int63n(SingleTableConfig.TableMaxNum-SingleTableConfig.TableMinNum) + SingleTableConfig.TableMinNum)
}
}
log.Debug(" 场次 ", tempRoomID, " 个数 ", h.TableNeedCount[tempRoomID])
}
go func() {
for LevelID, NeedCount := range h.TableNeedCount {
for h.TableCount[LevelID] < NeedCount {
h.CreateRoom(0, int32(LevelID), game.GAME_ID, 0, h.GameProxy)
log.Debug("创建房间成功 当前等级 ", LevelID, " 个数 ", h.TableCount[LevelID])
}
}
}()
h.NextCreateTable = milliseconds + h.TableConfig[0].CheckFrequency*1000
}
}
func gen() (int64, int64, bool) {
Gh.NextId++
try := 0
for try < 30 {
id := int64(0)
r := R.Int63n(70000) + 10000
first := r / 10000
first ^= *GsId
id = r*10 + first
if _, ok := Gh.Rhb[id]; !ok {
return id, Gh.NextId, true
}
try++
}
return 0, 0, false
}
func (h *Home) CreateRoom(Uid int64, RoomID, GameID, AgentID int32, GameProxy *node.Proxy) HomeBase {
h.lock.Lock()
defer h.lock.Unlock()
//创建新房间
for {
rid, _, ret := gen()
if !ret {
continue
}
r := game.New(Uid, rid, RoomID, GameID, AgentID, h.Destroy, h.QuitRoom, h.NotifyDTUser, GameProxy)
r.OnInit()
Gh.Rhb[rid] = r
Gh.RhbEx[Uid] = rid
Gh.RhbRoom[rid] = r
log.Debug("创建房间ok", rid, "所有房间==>", Gh.Rhb)
Gh.TableCount[RoomID]++
return r
}
}
func (h *Home) Send(msgId int32, message interface{}) { //发送给在桌子列表界面的玩家
for TempUserID, UserCtx := range Gh.EnterUser {
if msgId > 0 {
UserCtx.Request.Message.Route = msgId
}
err := UserCtx.Response(message)
if err != nil {
log.Debug("response message failed: ", err, "uid====>", TempUserID)
}
}
}
func (h *Home) CopyRoom(Uid int64, GameProxy *node.Proxy) HomeBase {
RoomID := Gh.GetRoomIdByUid(0)
GameRoom := Gh.GetGameRoomByRoomId(RoomID)
h.lock.Lock()
defer h.lock.Unlock()
//创建新房间
for {
rid, _, ret := gen()
if !ret {
continue
}
r := game.New(Uid, rid, GameRoom.RoomID, GameRoom.GameID, GameRoom.AgentID, h.Destroy, h.QuitRoom, h.NotifyDTUser, GameProxy)
r.Copy(GameRoom, Uid)
Gh.Rhb[rid] = r
Gh.RhbEx[Uid] = rid
Gh.RhbRoom[rid] = r
log.Debug("复制房间ok", rid, "所有房间==>", Gh.Rhb)
return r
}
}
// uid 获取房间对象
func (h *Home) GetRoomObject(UID int64) HomeBase {
//这里不用枷锁
roomId := h.GetRoomIdByUid(UID)
if roomId <= 0 {
//log.Debug("Get RoomId failed. you get outed room")
return nil
}
r := h.GetRoomByRoomId(roomId)
return r
}
func (h *Home) SearchRoom() HomeBase {
h.lock.Lock()
defer h.lock.Unlock()
for _, h := range Gh.Rhb {
if h.IsJoinRoom() {
return h
}
}
return nil
}
func (h *Home) SearchCZGRoom(RoomID int64) HomeBase {
h.lock.Lock()
defer h.lock.Unlock()
for _, h := range Gh.Rhb {
if h.GetRoomId() != RoomID {
continue
}
if h.IsJoinRoom() {
return h
}
}
return nil
}
func (h *Home) AllRoomInfo() []protocol.TableInfo {
h.lock.Lock()
defer h.lock.Unlock()
var AllTableInfo []protocol.TableInfo
for _, roomInfo := range Gh.RhbRoom {
tableInfo := protocol.TableInfo{
RoomID: roomInfo.RoomID,
TableID: roomInfo.RId,
GameID: game.GAME_ID,
AgentID: roomInfo.AgentID,
State: roomInfo.CurrentGameState,
}
for _, player := range roomInfo.PlayerMap {
TablePlayer := protocol.TablePlayer{
ChairID: player.ChairId,
Avatar: player.Avatar,
}
tableInfo.TablePlayer = append(tableInfo.TablePlayer, TablePlayer)
}
AllTableInfo = append(AllTableInfo, tableInfo)
}
sort.Slice(AllTableInfo, func(i, j int) bool {
return AllTableInfo[i].TableID < AllTableInfo[j].TableID
})
return AllTableInfo
}
// 通过uid获取房间Id
func (h *Home) GetRoomIdByUid(uid int64) int64 {
h.lock.Lock()
defer h.lock.Unlock()
//log.Debug("玩家绑定的roomid==>", Gh.RhbEx)
r := Gh.RhbEx[uid]
return r
}
// 通过房间id获取房间对象
func (h *Home) GetRoomByRoomId(roomId int64) HomeBase {
h.lock.Lock()
defer h.lock.Unlock()
//log.Debug("roomId绑定的room对象", Gh.Rhb)
r := Gh.Rhb[roomId]
return r
}
func (h *Home) GetGameRoomByRoomId(roomId int64) *game.Room { // 通过房间id获取房间对象
h.lock.Lock()
defer h.lock.Unlock()
//log.Debug("roomId绑定的room对象", Gh.Rhb)
r := Gh.RhbRoom[roomId]
return r
}
func (h *Home) QuitRoom(uid int64) { // 退出房间
h.lock.Lock()
defer h.lock.Unlock()
log.Debug("Home==>退出房间")
delete(Gh.RhbEx, uid)
//delete(Gh.EnterUser, uid)
}
func (h *Home) Destroy(roomId int64) { // 销毁房间
r := h.GetGameRoomByRoomId(roomId)
h.lock.Lock()
defer h.lock.Unlock()
log.Debug("Home==>销毁房间")
// 通知房间销毁
h.Send(route.NotifyRoomDestroy, protocol.NotifyRoomDestroy{
RoomID: r.RoomID,
TableID: r.RId,
})
Gh.TableCount[r.RoomID]--
delete(Gh.Rhb, roomId)
delete(Gh.RhbRoom, roomId)
}
func (rr *Home) GetSrcRoomByCtx(Proxy *node.Proxy, gameID, agentID, srcRoomId int32) *gamepb.FetchRoomsReply {
client, err := gameservice.NewClient(Proxy.NewServiceClient)
if err != nil {
return nil
}
reply, err := client.FetchRoomByID(context.Background(), &gamepb.FetchRoomByIdArgs{
GameID: gameID,
AgentID: agentID,
RoomID: srcRoomId,
})
if err != nil {
log.Debug("err==>", err)
}
rr.RoomsReply = reply
return reply
}
func (h *Home) NotifyDTUser(messageID int32, message interface{}) { // 通知大厅的消息
h.lock.Lock()
defer h.lock.Unlock()
log.Debug("通知大厅的消息 消息号", messageID)
h.Send(messageID, message)
}

142
game-server/game/TTZ/app/core/types/poker.go

@ -0,0 +1,142 @@
package types
import (
"base/log"
"fmt"
)
type Poker byte // Poker 扑克牌
type CardType = byte // CardType 牌型
const (
PokerTypeBiShi = 1 // 闭十 相加为10 1倍
PokerTypeBanQiDown = 2 // 7.5以下 1倍
PokerTypeBanQiUp = 3 // 7.5以上 2倍
PokerTypeDui = 4 // 对子 3倍
PokerTypeEBG = 5 // 二八杠 3倍
PokerTypeSB = 6 // 双百至尊 4倍
)
// OnePokers 一副牌
var OnePokers = [20]Poker{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // 1-9 白板
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // 1-9 白板
}
// IsValidValue 判断是否是有效值
func (p Poker) IsValidValue() bool {
for _, poker := range OnePokers {
if poker == p {
return true
}
}
return false
}
// PokerSlice 手牌牌组
type PokerSlice []Poker
// Insert 插入
func (p *PokerSlice) Insert(index int, Card Poker) {
if index < 0 || index > len(*p) {
fmt.Println("Index out of range")
return
}
// 追加一个空元素到切片中以便腾出空间
*p = append(*p, 0) // 先扩展切片
// 将插入点及之后的元素向后移动
copy((*p)[index+1:], (*p)[index:]) // 向后拷贝元素
(*p)[index] = Card // 插入新
}
// Delete 删除
func (p *PokerSlice) Delete(index int) {
if index < 0 || index >= len(*p) {
fmt.Println("Index out of range")
return
}
// 使用 copy 函数将后面的元素前移
copy((*p)[index:], (*p)[index+1:]) // 从 index 开始拷贝后续元素
*p = (*p)[:len(*p)-1] // 切片长度减一
}
// GetCardType 获取牌型,点数
func (p *PokerSlice) GetCardType() (CardType, int) {
Sum := Poker(0)
for _, tempPoker := range *p {
if tempPoker < 10 {
Sum += tempPoker * 10
} else {
Sum += 5
}
if Sum >= 100 {
Sum -= 100
}
}
if (*p)[0] == 10 && (*p)[1] == 10 {
return PokerTypeSB, int(Sum)
} else if (*p)[0] == 2 && (*p)[1] == 8 {
return PokerTypeEBG, int(Sum)
} else if (*p)[0] == (*p)[1] {
return PokerTypeDui, int(Sum)
} else if Sum > 70 {
return PokerTypeBanQiUp, int(Sum)
} else if Sum == 100 {
return PokerTypeBiShi, int(Sum)
} else {
return PokerTypeBanQiDown, int(Sum)
}
}
func (p *PokerSlice) GetCardTypeBei() int { // 获取倍数
srcType, _ := p.GetCardType()
switch srcType {
case PokerTypeSB:
return 4
case PokerTypeEBG:
return 3
case PokerTypeDui:
return 3
case PokerTypeBanQiUp:
return 2
case PokerTypeBanQiDown:
return 1
case PokerTypeBiShi:
return 1
}
log.Debug(" 牌型错误 牌是:", *p, " 牌型是(6 双白 5 二八 4 对子 3 板七上 2 板七下 1 闭十): ", srcType)
return 1
}
func (p *PokerSlice) Compare(desPoker PokerSlice) int32 { //比较输赢 赢 1 输 2 和 3
srcType, srcPoint := p.GetCardType()
desType, desPoint := desPoker.GetCardType()
// 先比较类型
if srcType > desType {
return 1
} else if srcType == desType {
if srcType == PokerTypeDui {
if (*p)[0] >= (*p)[1] {
return 1
} else {
return 2
}
} else {
// 再比较点数
if srcPoint >= desPoint {
return 1
} else {
return 2
}
}
} else {
return 2
}
}

451
game-server/game/TTZ/app/gamemanage/gamemanage.go

@ -0,0 +1,451 @@
package gamemanage
import (
"base/cluster/node"
"base/log"
"base/task"
"fmt"
"xgame/game/TTZ/app/core/game"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/core/room"
"xgame/game/TTZ/app/route"
"xgame/internal/code"
accountservice "xgame/internal/service/account"
accountpb "xgame/internal/service/account/pb"
assetservice "xgame/internal/service/asset"
assetspb "xgame/internal/service/asset/pb"
gameservice "xgame/internal/service/game"
gamepb "xgame/internal/service/game/pb"
"base/encoding/json"
"github.com/shopspring/decimal"
)
var GameProxy *node.Proxy
func EnterGame(ctx *node.Context) {
Ctx := ctx.Clone()
task.AddTask(func() {
room.Gh.EnterUser[Ctx.Request.UID] = Ctx
})
}
func JoinTable(ctx *node.Context) {
Ctx := ctx.Clone()
task.AddTask(func() {
if Ctx.Request.UID <= 0 {
return
}
req := &protocol.JoinRoomReq{}
res := &protocol.SitDownRes{}
//0.解析消息
if err := Ctx.Request.Parse(req); err != nil {
log.Debugf("response protocol.JoinRoomReq message failed: %v", err)
return
}
//3.new个Player 初始化
client2, err := assetservice.NewClient(Ctx.Proxy.NewServiceClient)
if err != nil {
log.Errorf("assetservice NewClient failed: %v", err)
return
}
client1, err := accountservice.NewClient(Ctx.Proxy.NewServiceClient)
if err != nil {
log.Errorf("accountservice NewClient failed: %v", err)
return
}
//4.个人信息
user, err := client1.GetUser(Ctx.Context(), &accountpb.GetUserArgs{
UID: Ctx.Request.UID,
})
if err != nil {
return
}
//5.个人资产
reply3, err := client2.GetAsset(Ctx.Context(), &assetspb.GetAssetArgs{
UID: Ctx.Request.UID,
})
if err != nil {
return
}
client3, err := gameservice.NewClient(Ctx.Proxy.NewServiceClient)
if err != nil {
return
}
//6.房间信息
reply4, err := client3.FetchRoomByID(Ctx.Context(), &gamepb.FetchRoomByIdArgs{
GameID: req.GameID,
AgentID: req.AgentID,
RoomID: req.RoomID,
})
if err != nil {
return
}
//6.5判断是否满足创建房间
var item protocol.Options
err2 := json.Unmarshal([]byte(reply4.RoomOpts), &item)
if err2 != nil {
return
}
if reply3.Gold < item.MinEnterAsset {
res.Code = -2
res.CodeMsg = "加入游戏失败,资产不足"
log.Debug(res.CodeMsg)
if err := Ctx.Response(res); err != nil {
log.Debugf("EnterGame ==> response message failed: %v", err)
}
return
}
log.Debug("玩家", Ctx.Request.UID, " 加入房间 req:", req)
if req.TableID == 1 && req.IsWatching == 1 {
res.Code = -4
res.CodeMsg = "不能以观战的身份创建桌子"
log.Debug(res.CodeMsg)
if err := Ctx.Response(res); err != nil {
log.Debugf("EnterGame ==> response message failed: %v", err)
}
return
}
//在房间中
var roomObj room.HomeBase
RoomID := room.Gh.GetRoomIdByUid(Ctx.Request.UID)
roomObj = room.Gh.GetRoomByRoomId(RoomID)
log.Debug("EnterGame user:", user.Nickname, " ==> RoomID:", RoomID, " roomObj", roomObj)
if roomObj == nil {
if req.TableID == 0 {
roomObj = room.Gh.SearchCZGRoom(int64(req.RoomID))
if roomObj == nil {
roomObj = room.Gh.CreateRoom(Ctx.Request.UID, req.RoomID, req.GameID, req.AgentID, GameProxy)
}
} else if req.TableID == 1 {
roomObj = room.Gh.CreateRoom(Ctx.Request.UID, req.RoomID, req.GameID, req.AgentID, GameProxy)
} else {
roomObj = room.Gh.GetRoomByRoomId(req.TableID)
if roomObj == nil {
res.Code = -3
res.CodeMsg = "加入游戏失败,桌子不存在"
log.Debug(res.CodeMsg, "桌子id :", req.TableID)
if err := Ctx.Response(res); err != nil {
log.Debugf("EnterGame ==> response message failed: %v", err)
}
return
}
}
} else {
log.Debug("玩家", Ctx.Request.UID, " name:", user.Nickname, "====>在房间中 发送最新的房间信息")
log.Debug("UpdateCtx EnterGame ", Ctx.Request.UID, " UserId", Ctx.Request.UserId, " UID", Ctx.Request.UID, " Request", Ctx.Request)
roomObj.OnGetRoomInfo(Ctx.Request.UID, Ctx)
return
}
_player := &game.Player{
Uid: Ctx.Request.UID,
IsRobot: false,
Ctx: Ctx,
Gold: decimal.NewFromFloat(float64(reply3.Gold)),
Diamond: decimal.NewFromFloat(float64(reply3.Diamond)),
Ticket: decimal.NewFromFloat(float64(reply3.Ticket)),
Nickname: user.Nickname,
Avatar: user.Avatar,
UserNo: user.UserNo,
AgentID: user.AgentID,
}
//6.判断玩家资产是否满足进入房间的需求
bRet := roomObj.JoinRoom(_player, req.IsWatching)
if -1 == bRet {
res.Code = -1
res.CodeMsg = "你已经在房间中了"
log.Debug(res.CodeMsg)
if err := Ctx.Response(res); err != nil {
log.Debugf("EnterGame ==> response message failed: %v", err)
}
return
}
if -2 == bRet {
res.Code = -2
res.CodeMsg = "资产不足"
log.Debug(res.CodeMsg)
if err := Ctx.Response(res); err != nil {
log.Debugf("EnterGame ==> response message failed: %v", err)
}
return
}
room.Gh.RhbEx[Ctx.Request.UID] = roomObj.GetRId()
roomObj.OnGetRoomInfo(Ctx.Request.UID, Ctx)
NotifySitDown := &protocol.NotifyDTSitDown{
Uid: _player.Uid,
RoomID: req.RoomID,
TableID: roomObj.GetRId(),
ChairID: _player.ChairId,
Avatar: _player.Avatar,
State: roomObj.GetState(),
}
room.Gh.Send(route.NotifyDTSitDown, NotifySitDown)
err = Ctx.BindNode()
if err != nil {
log.Error("绑定失败==>", Ctx.Request.UID)
return
}
})
}
// Reconnect 掉线重连
func Reconnect(event *node.Event) {
task.AddTask(func() {
if event.UID <= 0 {
return
}
//Ctx.Context()
/*
* 只能配合客户端想办法了,实在没办法,这服务器架构设计的有缺陷
*/
uid := event.UID
r := room.Gh.GetRoomObject(uid)
if r != nil {
r.OnReconnect(uid)
fmt.Println(uid, "断线重连")
}
})
}
// Disconnect 掉线
func Disconnect(event *node.Event) {
task.AddTask(func() {
if event.UID <= 0 {
return
}
uid := event.UID
//r := room.GetRoomByUid(uid)
r := room.Gh.GetRoomObject(uid)
if r != nil {
r.OnDisconnect(uid)
log.Debug(uid, "玩家掉线,设置为离线状态")
}
})
}
func UpdateUserGold(uid int64) { //更新用户的钱
task.AddTask(func() {
r := room.Gh.GetRoomObject(uid)
if r != nil {
r.OnUpdateGold(uid)
fmt.Println(uid, "更新用户的钱")
}
})
}
func UpdateTableConfig(gameID int64) { //更新桌子的配置
task.AddTask(func() {
log.Debug(gameID, " 更新桌子的配置 ", gameID)
if game.GAME_ID == gameID {
room.Gh.InitBloodControlByBackend()
log.Debug(gameID, " 更新桌子的配置")
}
})
}
func UserBet(ctx *node.Context) { // 用户下注
Ctx := ctx.Clone()
task.AddTask(func() {
r := room.Gh.GetRoomObject(Ctx.Request.UID)
if r == nil {
log.Error("StrokeCard==>>>>>>,不在该房间")
return
}
req := &protocol.BetReq{}
if err := Ctx.Request.Parse(req); err != nil {
log.Errorf("Parse StrokeCardReq failed. %v", err)
return
}
log.Debug("UpdateCtx UserBet ", Ctx.Request.UID, " UserId", ctx.Request.UserId, " UID", ctx.Request.UID, " Request", ctx.Request)
r.UpdateCtx(Ctx.Request.UID, Ctx)
r.OnUserBet(Ctx.Request.UID, req, false)
})
}
func GetBanker(ctx *node.Context) { // 抢庄
Ctx := ctx.Clone()
task.AddTask(func() {
RoomID := room.Gh.GetRoomIdByUid(Ctx.Request.UID)
r := room.Gh.GetGameRoomByRoomId(RoomID)
if r == nil {
log.Error("StrokeCard==>>>>>>,不在该房间")
return
}
req := &protocol.GetBankerReq{}
if err := Ctx.Request.Parse(req); err != nil {
log.Errorf("Parse StrokeCardReq failed. %v", err)
return
}
log.Debug("UpdateCtx GetBanker ", Ctx.Request.UID, " UserId", ctx.Request.UserId, " UID", ctx.Request.UID, " Request", ctx.Request)
r.UpdateCtx(Ctx.Request.UID, Ctx)
r.OnUserGetBanker(Ctx.Request.UID, req)
})
}
func WatchToDown(ctx *node.Context) { // 主动观战转坐下
Ctx := ctx.Clone()
task.AddTask(func() {
RoomID := room.Gh.GetRoomIdByUid(Ctx.Request.UID)
r := room.Gh.GetGameRoomByRoomId(RoomID)
if r == nil {
log.Error("StrokeCard==>>>>>>,不在该房间")
return
}
req := &protocol.WatchToDownReq{}
if err := Ctx.Request.Parse(req); err != nil {
log.Errorf("Parse StrokeCardReq failed. %v", err)
return
}
log.Debug("UpdateCtx WatchToDown ", Ctx.Request.UID, " UserId", ctx.Request.UserId, " UID", ctx.Request.UID, " Request", ctx.Request)
r.UpdateCtx(Ctx.Request.UID, Ctx)
//判断用户是否在观战
var WToDUser *game.Player
for _, TempWatchPlayer := range r.PlayerWatchMap {
if TempWatchPlayer.Uid == Ctx.Request.UID {
WToDUser = TempWatchPlayer
}
}
if WToDUser == nil {
log.Debug("玩家", Ctx.Request.UID, "不在观战席")
WatchToDownRes := &protocol.WatchToDownRes{}
WatchToDownRes.Code = protocol.ErrorWatchToDownNotInWatch
WatchToDownRes.CodeMsg = "不在观战席 不能坐下"
Ctx.Request.Message.Route = route.WatchToDownRes
if err := Ctx.Response(WatchToDownRes); err != nil {
log.Debugf("WatchToDown ==> response message failed: %v", err)
}
return
}
r.OnUserWatchToDown(Ctx.Request.UID, req)
})
}
func LogoutTable(ctx *node.Context) { // 用户退出游戏请求
Ctx := ctx.Clone()
task.AddTask(func() {
RoomID := room.Gh.GetRoomIdByUid(Ctx.Request.UID)
RoomInfo := room.Gh.GetGameRoomByRoomId(RoomID)
var TempPlayer *game.Player
if RoomInfo != nil {
TempPlayer = RoomInfo.GetRoomPlayerByUid(Ctx.Request.UID)
if TempPlayer != nil {
log.Debug("LogoutTable 没有获取到用户 ", Ctx.Request.UID)
}
if RoomInfo != nil && RoomInfo.CurrentGameState > protocol.STATE_BEGIN && RoomInfo.CurrentGameState != protocol.STATE_RESULT {
if TempPlayer.ChairId != -1 && (TempPlayer.ChairId == RoomInfo.CurrBanker || TempPlayer.CanBet) {
log.Debug("LogoutTable 开始游戏后不能退出游戏 ", " Request", ctx.Request)
LeaveTableRes := &protocol.LeaveTableRes{
Code: code.ErrorWaitGameEndOut.Code(),
CodeMsg: "开始游戏后不能退出游戏",
}
Ctx.Request.Message.Route = route.LogoutTableRes
if err := Ctx.Response(LeaveTableRes); err != nil {
log.Debugf("开始游戏后不能退出游戏 出错了: %v", err)
}
RoomInfo.UpdateUserOut(Ctx.Request.UID)
return
}
}
if TempPlayer != nil && TempPlayer.ChairId == RoomInfo.CurrBanker && RoomInfo.CurrentGameState > protocol.STATE_GET_BANKER && RoomInfo.CurrentGameState != protocol.STATE_RESULT {
log.Debug("LogoutTable 庄家不能离开房间 ", " Request", ctx.Request)
LeaveTableRes := &protocol.LeaveTableRes{
Code: protocol.ErrorLeaveTableBanker,
CodeMsg: "庄家不能离开房间",
}
Ctx.Request.Message.Route = route.LogoutTableRes
if err := Ctx.Response(LeaveTableRes); err != nil {
log.Debugf("庄家不能离开房间出错了: %v", err)
}
return
}
}
r := room.Gh.GetRoomObject(Ctx.Request.UID)
if r == nil {
delete(room.Gh.EnterUser, Ctx.Request.UID)
log.Debug(Ctx.Request.UID, " 不在该房间 退出游戏")
return
}
log.Debug("UpdateCtx LogoutTable ", Ctx.Request.UID, " UserId", ctx.Request.UserId, " UID", ctx.Request.UID, " Request", ctx.Request)
r.UpdateCtx(Ctx.Request.UID, Ctx)
r.OnGetOutRoom(Ctx.Request.UID)
log.Debug("UpdateCtx LogoutTable RoomInfo ", RoomInfo, " TempPlayer ", TempPlayer)
//if RoomInfo != nil {
// if TempPlayer != nil {
// NotifySitDown := &protocol.NotifyDTSitDown{
// Uid: TempPlayer.Uid,
// RoomID: RoomInfo.RoomID,
// TableID: RoomInfo.RId,
// ChairID: TempPlayer.ChairId,
// Avatar: TempPlayer.Avatar,
// State: RoomInfo.CurrentGameState,
// }
// log.Debug("UpdateCtx LogoutTable 发送用户退出")
// room.Gh.Send(route.NotifyDTStandUp, NotifySitDown)
// }
//}
})
}
func AllTable(ctx *node.Context) { // 用户下注
Ctx := ctx.Clone()
task.AddTask(func() {
res := &protocol.AllTableRes{
Code: 0,
CodeMsg: "获取成功",
}
res.TableInfo = room.Gh.AllRoomInfo()
log.Debug("当前的所有房间信息 : ", res)
Ctx.Request.Message.Route = route.AllTableInfoRes
if err := Ctx.Response(res); err != nil {
log.Debugf("AllTable ==> response message failed: %v", err)
}
})
}

39
game-server/game/TTZ/app/route/route.go

@ -0,0 +1,39 @@
package route
const (
// 客户端发送
Enter int32 = 2001301 // 进入游戏大厅
Bet int32 = 2001302 // 下注请求
LogoutTable int32 = 2001303 // 退出游戏请求
AllTable int32 = 2001304 // 获取所有桌子
JoinTable int32 = 2001305 // 加入桌子
GetBanker int32 = 2001306 // 抢庄
WatchToDown int32 = 2001307 // 主动观战转坐下
// 服务端返回
BetRes int32 = 2001311 // 下注返回
LogoutTableRes int32 = 2001312 // 退出游戏返回
SitDownRes int32 = 2001313 // 玩家坐下返回
RoomInfoRes int32 = 2001314 // 房间的信息返回
AllTableInfoRes int32 = 2001315 // 所有桌子的信息返回
GetBankerRes int32 = 2001316 // 抢庄返回
WatchToDownRes int32 = 2001317 // 主动观战转坐下返回
// 服务端通知
NotifySitDown int32 = 2001321 // 通知玩家坐下
NotifyStateTime int32 = 2001322 // 通知阶段倒计时
NotifyPoker int32 = 2001323 // 通知翻牌
NotifyBet int32 = 2001324 // 通知下注
NotifyGameConclude int32 = 2001325 // 通知结束结算
NotifyGetOutGame int32 = 2001326 // 通知退出房间
NotifyReEnter int32 = 2001327 // 通知点控重连
NotifyBanker int32 = 2001328 // 通知抢庄
NotifyCurrentBanker int32 = 2001329 // 通知本轮庄家
NotifyDTSitDown int32 = 2001330 // 通知大厅玩家坐下
NotifyDTStandUp int32 = 2001331 // 通知大厅玩家离开
NotifyWatchToDown int32 = 2001332 // 通知玩家观战转坐下
NotifyDownToWatch int32 = 2001333 // 通知玩家坐下转观战
NotifyRoomDestroy int32 = 2001334 // 通知房间销毁
NotifyCurrentBeginUser int32 = 2001380 // 通知本轮开始发牌的玩家
)

100
game-server/game/TTZ/config/blood.json

@ -0,0 +1,100 @@
{
"CommentRechargeLimit": "低于多少钱弹充值",
"RechargeLimit" : 10000,
"CommentMinGameScore": "最低能玩的金额",
"MinGameScore" : 50000,
"CommentChipList": "默认的筹码列表 后台有按后台的来",
"ChipList": [10000],
"GearConfigComment": "GearID 血池 WinPro AI胜率 BankerPro 抢庄概率 GetBankerPro 中庄概率 WinBetPro 赢下注权重 LoseBetPro 输下注权重 BankerWinPro 庄家胜率",
"GearConfig": [
{
"GearID": 3000,
"WinPro": 6000,
"BankerPro": 2000,
"GetBankerPro": 2000,
"WinBetPro": [[1,6000],[2,2000],[3,1000],[4,9999]],
"LoseBetPro": [[1,6000],[2,2000],[3,1000],[4,9999]],
"BankerWinPro": 5000
},{
"GearID": 2000,
"WinPro": 3000,
"BankerPro": 2000,
"GetBankerPro": 2000,
"WinBetPro": [[1,6000],[2,2000],[3,1000],[4,9999]],
"LoseBetPro": [[1,6000],[2,2000],[3,1000],[4,9999]],
"BankerWinPro": 5000
},{
"GearID": 1000,
"WinPro": 500,
"BankerPro": 2000,
"GetBankerPro": 2000,
"WinBetPro": [[1,6000],[2,2000],[3,1000],[4,9999]],
"LoseBetPro": [[1,6000],[2,2000],[3,1000],[4,9999]],
"BankerWinPro": 5000
},{
"GearID": -1000,
"WinPro": 0,
"BankerPro": 2000,
"GetBankerPro": 2000,
"WinBetPro": [[1,6000],[2,2000],[3,1000],[4,9999]],
"LoseBetPro": [[1,6000],[2,2000],[3,1000],[4,9999]],
"BankerWinPro": 5000
},{
"GearID": -2000,
"WinPro": 1500,
"BankerPro": 2000,
"GetBankerPro": 2000,
"WinBetPro": [[1,6000],[2,2000],[3,1000],[4,9999]],
"LoseBetPro": [[1,6000],[2,2000],[3,1000],[4,9999]],
"BankerWinPro": 5000
},{
"GearID": -3000,
"WinPro": 3000,
"BankerPro": 2000,
"GetBankerPro": 2000,
"WinBetPro": [[1,6000],[2,2000],[3,1000],[4,9999]],
"LoseBetPro": [[1,6000],[2,2000],[3,1000],[4,9999]],
"BankerWinPro": 5000
}
],
"CommentAndroidOprateConfig": "机器人的配置",
"AndroidOprateConfig": {
"CommentMustBanker": "是否兜底上庄 1是 0不是",
"MustBanker": 1,
"CommentInGold": "场次携带金币上下范围",
"InGold": [[1000000,2000000],[1000000,2000000],[1000000,2000000],[1000000,2000000],[1000000,2000000],[1000000,2000000]],
"CommenCreateTableHZ": "创建桌子的频率",
"CreateTableHZ": 30000,
"CommentTableCreatePro": "低于多少桌时创建桌子的概率 高于最后一个配置不再创建桌子",
"TableCreatePro": [[3,10000],[6,8000]],
"CommentCountMax": "房间多少个真实玩家情况下 机器人的个数概率",
"CountMax0": [[0,0],[1,0],[2,3000],[3,5000],[4,1500],[5,0]],
"CountMax1": [[0,0],[1,3000],[2,3000],[3,5000],[4,1500],[5,500]],
"CountMax2": [[0,0],[1,5000],[2,2000],[3,1000],[4,500],[5,0]],
"CountMax3": [[0,3000],[1,5000],[2,50],[3,0],[4,0],[5,0]],
"CountMax4": [[0,8000],[1,2000],[2,0],[3,0],[4,0],[5,0]],
"CountMax5": [[0,0],[1,0],[2,0],[3,0],[4,0],[5,0]],
"CommentJoinHZ": "机器人加入房间的时间间隔",
"JoinHZ": [10,30],
"CommentFlushHZ": "刷新机器人进房间的频率",
"FlushHZ": 300
},
"CommentTimeoutDestroy": "创建房间后没人的销毁时间",
"TimeoutDestroy": 60000,
"CommentTimeOutBegin": "下一局开始倒计时",
"TimeOutBegin" : 3000,
"CommentTimeOutGetBanker": "抢庄时间",
"TimeOutGetBanker" : 10000,
"CommentTimeOutBB": "抢庄后到开始下注的等待时间",
"TimeOutBB" : 5000,
"CommentTimeOutDT": "丢骰子动画展示倒计时",
"TimeOutDT" : 5000,
"CommentTimeOutBet": "下注时间",
"TimeOutBet" : 15000,
"CommentTimeOutCP": "搓牌时间",
"TimeOutCP" : 12000,
"CommentTimeOutResult": "结算的时间",
"TimeOutResult" : 10000,
"CommentTimeKickTimes": "多少次不下注踢出房间",
"TimeKickTimes" :5
}

126
game-server/game/TTZ/etc/etc.toml

@ -0,0 +1,126 @@
# 进程号
pid = "./run/game.pid"
# 统一时区设置。项目中的时间获取请使用xtime.Now()
timezone = "Local"
[cluster]
[cluster.node]
# 实例名称
name = "TTZ"
# 编解码器。可选:json | proto
codec = "json"
[config]
[config.file]
# 配置文件或配置目录路径
path = "./config"
# 读写模式。可选:read-only | read-write,默认为read-only
mode = "read-write"
[config.etcd]
# 客户端连接地址+
addrs = ["127.0.0.1:2379"]
# 客户端拨号超时时间,支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认为5s
dialTimeout = "5s"
# 路径。默认为/config
path = "/config"
# 读写模式。可选:read-only | read-write,默认为read-only
mode = "read-only"
[locate]
[locate.redis]
# 客户端连接地址
addrs = ["127.0.0.1:62331"]
# 数据库号
db = 0
# 用户名
username = ""
# 密码
password = "zetdCehWe5t3z9"
# 最大重试次数
maxRetries = 3
# key前缀
prefix = "xgame"
[registry]
[registry.etcd]
# 客户端连接地址
addrs = ["127.0.0.1:2379"]
# 客户端拨号超时时间(秒)
dialTimeout = 5
# 命名空间
namespace = "services"
# 超时时间(秒)
timeout = 3
# 心跳重试次数
retryTimes = 3
# 心跳重试间隔(秒)
retryInterval = 10
[registry.consul]
# 客户端连接地址
addr = "127.0.0.1:8500"
# 是否启用健康检查
healthCheck = true
# 健康检查时间间隔(秒),仅在启用健康检查后生效
healthCheckInterval = 10
# 健康检查超时时间(秒),仅在启用健康检查后生效
healthCheckTimeout = 5
# 是否启用心跳检查
heartbeatCheck = true
# 心跳检查时间间隔(秒),仅在启用心跳检查后生效
heartbeatCheckInterval = 10
# 健康检测失败后自动注销服务时间(秒)
deregisterCriticalServiceAfter = 30
[transport]
[transport.rpcx]
# RPCX服务器相关配置
[transport.rpcx.server]
# 服务器监听地址,空或:0时系统将会随机端口号
addr = ":0"
# 秘钥文件
keyFile = ""
# 证书文件
certFile = ""
# RPCX服务器相关配置
[transport.rpcx.client]
# 证书文件
certFile = ""
# 证书域名
serverName = ""
# 连接池大小,默认为10
poolSize = 10
[packet]
# 字节序,默认为big。可选:little | big
byteOrder = "big"
# 路由字节数,默认为2字节
routeBytes = 4
# 序列号字节数,默认为2字节
seqBytes = 4
# 消息字节数,默认为5000字节
bufferBytes = 100000
[log]
[log.zap]
# 日志输出文件
file = "./log/xgame.log"
# 日志输出级别,可选:debug | info | warn | error | fatal | panic
level = "debug"
# 日志输出格式,可选:text | json
format = "text"
# 是否输出到终端
stdout = true
# 时间格式,标准库时间格式
timeFormat = "2006/01/02 15:04:05.000000"
# 堆栈的最低输出级别,可选:debug | info | warn | error | fatal | panic
stackLevel = "error"
# 文件最大留存时间,d:天、h:时、m:分、s:秒
fileMaxAge = "7d"
# 文件最大尺寸限制,单位(MB)
fileMaxSize = 100
# 文件切割方式
fileCutRule = "day"
# 是否启用调用文件全路径
callerFullPath = false
# 是否启用分级存储
classifiedStorage = false

41
game-server/game/TTZ/etc/game.toml

@ -0,0 +1,41 @@
# 以下配置为游戏固定配置,修改必需重启游戏生效
# 游戏配置(游戏模型基础配置,不可随意变动配置名)
[game]
# 游戏ID
id = 20013
# 游戏类型
type = 1
# 游戏名称
name = "TTZ"
# 最小牌桌数
minTableNum = 1
# 最大牌桌数
maxTableNum = 1
# 是否动态牌桌
isDynamicTable = false
# 最小座位数
minSeatNum = 0
# 最大座位数
maxSeatNum = 0
# 是否动态座位
isDynamicSeat = true
# 是否回合制游戏,例如:(回合制游戏:麻将)(非回合制游戏:slots)
isTurnBasedGame = false
# 是否需要底池接口
isNeedPot = false
# 是否需要奖池接口
isNeedPrizePool = true
# 是否需要血池接口
isNeedBloodPool = false
# 自定义配置(由开发人员自行定义)
[custom]
# 押注时长(秒)
BaseLineCount = 25
# 准备起飞时长(秒)
readyingDuration = 4
# 结算时长(秒)
settlementDuration = 5

161
game-server/game/TTZ/game_test.go

@ -0,0 +1,161 @@
package main
import (
"base/cluster/node"
"base/config"
"base/config/file"
"base/encoding/json"
"base/eventbus"
"base/eventbus/nats"
"base/locate/redis"
"base/log"
"base/log/zap"
"base/registry/consul"
"base/transport/rpcx"
"github.com/shopspring/decimal"
"testing"
"xgame/game/TTZ/app/core"
"xgame/game/TTZ/app/core/game"
"xgame/game/TTZ/app/core/protocol"
"xgame/game/TTZ/app/core/types"
"xgame/game/TTZ/app/gamemanage"
"xgame/game/service/rpc_client"
optionRoom "xgame/internal/option/room"
)
func TestTable11_test(T *testing.T) {
// 设置日志
log.SetLogger(zap.NewLogger(zap.WithCallerSkip(2)))
// 初始化事件总线
eventbus.SetEventbus(nats.NewEventbus())
// 设置配置中心
config.SetConfigurator(config.NewConfigurator(config.WithSources(file.NewSource())))
// 创建容器
//container := base.NewContainer()
// 创建用户定位器
locator := redis.NewLocator()
// 创建服务发现
registry := consul.NewRegistry()
// 创建RPC传输器
transporter := rpcx.NewTransporter(rpcx.WithClientDiscovery(registry))
// 创建网关组件
component := node.NewNode(
node.WithLocator(locator),
node.WithRegistry(registry),
node.WithTransporter(transporter),
)
// 初始化游戏
gamemanage.GameProxy = component.Proxy()
core.NewCore(component.Proxy()).Init()
//// 添加网关组件
//container.Add(component)
//// 启动容器
//container.Serve()
//创建新房间
//RoomID := room.Gh.GetRoomIdByUid(0)
//rr := room.Gh.GetGameRoomByRoomId(RoomID)
rr := game.New(0, 123, 0, 20005, 0, nil, nil, gamemanage.GameProxy)
//rr.OnInit()
//1.初始化血池
rr.BoolePool.RoomGears = "3000"
//if rr.BoolePool.SetBoole(rr.RoomProxy, rr.GameID, rr.RoomID, "AB") == false {
// log.Debug("初始化血池失败")
//}
// 读取配置文件
rr.InitBloodControl()
//配置库存[房间的库存]
rr.StockValue = rr.BoolePool.BoolPoolSockNum
//2.初始化配置设置房间 opts
rr.Opt = &optionRoom.Options{}
replyOpt := rpc_client.GetSrcRoom(rr.RoomProxy, rr.GameID, rr.AgentID, rr.RoomID)
if replyOpt == nil {
log.Debug("初始化房间失败")
} else {
err1 := json.Unmarshal([]byte(replyOpt.RoomOpts), rr.Opt)
if err1 != nil {
log.Debug(err1)
}
}
log.Debug("房间配置: ", rr.Opt)
//机器人数
rr.MaxRobotPlayerCount = 0
//房间最大人数
rr.MaxPlayerCount = 100
// 个人倍率
rr.PersonalRate = rr.Opt.PersonalRate
// 房间倍率
rr.RoomRate = rr.Opt.RoomRate
// 最大轮数
rr.GameMaxRound = 1000 // todo 这里需要从配置获取
// 房间进入条件
rr.RoomLimit = decimal.NewFromFloat(float64(rr.Opt.MinEnterAsset))
// 底注
rr.BetList = rr.Opt.BetAssetOptions
// 区域当前下注
for _, s := range rr.BloodCtrl.AreaConfig {
rr.AreaAmount = append(rr.AreaAmount, protocol.BetAreaInfo{
BetArea: s.AreaID,
})
}
OneWinCount := []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
for i := 0; i < 1000000; i++ {
if i%1000 == 0 {
log.Debug("已经跑了 ", i, " 次了")
}
rr.RepeatRoom()
rr.OnEventGameConclude(2)
//赢得区域
switch rr.PokerMiddle.Color() {
case types.Red:
OneWinCount[protocol.AREA_HONG]++
case types.Black:
OneWinCount[protocol.AREA_HEI]++
case types.Plum:
OneWinCount[protocol.AREA_MEI]++
case types.Diamond:
OneWinCount[protocol.AREA_FANG]++
}
rr.OnBuildOtherResultPoker()
for i, poker := range rr.CardList {
if poker.Point() == rr.PokerMiddle.Point() {
if i%2 == 1 {
OneWinCount[protocol.AREA_BANHAR]++
} else {
OneWinCount[protocol.AREA_ANDAR]++
}
TotalABCount := i + 1
if TotalABCount < 9 {
OneWinCount[protocol.AREA_8]++
} else if TotalABCount < 17 {
OneWinCount[protocol.AREA_16]++
} else if TotalABCount < 25 {
OneWinCount[protocol.AREA_24]++
} else {
OneWinCount[protocol.AREA_99]++
}
break
}
}
}
log.Debug("♥️个数", OneWinCount[protocol.AREA_HONG], " ♠️个数", OneWinCount[protocol.AREA_HEI], " ♣️个数", OneWinCount[protocol.AREA_MEI], " ♦️个数", OneWinCount[protocol.AREA_FANG])
log.Debug("A个数", OneWinCount[protocol.AREA_ANDAR], " B个数", OneWinCount[protocol.AREA_BANHAR])
log.Debug("(1~8)个数", OneWinCount[protocol.AREA_8], " (9~16)个数", OneWinCount[protocol.AREA_16], " (17~24)个数", OneWinCount[protocol.AREA_24], " (>25)个数", OneWinCount[protocol.AREA_99])
}

48
game-server/game/TTZ/main.go

@ -0,0 +1,48 @@
package main
import (
"base/config/file"
"base/eventbus"
"base/eventbus/nats"
"xgame/game/TTZ/app"
"base"
"base/cluster/node"
"base/config"
"base/locate/redis"
"base/log"
"base/log/zap"
"base/registry/consul"
"base/transport/rpcx"
)
func main() {
// 设置日志
log.SetLogger(zap.NewLogger(zap.WithCallerSkip(2)))
// 初始化事件总线
eventbus.SetEventbus(nats.NewEventbus())
// 设置配置中心
config.SetConfigurator(config.NewConfigurator(config.WithSources(file.NewSource())))
// 创建容器
container := base.NewContainer()
// 创建用户定位器
locator := redis.NewLocator()
// 创建服务发现
registry := consul.NewRegistry()
// 创建RPC传输器
transporter := rpcx.NewTransporter(rpcx.WithClientDiscovery(registry))
// 创建网关组件
component := node.NewNode(
node.WithLocator(locator),
node.WithRegistry(registry),
node.WithTransporter(transporter),
)
// 初始化应用
app.Init(component.Proxy())
// 添加网关组件
container.Add(component)
// 启动容器
container.Serve()
}
Loading…
Cancel
Save