Browse Source

1 虎虎生财slot游戏初版本上传

dev
MoQingYu 3 months ago
parent
commit
f3161f504a
  1. 118
      game-server/game/TigerSlot/Tiger/config/blood.json
  2. 126
      game-server/game/TigerSlot/Tiger/etc/etc.toml
  3. 41
      game-server/game/TigerSlot/Tiger/etc/game.toml
  4. 13
      game-server/game/TigerSlot/app/app.go
  5. 155
      game-server/game/TigerSlot/app/config/config.go
  6. 35
      game-server/game/TigerSlot/app/core/blood/blood.go
  7. 7
      game-server/game/TigerSlot/app/core/comm/comm.go
  8. 88
      game-server/game/TigerSlot/app/core/core.go
  9. 201
      game-server/game/TigerSlot/app/core/game/game.go
  10. 203
      game-server/game/TigerSlot/app/core/game/game_bet.go
  11. 24
      game-server/game/TigerSlot/app/core/game/game_check_room.go
  12. 103
      game-server/game/TigerSlot/app/core/game/game_comm.go
  13. 18
      game-server/game/TigerSlot/app/core/game/game_config.go
  14. 25
      game-server/game/TigerSlot/app/core/game/game_disconnect.go
  15. 10
      game-server/game/TigerSlot/app/core/game/game_dissolution.go
  16. 63
      game-server/game/TigerSlot/app/core/game/game_get_room_info.go
  17. 130
      game-server/game/TigerSlot/app/core/game/game_init.go
  18. 104
      game-server/game/TigerSlot/app/core/game/game_join_room.go
  19. 676
      game-server/game/TigerSlot/app/core/game/game_logic.go
  20. 37
      game-server/game/TigerSlot/app/core/game/game_new.go
  21. 78
      game-server/game/TigerSlot/app/core/game/game_out_room.go
  22. 61
      game-server/game/TigerSlot/app/core/game/game_over.go
  23. 49
      game-server/game/TigerSlot/app/core/game/game_player.go
  24. 30
      game-server/game/TigerSlot/app/core/game/game_reconnect.go
  25. 21
      game-server/game/TigerSlot/app/core/game/game_repeat_room.go
  26. 12
      game-server/game/TigerSlot/app/core/game/game_result_set.go
  27. 8
      game-server/game/TigerSlot/app/core/game/game_resume_gameinfo.go
  28. 44
      game-server/game/TigerSlot/app/core/game/game_service.go
  29. 38
      game-server/game/TigerSlot/app/core/game/game_update_gold.go
  30. 123
      game-server/game/TigerSlot/app/core/protocol/protocol_msg.go
  31. 79
      game-server/game/TigerSlot/app/core/protocol/protocol_struct.go
  32. 46
      game-server/game/TigerSlot/app/core/room/base.go
  33. 210
      game-server/game/TigerSlot/app/core/room/roommgr.go
  34. 288
      game-server/game/TigerSlot/app/gamemanage/gamemanage.go
  35. 16
      game-server/game/TigerSlot/app/route/route.go
  36. 286
      game-server/game/TigerSlot/config/Tigers.json
  37. 134
      game-server/game/TigerSlot/etc/etc.toml
  38. 41
      game-server/game/TigerSlot/etc/game.toml
  39. 161
      game-server/game/TigerSlot/game_test.go
  40. 66
      game-server/game/TigerSlot/main.go

118
game-server/game/TigerSlot/Tiger/config/blood.json

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

126
game-server/game/TigerSlot/Tiger/etc/etc.toml

@ -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

41
game-server/game/TigerSlot/Tiger/etc/game.toml

@ -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

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

@ -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()
}

155
game-server/game/TigerSlot/app/config/config.go

@ -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
}

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

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

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

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

88
game-server/game/TigerSlot/app/core/core.go

@ -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, "玩家掉线,设置为离线状态")
}
})
}

201
game-server/game/TigerSlot/app/core/game/game.go

@ -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] // 交换
}
}

203
game-server/game/TigerSlot/app/core/game/game_bet.go

@ -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)
}

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

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

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

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

18
game-server/game/TigerSlot/app/core/game/game_config.go

@ -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)
}
}

25
game-server/game/TigerSlot/app/core/game/game_disconnect.go

@ -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()
}

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

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

63
game-server/game/TigerSlot/app/core/game/game_get_room_info.go

@ -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
}

130
game-server/game/TigerSlot/app/core/game/game_init.go

@ -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
}

104
game-server/game/TigerSlot/app/core/game/game_join_room.go

@ -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
}

676
game-server/game/TigerSlot/app/core/game/game_logic.go

@ -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
}
}

37
game-server/game/TigerSlot/app/core/game/game_new.go

@ -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
}

78
game-server/game/TigerSlot/app/core/game/game_out_room.go

@ -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
}

61
game-server/game/TigerSlot/app/core/game/game_over.go

@ -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
}

49
game-server/game/TigerSlot/app/core/game/game_player.go

@ -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 // 结算时的总输赢
}

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

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

21
game-server/game/TigerSlot/app/core/game/game_repeat_room.go

@ -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() { //复位玩家的数据
}

12
game-server/game/TigerSlot/app/core/game/game_result_set.go

@ -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}},
}

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

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

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

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

38
game-server/game/TigerSlot/app/core/game/game_update_gold.go

@ -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
}
}
}

123
game-server/game/TigerSlot/app/core/protocol/protocol_msg.go

@ -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"` // 单独用户信息
}

79
game-server/game/TigerSlot/app/core/protocol/protocol_struct.go

@ -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
}

46
game-server/game/TigerSlot/app/core/room/base.go

@ -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())))
}

210
game-server/game/TigerSlot/app/core/room/roommgr.go

@ -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)
}

288
game-server/game/TigerSlot/app/gamemanage/gamemanage.go

@ -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)
})
}

16
game-server/game/TigerSlot/app/route/route.go

@ -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 // 房间的信息返回
)

286
game-server/game/TigerSlot/config/Tigers.json

@ -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]
]
}
}
}

134
game-server/game/TigerSlot/etc/etc.toml

@ -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

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

@ -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

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

@ -0,0 +1,161 @@
package main
import (
"base/cluster/node"
"base/config"
"base/config/file"
"base/encoding/json"
"base/eventbus"
"base/eventbus/nats"
"base/locate/redis"
"base/log"
"base/log/zap"
"base/registry/consul"
"base/transport/rpcx"
"github.com/shopspring/decimal"
"testing"
"xgame/game/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])
}

66
game-server/game/TigerSlot/main.go

@ -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 := &registry.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…
Cancel
Save