40 changed files with 3968 additions and 0 deletions
@ -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 |
|||
} |
@ -0,0 +1,126 @@ |
|||
# 进程号 |
|||
pid = "./run/game.pid" |
|||
# 统一时区设置。项目中的时间获取请使用xtime.Now() |
|||
timezone = "Local" |
|||
|
|||
[cluster] |
|||
[cluster.node] |
|||
# 实例名称 |
|||
name = "TigerSlot" |
|||
# 编解码器。可选: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 |
@ -0,0 +1,41 @@ |
|||
# 以下配置为游戏固定配置,修改必需重启游戏生效 |
|||
|
|||
# 游戏配置(游戏模型基础配置,不可随意变动配置名) |
|||
[game] |
|||
# 游戏ID |
|||
id = 20013 |
|||
# 游戏类型 |
|||
type = 1 |
|||
# 游戏名称 |
|||
name = "TigerSlot" |
|||
# 最小牌桌数 |
|||
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 |
|||
|
|||
|
|||
|
@ -0,0 +1,13 @@ |
|||
package app |
|||
|
|||
import ( |
|||
"base/cluster/node" |
|||
"xgame/game/TigerSlot/app/core" |
|||
"xgame/game/TigerSlot/app/gamemanage" |
|||
) |
|||
|
|||
func Init(proxy *node.Proxy) { |
|||
// 初始化游戏
|
|||
gamemanage.GameProxy = proxy |
|||
core.NewCore(proxy).Init() |
|||
} |
@ -0,0 +1,155 @@ |
|||
package conf |
|||
|
|||
import ( |
|||
"base/config" |
|||
"base/log" |
|||
) |
|||
|
|||
//var (
|
|||
// prizePoolCfg *prizePoolConfig
|
|||
//)
|
|||
|
|||
const ( |
|||
ConfigBloodName = "Tigers" |
|||
fileType = ".json" |
|||
) |
|||
|
|||
type BloodCtrl struct { |
|||
RechargeLimit int `json:"RechargeLimit"` |
|||
MinGameScore int `json:"MinGameScore"` |
|||
ChipList []int `json:"ChipList"` |
|||
Reels struct { |
|||
Matrix [][]int `json:"matrix"` |
|||
Initial [][]int `json:"initial"` |
|||
} `json:"reels"` |
|||
PayLine struct { |
|||
Type int `json:"type"` |
|||
Number int `json:"number"` |
|||
Line [][]int `json:"line"` |
|||
Hit int `json:"hit"` |
|||
Bet []int `json:"bet"` |
|||
} `json:"payLine"` |
|||
Jackpot struct { |
|||
Switch int `json:"switch"` |
|||
Cut int `json:"cut"` |
|||
Ratio1 int `json:"ratio1"` |
|||
Add int `json:"add"` |
|||
Repayment int `json:"repayment"` |
|||
Min int64 `json:"min"` |
|||
Max int64 `json:"max"` |
|||
Ratio2 []int `json:"ratio2"` |
|||
} `json:"jackpot"` |
|||
SymbolComment string `json:"symbolComment"` |
|||
Symbol struct { |
|||
Multiple [][]int `json:"multiple"` |
|||
Wild struct { |
|||
ID int `json:"ID"` |
|||
Replace []int `json:"replace"` |
|||
FiveHit int `json:"fiveHit"` |
|||
ColumnMore int `json:"columnMore"` |
|||
ReelsMore int `json:"reelsMore"` |
|||
} `json:"wild"` |
|||
Scatter struct { |
|||
ID int `json:"ID"` |
|||
Hit int `json:"hit"` |
|||
ColumnMore int `json:"columnMore"` |
|||
ReelsMore int `json:"reelsMore"` |
|||
Number [][]int `json:"number"` |
|||
} `json:"scatter"` |
|||
Bonus struct { |
|||
ID int `json:"ID"` |
|||
Hit int `json:"hit"` |
|||
ColumnMore int `json:"columnMore"` |
|||
ReelsMore int `json:"reelsMore"` |
|||
Number [][]int `json:"number"` |
|||
Value []int `json:"value"` |
|||
ValueWeight []int `json:"valueWeight"` |
|||
CloseNumber []int `json:"closeNumber"` |
|||
CloseNumberWeight []int `json:"closeNumberWeight"` |
|||
} `json:"bonus"` |
|||
Jackpot struct { |
|||
ID int `json:"ID"` |
|||
Hit int `json:"hit"` |
|||
ColumnMore int `json:"columnMore"` |
|||
ReelsMore int `json:"reelsMore"` |
|||
JackPotNo []int `json:"jackPotNo"` |
|||
} `json:"jackpot"` |
|||
} `json:"symbol"` |
|||
Commonly struct { |
|||
Normal struct { |
|||
Win int `json:"win"` |
|||
Range [][]int `json:"range"` |
|||
TriggerWeight []int `json:"triggerWeight"` |
|||
Limit []int `json:"limit"` |
|||
Guarantee []int `json:"guarantee"` |
|||
Generate [][]int `json:"generate"` |
|||
} `json:"normal"` |
|||
Free struct { |
|||
TriggerPro int `json:"triggerPro"` |
|||
TriggerRange [][]int `json:"triggerRange"` |
|||
TriggerWeight []int `json:"triggerWeight"` |
|||
Win int `json:"win"` |
|||
ResultRange [][]int `json:"resultRange"` |
|||
ResultWeight []int `json:"resultWeight"` |
|||
Limit []int `json:"limit"` |
|||
Guarantee []int `json:"guarantee"` |
|||
Generate [][]int `json:"generate"` |
|||
} `json:"free"` |
|||
Bonus struct { |
|||
TriggerPro int `json:"triggerPro"` |
|||
TriggerRange [][]int `json:"triggerRange"` |
|||
TriggerWeight []int `json:"triggerWeight"` |
|||
Win int `json:"win"` |
|||
ResultRange [][]int `json:"resultRange"` |
|||
ResultWeight []int `json:"resultWeight"` |
|||
Limit []int `json:"limit"` |
|||
Guarantee []int `json:"guarantee"` |
|||
Generate [][]int `json:"generate"` |
|||
} `json:"bonus"` |
|||
} `json:"commonly"` |
|||
Win struct { |
|||
Normal struct { |
|||
Win int `json:"win"` |
|||
Range [][]int `json:"range"` |
|||
TriggerWeight []int `json:"triggerWeight"` |
|||
Limit []int `json:"limit"` |
|||
Guarantee []int `json:"guarantee"` |
|||
Generate [][]int `json:"generate"` |
|||
} `json:"normal"` |
|||
Free struct { |
|||
TriggerPro int `json:"triggerPro"` |
|||
TriggerRange [][]int `json:"triggerRange"` |
|||
TriggerWeight []int `json:"triggerWeight"` |
|||
Win int `json:"win"` |
|||
ResultRange [][]int `json:"resultRange"` |
|||
ResultWeight []int `json:"resultWeight"` |
|||
Limit []int `json:"limit"` |
|||
Guarantee []int `json:"guarantee"` |
|||
Generate [][]int `json:"generate"` |
|||
} `json:"free"` |
|||
Bonus struct { |
|||
TriggerPro int `json:"triggerPro"` |
|||
TriggerRange [][]int `json:"triggerRange"` |
|||
TriggerWeight []int `json:"triggerWeight"` |
|||
Win int `json:"win"` |
|||
ResultRange [][]int `json:"resultRange"` |
|||
ResultWeight []int `json:"resultWeight"` |
|||
Limit []int `json:"limit"` |
|||
Guarantee []int `json:"guarantee"` |
|||
Generate [][]int `json:"generate"` |
|||
} `json:"bonus"` |
|||
} `json:"win"` |
|||
} |
|||
|
|||
func ReadBloodConfig() BloodCtrl { |
|||
log.Debug("获取配置 表名", ConfigBloodName) |
|||
var readData BloodCtrl |
|||
err := config.Get(ConfigBloodName).Scan(&readData) |
|||
if err != nil { |
|||
log.Fatalf("Read Tigger BloodConfig err:%v", err) |
|||
} |
|||
|
|||
log.Debug("获取配置成功 ", readData.RechargeLimit) |
|||
|
|||
return readData |
|||
} |
@ -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 |
|||
} |
@ -0,0 +1,7 @@ |
|||
package comm |
|||
|
|||
//销毁房间回调
|
|||
type DestoryCB func(int64) |
|||
|
|||
// 大厅通知回调
|
|||
type NotifyDTUser func(int32, interface{}) |
@ -0,0 +1,88 @@ |
|||
package core |
|||
|
|||
import ( |
|||
"base/cluster" |
|||
"base/cluster/node" |
|||
"base/log" |
|||
"base/task" |
|||
"fmt" |
|||
"xgame/game/TigerSlot/app/core/room" |
|||
"xgame/game/TigerSlot/app/gamemanage" |
|||
"xgame/game/TigerSlot/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.JoinTable, false, gamemanage.JoinTable) // 进入游戏
|
|||
group.AddRouteHandler(route.Bet, false, gamemanage.UserBet) // 下注请求
|
|||
group.AddRouteHandler(route.LogoutTable, false, gamemanage.LogoutTable) // 退出游戏请求
|
|||
group.AddRouteHandler(route.SpecialBet, false, gamemanage.SpecialBet) // 特殊游戏押注 重转+超级重转
|
|||
}) |
|||
|
|||
// 断线重连-好像没用
|
|||
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) |
|||
}) |
|||
} |
|||
|
|||
// 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, "玩家掉线,设置为离线状态") |
|||
} |
|||
}) |
|||
} |
@ -0,0 +1,201 @@ |
|||
package game |
|||
|
|||
import ( |
|||
"base/cluster/node" |
|||
"context" |
|||
"math/rand" |
|||
"sync" |
|||
"time" |
|||
conf "xgame/game/TigerSlot/app/config" |
|||
"xgame/game/TigerSlot/app/core/blood" |
|||
"xgame/game/TigerSlot/app/core/protocol" |
|||
"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 = 60001 //游戏ID
|
|||
MAX_USER_NUM = 1000 //房间最大人数
|
|||
MAX_ROBOT_BUN = 0 //房间最多机器人
|
|||
ICON_LINE_HIGH = 3 //图标最大高度 行数
|
|||
ICON_LINE_WHITH = 3 //图标最大宽度 列数
|
|||
MAX_ICON = 9 //游戏图标最大数量
|
|||
MAX_REELS_COUNT = 6 //上下附带轴数据个数
|
|||
GAME_PRIZE_LINE_MAX = 5 //中奖线数量
|
|||
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 // 结算中
|
|||
) |
|||
|
|||
const ( |
|||
NormalsGame int32 = 0 //普通类型
|
|||
FreeGame int32 = 1 //免费类型
|
|||
ReloadTurn int32 = 2 //重转
|
|||
SuperReloadTurn int32 = 3 //超级重转
|
|||
) |
|||
|
|||
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 //
|
|||
MaxPlayerNum int32 //
|
|||
State int32 //房间状态
|
|||
CurrOperationPlayer int32 //
|
|||
CurrentGameState int32 //游戏阶段
|
|||
CreatorId int64 //创建者 0 主房间 其它表示是为哪个点控玩家生成的房间
|
|||
BoolePool *blood.BoolePool //
|
|||
StockValue int64 //库存值[在初始化房间时候获取,在解散房间时候落地]
|
|||
GronTimer *gron.Cron //
|
|||
CurrentOperationTimer int64 //当前操作时间(单位:毫秒)
|
|||
roomRWLock sync.RWMutex //
|
|||
IsP2pControls bool //
|
|||
BetList []int64 //筹码列表
|
|||
|
|||
Record []protocol.HistoryRecord //历史记录
|
|||
BloodCtrl conf.BloodCtrl //血池控制
|
|||
isUserCtl bool //本桌是否人物点控
|
|||
UserCtlGear int64 //人物点控档位
|
|||
|
|||
MinGameScore int64 //最低能玩的金额
|
|||
|
|||
// 计算用的
|
|||
HeepPrizePL int // 累计倍数
|
|||
LineArr []int // 中奖线 每条线中了几个
|
|||
LineDataArr []int // 中奖线 每条线中了啥
|
|||
PrizePos []int // 中奖位置
|
|||
} |
|||
|
|||
// 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 { |
|||
count++ |
|||
} |
|||
} |
|||
return count |
|||
} |
|||
|
|||
// GetRoomPlayerCount 房间人数
|
|||
func (rr *Room) GetRoomPlayerCount() int32 { |
|||
return int32(len(rr.PlayerMap)) |
|||
} |
|||
|
|||
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 |
|||
} |
|||
} |
|||
|
|||
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 |
|||
} |
|||
|
|||
func (rr *Room) OnGetGameId() int32 { |
|||
return rr.GameID |
|||
} |
|||
|
|||
func (rr *Room) DeepCopy(s []int) []int { //深拷贝
|
|||
if s == nil { |
|||
return nil |
|||
} |
|||
c := make([]int, len(s)) |
|||
copy(c, s) |
|||
return c |
|||
} |
|||
|
|||
func (rr *Room) shuffleDeck(deck *[]int) { // 乱序函数
|
|||
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] // 交换
|
|||
} |
|||
} |
@ -0,0 +1,203 @@ |
|||
package game |
|||
|
|||
import ( |
|||
"base/log" |
|||
"github.com/shopspring/decimal" |
|||
"strconv" |
|||
"time" |
|||
"xgame/game/TigerSlot/app/core/protocol" |
|||
"xgame/game/TigerSlot/app/route" |
|||
"xgame/game/service/rpc_client" |
|||
) |
|||
|
|||
func (rr *Room) OnUserBet(uid int64, msg interface{}) { // 普通旋转
|
|||
message := msg.(*protocol.BetReq) |
|||
GameResultRes := &protocol.GameResultRes{} |
|||
|
|||
ChairId := int32(-1) |
|||
for _, player := range rr.PlayerMap { |
|||
if player.Uid == uid { |
|||
ChairId = player.ChairId |
|||
break |
|||
} |
|||
} |
|||
|
|||
if ChairId == -1 { |
|||
log.Debug("ChairId == -1") |
|||
return |
|||
} |
|||
|
|||
// 下注区域判断
|
|||
player := rr.PlayerMap[ChairId] |
|||
|
|||
if player.GameState != protocol.GameStatusNormal { //玩家是否为有效状态
|
|||
log.Debug("玩家", ChairId, " 用户状态值", player.GameState, " 需要游戏状态: GameStatusNormal") |
|||
GameResultRes.Code = protocol.ErrorCodeBetError |
|||
GameResultRes.CodeMsg = "当前是普通旋转 不是特殊旋转 " |
|||
rr.Send(player, route.GameResultRes, GameResultRes) |
|||
return |
|||
} |
|||
|
|||
if message.Level < 0 || message.Level > len(rr.Opt.BetAssetOptions) { //玩家押注错误(下注下标)
|
|||
log.Debug("玩家", ChairId, " 下注下标", message.Level, " 实际只有: ", rr.Opt.BetAssetOptions) |
|||
GameResultRes.Code = protocol.ErrorCodeBetError |
|||
GameResultRes.CodeMsg = "下注下标错误 " + strconv.Itoa(message.Level) |
|||
rr.Send(player, route.GameResultRes, GameResultRes) |
|||
return |
|||
} |
|||
|
|||
milliseconds := time.Now().UnixMilli() |
|||
if milliseconds-player.BetTick < 600 { // 操作过快
|
|||
log.Debug("玩家", ChairId, " 操作过快 上次操作时间", player.BetTick, " 当前时间: ", milliseconds) |
|||
GameResultRes.Code = protocol.ErrorCodeBetError |
|||
GameResultRes.CodeMsg = "操作过快 " |
|||
rr.Send(player, route.GameResultRes, GameResultRes) |
|||
return |
|||
} |
|||
|
|||
player.BetLevel = message.Level |
|||
player.BetTotal = rr.Opt.BetAssetOptions[message.Level] * GAME_PRIZE_LINE_MAX |
|||
if player.BetTotal > rr.PlayerMap[ChairId].Gold.IntPart() { // 钱不够
|
|||
log.Debug("玩家", ChairId, "个人下注总值", player.BetTotal, " 拥有的钱: ", rr.PlayerMap[ChairId].Gold.IntPart()) |
|||
GameResultRes.Code = protocol.ErrorCodeBetError |
|||
GameResultRes.CodeMsg = "玩家最多再下注 " + strconv.Itoa(int(rr.PlayerMap[ChairId].Gold.IntPart())) |
|||
rr.Send(player, route.GameResultRes, GameResultRes) |
|||
return |
|||
} |
|||
|
|||
//点控
|
|||
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) |
|||
} |
|||
} |
|||
} |
|||
|
|||
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) |
|||
player.CurrentGears = CurrentGears |
|||
|
|||
var tempIcon []int |
|||
GameResult := rr.NormalGameResult(&tempIcon, &player.ReloadIcons, player.CurrentGears, int(NormalsGame), rr.Opt.BetAssetOptions[message.Level], player.MissCount, player.LossCount, true) |
|||
player.ReloadID = GameResult.ReloadID |
|||
player.BetTick = milliseconds |
|||
player.WinScore += int64(GameResult.HeepPrizePL) * player.BetTotal |
|||
|
|||
GameResultRes.IconData = tempIcon |
|||
GameResultRes.BonusIconData = player.ReloadIcons |
|||
GameResultRes.PrizeLine = GameResult.LineArr |
|||
GameResultRes.SpecialIconData = GameResult.SpecialIconData |
|||
GameResultRes.PlayerGold = player.Gold.IntPart() |
|||
GameResultRes.DisplayGold = player.WinScore |
|||
log.Debug("中奖图标 ", GameResult.LineDataArr, " 中奖个数 ", GameResult.LineArr) |
|||
|
|||
if player.WinScore > 0 { |
|||
TempRateAfter := int64(float64(player.WinScore-player.BetTotal)*(1-rr.Opt.BrightRate)) + player.BetTotal |
|||
player.Gold = player.Gold.Add(decimal.NewFromInt(TempRateAfter)) |
|||
} else { |
|||
player.Gold = player.Gold.Sub(decimal.NewFromInt(player.BetTotal)) |
|||
} |
|||
|
|||
if !player.IsRobot && (player.Ctx != nil) { |
|||
AfterGold, BloodPool, StockNum := rr.WriteUserScore(player, 2, player.BetTotal, 0, player.WinScore, rr.ReasonString, "", "", 0) |
|||
log.Debug("WriteUserScore 后 AfterGold=", AfterGold, " BloodPool=", BloodPool, " StockNum=", StockNum) |
|||
} |
|||
|
|||
rr.Send(player, route.GameResultRes, GameResultRes) |
|||
} |
|||
|
|||
func (rr *Room) OnUserSpecialBet(uid int64, msg interface{}) { |
|||
GameResultRes := &protocol.GameResultRes{} |
|||
|
|||
ChairId := int32(-1) |
|||
for _, player := range rr.PlayerMap { |
|||
if player.Uid == uid { |
|||
ChairId = player.ChairId |
|||
break |
|||
} |
|||
} |
|||
|
|||
if ChairId == -1 { |
|||
log.Debug("ChairId == -1") |
|||
return |
|||
} |
|||
|
|||
// 下注区域判断
|
|||
player := rr.PlayerMap[ChairId] |
|||
|
|||
if player.GameState != protocol.GameStatusJK { //玩家是否为有效状态
|
|||
log.Debug("玩家", ChairId, " 用户状态值", player.GameState, " 需要游戏状态: GameStatusJK") |
|||
GameResultRes.Code = protocol.ErrorCodeBetError |
|||
GameResultRes.CodeMsg = "当前是普通旋转 不是特殊旋转 " |
|||
rr.Send(player, route.GameResultRes, GameResultRes) |
|||
return |
|||
} |
|||
|
|||
milliseconds := time.Now().UnixMilli() |
|||
if milliseconds-player.BetTick < 600 { // 操作过快
|
|||
log.Debug("玩家", ChairId, " 操作过快 上次操作时间", player.BetTick, " 当前时间: ", milliseconds) |
|||
GameResultRes.Code = protocol.ErrorCodeBetError |
|||
GameResultRes.CodeMsg = "操作过快 " |
|||
rr.Send(player, route.GameResultRes, GameResultRes) |
|||
return |
|||
} |
|||
|
|||
rr.GetReloadIcon(player) |
|||
player.BetTick = milliseconds |
|||
player.WinScore += int64(rr.HeepPrizePL) * player.BetTotal |
|||
|
|||
GameResultRes.BonusIconData = player.ReloadIcons |
|||
GameResultRes.PrizeLine = rr.LineArr |
|||
GameResultRes.PlayerGold = player.Gold.IntPart() |
|||
GameResultRes.DisplayGold = player.WinScore |
|||
log.Debug("中奖图标 ", rr.LineDataArr, " 中奖个数 ", rr.LineArr) |
|||
|
|||
// 如果没有新的倍数
|
|||
if rr.HeepPrizePL == player.HeepPrizePL { |
|||
if player.WinScore > 0 { |
|||
TempRateAfter := int64(float64(player.WinScore-player.BetTotal)*(1-rr.Opt.BrightRate)) + player.BetTotal |
|||
player.Gold = player.Gold.Add(decimal.NewFromInt(TempRateAfter)) |
|||
} else { |
|||
player.Gold = player.Gold.Sub(decimal.NewFromInt(player.BetTotal)) |
|||
} |
|||
|
|||
if !player.IsRobot && (player.Ctx != nil) { |
|||
AfterGold, BloodPool, StockNum := rr.WriteUserScore(player, 2, player.BetTotal, 0, player.WinScore, rr.ReasonString, "", "", 0) |
|||
log.Debug("WriteUserScore 后 AfterGold=", AfterGold, " BloodPool=", BloodPool, " StockNum=", StockNum) |
|||
} |
|||
} |
|||
|
|||
player.HeepPrizePL = rr.HeepPrizePL |
|||
rr.Send(player, route.GameResultRes, GameResultRes) |
|||
} |
@ -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 |
|||
} |
@ -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 |
|||
} |
@ -0,0 +1,18 @@ |
|||
package game |
|||
|
|||
import ( |
|||
"base/log" |
|||
conf "xgame/game/TigerSlot/app/config" |
|||
) |
|||
|
|||
func (rr *Room) InitBloodControl() { |
|||
rr.BloodCtrl = conf.ReadBloodConfig() |
|||
|
|||
log.Debug("InitBloodControl: ChipList=", rr.BloodCtrl.ChipList) |
|||
|
|||
if rr.BloodCtrl.MinGameScore == 0 { |
|||
rr.MinGameScore = MIN_GAME_SCORE |
|||
} else { |
|||
rr.MinGameScore = int64(rr.BloodCtrl.MinGameScore) |
|||
} |
|||
} |
@ -0,0 +1,25 @@ |
|||
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 |
|||
} |
|||
} |
|||
} |
|||
|
|||
rr.roomRWLock.Unlock() |
|||
} |
@ -0,0 +1,10 @@ |
|||
package game |
|||
|
|||
func (rr *Room) IsCanDissolution() bool { // IsCanDissolution是否能解散房间
|
|||
|
|||
return false |
|||
} |
|||
|
|||
func (rr *Room) OnDestroy(uid int64) { |
|||
rr.DestroyRoomFunc(uid) |
|||
} |
@ -0,0 +1,63 @@ |
|||
package game |
|||
|
|||
import ( |
|||
"base/cluster/node" |
|||
"base/log" |
|||
"time" |
|||
"xgame/game/TigerSlot/app/core/protocol" |
|||
"xgame/game/TigerSlot/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.RoomConfig = &protocol.TableConfig{ |
|||
RechargeLimit: int64(rr.BloodCtrl.RechargeLimit), |
|||
TaxMing: rr.Opt.BrightRate, |
|||
BetList: rr.Opt.BetAssetOptions, |
|||
LineData: rr.BloodCtrl.PayLine.Line, |
|||
ReelsConfig: rr.BloodCtrl.Commonly.Normal.Generate, |
|||
} |
|||
|
|||
for _, player := range rr.PlayerMap { |
|||
if player.Uid == uid { |
|||
pPlayer = player |
|||
TableInfo.PlayerInfo.Uid = player.Uid |
|||
TableInfo.PlayerInfo.ChairID = player.ChairId |
|||
TableInfo.PlayerInfo.State = rr.State |
|||
TableInfo.PlayerInfo.Avatar = player.Avatar |
|||
TableInfo.PlayerInfo.Gold = player.Gold.IntPart() |
|||
TableInfo.PlayerInfo.Nickname = player.Nickname |
|||
TableInfo.PlayerInfo.Sex = player.Sex |
|||
TableInfo.PlayerInfo.Vip = 0 |
|||
TableInfo.PlayerInfo.Level = player.Level |
|||
TableInfo.PlayerInfo.Exp = 0 |
|||
TableInfo.PlayerInfo.BetScore = player.BetScore |
|||
TableInfo.PlayerInfo.EndScore = player.WinScore |
|||
break |
|||
} |
|||
} |
|||
|
|||
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) |
|||
} |
|||
} |
|||
|
|||
pPlayer.Ctx = ctx |
|||
rr.SendTableInfo(pPlayer, route.RoomInfoRes, TableInfo) |
|||
|
|||
// 发送完桌子消息 再激活用户状态
|
|||
pPlayer.OnlineState = USER_ON_LINE |
|||
|
|||
return TableInfo |
|||
} |
@ -0,0 +1,130 @@ |
|||
package game |
|||
|
|||
import ( |
|||
"base/encoding/json" |
|||
"base/log" |
|||
"strconv" |
|||
"time" |
|||
"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.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.State = EN_TABLE_STATE_READY_TO_START |
|||
|
|||
return true |
|||
} |
@ -0,0 +1,104 @@ |
|||
package game |
|||
|
|||
import ( |
|||
"base/log" |
|||
"github.com/shopspring/decimal" |
|||
"xgame/game/TigerSlot/app/core/protocol" |
|||
"xgame/game/TigerSlot/app/route" |
|||
) |
|||
|
|||
// JoinRoom 玩家加入房间 0==加入房间成功,-1==满员了
|
|||
func (rr *Room) JoinRoom(pPlayer *Player) 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)) { |
|||
return -1 |
|||
} |
|||
|
|||
//rr.RoomLimit
|
|||
|
|||
pPlayer.ChairId = chairId |
|||
pPlayer.OnlineState = USER_LEAVE |
|||
pPlayer.IsCheat = 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{}, |
|||
} |
|||
|
|||
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 |
|||
|
|||
rr.PlayerMap[pPlayer.ChairId] = pPlayer |
|||
|
|||
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 |
|||
} |
@ -0,0 +1,676 @@ |
|||
package game |
|||
|
|||
import ( |
|||
"base/cluster/node" |
|||
"base/log" |
|||
"math/rand" |
|||
"xgame/game/TigerSlot/app/core/protocol" |
|||
) |
|||
|
|||
// 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 |
|||
} |
|||
} |
|||
} |
|||
|
|||
func (rr *Room) run() { |
|||
|
|||
// milliseconds := time.Now().UnixMilli()
|
|||
|
|||
if rr.State == EN_TABLE_STATE_UNKNOWN { |
|||
return |
|||
} |
|||
|
|||
switch rr.CurrentGameState { |
|||
|
|||
} |
|||
} |
|||
|
|||
func (rr *Room) Send(player *Player, msgId int32, message interface{}) { |
|||
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) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func (rr *Room) SendTableInfo(player *Player, msgId int32, message interface{}) { |
|||
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) |
|||
} |
|||
} |
|||
} |
|||
|
|||
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) |
|||
} |
|||
} |
|||
}() |
|||
} |
|||
|
|||
func (rr *Room) CalWeightSum(Weight []int) int { //计算权重之和
|
|||
sum := 0 |
|||
for _, i2 := range Weight { |
|||
sum += i2 |
|||
} |
|||
return sum |
|||
} |
|||
|
|||
func (rr *Room) GetGuaranteeResultNo(max int, min int) int { //计算权重之和
|
|||
RandNum := 0 |
|||
Multiple := 0 |
|||
count := 0 |
|||
for i := 0; i < 1000; i++ { |
|||
if count > 100 { |
|||
break |
|||
} |
|||
RandNum = rand.Intn(max-min) + min |
|||
Multiple = MyResultSet[RandNum].Multiple |
|||
count++ |
|||
|
|||
if Multiple >= len(rr.BloodCtrl.PayLine.Line) { |
|||
break |
|||
} |
|||
} |
|||
|
|||
return RandNum |
|||
} |
|||
|
|||
func (rr *Room) GetRealSlotData(Icons *[]int, SpecialIconData *[]int, realPoints []int, CurrentGears int) { //计算权重之和
|
|||
CurrentGearConfig := rr.BloodCtrl.Commonly |
|||
switch CurrentGears { |
|||
case 3000: |
|||
CurrentGearConfig = rr.BloodCtrl.Win |
|||
case 2000: |
|||
CurrentGearConfig = rr.BloodCtrl.Win |
|||
case 1000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -1000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -2000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -3000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
} |
|||
|
|||
*SpecialIconData = (*SpecialIconData)[:0] |
|||
Generate := CurrentGearConfig.Normal.Generate |
|||
|
|||
for i, point := range realPoints { |
|||
GenerateCount := len(Generate[i]) |
|||
TempPos := point |
|||
TempSpecialPos := TempPos - MAX_USER_NUM |
|||
|
|||
for j := 0; j < ICON_LINE_HIGH; j++ { |
|||
(*Icons)[rr.BloodCtrl.Reels.Matrix[j][i]-1] = Generate[i][TempPos] |
|||
TempPos++ |
|||
if TempPos == GenerateCount { |
|||
TempPos = 0 |
|||
} |
|||
} |
|||
|
|||
//生成上下数据
|
|||
if TempSpecialPos < 0 { |
|||
TempSpecialPos += GenerateCount |
|||
} |
|||
|
|||
for j := 0; j < ICON_LINE_HIGH+MAX_REELS_COUNT*2; j++ { |
|||
*SpecialIconData = append(*SpecialIconData, Generate[i][TempSpecialPos]) |
|||
TempSpecialPos++ |
|||
if TempSpecialPos == GenerateCount { |
|||
TempSpecialPos = 0 |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
func (rr *Room) CheckReplace(icon int) bool { //是否能用百搭替换
|
|||
for _, ReplaceID := range rr.BloodCtrl.Symbol.Wild.Replace { |
|||
if icon == ReplaceID { |
|||
return true |
|||
} |
|||
} |
|||
|
|||
return false |
|||
} |
|||
|
|||
func (rr *Room) GetReloadIconsFromResultSet(ReloadIcons *[]int, CurrentGears int) int { //根据权重取出重转结果集内容
|
|||
CurrentGearConfig := rr.BloodCtrl.Commonly |
|||
switch CurrentGears { |
|||
case 3000: |
|||
CurrentGearConfig = rr.BloodCtrl.Win |
|||
case 2000: |
|||
CurrentGearConfig = rr.BloodCtrl.Win |
|||
case 1000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -1000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -2000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -3000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
} |
|||
|
|||
weightSum := 0 |
|||
for _, ResultWeight := range CurrentGearConfig.Bonus.ResultWeight { |
|||
weightSum += ResultWeight |
|||
} |
|||
randTotal := rand.Intn(weightSum) |
|||
ResultID := -1 |
|||
for Index1, ResultWeight := range CurrentGearConfig.Bonus.ResultWeight { |
|||
if randTotal <= ResultWeight { |
|||
Min := CurrentGearConfig.Bonus.ResultRange[Index1][0] |
|||
Max := CurrentGearConfig.Bonus.ResultRange[Index1][1] |
|||
tRandTotal := rand.Intn(Max-Min+1) + Min |
|||
|
|||
for Lie, Pos := range MyResultSet[tRandTotal].Result { |
|||
GenerateCount := len(CurrentGearConfig.Normal.Generate[Lie]) |
|||
TempPos := Pos |
|||
TempSpecialPos := TempPos - MAX_REELS_COUNT |
|||
|
|||
for j := 0; j < ICON_LINE_HIGH; j++ { |
|||
(*ReloadIcons)[rr.BloodCtrl.Reels.Matrix[j][Lie]] = CurrentGearConfig.Normal.Generate[Lie][TempPos] |
|||
TempPos++ |
|||
if TempPos == GenerateCount { |
|||
TempPos = 0 |
|||
} |
|||
} |
|||
|
|||
//生成上下数据
|
|||
if TempSpecialPos < 0 { |
|||
TempSpecialPos += GenerateCount |
|||
} |
|||
} |
|||
|
|||
ResultID = tRandTotal + 1 |
|||
|
|||
break |
|||
} else { |
|||
randTotal -= ResultWeight |
|||
} |
|||
} |
|||
|
|||
return ResultID |
|||
} |
|||
|
|||
func (rr *Room) GetReloadTriggerFromResultSet(Icons *[]int, CurrentGears int) int { //根据权重取出重转触发结果集
|
|||
CurrentGearConfig := rr.BloodCtrl.Commonly |
|||
switch CurrentGears { |
|||
case 3000: |
|||
CurrentGearConfig = rr.BloodCtrl.Win |
|||
case 2000: |
|||
CurrentGearConfig = rr.BloodCtrl.Win |
|||
case 1000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -1000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -2000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -3000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
} |
|||
|
|||
weightSum := 0 |
|||
for _, TriggerWeight := range CurrentGearConfig.Bonus.TriggerWeight { |
|||
weightSum += TriggerWeight |
|||
} |
|||
randTotal := rand.Intn(weightSum) |
|||
ResultID := -1 |
|||
for Index1, TriggerWeight := range CurrentGearConfig.Bonus.TriggerWeight { |
|||
if randTotal <= TriggerWeight { |
|||
Min := CurrentGearConfig.Bonus.TriggerRange[Index1][0] |
|||
Max := CurrentGearConfig.Bonus.TriggerRange[Index1][1] |
|||
tRandTotal := rand.Intn(Max-Min+1) + Min |
|||
|
|||
for Lie, Pos := range MyResultSet[tRandTotal].Result { |
|||
GenerateCount := len(CurrentGearConfig.Normal.Generate[Lie]) |
|||
TempPos := Pos |
|||
TempSpecialPos := TempPos - MAX_REELS_COUNT |
|||
|
|||
for j := 0; j < ICON_LINE_HIGH; j++ { |
|||
(*Icons)[rr.BloodCtrl.Reels.Matrix[j][Lie]] = CurrentGearConfig.Normal.Generate[Lie][TempPos] |
|||
TempPos++ |
|||
if TempPos == GenerateCount { |
|||
TempPos = 0 |
|||
} |
|||
} |
|||
|
|||
//生成上下数据
|
|||
if TempSpecialPos < 0 { |
|||
TempSpecialPos += GenerateCount |
|||
} |
|||
} |
|||
|
|||
ResultID = tRandTotal + 1 |
|||
|
|||
break |
|||
} else { |
|||
randTotal -= TriggerWeight |
|||
} |
|||
} |
|||
|
|||
return ResultID |
|||
} |
|||
|
|||
func (rr *Room) GetBonusPos(Icons *[]int) []int { //统计sBonus图标的位置
|
|||
var Pos []int |
|||
for i := 0; i < MAX_ICON; i++ { |
|||
if (*Icons)[i] == rr.BloodCtrl.Symbol.Bonus.ID { |
|||
Pos = append(Pos, i) |
|||
} |
|||
} |
|||
|
|||
return Pos |
|||
} |
|||
|
|||
func (rr *Room) CheckReloadResultTriggerVal(Icons *[]int, ReloadIcon *[]int) bool { //检测触发重转与最终重转匹配
|
|||
bVal := false |
|||
Pos := rr.GetBonusPos(Icons) |
|||
Pos2 := rr.GetBonusPos(ReloadIcon) |
|||
PosNum := 0 |
|||
|
|||
if len(Pos2) >= 6 { |
|||
for i := 0; i < 15; i++ { |
|||
if (*ReloadIcon)[i] == rr.BloodCtrl.Symbol.Bonus.ID && PosNum < 6 { |
|||
PosNum++ |
|||
(*Icons)[i] = rr.BloodCtrl.Symbol.Bonus.ID |
|||
} else { |
|||
(*Icons)[i] = rand.Intn(9) + 1 |
|||
} |
|||
} |
|||
} |
|||
|
|||
if len(Pos2) == len(Pos) { |
|||
bVal = true |
|||
} |
|||
|
|||
return bVal |
|||
} |
|||
|
|||
func (rr *Room) GetReloadResultFromResultSet(Icons *[]int, ReloadIcon *[]int, GearID int) int { //从结果集中获取匹配重转结果,返回结果集ID
|
|||
ResultID := -1 |
|||
Count := 0 |
|||
for i := 0; i < 1000; i++ { |
|||
*Icons = (*Icons)[:0] |
|||
if Count > 2000 { |
|||
break |
|||
} |
|||
ResultID = rr.GetReloadTriggerFromResultSet(Icons, GearID) |
|||
Count++ |
|||
|
|||
if !rr.CheckReloadResultTriggerVal(Icons, ReloadIcon) { |
|||
break |
|||
} |
|||
} |
|||
|
|||
if Count > 2000 { |
|||
ResultID = -1 |
|||
} |
|||
return ResultID |
|||
} |
|||
|
|||
func (rr *Room) CheckDataLimit(tempIcon *[]int, CurrentGears int, GameType int) bool { |
|||
CurrentGearConfig := rr.BloodCtrl.Commonly |
|||
switch CurrentGears { |
|||
case 3000: |
|||
CurrentGearConfig = rr.BloodCtrl.Win |
|||
case 2000: |
|||
CurrentGearConfig = rr.BloodCtrl.Win |
|||
case 1000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -1000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -2000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -3000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
} |
|||
//中奖线最高倍数检测
|
|||
Min := 0 |
|||
Max := 0 |
|||
switch int32(GameType) { |
|||
case NormalsGame: |
|||
Min = CurrentGearConfig.Normal.Limit[0] |
|||
Max = CurrentGearConfig.Normal.Limit[1] |
|||
break |
|||
case FreeGame: |
|||
Min = CurrentGearConfig.Free.Limit[0] |
|||
Max = CurrentGearConfig.Free.Limit[1] |
|||
break |
|||
case ReloadTurn: |
|||
Min = CurrentGearConfig.Bonus.Limit[0] |
|||
Max = CurrentGearConfig.Bonus.Limit[1] |
|||
break |
|||
default: |
|||
Min = CurrentGearConfig.Normal.Limit[0] |
|||
Max = CurrentGearConfig.Normal.Limit[1] |
|||
break |
|||
} |
|||
|
|||
if Max != 0 && rr.HeepPrizePL > Max { |
|||
return false |
|||
} |
|||
|
|||
if rr.HeepPrizePL < Min { |
|||
return false |
|||
} |
|||
return true |
|||
} |
|||
|
|||
func (rr *Room) GetPrizeLine(tempIcon []int, LineArr *[]int, PrizePos *[]int) bool { //中奖线
|
|||
isPrize := false |
|||
rr.HeepPrizePL = 0 |
|||
var tIcon []int |
|||
|
|||
for _, i2 := range tempIcon { |
|||
tIcon = append(tIcon, i2) |
|||
} |
|||
|
|||
switch rr.BloodCtrl.PayLine.Type { |
|||
case 1: |
|||
case 2: |
|||
for i := 0; i < len(rr.BloodCtrl.PayLine.Line); i++ { |
|||
//依次获得每条中奖线x1~X5的数据
|
|||
var pos []int |
|||
var tempLine []int |
|||
|
|||
for j := 0; j < MAX_REELS_COUNT; j++ { |
|||
pos = append(pos, -1) |
|||
tempLine = append(tempLine, 0) |
|||
} |
|||
|
|||
if rr.BloodCtrl.PayLine.Hit == 1 || rr.BloodCtrl.PayLine.Hit == 3 { |
|||
for mx := 0; mx < len(rr.BloodCtrl.PayLine.Line[0]); mx++ { |
|||
pos[mx] = rr.BloodCtrl.PayLine.Line[i][mx] |
|||
tempLine[mx] = tIcon[pos[mx]-1] |
|||
} |
|||
|
|||
// 获得中奖线第一个数据
|
|||
firstData := tempLine[0] |
|||
|
|||
// 相同图标数量
|
|||
tag := 0 |
|||
if firstData != rr.BloodCtrl.Symbol.Scatter.ID || firstData != rr.BloodCtrl.Symbol.Bonus.ID { |
|||
for j := 1; j < len(rr.BloodCtrl.PayLine.Line[0]); j++ { |
|||
if rr.BloodCtrl.Symbol.Wild.ID == firstData { |
|||
if rr.CheckReplace(tempLine[j]) || tempLine[j] == rr.BloodCtrl.Symbol.Wild.ID { |
|||
firstData = tempLine[j] |
|||
tag++ |
|||
continue |
|||
} |
|||
} else { |
|||
break |
|||
} |
|||
|
|||
//相同图标检测
|
|||
if tempLine[j] == firstData || (tempLine[j] == rr.BloodCtrl.Symbol.Wild.ID && rr.CheckReplace(firstData)) { |
|||
tag++ |
|||
} else { |
|||
break |
|||
} |
|||
} |
|||
|
|||
//判断五连百搭
|
|||
if tag == 5 && firstData == rr.BloodCtrl.Symbol.Wild.ID { |
|||
if rr.BloodCtrl.Symbol.Wild.FiveHit != 1 { |
|||
continue |
|||
} |
|||
} |
|||
|
|||
//根据图标数量获取配置表对应倍数
|
|||
beiShu := rr.BloodCtrl.Symbol.Multiple[firstData-1][tag] |
|||
|
|||
if beiShu != 0 { |
|||
//中奖位置记录
|
|||
for psp := 0; psp <= tag; psp++ { |
|||
(*PrizePos)[pos[psp]] = tempLine[psp] |
|||
} |
|||
(*LineArr)[i] = tag + 1 |
|||
rr.LineDataArr[i] = tempLine[0] |
|||
rr.HeepPrizePL += beiShu |
|||
isPrize = true |
|||
} |
|||
} |
|||
} |
|||
} |
|||
case 3: |
|||
} |
|||
|
|||
return isPrize |
|||
} |
|||
|
|||
func (rr *Room) BrithIConData(tempIcon *[]int, ReloadIcon *[]int, SpecialIconData *[]int, CurrentGears int, GameType int, Times int, TotalTimes int, Trigger bool) int { |
|||
CurrentGearConfig := rr.BloodCtrl.Commonly |
|||
switch CurrentGears { |
|||
case 3000: |
|||
CurrentGearConfig = rr.BloodCtrl.Win |
|||
case 2000: |
|||
CurrentGearConfig = rr.BloodCtrl.Win |
|||
case 1000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -1000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -2000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
case -3000: |
|||
CurrentGearConfig = rr.BloodCtrl.Commonly |
|||
} |
|||
|
|||
NeedGameType := 0 // 强行控制 0不控制 1不中奖 2必须中奖 3必须大于总压的中奖
|
|||
weightSum := 0 |
|||
randTotal := 0 |
|||
TempPos := 0 |
|||
TempSpecialPos := 0 |
|||
GenerateCount := 0 |
|||
ReloadID := 0 |
|||
|
|||
switch int32(GameType) { |
|||
case NormalsGame: |
|||
//触发重转
|
|||
if rand.Intn(10000) <= CurrentGearConfig.Bonus.TriggerPro { |
|||
*ReloadIcon = (*ReloadIcon)[:0] |
|||
ResultID := rr.GetReloadIconsFromResultSet(ReloadIcon, CurrentGears) |
|||
TriggerID := rr.GetReloadResultFromResultSet(tempIcon, ReloadIcon, CurrentGears) |
|||
|
|||
if TriggerID > 0 { |
|||
ReloadID = ResultID |
|||
} else { |
|||
*ReloadIcon = (*ReloadIcon)[:0] |
|||
NeedGameType = 1 |
|||
|
|||
} |
|||
} |
|||
|
|||
if Times >= CurrentGearConfig.Normal.Guarantee[0] { |
|||
NeedGameType = 2 |
|||
} else if TotalTimes >= CurrentGearConfig.Normal.Guarantee[1] { |
|||
NeedGameType = 3 |
|||
} |
|||
|
|||
//判断是否中奖
|
|||
if NeedGameType == 0 && rand.Intn(10000) > CurrentGearConfig.Normal.Win { |
|||
log.Debug("档位:", CurrentGears, "游戏类型:", GameType, " 普通不中奖") |
|||
NeedGameType = 1 |
|||
break |
|||
} else { |
|||
//NeedGameType = 0;
|
|||
weightSum = rr.CalWeightSum(CurrentGearConfig.Normal.TriggerWeight) |
|||
randTotal = rand.Intn(weightSum) |
|||
for i, i2 := range CurrentGearConfig.Normal.TriggerWeight { |
|||
if randTotal <= i2 { |
|||
MinRange := CurrentGearConfig.Normal.Range[i][0] |
|||
MaxRange := CurrentGearConfig.Normal.Range[i][1] |
|||
tRandTotal := rand.Intn(MaxRange-MinRange+1) + MinRange |
|||
|
|||
log.Debug("普通游戏中奖旋转:", tRandTotal) |
|||
|
|||
if NeedGameType == 3 { |
|||
//获取保底
|
|||
tRandTotal = rr.GetGuaranteeResultNo(MinRange, MaxRange+1) |
|||
} |
|||
rr.GetRealSlotData(tempIcon, SpecialIconData, MyResultSet[tRandTotal].Result, CurrentGears) |
|||
|
|||
break |
|||
} else { |
|||
randTotal -= i2 |
|||
} |
|||
} |
|||
NeedGameType = 0 |
|||
} |
|||
} |
|||
|
|||
log.Debug("111ReloadID ", ReloadID) |
|||
|
|||
// 不控制直接返回
|
|||
if NeedGameType == 0 { |
|||
if ReloadID > 0 { |
|||
return ReloadID |
|||
} |
|||
return 0 |
|||
} |
|||
|
|||
Generate := CurrentGearConfig.Normal.Generate |
|||
for x := 0; x < 100; x++ { |
|||
*SpecialIconData = (*SpecialIconData)[:0] |
|||
|
|||
for i := 0; i < ICON_LINE_WHITH; i++ { |
|||
switch int32(GameType) { |
|||
case NormalsGame: |
|||
GenerateCount = len(CurrentGearConfig.Normal.Generate[i]) |
|||
|
|||
TempPos = rand.Intn(GenerateCount) |
|||
TempSpecialPos = TempPos - MAX_REELS_COUNT |
|||
for j := 0; j < ICON_LINE_HIGH; j++ { |
|||
(*tempIcon)[rr.BloodCtrl.Reels.Matrix[j][i]-1] = Generate[i][TempPos] |
|||
TempPos++ |
|||
if TempPos == GenerateCount { |
|||
TempPos = 0 |
|||
} |
|||
|
|||
//生成上下数据
|
|||
if TempSpecialPos < 0 { |
|||
TempSpecialPos += GenerateCount |
|||
} |
|||
|
|||
for j := 0; j < ICON_LINE_HIGH+MAX_REELS_COUNT*2; j++ { |
|||
*SpecialIconData = append(*SpecialIconData, Generate[i][TempSpecialPos]) |
|||
TempSpecialPos++ |
|||
if TempSpecialPos == GenerateCount { |
|||
TempSpecialPos = 0 |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
func (rr *Room) NormalGameResult(tempIcon *[]int, ReloadIcon *[]int, CurrentGears int, GameType int, Bet int64, Times int, TotalTimes int, Trigger bool) protocol.LGameResult { |
|||
GameResult := protocol.LGameResult{ |
|||
Result: false, |
|||
} |
|||
|
|||
//图标生成
|
|||
bDataBirth := true |
|||
bSpecialGame := false |
|||
BrithNumber := 0 |
|||
|
|||
for i := 0; i < 1000; i++ { |
|||
bDataBirth = true |
|||
BrithNumber++ |
|||
|
|||
//生成游戏数据
|
|||
GameResult.SpecialIconData = GameResult.SpecialIconData[:0] |
|||
ReloadID := rr.BrithIConData(tempIcon, ReloadIcon, &GameResult.SpecialIconData, CurrentGears, GameType, Times, TotalTimes, Trigger) |
|||
|
|||
if ReloadID > 0 { |
|||
GameResult.ReloadID = ReloadID |
|||
} |
|||
//判断是否中奖
|
|||
rr.LineArr = rr.LineArr[:0] |
|||
rr.LineDataArr = rr.LineDataArr[:0] |
|||
rr.PrizePos = rr.PrizePos[:0] |
|||
rr.GetPrizeLine(*tempIcon, &rr.LineArr, &rr.PrizePos) |
|||
|
|||
//特殊情况直接跳出循环
|
|||
if bSpecialGame { |
|||
break |
|||
} |
|||
|
|||
//倍数超过返回False (重新生成);
|
|||
if rr.CheckDataLimit(tempIcon, CurrentGears, GameType) { |
|||
bDataBirth = false //监测通过数据生成
|
|||
} |
|||
|
|||
if bDataBirth { |
|||
break |
|||
} |
|||
|
|||
if BrithNumber >= 100 { |
|||
log.Debug("走特殊配置") |
|||
//走特殊配置
|
|||
bSpecialGame = true |
|||
continue |
|||
} |
|||
} |
|||
|
|||
GameResult.Result = true |
|||
GameResult.HeepPrizePL = rr.HeepPrizePL |
|||
GameResult.LineArr = rr.LineArr |
|||
GameResult.LineDataArr = rr.LineDataArr |
|||
GameResult.PrizePos = rr.PrizePos |
|||
|
|||
return GameResult |
|||
} |
|||
|
|||
func (rr *Room) GetReloadIcon(tempPlayer *Player) { |
|||
var CurrentIcons []int |
|||
var CurrentCanPos []int |
|||
for i, i2 := range tempPlayer.ReloadIcons { |
|||
if i2 == 0 && MyResultSet[tempPlayer.ReloadID].Result[i] != 0 { |
|||
CurrentCanPos = append(CurrentCanPos, i) |
|||
} |
|||
} |
|||
|
|||
rr.shuffleDeck(&CurrentCanPos) |
|||
|
|||
for count := 0; count < 1000; count++ { |
|||
CurrentIcons = rr.DeepCopy(tempPlayer.ReloadIcons) |
|||
CurrentCount := rand.Intn(len(CurrentCanPos)) + 1 |
|||
|
|||
for i := 0; i < CurrentCount; i++ { |
|||
CurrentIcons[CurrentCanPos[i]] = tempPlayer.ReloadIcon |
|||
} |
|||
|
|||
rr.GetPrizeLine(CurrentIcons, &rr.LineArr, &rr.PrizePos) |
|||
|
|||
// 如果本次不是全部图标出完 并且还没有倍数 就要重新随机
|
|||
if CurrentCount != len(CurrentCanPos) && rr.HeepPrizePL == tempPlayer.HeepPrizePL { |
|||
continue |
|||
} |
|||
|
|||
break |
|||
} |
|||
} |
@ -0,0 +1,37 @@ |
|||
package game |
|||
|
|||
import ( |
|||
"base/cluster/node" |
|||
"context" |
|||
"github.com/shopspring/decimal" |
|||
"strconv" |
|||
"sync" |
|||
"xgame/game/TigerSlot/app/core/blood" |
|||
"xgame/game/TigerSlot/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), |
|||
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), |
|||
} |
|||
|
|||
return r |
|||
} |
@ -0,0 +1,78 @@ |
|||
package game |
|||
|
|||
import ( |
|||
"base/log" |
|||
"xgame/game/TigerSlot/app/core/protocol" |
|||
"xgame/game/TigerSlot/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{} |
|||
|
|||
ret := false |
|||
|
|||
var tempPlayerMap = map[int32]*Player{} |
|||
for seatId, pPlayer := range rr.PlayerMap { |
|||
if pPlayer == nil { |
|||
continue |
|||
} |
|||
tempPlayerMap[seatId] = pPlayer |
|||
} |
|||
|
|||
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 |
|||
|
|||
//为了下面的rr.PlayerMap = tempPlayerMap这个表达式
|
|||
tempPlayerMap[pPlayer.ChairId].OnlineState = USER_LEAVE |
|||
tempPlayerMap[pPlayer.ChairId].Ctx = nil |
|||
rr.QuitRoomFunc(pPlayer.Uid) |
|||
|
|||
delete(tempPlayerMap, pPlayer.ChairId) |
|||
} else { |
|||
//针对机器人的删除
|
|||
delete(tempPlayerMap, pPlayer.ChairId) |
|||
} |
|||
|
|||
ret = true |
|||
} |
|||
} |
|||
rr.PlayerMap = tempPlayerMap |
|||
|
|||
if ret { |
|||
log.Debug(" 桌子号", rr.RId, "玩家", uid, "退出房间成功") |
|||
playerCount := rr.GetRoomPlayerCount() |
|||
|
|||
log.Debug(" 桌子号", rr.RId, "玩家人数", playerCount) |
|||
} |
|||
return true |
|||
} |
@ -0,0 +1,61 @@ |
|||
package game |
|||
|
|||
import ( |
|||
"base/log" |
|||
"fmt" |
|||
"strconv" |
|||
"time" |
|||
"xgame/game/service/rpc_client" |
|||
writeUserScorepb "xgame/internal/service/writeUserScore/pb" |
|||
"xgame/tools" |
|||
) |
|||
|
|||
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 |
|||
} |
@ -0,0 +1,49 @@ |
|||
package game |
|||
|
|||
import ( |
|||
"base/cluster/node" |
|||
"github.com/shopspring/decimal" |
|||
) |
|||
|
|||
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比较用于确定是否点控该玩家]
|
|||
CurrentGears int // 控制档位
|
|||
|
|||
PlayCount int // 在桌子上呆了几局
|
|||
GameState int // 当前游戏状态
|
|||
BetTick int64 // 下注时间
|
|||
BetLevel int // 本次押注等级
|
|||
BetTotal int64 // 本次总压
|
|||
MissCount int // 不中奖次数
|
|||
LossCount int // 输的次数
|
|||
ReloadID int // 重转最终结果ID
|
|||
ReloadIcon int // 本次重转的固定图标
|
|||
ReloadIcons []int // 重转图标
|
|||
HeepPrizePL int // 累计倍数
|
|||
TableWin int64 // 桌子上的总输赢
|
|||
BetScore int64 // 结算时的总下注
|
|||
WinScore int64 // 结算时的总输赢
|
|||
} |
@ -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, |
|||
}, |
|||
}, |
|||
}) |
|||
|
|||
} |
@ -0,0 +1,21 @@ |
|||
package game |
|||
|
|||
import ( |
|||
"base/log" |
|||
) |
|||
|
|||
// RepeatRoom 复位桌子
|
|||
func (rr *Room) RepeatRoom() { |
|||
rr.CurrOperationPlayer = -1 //
|
|||
rr.IsP2pControls = false //点控
|
|||
|
|||
// 重新获得血池 测试关闭
|
|||
if rr.BoolePool.SetBoole(rr.RoomProxy, rr.Opt.BloodId) == false { |
|||
log.Debug(" 桌子号", rr.RId, "复位桌子获取血池失败") |
|||
} |
|||
|
|||
} |
|||
|
|||
func (rr *Room) RepeatRoomUser() { //复位玩家的数据
|
|||
|
|||
} |
@ -0,0 +1,12 @@ |
|||
package game |
|||
|
|||
type ResultSet struct { |
|||
ID int //编号
|
|||
Type int //类型(1为普通游戏中奖结果,2为免费游戏触发结果,3 为免费游戏结果,4为重转触发结果,5为重转结果,6为超级重转结果)
|
|||
Multiple int //倍数(本次结果的单线倍数)
|
|||
Result []int //结果(重转是直接的结果,其他是下标)
|
|||
} |
|||
|
|||
var MyResultSet = []ResultSet{ |
|||
{1, 0, 40, []int{59, 39, 4, 23, 48}}, |
|||
} |
@ -0,0 +1,8 @@ |
|||
package game |
|||
|
|||
// 恢复游戏信息
|
|||
func (rr *Room) OnResumeGameData(uid int64) { |
|||
rr.roomRWLock.Lock() |
|||
defer rr.roomRWLock.Unlock() |
|||
|
|||
} |
@ -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 |
|||
} |
@ -0,0 +1,38 @@ |
|||
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 |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,123 @@ |
|||
package protocol |
|||
|
|||
type JoinRoomReq 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 RoomInfoRes struct { |
|||
RoomId int32 `json:"roomId"` //房间ID
|
|||
RoomConfig *TableConfig `json:"roomConfig,omitempty"` //房间配置
|
|||
ServerLocation float64 `json:"serverLocation"` //同步服务器时区
|
|||
ServerTimestamp int64 `json:"serverTimestamp"` //同步服务器时间戳
|
|||
Record []HistoryRecord `json:"record,omitempty"` //历史记录
|
|||
PlayerInfo RoomPlayer `json:"playerList"` //用户信息
|
|||
} |
|||
|
|||
// ResumeGameNotice 通知恢复游戏
|
|||
type ResumeGameNotice struct { |
|||
RoomId int32 `json:"roomId"` //房间ID
|
|||
GameId int32 `json:"gameId"` //游戏ID
|
|||
} |
|||
|
|||
type BetReq struct { //普通游戏押注
|
|||
Level int `json:"level"` //押注等级
|
|||
LineNum int `json:"lineNum"` //线的数量
|
|||
} |
|||
|
|||
type GameResultRes struct { //普通游戏押注返回
|
|||
Code int32 `json:"code"` //错误码
|
|||
CodeMsg string `json:"codeMsg"` //错误内容
|
|||
GameType int32 `json:"gameType"` //1普通 2免费 3重转
|
|||
IconData []int `json:"iconData,omitempty"` //图标信息
|
|||
SpecialIconData []int `json:"specialIconData,omitempty"` //带上下的图标信息
|
|||
BonusIconData []int `json:"bonusIconData,omitempty"` //bonus停留的图标信息
|
|||
PrizeLine []int `json:"prizeLine,omitempty"` //中奖线数据
|
|||
PlayerGold int64 `json:"PlayerGold"` //玩家持有金币
|
|||
DisplayGold int64 `json:"displayGold"` //本次中奖显示的金币
|
|||
PrizeGold int64 `json:"prizeGold"` //累计金币
|
|||
UdIconData []int32 `json:"udIconData"` //带上下的图标信息
|
|||
} |
|||
|
|||
type LeaveTable struct { //退出桌子请求
|
|||
} |
|||
|
|||
type LeaveTableRes struct { // 退出桌子返回
|
|||
Code int32 `json:"code"` //错误码
|
|||
CodeMsg string `json:"codeMsg"` //错误内容
|
|||
} |
|||
|
|||
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
|
|||
IconData []int32 `json:"iconData,omitempty"` // 转的信息
|
|||
IsWin bool `json:"isWin"` |
|||
} |
|||
|
|||
type RecordInfo struct { // 存储的结构显示
|
|||
Player []RecordInfoPlayer `json:"player,omitempty"` // 单独用户信息
|
|||
} |
@ -0,0 +1,79 @@ |
|||
package protocol |
|||
|
|||
const ( |
|||
ErrorCodeLessThanMinEnter = 20013001 //低于最小进入条件
|
|||
ErrorCodeGameNoStart = 20013002 //游戏未开启
|
|||
ErrorCodeUnknown = 20013003 //未知错误
|
|||
ErrorCodeNoBetState = 20013004 //非下注阶段
|
|||
ErrorCodeBetError = 20013005 //下注失败
|
|||
) |
|||
|
|||
const ( |
|||
Win = 1 //赢
|
|||
Lose = 2 //输
|
|||
He = 3 //和
|
|||
) |
|||
|
|||
const ( |
|||
GameStatusNone = 0 //无效状态
|
|||
GameStatusNormal = 1 //普通游戏状态
|
|||
GameStatusFree = 2 //免费游戏状态
|
|||
GameStatusJK = 3 //重转游戏状态
|
|||
) |
|||
|
|||
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 和
|
|||
} |
|||
|
|||
type HistoryRecordSub struct { //历史记录信息
|
|||
ChairID []int32 `json:"chairId"` // 座位号
|
|||
Avatar []string `json:"avatar"` // 玩家头像
|
|||
IconData []int32 `json:"iconData,omitempty"` // 转的信息
|
|||
IsWin bool `json:"isWin"` |
|||
} |
|||
|
|||
type HistoryRecord struct { //历史记录信息
|
|||
List []HistoryRecordSub `json:"list"` // 记录列表
|
|||
} |
|||
|
|||
type TableConfig struct { // 桌子配置
|
|||
RechargeLimit int64 `json:"rechargeLimit"` // 弹充值限制
|
|||
TaxMing float64 `json:"taxMing"` // 明税
|
|||
BetList []int64 `json:"betList"` // 押注档位
|
|||
LineData [][]int `json:"lineData"` // 中奖线
|
|||
ReelsConfig [][]int `json:"reelsConfig"` // 真轴数据
|
|||
} |
|||
|
|||
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"` // 房间状态
|
|||
} |
|||
|
|||
type LGameResult struct { |
|||
Result bool // 是否成功
|
|||
EnterBonus bool // 是否进入Bonus游戏
|
|||
HeepPrizePL int // 累计倍数
|
|||
IconData []int // 图标信息
|
|||
LineArr []int // 中奖线 每条线中了几个
|
|||
LineDataArr []int // 中奖线 每条线中了啥
|
|||
PrizePos []int // 中奖位置
|
|||
SpecialIconData []int // 带上下的图标信息
|
|||
RealPos []int // 真实轴的位置记录
|
|||
ReloadID int // 重转最终结果ID
|
|||
} |
@ -0,0 +1,46 @@ |
|||
package room |
|||
|
|||
import ( |
|||
"base/cluster/node" |
|||
"flag" |
|||
"golang.org/x/exp/rand" |
|||
"time" |
|||
"xgame/game/TigerSlot/app/core/game" |
|||
) |
|||
|
|||
type HomeBase interface { |
|||
OnInit() bool // 初始化房间
|
|||
OnPlayerReady(userId int64, p interface{}) // 准备
|
|||
OnReconnect(userId int64) // 断线重连
|
|||
OnDisconnect(userId int64) // 玩家离线
|
|||
JoinRoom(pPlayer *game.Player) 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
|
|||
OnUserBet(uid int64, msg interface{}) // 用户下注
|
|||
OnUserSpecialBet(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()))) |
|||
} |
@ -0,0 +1,210 @@ |
|||
package room |
|||
|
|||
import ( |
|||
"base/cluster/node" |
|||
"base/log" |
|||
"context" |
|||
"github.com/roylee0704/gron" |
|||
"sync" |
|||
conf "xgame/game/TigerSlot/app/config" |
|||
"xgame/game/TigerSlot/app/core/game" |
|||
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 //血池控制
|
|||
Rhb map[int64]HomeBase |
|||
RhbEx map[int64]int64 |
|||
RhbRoom map[int64]*game.Room |
|||
NextId int64 |
|||
lock sync.Mutex |
|||
RoomsReply *gamepb.FetchRoomsReply |
|||
} |
|||
|
|||
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), |
|||
} |
|||
|
|||
Gh.GameProxy = GameProxy |
|||
|
|||
if Gh.GronTimer != nil { |
|||
Gh.GronTimer.Stop() |
|||
Gh.GronTimer = nil |
|||
} |
|||
|
|||
Gh.GronTimer = gron.New() |
|||
|
|||
Gh.GronTimer.Start() |
|||
} |
|||
|
|||
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) |
|||
|
|||
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 |
|||
} |
|||
|
|||
// 通过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) { // 销毁房间
|
|||
h.lock.Lock() |
|||
defer h.lock.Unlock() |
|||
log.Debug("Home==>销毁房间") |
|||
|
|||
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)
|
|||
} |
@ -0,0 +1,288 @@ |
|||
package gamemanage |
|||
|
|||
import ( |
|||
"base/cluster/node" |
|||
"base/log" |
|||
"base/task" |
|||
"fmt" |
|||
"xgame/game/TigerSlot/app/core/game" |
|||
"xgame/game/TigerSlot/app/core/protocol" |
|||
"xgame/game/TigerSlot/app/core/room" |
|||
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 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) |
|||
|
|||
//在房间中
|
|||
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 { |
|||
roomObj = room.Gh.SearchRoom() |
|||
if roomObj == nil { |
|||
roomObj = room.Gh.CreateRoom(Ctx.Request.UID, req.RoomID, req.GameID, req.AgentID, GameProxy) |
|||
} |
|||
} 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) |
|||
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) |
|||
|
|||
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 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) |
|||
}) |
|||
} |
|||
|
|||
func SpecialBet(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.BetReq{} |
|||
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.OnUserSpecialBet(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) |
|||
} |
|||
} |
|||
|
|||
r := room.Gh.GetRoomObject(Ctx.Request.UID) |
|||
if r == nil { |
|||
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) |
|||
}) |
|||
} |
@ -0,0 +1,16 @@ |
|||
package route |
|||
|
|||
const ( |
|||
// 客户端发送
|
|||
JoinTable int32 = 2001301 // 加入桌子
|
|||
LogoutTable int32 = 2001302 // 退出游戏请求
|
|||
Bet int32 = 2001303 // 普通游戏押注
|
|||
SpecialBet int32 = 2001304 // 特殊游戏押注 重转+超级重转
|
|||
|
|||
// 服务端返回
|
|||
GameResultRes int32 = 2001311 // 游戏结果返回
|
|||
LogoutTableRes int32 = 2001312 // 退出游戏返回
|
|||
SitDownRes int32 = 2001313 // 玩家坐下返回
|
|||
RoomInfoRes int32 = 2001314 // 房间的信息返回
|
|||
|
|||
) |
@ -0,0 +1,286 @@ |
|||
{ |
|||
"CommentRechargeLimit": "低于多少钱弹充值", |
|||
"RechargeLimit" : 10000, |
|||
"CommentMinGameScore": "最低能玩的金额", |
|||
"MinGameScore" : 50000, |
|||
"CommentChipList": "默认的筹码列表 后台有按后台的来", |
|||
"ChipList": [10000], |
|||
"reelsComment":"卷轴部分", |
|||
"reels":{ |
|||
"matrixComment":"游戏卷轴构成(二位数组表示,X轴为行,Y轴为列,从0开始表示位置,-1为无用格【异形卷轴用】)", |
|||
"matrixComment1":"异形卷轴示例", |
|||
"matrixComment2":[ |
|||
[0 ,-1,-1,-1,1 ], |
|||
[2 ,3 ,4 ,5 ,6 ], |
|||
[7 ,8 ,9 ,10,11], |
|||
[12,13,14,15,16], |
|||
[17,-1,-1,-1,18] |
|||
], |
|||
"matrix":[ |
|||
[1 ,2 ,3], |
|||
[4 ,5 ,6], |
|||
[7 ,8 ,9] |
|||
], |
|||
"initialComment":"卷轴初始化构成,(代表初始化卷轴可以用位置,1可用,0不可用,主要针对可解锁型卷轴)", |
|||
"initialComment1":"示例,(如果有连线中奖结果,会随机将一个0变成1并且开始下次旋转,重复此过程直到出现没有中奖结果,卷轴回到初始化状态)", |
|||
"initialComment2":[ |
|||
[0 ,1 ,1 ,1 ,0 ], |
|||
[1 ,1 ,1 ,1 ,1 ], |
|||
[0 ,1 ,1 ,1 ,0 ] |
|||
], |
|||
"initial":[ |
|||
[1 ,1 ,1], |
|||
[1 ,1 ,1], |
|||
[1 ,1 ,1] |
|||
] |
|||
}, |
|||
|
|||
"payLineComment":"派奖线部分", |
|||
"payLine":{ |
|||
"typeComment":"连线类型,(1为不锁线连线,2为锁线连线,3为满线)【不锁线代表可以按照顺序选择押几条线,锁线代表必须按照满线押注,满线代表按照相邻两轴之间相同值值判断连线】", |
|||
"type":2, |
|||
"numberComment":"线数,(主要作为计算的参数用,特别是满线的情况)", |
|||
"number":50, |
|||
"lineComment":"线配置,(数组下标代表是第几根线)", |
|||
"line":[ |
|||
[4,5,6], |
|||
[1,2,3], |
|||
[7,8,9], |
|||
[1,5,9], |
|||
[7,5,3] |
|||
], |
|||
"hitComment":"连线判断方式,(1为左起判断,2为右起判断,3为双向判断)", |
|||
"hit": 1, |
|||
"betComment":"单线押注,(数组下标代表档位,值代表单线押注值)【押注总值=线数*单线押注】", |
|||
"bet":[10,20,30,40,50,60,70,80,90,100] |
|||
}, |
|||
|
|||
"jackpotComment":"奖金池部分", |
|||
"jackpot":{ |
|||
"switchComment":"是否启用奖金池,(0为不启用,1为启用)", |
|||
"switch":1, |
|||
"cutComment":"奖金池抽成类型,(1为押注抽成,2为获奖抽成,3为其他)", |
|||
"cut":1, |
|||
"ratio1Comment":"奖金池抽成比例(=类型获取的值*比例值)【万分比】", |
|||
"ratio1":1735, |
|||
"addComment":"奖金池根据押注等级,,分池显示和计算方法(当前奖池=总奖池*分池比例(当前押注值/所有押注档位的和))", |
|||
"addComment1":"奖金池构成,(实际上就一个奖金池,数组长度决定分为几个奖金池显示,默认按照从小到大排列,数组里的值代表占总池比例)", |
|||
"addComment2":"奖金池自增设计,:在玩家抽成不足以支撑奖金池持续增长时,系统采用自增方式增加奖金池,所有自增部分需额外计入该游戏债务池,债务池大于0时抽成会有一部分用于偿还债务", |
|||
"addComment3":"奖金池自增开启阈值,(10秒内的抽成值之和低于该值时启动自增,自增启动时抽成全部用于偿还债务,直到抽成值大于阈值时停止)", |
|||
"add":90000, |
|||
"repaymentComment":"债务池还债比例(当债务池大于0并且奖金池自增未启动时,将抽成的一部分用于偿还债务) 【万分比】", |
|||
"repayment":2000, |
|||
"minComment":"奖金池初始化值,(初始化了多少,债务池同时增加多少)", |
|||
"min":173745000000, |
|||
"maxComment":"奖金池最大值(一般设定为接近显示极限的值,,如果奖金池当前值超过这个值,那么根据本游戏自身奖金池派奖规则让机器人获得奖金,所领取金额直接用于还债(此操作可产生负数))", |
|||
"max":218457673692, |
|||
"ratio2Comment":"彩金池支付比例,,对应彩金触发档位 【万分比】, 【mini,minor,major,grand】(对应-1,-2,-3,-4)", |
|||
"ratio2": [133,533,1333,4000] |
|||
}, |
|||
|
|||
"symbolComment":"图标部分", |
|||
"symbol":{ |
|||
"multipleComment1":"图标赔率,,单线倍数(数组名代表图标ID,数组下标代表几连倍数,数组长度代表最大有几连返奖,数组值代表倍数,值为0代表不判断返奖)", |
|||
"multipleComment2":"说明,【图标ID:1连倍数,2连倍数,3连倍数,4连倍数,5连倍数,6连倍数】", |
|||
"multipleComment3":"1 橙子 2 爆竹 3 红包 4 钱袋 5 如意 6 元宝 7 WILD百搭 8 空图标(重转用)", |
|||
"multiple":[ |
|||
[0,0,3], |
|||
[0,0,5], |
|||
[0,0,8], |
|||
[0,0,10], |
|||
[0,0,25], |
|||
[0,0,100], |
|||
[0,0,250], |
|||
[0,0,0] |
|||
], |
|||
"wildComment1":"【特殊图标部分】【特殊图标不止以下几种,根据游戏不同也会新增】【特殊规则的触发与配置需额外添加字段】", |
|||
"wildComment2":"常规百搭,(WILD),普通游戏中不出现在第一列", |
|||
"wild":{ |
|||
"IDComment":"百搭的ID,(-1代表此游戏没有该图标)", |
|||
"ID":10, |
|||
"replaceComment":"百搭图标可替换的", |
|||
"replace":[1,2,3,4,5,6], |
|||
"fiveHitComment":"百搭5连限制(1为允许【额外说明,如果允许5连百搭而上方百搭又没有配置5连倍数则不计算该条中奖线】,2为不允许)", |
|||
"fiveHit":2, |
|||
"columnMoreComment":"百搭列限制,(填几就代表单列最多允许同时出现几个)", |
|||
"columnMore":3, |
|||
"reelsMoreComment":"全盘限制,(填几就代表全盘最多允许出现多少个,免费中,固定1,3,5列并生成长图百搭)", |
|||
"reelsMore":12 |
|||
}, |
|||
"scatterComment":"分散图标,(Scatter)", |
|||
"scatter":{ |
|||
"IDComment":"Scatter图标ID(-1代表此游戏没有该图标)", |
|||
"ID":11, |
|||
"hitComment":"Scatter图标命中判断(1为常规连线判断,2为线上连续判断,3为连续列判断,4为列数判断,5为数量判断,6整列相同判断,7自身独立判断)", |
|||
"hit":5, |
|||
"columnMoreComment":"列限制(填几就代表单列最多允许同时出现几个)", |
|||
"columnMore":1, |
|||
"reelsMoreComment":"全盘限制(填几就代表全盘最多允许出现多少个)", |
|||
"reelsMore":3, |
|||
"numberComment":"免费旋转次数【图标出现个数,旋转次数】", |
|||
"number":[[3,3]] |
|||
}, |
|||
"bonusComment":"奖金图标,(BONUS,可触发重转、金币、奖池)", |
|||
"bonus":{ |
|||
"IDComment":"bonus图标ID(-1代表此游戏没有该图标)", |
|||
"ID":12, |
|||
"hitComment":"bonus图标命中判断(1为常规连线判断,2为线上连续判断,3为连续列判断,4为列数判断,5为数量判断,6整列相同判断,7自身独立判断)", |
|||
"hit":5, |
|||
"columnMoreComment":"列限制(填几就代表单列最多允许同时出现几个)", |
|||
"columnMore":3, |
|||
"reelsMoreComment":"全盘限制(填几就代表全盘最多允许出现多少个)", |
|||
"reelsMore":15, |
|||
"numberComment":"重转次数【图标出现个数,旋转次数】", |
|||
"number":[[6,3]], |
|||
"valueComment":"图标赋值,(正数为单线倍数,负数为奖池)", |
|||
"value":[5,10,15,20,25,30,35,40,45,50,100,150,200,250,300,350,400,450,500,-1,-2,-3,-4], |
|||
"Comment":"图标赋值权重", |
|||
"valueWeight":[250,200,300,500,1800,1500,1200,800,800,1200,300,200,100,200,100,80,50,30,20,200,100,50,20], |
|||
"closeNumberComment":"结算数量,(生成结果用)(结算数量以外用空图标填充)", |
|||
"closeNumber":[6,7,8,9,10,11,12,13,14,15], |
|||
"closeNumberWeightComment":"结算数量权重,(生成结果用)", |
|||
"closeNumberWeight":[350,500,1000,1800,2200,1800,1200,800,300,50] |
|||
}, |
|||
"jackpotComment":"奖池图标,(JACKPOT)", |
|||
"jackpot":{ |
|||
"IDComment":"jackpot图标ID(-1代表此游戏没有该图标)", |
|||
"ID":-1, |
|||
"hitComment":"jackpot图标命中判断(1为常规连线判断,2为线上连续判断,3为连续列判断,4为列数判断,5为数量判断,6整列相同判断,7自身独立判断)", |
|||
"hit":7, |
|||
"columnMoreComment":"列限制(填几就代表单列最多允许同时出现几个百搭)", |
|||
"columnMore":1, |
|||
"reelsMoreComment":"全盘限制(填几就代表全盘最多允许出现多少个)", |
|||
"reelsMore":15, |
|||
"jackPotNoComment":"奖金池图标出现个数限制(对应-1,-2,-3的奖金池出现个数)", |
|||
"jackPotNo":[2,1,1,1] |
|||
} |
|||
}, |
|||
"commonlyComment":"数据生成部分 非控制状态默认调用的档位(rtp:0.98)", |
|||
"commonly":{ |
|||
"normalComment":"普通游戏", |
|||
"normal": { |
|||
"winComment":"中奖率【万分比】", |
|||
"win":2239, |
|||
"rangeComment":"取值范围", |
|||
"range":[[1,10000],[10001,18000],[18001,20000],[20001,22000],[22001,24000],[24001,25000],[25001,25500],[25501,25700]], |
|||
"triggerWeightComment":"范围权重", |
|||
"triggerWeight":[4050,4500,1000,200,100,80,50,20], |
|||
"limitComment":"限制倍数(【最小限制倍数,最大限制倍数】如果随机出来的结果不在这个区间就重新随,以单线倍数计算)", |
|||
"limit": [0,60000], |
|||
"guaranteeComment":"连输保底【连续不中奖保底,连续输保底】(值为连续次数超过限制则触发保底)(不中奖保底是一定随一个中奖结果,连续输保底是一定随一个获奖大于押注的金额)", |
|||
"guarantee":[10,20], |
|||
"generateComment":"轴", |
|||
"generate":[ |
|||
[2,4,5,4,7,7,11,4,4,8,8,8,9,9,9,9,12,12,12,4,4,6,6,6,6,3,3,7,7,7,9,9,9,2,5,5,5,5,11,8,8,8,8,6,1,1,1,8,5,2,2,2,1,9,9,9,6,6,7,5,12,12,8,6,6,8,3,3,3,11,2,2,2,8,3,6,9,12,7,8,3,11,1,1,7,7,7,7,12,4,3,9,5,4,4,7,12,9,5,9], |
|||
[7,8,8,1,1,1,10,9,7,4,4,4,4,6,9,9,9,9,9,3,3,7,10,6,9,2,4,4,5,5,10,10,9,4,8,7,12,12,5,5,5,5,7,6,7,9,8,2,2,2,8,8,8,8,8,4,10,9,3,3,3,3,7,1,1,6,6,5,12,12,12,2,8,8,9,9,3,6,6,6,6,2,2,8,5,10,7,7,7,7,7,3,1,5,4,12], |
|||
[11,4,5,4,7,7,5,10,8,8,8,2,6,9,9,10,4,4,6,6,12,12,3,3,3,7,6,6,7,10,9,9,8,5,5,5,2,2,10,3,3,12,7,8,1,1,1,5,7,7,9,2,1,9,9,11,10,6,7,5,8,6,6,8,3,8,2,2,2,8,4,4,8,8,8,9,3,11,1,1,12,12,12,9,2,2,7,7,7,4,3,9,5,4,4,7,9,5,9], |
|||
[4,5,2,1,7,7,7,7,7,10,5,9,1,1,6,6,6,6,12,12,12,3,3,9,8,8,2,2,5,6,6,12,12,8,9,3,3,3,3,7,10,4,8,8,8,8,8,2,2,2,8,9,7,6,7,12,12,5,5,5,5,7,8,4,9,10,10,3,5,5,4,4,9,6,10,3,2,12,7,9,9,9,9,9,6,4,4,4,4,7,12,9,10,1,1,1,1,8,7], |
|||
[10,4,5,4,12,7,7,5,10,8,8,8,8,12,12,6,9,9,3,3,4,11,5,8,6,6,7,7,3,3,3,10,8,1,5,5,5,6,2,2,3,9,6,1,1,1,1,12,12,12,7,8,9,9,1,1,4,4,2,11,10,10,7,7,6,6,6,8,3,8,12,5,2,10,9,9,4,8,8,8,9,3,2,2,2,9,7,7,7,4,7,9,12,5,4,4,6,9,5,9] |
|||
] |
|||
}, |
|||
"freeComment":"免费游戏", |
|||
"free":{ |
|||
"triggerProComment":"触发概率【万分比】", |
|||
"triggerPro":151, |
|||
"triggerRangeComment":"触发结果ID范围", |
|||
"triggerRange":[[25701,30700]], |
|||
"triggerWeightComment":"触发结果权重", |
|||
"triggerWeight":[10000], |
|||
"winComment":"中奖率【万分比】", |
|||
"win":10000, |
|||
"resultRangeComment":"结果ID范围", |
|||
"resultRange":[[30701,33700],[33701,38700],[38701,41700],[41701,43700],[43701,44700],[44701,45700],[45701,46700],[46701,47700]], |
|||
"resultWeightComment":"结果ID范围权重", |
|||
"resultWeight":[5350,2500,1000,500,300,200,100,50], |
|||
"limitComment":"限制倍数(【最小限制倍数,最大限制倍数】如果随机出来的结果不在这个区间就重新随,以单线倍数计算)", |
|||
"limit": [0,60000], |
|||
"guaranteeComment":"连输保底【连续不中奖保底,连续输保底】(值为连续次数超过限制则触发保底)(不中奖保底是一定随一个中奖结果,连续输保底是一定随一个获奖大于押注的金额)", |
|||
"guarantee":[100,100], |
|||
"generateComment":"轴 字段名代表列数(可增减),数组下标为真轴位置(随机这个值,然后向下取几个值形成结果,遇到结尾就从头部取,每列单独随机),值为图标ID", |
|||
"generate":[ |
|||
[10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10], |
|||
[2,8,3,8,5,5,5,8,8,10,10,10,8,8,8,1,1,1,2,1,3,3,3,9,6,6,6,4,4,1,6,10,4,9,2,4,7,6,1,3,3,9,9,9,9,9,9,9,9,5,5,5,10,10,10,7,7,7,8,8,8,8,8,8,7,9,9,9,9,2,2,4,4,4,6,6,5,9,9,9,5,3,1,1,9,10,2,2,2,3,4,7,7,7,8,8,6], |
|||
[10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10], |
|||
[9,9,9,8,8,6,6,6,7,7,6,4,9,9,4,4,7,5,5,5,4,1,9,9,8,6,3,9,1,1,1,3,3,3,3,1,1,1,1,1,1,5,5,9,5,5,2,2,2,2,7,1,9,9,9,2,8,7,7,7,8,8,8,8,6,5,3,3,3,6,6,2,2,2,2,2,2,8,4,1,4,4,4,7,2,7,7,7,8,8,6,10,2,2,2,3,4], |
|||
[10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10] |
|||
] |
|||
}, |
|||
"bonusComment":"重转游戏【配置方式根据特殊玩法改变】", |
|||
"bonus":{ |
|||
"triggerProComment":"触发概率", |
|||
"triggerPro":79, |
|||
"triggerRangeComment":"触发结果ID范围", |
|||
"triggerRange":[[47701,51700],[51701,52700],[52701,52750]], |
|||
"triggerWeightComment":"触发结果权重(本游戏没用)", |
|||
"triggerWeight":[6000,3500,500], |
|||
"winComment":"中奖率【万分比】", |
|||
"win":10000, |
|||
"resultRangeComment":"结果ID范围", |
|||
"resultRange":[[52751,53378],[53379,58009],[58010,61039],[61040,62717],[62718,62750],[62751,63970],[63971,65616],[65617,66253],[66254,66949],[66950,67250]], |
|||
"resultWeightComment":"结果ID范围权重", |
|||
"resultWeight":[300,300,1200,1800,300,500,1800,2000,1200,600], |
|||
"limitComment":"限制倍数(【最小限制倍数,最大限制倍数】如果随机出来的结果不在这个区间就重新随,以单线倍数计算)", |
|||
"limit": [0,60000], |
|||
"guaranteeComment":"连输保底【连续不中奖保底,连续输保底】(值为连续次数超过限制则触发保底)(不中奖保底是一定随一个中奖结果,连续输保底是一定随一个获奖大于押注的金额)", |
|||
"guarantee":[-1,-1], |
|||
"generateComment":"轴(本游戏bonus不用轴)", |
|||
"generate":[ |
|||
[4,8,9,7,4,1,6,6,3,12,9,9,1,2,6,6,5,5,12,8,8,1,9,9,3,1,4,6,2,5,5,5,5,5,4,2,2,1,3,5,2,9,9,9,2,1,8,8,8,1,3,3,2,7,7,12,12,1,5,7,7,7,2,12,1,3,3,3,1,1,1,4,8,3,4,4,4,1,3,2,6,1,8,8,8,2,6,12,12,12,1,2,9,1,5,5,5,2,4,4,1,1,2,2,2,1,6,6,6,8,12,1,1,1,2,3,7,7,7,7,1,8,1,8,4], |
|||
[3,4,5,1,9,2,4,2,6,6,12,5,2,5,1,7,8,6,2,6,7,7,8,2,12,12,1,8,6,6,6,8,8,8,8,1,5,2,4,5,6,2,9,9,1,3,2,2,2,3,3,4,4,1,7,8,8,8,2,3,7,7,7,1,6,4,12,5,5,12,12,1,2,2,6,6,5,9,9,9,1,3,3,3,2,2,2,2,1,5,5,5,4,2,12,12,12,1,1,1,8,1,1,7,7,7,7,7,1,3,3,4,4,4,8,8,1,7,3,3,7,6,4,2,1], |
|||
[6,3,2,5,5,2,8,8,1,12,2,9,9,1,6,6,4,2,4,6,12,6,6,1,2,7,1,12,3,3,3,2,1,12,9,6,1,5,6,6,6,12,1,8,8,8,7,7,1,4,9,3,3,3,4,12,12,1,2,2,2,6,4,4,4,9,9,9,5,7,1,2,2,2,5,5,5,5,8,8,2,12,12,12,3,3,7,7,5,12,1,12,1,1,1,7,7,7,8,1,2,8,1,1,3,4,4,4,12,4,1,2,2,5,5,5,1,2,7,1,3,4,4,3,2], |
|||
[5,1,1,1,2,12,12,6,3,1,6,8,8,2,4,1,3,5,1,5,2,4,9,9,9,5,2,2,2,3,4,12,1,3,3,9,3,12,12,12,1,1,1,1,2,7,7,7,5,5,5,7,6,6,8,1,9,5,6,1,6,6,3,3,3,2,12,12,1,7,7,2,2,12,1,9,9,6,6,6,3,1,8,8,5,5,2,1,5,5,2,7,7,8,8,3,1,2,2,2,2,4,4,12,12,2,3,8,8,8,2,12,1,1,7,7,4,4,4,9,8,12,2,7,6], |
|||
[2,12,12,12,4,1,3,3,2,3,2,1,5,5,1,5,5,6,12,12,3,9,2,2,5,1,8,5,2,12,12,4,1,6,6,6,2,1,1,1,8,8,8,8,1,3,7,2,8,2,4,4,4,2,2,1,7,7,7,7,5,5,5,4,6,6,6,12,12,7,7,7,2,9,2,12,1,6,8,8,8,12,2,2,2,9,9,1,12,7,7,9,9,9,5,6,6,1,5,5,4,4,12,4,1,3,3,1,12,4,12,1,1,3,4,4,12,3,3,3,8,8,3,2,8] |
|||
] |
|||
} |
|||
}, |
|||
"win":{ |
|||
"normal": { |
|||
"win":2239, |
|||
"range":[[1,10000],[10001,18000],[18001,20000],[20001,22000],[22001,24000],[24001,25000],[25001,25500],[25501,25700]], |
|||
"triggerWeight":[4050,4500,1000,200,100,80,50,20], |
|||
"limit": [0,60000], |
|||
"guarantee":[10,20], |
|||
"generate":[ |
|||
[2,4,5,4,7,7,11,4,4,8,8,8,9,9,9,9,12,12,12,4,4,6,6,6,6,3,3,7,7,7,9,9,9,2,5,5,5,5,11,8,8,8,8,6,1,1,1,8,5,2,2,2,1,9,9,9,6,6,7,5,12,12,8,6,6,8,3,3,3,11,2,2,2,8,3,6,9,12,7,8,3,11,1,1,7,7,7,7,12,4,3,9,5,4,4,7,12,9,5,9], |
|||
[7,8,8,1,1,1,10,9,7,4,4,4,4,6,9,9,9,9,9,3,3,7,10,6,9,2,4,4,5,5,10,10,9,4,8,7,12,12,5,5,5,5,7,6,7,9,8,2,2,2,8,8,8,8,8,4,10,9,3,3,3,3,7,1,1,6,6,5,12,12,12,2,8,8,9,9,3,6,6,6,6,2,2,8,5,10,7,7,7,7,7,3,1,5,4,12], |
|||
[11,4,5,4,7,7,5,10,8,8,8,2,6,9,9,10,4,4,6,6,12,12,3,3,3,7,6,6,7,10,9,9,8,5,5,5,2,2,10,3,3,12,7,8,1,1,1,5,7,7,9,2,1,9,9,11,10,6,7,5,8,6,6,8,3,8,2,2,2,8,4,4,8,8,8,9,3,11,1,1,12,12,12,9,2,2,7,7,7,4,3,9,5,4,4,7,9,5,9], |
|||
[4,5,2,1,7,7,7,7,7,10,5,9,1,1,6,6,6,6,12,12,12,3,3,9,8,8,2,2,5,6,6,12,12,8,9,3,3,3,3,7,10,4,8,8,8,8,8,2,2,2,8,9,7,6,7,12,12,5,5,5,5,7,8,4,9,10,10,3,5,5,4,4,9,6,10,3,2,12,7,9,9,9,9,9,6,4,4,4,4,7,12,9,10,1,1,1,1,8,7], |
|||
[10,4,5,4,12,7,7,5,10,8,8,8,8,12,12,6,9,9,3,3,4,11,5,8,6,6,7,7,3,3,3,10,8,1,5,5,5,6,2,2,3,9,6,1,1,1,1,12,12,12,7,8,9,9,1,1,4,4,2,11,10,10,7,7,6,6,6,8,3,8,12,5,2,10,9,9,4,8,8,8,9,3,2,2,2,9,7,7,7,4,7,9,12,5,4,4,6,9,5,9] |
|||
] |
|||
}, |
|||
"free":{ |
|||
"triggerPro":211, |
|||
"triggerRange":[[25701,30700]], |
|||
"triggerWeight":[10000], |
|||
"win":10000, |
|||
"resultRange":[[30701,33700],[33701,38700],[38701,41700],[41701,43700],[43701,44700],[44701,45700],[45701,46700],[46701,47700]], |
|||
"resultWeight":[5350,2500,1000,500,300,200,100,50], |
|||
"limit": [0,60000], |
|||
"guarantee":[100,100], |
|||
"generate":[ |
|||
[10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10], |
|||
[2,8,3,8,5,5,5,8,8,10,10,10,8,8,8,1,1,1,2,1,3,3,3,9,6,6,6,4,4,1,6,10,4,9,2,4,7,6,1,3,3,9,9,9,9,9,9,9,9,5,5,5,10,10,10,7,7,7,8,8,8,8,8,8,7,9,9,9,9,2,2,4,4,4,6,6,5,9,9,9,5,3,1,1,9,10,2,2,2,3,4,7,7,7,8,8,6], |
|||
[10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10], |
|||
[9,9,9,8,8,6,6,6,7,7,6,4,9,9,4,4,7,5,5,5,4,1,9,9,8,6,3,9,1,1,1,3,3,3,3,1,1,1,1,1,1,5,5,9,5,5,2,2,2,2,7,1,9,9,9,2,8,7,7,7,8,8,8,8,6,5,3,3,3,6,6,2,2,2,2,2,2,8,4,1,4,4,4,7,2,7,7,7,8,8,6,10,2,2,2,3,4], |
|||
[10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10] |
|||
] |
|||
}, |
|||
"bonus":{ |
|||
"triggerPro":115, |
|||
"triggerRange":[[47701,51700],[51701,52700],[52701,52750]], |
|||
"triggerWeight":[6000,3500,500], |
|||
"win":10000, |
|||
"resultRange":[[52751,53378],[53379,58009],[58010,61039],[61040,62717],[62718,62750],[62751,63970],[63971,65616],[65617,66253],[66254,66949],[66950,67250]], |
|||
"resultWeight":[300,300,1200,1800,300,500,1800,2000,1200,600], |
|||
"limit": [0,60000], |
|||
"guarantee":[-1,-1], |
|||
"generate":[ |
|||
[4,8,9,7,4,1,6,6,3,12,9,9,1,2,6,6,5,5,12,8,8,1,9,9,3,1,4,6,2,5,5,5,5,5,4,2,2,1,3,5,2,9,9,9,2,1,8,8,8,1,3,3,2,7,7,12,12,1,5,7,7,7,2,12,1,3,3,3,1,1,1,4,8,3,4,4,4,1,3,2,6,1,8,8,8,2,6,12,12,12,1,2,9,1,5,5,5,2,4,4,1,1,2,2,2,1,6,6,6,8,12,1,1,1,2,3,7,7,7,7,1,8,1,8,4], |
|||
[3,4,5,1,9,2,4,2,6,6,12,5,2,5,1,7,8,6,2,6,7,7,8,2,12,12,1,8,6,6,6,8,8,8,8,1,5,2,4,5,6,2,9,9,1,3,2,2,2,3,3,4,4,1,7,8,8,8,2,3,7,7,7,1,6,4,12,5,5,12,12,1,2,2,6,6,5,9,9,9,1,3,3,3,2,2,2,2,1,5,5,5,4,2,12,12,12,1,1,1,8,1,1,7,7,7,7,7,1,3,3,4,4,4,8,8,1,7,3,3,7,6,4,2,1], |
|||
[6,3,2,5,5,2,8,8,1,12,2,9,9,1,6,6,4,2,4,6,12,6,6,1,2,7,1,12,3,3,3,2,1,12,9,6,1,5,6,6,6,12,1,8,8,8,7,7,1,4,9,3,3,3,4,12,12,1,2,2,2,6,4,4,4,9,9,9,5,7,1,2,2,2,5,5,5,5,8,8,2,12,12,12,3,3,7,7,5,12,1,12,1,1,1,7,7,7,8,1,2,8,1,1,3,4,4,4,12,4,1,2,2,5,5,5,1,2,7,1,3,4,4,3,2], |
|||
[5,1,1,1,2,12,12,6,3,1,6,8,8,2,4,1,3,5,1,5,2,4,9,9,9,5,2,2,2,3,4,12,1,3,3,9,3,12,12,12,1,1,1,1,2,7,7,7,5,5,5,7,6,6,8,1,9,5,6,1,6,6,3,3,3,2,12,12,1,7,7,2,2,12,1,9,9,6,6,6,3,1,8,8,5,5,2,1,5,5,2,7,7,8,8,3,1,2,2,2,2,4,4,12,12,2,3,8,8,8,2,12,1,1,7,7,4,4,4,9,8,12,2,7,6], |
|||
[2,12,12,12,4,1,3,3,2,3,2,1,5,5,1,5,5,6,12,12,3,9,2,2,5,1,8,5,2,12,12,4,1,6,6,6,2,1,1,1,8,8,8,8,1,3,7,2,8,2,4,4,4,2,2,1,7,7,7,7,5,5,5,4,6,6,6,12,12,7,7,7,2,9,2,12,1,6,8,8,8,12,2,2,2,9,9,1,12,7,7,9,9,9,5,6,6,1,5,5,4,4,12,4,1,3,3,1,12,4,12,1,1,3,4,4,12,3,3,3,8,8,3,2,8] |
|||
] |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,134 @@ |
|||
# 进程号 |
|||
pid = "./run/game.pid" |
|||
# 统一时区设置。项目中的时间获取请使用xtime.Now() |
|||
timezone = "Local" |
|||
|
|||
[cluster] |
|||
[cluster.node] |
|||
# 实例名称 |
|||
name = "TigerSlot" |
|||
# 编解码器。可选:json | proto |
|||
codec = "json" |
|||
Endpoint = "grpc://115.236.153.170:43831" |
|||
|
|||
[config] |
|||
[config.file] |
|||
# 配置文件或配置目录路径 |
|||
path = "./config" |
|||
# 读写模式。可选:read-only | read-write,默认为read-only |
|||
mode = "read-write" |
|||
[config.etcd] |
|||
# 客户端连接地址+ |
|||
addrs = ["35.154.10.209: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 = ["35.154.10.209:62331"] |
|||
# 数据库号 |
|||
db = 0 |
|||
# 用户名 |
|||
username = "" |
|||
# 密码 |
|||
password = "zetdCehWe5t3z9" |
|||
# 最大重试次数 |
|||
maxRetries = 3 |
|||
# key前缀 |
|||
prefix = "xgame" |
|||
|
|||
[registry] |
|||
[registry.etcd] |
|||
# 客户端连接地址 |
|||
addrs = ["35.154.10.209:2379"] |
|||
# 客户端拨号超时时间(秒) |
|||
dialTimeout = 5 |
|||
# 命名空间 |
|||
namespace = "services" |
|||
# 超时时间(秒) |
|||
timeout = 3 |
|||
# 心跳重试次数 |
|||
retryTimes = 3 |
|||
# 心跳重试间隔(秒) |
|||
retryInterval = 10 |
|||
[registry.consul] |
|||
# 客户端连接地址 |
|||
addr = "35.154.10.209:8500" |
|||
# 是否启用健康检查 |
|||
healthCheck = true |
|||
# 健康检查时间间隔(秒),仅在启用健康检查后生效 |
|||
healthCheckInterval = 10 |
|||
# 健康检查超时时间(秒),仅在启用健康检查后生效 |
|||
healthCheckTimeout = 5 |
|||
# 是否启用心跳检查 |
|||
heartbeatCheck = true |
|||
# 心跳检查时间间隔(秒),仅在启用心跳检查后生效 |
|||
heartbeatCheckInterval = 10 |
|||
# 健康检测失败后自动注销服务时间(秒) |
|||
deregisterCriticalServiceAfter = 30 |
|||
|
|||
[transport] |
|||
[transport.rpcx] |
|||
# RPCX服务器相关配置 |
|||
[transport.rpcx.server] |
|||
# 服务器监听地址,空或:0时系统将会随机端口号 |
|||
addr = ":50680" |
|||
# 秘钥文件 |
|||
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 |
|||
|
|||
[eventbus] |
|||
[eventbus.nats] |
|||
# 客户端连接地址,默认为nats://127.0.0.1:4222 |
|||
url = "nats://35.154.10.209:4222" |
|||
# 客户端连接超时时间,支持单位:纳秒(ns)、微秒(us | µs)、毫秒(ms)、秒(s)、分(m)、小时(h)、天(d)。默认为2s |
|||
timeout = "2s" |
|||
|
|||
[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 |
@ -0,0 +1,41 @@ |
|||
# 以下配置为游戏固定配置,修改必需重启游戏生效 |
|||
|
|||
# 游戏配置(游戏模型基础配置,不可随意变动配置名) |
|||
[game] |
|||
# 游戏ID |
|||
id = 20013 |
|||
# 游戏类型 |
|||
type = 1 |
|||
# 游戏名称 |
|||
name = "TigerSlot" |
|||
# 最小牌桌数 |
|||
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 |
|||
|
|||
|
|||
|
@ -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/TigerSlot/app/core" |
|||
"xgame/game/TigerSlot/app/core/game" |
|||
"xgame/game/TigerSlot/app/core/protocol" |
|||
"xgame/game/TigerSlot/app/core/types" |
|||
"xgame/game/TigerSlot/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]) |
|||
} |
@ -0,0 +1,66 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"base/config/file" |
|||
"base/eventbus" |
|||
"base/eventbus/nats" |
|||
"xgame/game/TigerSlot/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() |
|||
// 创建服务发现
|
|||
reg := consul.NewRegistry() |
|||
|
|||
//// Begin 这里是手动注册到远端 需要内网穿透
|
|||
//ctx := context.Background()
|
|||
//ins := ®istry.ServiceInstance{
|
|||
// ID: "test-1",
|
|||
// Name: "node",
|
|||
// Kind: cluster.Node.String(),
|
|||
// Alias: "slot",
|
|||
// State: cluster.Work.String(),
|
|||
// Endpoint: fmt.Sprintf("grpc://%s:%d", "115.236.153.170", 43831),
|
|||
//}
|
|||
//
|
|||
//if err := reg.Register(ctx, ins); err != nil {
|
|||
// log.Debug("这里怎么就失败了")
|
|||
//}
|
|||
//// End
|
|||
|
|||
// 创建RPC传输器
|
|||
transporter := rpcx.NewTransporter(rpcx.WithClientDiscovery(reg)) |
|||
|
|||
// 创建网关组件
|
|||
component := node.NewNode( |
|||
node.WithLocator(locator), |
|||
node.WithRegistry(reg), |
|||
node.WithTransporter(transporter), |
|||
) |
|||
|
|||
// 初始化应用
|
|||
app.Init(component.Proxy()) |
|||
// 添加网关组件
|
|||
container.Add(component) |
|||
// 启动容器
|
|||
container.Serve() |
|||
|
|||
} |
Loading…
Reference in new issue