This commit is contained in:
liuxiaobo 2025-06-07 22:53:54 +08:00
parent 5db1090d7d
commit 8cbd113015
32 changed files with 1224 additions and 98 deletions

View File

@ -2,6 +2,7 @@ package baseroom
import (
"game/common/proto/pb"
"github.com/fox/fox/service"
)
type IRoom interface {
@ -38,7 +39,7 @@ type IRobot interface {
}
type ICreateRoom interface {
CreateRoom(id, roomType int) (IRoom, pb.ErrCode)
CreateRoom(id, roomType int, service service.IService) (IRoom, pb.ErrCode)
}
type ICreatePlayer interface {

View File

@ -0,0 +1,62 @@
package game
const (
ColorKey = "color_config"
)
type ColorMulRate struct {
Mul int64 `json:"mul"` // 赔率
Rate int `json:"rate"` // 概率
}
type ColorRoomConfig struct {
RoomType int `json:"room_type"` // 房间类型:初级,低级,中级,高级
Name string `json:"name"` // 游戏房间名称
Blind int64 `json:"blind"` // 底注
Rate int64 `json:"rate"` // 房间明税率
WinSingleColorWeight []int `json:"win_single_color_weight"` // 胜利单色奖励三个权重
WinSingleColorMul [][]*ColorMulRate `json:"win_single_color_mul"` // 胜利单色奖励赔率 (1个同色,2个同色,3个同色)
WinDoubleColorMul []*ColorMulRate `json:"win_double_color_mul"` // 胜利双色奖励赔率
WinThreeColorMul []*ColorMulRate `json:"win_three_color_mul"` // 胜利三色奖励赔率
InitJackpot int64 `json:"init_jackpot"` // 初始jackpot值
JackpotRate int `json:"jackpot_rate"` // 单色投注区域每个颜色出现jackpot的概率
JpXRate int `json:"jp_x_rate"` // jp池赎回比例
JpYRate int `json:"jp_y_rate"` // jp池追加比例
JpXYRate int `json:"jp_xy_rate"` // 系统池为正时jackpot追加比例
AreaBetLimit int64 `json:"area_bet_limit"` // 下注区域自己的下注限制
NoBetCountMax int `json:"no_bet_count_max"` // 未操作回合踢出房间
BetList [][]int64 `json:"bet_list"` // 筹码
BetLevel []int64 `json:"bet_level"` // 筹码等级
OneCreateMin int32 `json:"one_create_min"` // 一次创建机器最少数
OneCreateMax int32 `json:"one_create_max"` // 一次创建机器人最大数
UserAddRobotNum int32 `json:"user_add_robot_num"` // 真人+机器人最小数
UserAddRobotNumMax int32 `json:"user_add_robot_num_max"` // 真人+机器人最大数
OneDeleteNum int32 `json:"one_delete_num"` // 一次删除机器人数量
BalanceMin int64 `json:"balance_min"` // 机器人生成最小金币
BalanceMax int64 `json:"balance_max"` // 机器人生成最大金币
BalanceMinDelete int64 `json:"balance_min_delete"` // 机器人低于多少金币删除
RobotCreateTime int32 `json:"robot_create_time"` // 多少时间创建机器人
RobotDeleteTime int32 `json:"robot_delete_time"` // 多少时间删除机器人
RobotBetNumMin int32 `json:"robot_bet_num_min"` // 机器人每轮下注最少次数
RobotBetNumMax int32 `json:"robot_bet_num_max"` // 机器人每轮下注最大次数
OpenRobot bool `json:"open_robot"` // 机器人开关
BetMap map[int64][]int32
TotalBetLimit int64 `json:"total_bet_limit"`
}
type ColorGameTiming struct {
//Ready int64 // 准备倒计时
Start int64 // 开始
Betting int64 // 下注
EndBetting int64 // 结束下注
OpenThreeDice int64 // 开普通3个骰子
Settle int64 // 结算
//Ranking int64 // 排行榜
}
type ColorConfig struct {
Rooms []*ColorRoomConfig `json:"rooms"` // 房间信息
GameTiming *ColorGameTiming `json:"game_timing"`
}

View File

@ -33,7 +33,7 @@ const (
func LoadSpecialConfig[T any](rd *redis.Client, specialKey string, comm *Common[T]) error {
s, err := rd.Get(context.Background(), specialKey).Result()
if err != nil && !errors.Is(err, redis.Nil) {
log.FatalF("init config:%v", err)
log.ErrorF("init config:%v", err)
return err
}
if s == "" {

View File

@ -17,3 +17,8 @@ func (u User) GetId() int64 {
func (u User) TableName() string {
return "user"
}
type GameUser struct {
User
UserResources
}

View File

@ -0,0 +1,9 @@
syntax = "proto3";
package pb;
option go_package = "common/proto/pb";

36
common/pb/match.proto Normal file
View File

@ -0,0 +1,36 @@
syntax = "proto3";
package pb;
option go_package = "common/proto/pb";
import "code.proto";
import "user.proto";
//
message C2SMatchRoom
{
int32 PlayType = 1; // id
int32 RoomType = 2; // ,
}
// NotifyUserEnterRoom才是真房间
message S2CMatchRoom
{
// color玩法配置信息
message ColorInfo
{
}
ErrCode code = 1;
int32 PlayType = 2; // id
int32 RoomType = 3; // ,
GameUser User = 4; //
ColorInfo colorInfo = 20; // color玩法配置信息
}
message NotifyUserEnterRoom
{
repeated GameUser User = 1; // 广
}

View File

@ -14,17 +14,22 @@ enum MsgId
NtfMaintainId = 1000; //
// 2000-2100
// 2000-2099
C2SChatId = 2000; //
S2CChatId = 2001; // C2SChatMsg
// 2100-2200
// 2100-2199
C2SUserLoginId = 2100; //
S2CUserLoginId = 2101;
NtfUserOnlineId = 2102;
C2SUserLogoutId = 2104;
S2CUserLogoutId = 2105;
NtfUserOfflineId = 2106;
// 2200-2299
C2SMatchRoomId = 2200; //
S2CMatchRoomId = 2201;
NtfUserEnterRoomId = 2202; //
}

View File

@ -9,6 +9,7 @@ enum ServiceTypeId
STI_Login = 101; //
STI_Chat = 102; //
STI_DB = 103; // db服
STI_Match = 104; //
STI_ColorGame = 120; // color game
}

View File

@ -7,11 +7,24 @@ option go_package = "common/proto/pb";
message ChatUser
{
int64 user_id = 1;
string nickname = 2; //
string nickname = 2; //
string avatar = 3; //
string avatar_frame = 4; //
string vip_level = 5; // vip等级
}
//
message GameUser
{
int64 user_id = 1;
string nickname = 2; //
string avatar = 3; //
string avatar_frame = 4; //
int32 vip_level = 5; // vip等级
int32 vip_exp = 6; // vip经验
int64 seat = 20; //
int64 gold = 25; //
}

View File

@ -0,0 +1,59 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.31.0
// source: colorgame.proto
package pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
var File_colorgame_proto protoreflect.FileDescriptor
const file_colorgame_proto_rawDesc = "" +
"\n" +
"\x0fcolorgame.proto\x12\x02pbB\x11Z\x0fcommon/proto/pbb\x06proto3"
var file_colorgame_proto_goTypes = []any{}
var file_colorgame_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_colorgame_proto_init() }
func file_colorgame_proto_init() {
if File_colorgame_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_colorgame_proto_rawDesc), len(file_colorgame_proto_rawDesc)),
NumEnums: 0,
NumMessages: 0,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_colorgame_proto_goTypes,
DependencyIndexes: file_colorgame_proto_depIdxs,
}.Build()
File_colorgame_proto = out.File
file_colorgame_proto_goTypes = nil
file_colorgame_proto_depIdxs = nil
}

312
common/proto/pb/match.pb.go Normal file
View File

@ -0,0 +1,312 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.6
// protoc v6.31.0
// source: match.proto
package pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// 房间匹配
type C2SMatchRoom struct {
state protoimpl.MessageState `protogen:"open.v1"`
PlayType int32 `protobuf:"varint,1,opt,name=PlayType,proto3" json:"PlayType,omitempty"` // 玩法id
RoomType int32 `protobuf:"varint,2,opt,name=RoomType,proto3" json:"RoomType,omitempty"` // 房间类型,低级,中级,高级等
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *C2SMatchRoom) Reset() {
*x = C2SMatchRoom{}
mi := &file_match_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *C2SMatchRoom) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*C2SMatchRoom) ProtoMessage() {}
func (x *C2SMatchRoom) ProtoReflect() protoreflect.Message {
mi := &file_match_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use C2SMatchRoom.ProtoReflect.Descriptor instead.
func (*C2SMatchRoom) Descriptor() ([]byte, []int) {
return file_match_proto_rawDescGZIP(), []int{0}
}
func (x *C2SMatchRoom) GetPlayType() int32 {
if x != nil {
return x.PlayType
}
return 0
}
func (x *C2SMatchRoom) GetRoomType() int32 {
if x != nil {
return x.RoomType
}
return 0
}
// 玩家进房间返回 匹配服返回该消息假房间。匹配成功后发给对应玩法服玩法服发NotifyUserEnterRoom才是真房间
type S2CMatchRoom struct {
state protoimpl.MessageState `protogen:"open.v1"`
Code ErrCode `protobuf:"varint,1,opt,name=code,proto3,enum=pb.ErrCode" json:"code,omitempty"`
PlayType int32 `protobuf:"varint,2,opt,name=PlayType,proto3" json:"PlayType,omitempty"` // 玩法id
RoomType int32 `protobuf:"varint,3,opt,name=RoomType,proto3" json:"RoomType,omitempty"` // 房间类型,低级,中级,高级等
User *GameUser `protobuf:"bytes,4,opt,name=User,proto3" json:"User,omitempty"` // 玩家数据
ColorInfo *S2CMatchRoom_ColorInfo `protobuf:"bytes,20,opt,name=colorInfo,proto3" json:"colorInfo,omitempty"` // color玩法配置信息
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *S2CMatchRoom) Reset() {
*x = S2CMatchRoom{}
mi := &file_match_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *S2CMatchRoom) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*S2CMatchRoom) ProtoMessage() {}
func (x *S2CMatchRoom) ProtoReflect() protoreflect.Message {
mi := &file_match_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use S2CMatchRoom.ProtoReflect.Descriptor instead.
func (*S2CMatchRoom) Descriptor() ([]byte, []int) {
return file_match_proto_rawDescGZIP(), []int{1}
}
func (x *S2CMatchRoom) GetCode() ErrCode {
if x != nil {
return x.Code
}
return ErrCode_OK
}
func (x *S2CMatchRoom) GetPlayType() int32 {
if x != nil {
return x.PlayType
}
return 0
}
func (x *S2CMatchRoom) GetRoomType() int32 {
if x != nil {
return x.RoomType
}
return 0
}
func (x *S2CMatchRoom) GetUser() *GameUser {
if x != nil {
return x.User
}
return nil
}
func (x *S2CMatchRoom) GetColorInfo() *S2CMatchRoom_ColorInfo {
if x != nil {
return x.ColorInfo
}
return nil
}
type NotifyUserEnterRoom struct {
state protoimpl.MessageState `protogen:"open.v1"`
User []*GameUser `protobuf:"bytes,1,rep,name=User,proto3" json:"User,omitempty"` // 玩家进房间广播
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *NotifyUserEnterRoom) Reset() {
*x = NotifyUserEnterRoom{}
mi := &file_match_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *NotifyUserEnterRoom) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NotifyUserEnterRoom) ProtoMessage() {}
func (x *NotifyUserEnterRoom) ProtoReflect() protoreflect.Message {
mi := &file_match_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use NotifyUserEnterRoom.ProtoReflect.Descriptor instead.
func (*NotifyUserEnterRoom) Descriptor() ([]byte, []int) {
return file_match_proto_rawDescGZIP(), []int{2}
}
func (x *NotifyUserEnterRoom) GetUser() []*GameUser {
if x != nil {
return x.User
}
return nil
}
// color玩法配置信息
type S2CMatchRoom_ColorInfo struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *S2CMatchRoom_ColorInfo) Reset() {
*x = S2CMatchRoom_ColorInfo{}
mi := &file_match_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *S2CMatchRoom_ColorInfo) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*S2CMatchRoom_ColorInfo) ProtoMessage() {}
func (x *S2CMatchRoom_ColorInfo) ProtoReflect() protoreflect.Message {
mi := &file_match_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use S2CMatchRoom_ColorInfo.ProtoReflect.Descriptor instead.
func (*S2CMatchRoom_ColorInfo) Descriptor() ([]byte, []int) {
return file_match_proto_rawDescGZIP(), []int{1, 0}
}
var File_match_proto protoreflect.FileDescriptor
const file_match_proto_rawDesc = "" +
"\n" +
"\vmatch.proto\x12\x02pb\x1a\n" +
"code.proto\x1a\n" +
"user.proto\"F\n" +
"\fC2SMatchRoom\x12\x1a\n" +
"\bPlayType\x18\x01 \x01(\x05R\bPlayType\x12\x1a\n" +
"\bRoomType\x18\x02 \x01(\x05R\bRoomType\"\xd0\x01\n" +
"\fS2CMatchRoom\x12\x1f\n" +
"\x04code\x18\x01 \x01(\x0e2\v.pb.ErrCodeR\x04code\x12\x1a\n" +
"\bPlayType\x18\x02 \x01(\x05R\bPlayType\x12\x1a\n" +
"\bRoomType\x18\x03 \x01(\x05R\bRoomType\x12 \n" +
"\x04User\x18\x04 \x01(\v2\f.pb.GameUserR\x04User\x128\n" +
"\tcolorInfo\x18\x14 \x01(\v2\x1a.pb.S2CMatchRoom.ColorInfoR\tcolorInfo\x1a\v\n" +
"\tColorInfo\"7\n" +
"\x13NotifyUserEnterRoom\x12 \n" +
"\x04User\x18\x01 \x03(\v2\f.pb.GameUserR\x04UserB\x11Z\x0fcommon/proto/pbb\x06proto3"
var (
file_match_proto_rawDescOnce sync.Once
file_match_proto_rawDescData []byte
)
func file_match_proto_rawDescGZIP() []byte {
file_match_proto_rawDescOnce.Do(func() {
file_match_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_match_proto_rawDesc), len(file_match_proto_rawDesc)))
})
return file_match_proto_rawDescData
}
var file_match_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_match_proto_goTypes = []any{
(*C2SMatchRoom)(nil), // 0: pb.C2SMatchRoom
(*S2CMatchRoom)(nil), // 1: pb.S2CMatchRoom
(*NotifyUserEnterRoom)(nil), // 2: pb.NotifyUserEnterRoom
(*S2CMatchRoom_ColorInfo)(nil), // 3: pb.S2CMatchRoom.ColorInfo
(ErrCode)(0), // 4: pb.ErrCode
(*GameUser)(nil), // 5: pb.GameUser
}
var file_match_proto_depIdxs = []int32{
4, // 0: pb.S2CMatchRoom.code:type_name -> pb.ErrCode
5, // 1: pb.S2CMatchRoom.User:type_name -> pb.GameUser
3, // 2: pb.S2CMatchRoom.colorInfo:type_name -> pb.S2CMatchRoom.ColorInfo
5, // 3: pb.NotifyUserEnterRoom.User:type_name -> pb.GameUser
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_match_proto_init() }
func file_match_proto_init() {
if File_match_proto != nil {
return
}
file_code_proto_init()
file_user_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_match_proto_rawDesc), len(file_match_proto_rawDesc)),
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_match_proto_goTypes,
DependencyIndexes: file_match_proto_depIdxs,
MessageInfos: file_match_proto_msgTypes,
}.Build()
File_match_proto = out.File
file_match_proto_goTypes = nil
file_match_proto_depIdxs = nil
}

View File

@ -30,16 +30,20 @@ type MsgId int32
const (
MsgId_MI_Unknown MsgId = 0
MsgId_NtfMaintainId MsgId = 1000 // 通知维护
// 聊天服 2000-2100
// 聊天服 2000-2099
MsgId_C2SChatId MsgId = 2000 // 玩家聊天消息
MsgId_S2CChatId MsgId = 2001 // 复用C2SChatMsg
// 登陆服 2100-2200
// 登陆服 2100-2199
MsgId_C2SUserLoginId MsgId = 2100 // 玩家登陆
MsgId_S2CUserLoginId MsgId = 2101
MsgId_NtfUserOnlineId MsgId = 2102
MsgId_C2SUserLogoutId MsgId = 2104
MsgId_S2CUserLogoutId MsgId = 2105
MsgId_NtfUserOfflineId MsgId = 2106
// 匹配服 2200-2299
MsgId_C2SMatchRoomId MsgId = 2200 // 匹配服
MsgId_S2CMatchRoomId MsgId = 2201
MsgId_NtfUserEnterRoomId MsgId = 2202 // 玩家进入房间 所有玩法共用此消息
)
// Enum value maps for MsgId.
@ -55,18 +59,24 @@ var (
2104: "C2SUserLogoutId",
2105: "S2CUserLogoutId",
2106: "NtfUserOfflineId",
2200: "C2SMatchRoomId",
2201: "S2CMatchRoomId",
2202: "NtfUserEnterRoomId",
}
MsgId_value = map[string]int32{
"MI_Unknown": 0,
"NtfMaintainId": 1000,
"C2SChatId": 2000,
"S2CChatId": 2001,
"C2SUserLoginId": 2100,
"S2CUserLoginId": 2101,
"NtfUserOnlineId": 2102,
"C2SUserLogoutId": 2104,
"S2CUserLogoutId": 2105,
"NtfUserOfflineId": 2106,
"MI_Unknown": 0,
"NtfMaintainId": 1000,
"C2SChatId": 2000,
"S2CChatId": 2001,
"C2SUserLoginId": 2100,
"S2CUserLoginId": 2101,
"NtfUserOnlineId": 2102,
"C2SUserLogoutId": 2104,
"S2CUserLogoutId": 2105,
"NtfUserOfflineId": 2106,
"C2SMatchRoomId": 2200,
"S2CMatchRoomId": 2201,
"NtfUserEnterRoomId": 2202,
}
)
@ -101,7 +111,7 @@ var File_msgId_proto protoreflect.FileDescriptor
const file_msgId_proto_rawDesc = "" +
"\n" +
"\vmsgId.proto\x12\x02pb*\xce\x01\n" +
"\vmsgId.proto\x12\x02pb*\x91\x02\n" +
"\x05MsgId\x12\x0e\n" +
"\n" +
"MI_Unknown\x10\x00\x12\x12\n" +
@ -113,7 +123,10 @@ const file_msgId_proto_rawDesc = "" +
"\x0fNtfUserOnlineId\x10\xb6\x10\x12\x14\n" +
"\x0fC2SUserLogoutId\x10\xb8\x10\x12\x14\n" +
"\x0fS2CUserLogoutId\x10\xb9\x10\x12\x15\n" +
"\x10NtfUserOfflineId\x10\xba\x10B\x11Z\x0fcommon/proto/pbb\x06proto3"
"\x10NtfUserOfflineId\x10\xba\x10\x12\x13\n" +
"\x0eC2SMatchRoomId\x10\x98\x11\x12\x13\n" +
"\x0eS2CMatchRoomId\x10\x99\x11\x12\x17\n" +
"\x12NtfUserEnterRoomId\x10\x9a\x11B\x11Z\x0fcommon/proto/pbb\x06proto3"
var (
file_msgId_proto_rawDescOnce sync.Once

View File

@ -29,6 +29,7 @@ const (
ServiceTypeId_STI_Login ServiceTypeId = 101 // 登陆服
ServiceTypeId_STI_Chat ServiceTypeId = 102 // 聊天服
ServiceTypeId_STI_DB ServiceTypeId = 103 // db服
ServiceTypeId_STI_Match ServiceTypeId = 104 // 匹配服
ServiceTypeId_STI_ColorGame ServiceTypeId = 120 // color game
)
@ -40,6 +41,7 @@ var (
101: "STI_Login",
102: "STI_Chat",
103: "STI_DB",
104: "STI_Match",
120: "STI_ColorGame",
}
ServiceTypeId_value = map[string]int32{
@ -48,6 +50,7 @@ var (
"STI_Login": 101,
"STI_Chat": 102,
"STI_DB": 103,
"STI_Match": 104,
"STI_ColorGame": 120,
}
)
@ -83,14 +86,15 @@ var File_service_proto protoreflect.FileDescriptor
const file_service_proto_rawDesc = "" +
"\n" +
"\rservice.proto\x12\x02pb*j\n" +
"\rservice.proto\x12\x02pb*y\n" +
"\rServiceTypeId\x12\x0f\n" +
"\vSTI_Unknown\x10\x00\x12\f\n" +
"\bSTI_Gate\x10d\x12\r\n" +
"\tSTI_Login\x10e\x12\f\n" +
"\bSTI_Chat\x10f\x12\n" +
"\n" +
"\x06STI_DB\x10g\x12\x11\n" +
"\x06STI_DB\x10g\x12\r\n" +
"\tSTI_Match\x10h\x12\x11\n" +
"\rSTI_ColorGame\x10xB\x11Z\x0fcommon/proto/pbb\x06proto3"
var (

View File

@ -25,7 +25,7 @@ const (
type ChatUser struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
Nickname string `protobuf:"bytes,2,opt,name=nickname,proto3" json:"nickname,omitempty"` // 用户名
Nickname string `protobuf:"bytes,2,opt,name=nickname,proto3" json:"nickname,omitempty"` // 昵称
Avatar string `protobuf:"bytes,3,opt,name=avatar,proto3" json:"avatar,omitempty"` // 头像
AvatarFrame string `protobuf:"bytes,4,opt,name=avatar_frame,json=avatarFrame,proto3" json:"avatar_frame,omitempty"` // 头像框
VipLevel string `protobuf:"bytes,5,opt,name=vip_level,json=vipLevel,proto3" json:"vip_level,omitempty"` // vip等级
@ -98,6 +98,107 @@ func (x *ChatUser) GetVipLevel() string {
return ""
}
// 房间内的玩家数据
type GameUser struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
Nickname string `protobuf:"bytes,2,opt,name=nickname,proto3" json:"nickname,omitempty"` // 昵称
Avatar string `protobuf:"bytes,3,opt,name=avatar,proto3" json:"avatar,omitempty"` // 头像
AvatarFrame string `protobuf:"bytes,4,opt,name=avatar_frame,json=avatarFrame,proto3" json:"avatar_frame,omitempty"` // 头像框
VipLevel int32 `protobuf:"varint,5,opt,name=vip_level,json=vipLevel,proto3" json:"vip_level,omitempty"` // vip等级
VipExp int32 `protobuf:"varint,6,opt,name=vip_exp,json=vipExp,proto3" json:"vip_exp,omitempty"` // vip经验
Seat int64 `protobuf:"varint,20,opt,name=seat,proto3" json:"seat,omitempty"` // 座位
Gold int64 `protobuf:"varint,25,opt,name=gold,proto3" json:"gold,omitempty"` // 金币
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GameUser) Reset() {
*x = GameUser{}
mi := &file_user_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GameUser) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GameUser) ProtoMessage() {}
func (x *GameUser) ProtoReflect() protoreflect.Message {
mi := &file_user_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GameUser.ProtoReflect.Descriptor instead.
func (*GameUser) Descriptor() ([]byte, []int) {
return file_user_proto_rawDescGZIP(), []int{1}
}
func (x *GameUser) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
func (x *GameUser) GetNickname() string {
if x != nil {
return x.Nickname
}
return ""
}
func (x *GameUser) GetAvatar() string {
if x != nil {
return x.Avatar
}
return ""
}
func (x *GameUser) GetAvatarFrame() string {
if x != nil {
return x.AvatarFrame
}
return ""
}
func (x *GameUser) GetVipLevel() int32 {
if x != nil {
return x.VipLevel
}
return 0
}
func (x *GameUser) GetVipExp() int32 {
if x != nil {
return x.VipExp
}
return 0
}
func (x *GameUser) GetSeat() int64 {
if x != nil {
return x.Seat
}
return 0
}
func (x *GameUser) GetGold() int64 {
if x != nil {
return x.Gold
}
return 0
}
var File_user_proto protoreflect.FileDescriptor
const file_user_proto_rawDesc = "" +
@ -109,7 +210,16 @@ const file_user_proto_rawDesc = "" +
"\bnickname\x18\x02 \x01(\tR\bnickname\x12\x16\n" +
"\x06avatar\x18\x03 \x01(\tR\x06avatar\x12!\n" +
"\favatar_frame\x18\x04 \x01(\tR\vavatarFrame\x12\x1b\n" +
"\tvip_level\x18\x05 \x01(\tR\bvipLevelB\x11Z\x0fcommon/proto/pbb\x06proto3"
"\tvip_level\x18\x05 \x01(\tR\bvipLevel\"\xd8\x01\n" +
"\bGameUser\x12\x17\n" +
"\auser_id\x18\x01 \x01(\x03R\x06userId\x12\x1a\n" +
"\bnickname\x18\x02 \x01(\tR\bnickname\x12\x16\n" +
"\x06avatar\x18\x03 \x01(\tR\x06avatar\x12!\n" +
"\favatar_frame\x18\x04 \x01(\tR\vavatarFrame\x12\x1b\n" +
"\tvip_level\x18\x05 \x01(\x05R\bvipLevel\x12\x17\n" +
"\avip_exp\x18\x06 \x01(\x05R\x06vipExp\x12\x12\n" +
"\x04seat\x18\x14 \x01(\x03R\x04seat\x12\x12\n" +
"\x04gold\x18\x19 \x01(\x03R\x04goldB\x11Z\x0fcommon/proto/pbb\x06proto3"
var (
file_user_proto_rawDescOnce sync.Once
@ -123,9 +233,10 @@ func file_user_proto_rawDescGZIP() []byte {
return file_user_proto_rawDescData
}
var file_user_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_user_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_user_proto_goTypes = []any{
(*ChatUser)(nil), // 0: pb.ChatUser
(*GameUser)(nil), // 1: pb.GameUser
}
var file_user_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
@ -146,7 +257,7 @@ func file_user_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_proto_rawDesc), len(file_user_proto_rawDesc)),
NumEnums: 0,
NumMessages: 1,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},

View File

@ -9,4 +9,5 @@ const (
GetUserByUid = "get.user.uid.rpc"
GetUserByAccountId = "get.user.account.id.rpc"
GetUserResources = "get.user.resources.rpc"
GetGameUser = "get.game.user.rpc"
)

View File

@ -5,6 +5,7 @@ const (
Chat = "chat"
Login = "login"
Db = "db"
Match = "match"
// 下面是具体玩法服
ColorGame = "color_game"

View File

@ -148,3 +148,31 @@ func (m *UserBindService) FindTopic(userId int64, serviceTypeId pb.ServiceTypeId
}
return "", sName
}
// 从etcd中hash一个服务节点
func (m *UserBindService) HashServiceNode(typeId pb.ServiceTypeId, uid int64) (*etcd.ServiceNode, error) {
var nodes []etcd.ServiceNode
var version string
m.etcdRegistry.GetNodes().Range(func(_, value any) bool {
node, ok := value.(etcd.ServiceNode)
if ok && node.TypeId == int(typeId) {
if version < node.Version {
version = node.Version
}
}
return true
})
m.etcdRegistry.GetNodes().Range(func(_, value any) bool {
if node, ok := value.(etcd.ServiceNode); ok && node.TypeId == int(typeId) {
if version == node.Version {
nodes = append(nodes, node)
}
}
return true
})
if len(nodes) == 0 {
return nil, fmt.Errorf("not found service node.type id:%v. all node:%v", typeId, m.stringAllServiceNode())
}
n := uid % int64(len(nodes))
return &nodes[n], nil
}

View File

@ -1,8 +1,23 @@
package utils
import "math"
func Tie[T any](ret bool, v1, v2 T) T {
if ret {
return v1
}
return v2
}
func VipLevel(sumExp int32) (lv, exp int32) {
lv = 1
exp = 100
for {
if sumExp < exp {
return lv, sumExp
}
sumExp -= exp
lv++
exp = int32(math.Pow(float64(lv), 1.6))
}
}

View File

@ -0,0 +1,7 @@
package utils
import "testing"
func TestCleanWarn(t *testing.T) {
_ = Tie[int](true, 1, 2)
}

View File

@ -1,79 +1,21 @@
package config
import (
"context"
"encoding/json"
"game/common/config"
"game/common/config/game"
"game/common/constant"
"github.com/fox/fox/db"
"github.com/fox/fox/log"
"github.com/go-redis/redis/v8"
)
const (
specialKey = "color_config"
)
var Command *config.Command
var Cfg *config.Common[ColorConfig]
type MulRate struct {
Mul int64 `json:"mul"` // 赔率
Rate int `json:"rate"` // 概率
}
type RoomConfig struct {
RoomType int `json:"room_type"` // 房间类型:初级,低级,中级,高级
Name string `json:"name"` // 游戏房间名称
Blind int64 `json:"blind"` // 底注
Rate int64 `json:"rate"` // 房间明税率
WinSingleColorWeight []int `json:"win_single_color_weight"` // 胜利单色奖励三个权重
WinSingleColorMul [][]*MulRate `json:"win_single_color_mul"` // 胜利单色奖励赔率 (1个同色,2个同色,3个同色)
WinDoubleColorMul []*MulRate `json:"win_double_color_mul"` // 胜利双色奖励赔率
WinThreeColorMul []*MulRate `json:"win_three_color_mul"` // 胜利三色奖励赔率
InitJackpot int64 `json:"init_jackpot"` // 初始jackpot值
JackpotRate int `json:"jackpot_rate"` // 单色投注区域每个颜色出现jackpot的概率
JpXRate int `json:"jp_x_rate"` // jp池赎回比例
JpYRate int `json:"jp_y_rate"` // jp池追加比例
JpXYRate int `json:"jp_xy_rate"` // 系统池为正时jackpot追加比例
AreaBetLimit int64 `json:"area_bet_limit"` // 下注区域自己的下注限制
NoBetCountMax int `json:"no_bet_count_max"` // 未操作回合踢出房间
BetList [][]int64 `json:"bet_list"` // 筹码
BetLevel []int64 `json:"bet_level"` // 筹码等级
OneCreateMin int32 `json:"one_create_min"` // 一次创建机器最少数
OneCreateMax int32 `json:"one_create_max"` // 一次创建机器人最大数
UserAddRobotNum int32 `json:"user_add_robot_num"` // 真人+机器人最小数
UserAddRobotNumMax int32 `json:"user_add_robot_num_max"` // 真人+机器人最大数
OneDeleteNum int32 `json:"one_delete_num"` // 一次删除机器人数量
BalanceMin int64 `json:"balance_min"` // 机器人生成最小金币
BalanceMax int64 `json:"balance_max"` // 机器人生成最大金币
BalanceMinDelete int64 `json:"balance_min_delete"` // 机器人低于多少金币删除
RobotCreateTime int32 `json:"robot_create_time"` // 多少时间创建机器人
RobotDeleteTime int32 `json:"robot_delete_time"` // 多少时间删除机器人
RobotBetNumMin int32 `json:"robot_bet_num_min"` // 机器人每轮下注最少次数
RobotBetNumMax int32 `json:"robot_bet_num_max"` // 机器人每轮下注最大次数
OpenRobot bool `json:"open_robot"` // 机器人开关
BetMap map[int64][]int32
TotalBetLimit int64 `json:"total_bet_limit"`
}
type GameTiming struct {
//Ready int64 // 准备倒计时
Start int64 // 开始
Betting int64 // 下注
EndBetting int64 // 结束下注
OpenThreeDice int64 // 开普通3个骰子
Settle int64 // 结算
//Ranking int64 // 排行榜
}
type ColorConfig struct {
Rooms []*RoomConfig `json:"rooms"` // 房间信息
GameTiming *GameTiming `json:"game_timing"`
}
var Cfg *config.Common[game.ColorConfig]
func InitLog() {
log.Open("./log/login.log", log.DebugL)
log.Open("./log/color.log", log.DebugL)
log.Info("")
log.Info("")
log.Info("")
@ -88,7 +30,7 @@ func LoadConfig(GitCommit, GitBranch, BuildDate string) {
return
}
defer func() { _ = rdb.Close() }()
Cfg, err = config.LoadCommonConfig[ColorConfig](rdb, GitCommit, GitBranch, BuildDate)
Cfg, err = config.LoadCommonConfig[game.ColorConfig](rdb, GitCommit, GitBranch, BuildDate)
if err != nil {
log.Error(err.Error())
return
@ -98,11 +40,11 @@ func LoadConfig(GitCommit, GitBranch, BuildDate string) {
}
func LoadColorConfig(rdb *redis.Client) {
if err := config.LoadSpecialConfig[ColorConfig](rdb, specialKey, Cfg); err == nil {
if err := config.LoadSpecialConfig[game.ColorConfig](rdb, game.ColorKey, Cfg); err == nil {
return
}
Cfg.Special = &ColorConfig{}
Cfg.Special.GameTiming = &GameTiming{
Cfg.Special = &game.ColorConfig{}
Cfg.Special.GameTiming = &game.ColorGameTiming{
//Ready: 100, // 倒计时321
Start: 2000,
Betting: 15000,
@ -113,7 +55,7 @@ func LoadColorConfig(rdb *redis.Client) {
}
WinSingleColorWeight := [3]int{50, 25, 25}
WinSingleColorMul := [3][]*MulRate{
WinSingleColorMul := [3][]*game.ColorMulRate{
{{Mul: 0.6 * 100, Rate: 75 * 100}, {Mul: 1 * 100, Rate: 12 * 100},
{Mul: 1.5 * 100, Rate: 7.5 * 100}, {Mul: 2 * 100, Rate: 5.5 * 100}},
@ -127,16 +69,16 @@ func LoadColorConfig(rdb *redis.Client) {
{Mul: 29 * 100, Rate: 0.8 * 100}, {Mul: 49 * 100, Rate: 0.7 * 100},
{Mul: 99 * 100, Rate: 0.6 * 100}, {Mul: 9 * 100, Rate: 0.5 * 100}},
}
WinDoubleColorMul := []*MulRate{{Mul: 8 * 100, Rate: 66 * 100}, {Mul: 11 * 100, Rate: 17 * 100},
WinDoubleColorMul := []*game.ColorMulRate{{Mul: 8 * 100, Rate: 66 * 100}, {Mul: 11 * 100, Rate: 17 * 100},
{Mul: 14 * 100, Rate: 9 * 100}, {Mul: 19 * 100, Rate: 3.5 * 100},
{Mul: 24 * 100, Rate: 1.9 * 100}, {Mul: 54 * 100, Rate: 1.5 * 100},
{Mul: 74 * 100, Rate: 1.05 * 100}, {Mul: 99 * 100, Rate: 0.05 * 100}}
WinThreeColorMul := []*MulRate{{Mul: 150 * 100, Rate: 84.5 * 100}, {Mul: 199 * 100, Rate: 5.5 * 100},
WinThreeColorMul := []*game.ColorMulRate{{Mul: 150 * 100, Rate: 84.5 * 100}, {Mul: 199 * 100, Rate: 5.5 * 100},
{Mul: 299 * 100, Rate: 4 * 100}, {Mul: 599 * 100, Rate: 3 * 100},
{Mul: 799 * 100, Rate: 2 * 100}, {Mul: 999 * 100, Rate: 1 * 100}}
rmConfig := &RoomConfig{
rmConfig := &game.ColorRoomConfig{
BetList: [][]int64{
{1000, 2000, 3000, 5000, 10000, 20000},
{5000, 20000, 30000, 40000, 50000, 800000},
@ -170,4 +112,8 @@ func LoadColorConfig(rdb *redis.Client) {
RobotBetNumMax: 4,
}
Cfg.Special.Rooms = append(Cfg.Special.Rooms, rmConfig)
if bs, err := json.Marshal(&Cfg.Special); err == nil {
err = rdb.Set(context.Background(), game.ColorKey, string(bs), 0).Err()
}
}

View File

@ -7,6 +7,8 @@ import (
type ColorPlayer struct {
user.User
gateTopicName string
roomId int
}
func (p *ColorPlayer) Id() int64 {
@ -16,3 +18,11 @@ func (p *ColorPlayer) Id() int64 {
func (p *ColorPlayer) Robot() baseroom.IRobot {
return nil
}
func (p *ColorPlayer) GateTopicName() string {
return p.gateTopicName
}
func (p *ColorPlayer) RoomId() int {
return p.roomId
}

View File

@ -3,13 +3,14 @@ package room
import (
"game/common/baseroom"
"game/common/proto/pb"
"github.com/fox/fox/service"
)
type RoomFactory struct {
}
func (r *RoomFactory) CreateRoom(id, roomType int) (baseroom.IRoom, pb.ErrCode) {
return newColorRoom(id, roomType)
func (r *RoomFactory) CreateRoom(id, roomType int, srv service.IService) (baseroom.IRoom, pb.ErrCode) {
return newColorRoom(id, roomType, srv)
}
type PlayerFactory struct {

View File

@ -90,3 +90,21 @@ func (s *DbService) onGetUserResources(iMsg *ipb.InternalMsg) *ipb.InternalMsg {
})
return iMsg
}
// 获取用户数据
func (s *DbService) onGetGameUser(iMsg *ipb.InternalMsg) *ipb.InternalMsg {
operationDb[user.GameUser](iMsg, func(gameUser *user.GameUser) (*user.GameUser, pb.ErrCode) {
us, code := operation.NewUserOp().Find(gameUser.ID)
if code != pb.ErrCode_OK {
return nil, code
}
res, code1 := operation.NewUserResourcesOp().Find(gameUser.ID)
if code1 != pb.ErrCode_OK {
return nil, code
}
gameUser.User = *us
gameUser.UserResources = *res
return gameUser, pb.ErrCode_OK
})
return iMsg
}

View File

@ -14,5 +14,6 @@ func (s *DbService) initRpcProcessor() {
rpcName.GetUserByUid: s.onGetUserByUid,
rpcName.GetUserByAccountId: s.onGetUserByAccountId,
rpcName.GetUserResources: s.onGetUserResources,
rpcName.GetGameUser: s.onGetGameUser,
})
}

32
server/match/cmd/cmd.go Normal file
View File

@ -0,0 +1,32 @@
package cmd
import (
"fmt"
"game/server/match/config"
"game/server/match/model"
"game/server/match/server"
"github.com/fox/fox/log"
"os"
"os/signal"
"syscall"
)
func initRepo() {
model.InitRedis()
//model.InitDb()
}
func Run(GitCommit, GitBranch, BuildDate string) {
config.InitLog()
config.LoadConfig(GitCommit, GitBranch, BuildDate)
log.Info(fmt.Sprintf("版本分支:%v,hash值:%v,编译时间:%v", GitBranch, GitCommit, BuildDate))
initRepo()
server.Init()
// 截获 SIGINT 和 SIGTERM 信号
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
sig := <-c
server.Stop()
log.Info(fmt.Sprintf("received %s, initiating shutdown...", sig))
}

View File

@ -0,0 +1,71 @@
package config
import (
"context"
"encoding/json"
"errors"
"fmt"
"game/common/config"
"game/common/constant"
"github.com/fox/fox/db"
"github.com/fox/fox/log"
"github.com/go-redis/redis/v8"
)
var Command *config.Command
var Cfg *config.Common[MatchConfig]
type MatchConfig struct {
//Color *game.ColorConfig
}
func InitLog() {
log.Open("./log/match.log", log.DebugL)
log.Info("")
log.Info("")
log.Info("")
log.Info("-----init log success-----")
}
func LoadConfig(GitCommit, GitBranch, BuildDate string) {
Command = config.ParseCommand()
rdb, err := db.InitRedis(Command.RedisPassword, Command.RedisHost, Command.RedisPort, constant.Redis0Config)
if err != nil {
log.Error(err.Error())
return
}
defer func() { _ = rdb.Close() }()
Cfg, err = config.LoadCommonConfig[MatchConfig](rdb, GitCommit, GitBranch, BuildDate)
if err != nil {
log.Error(err.Error())
return
}
log.DebugF("load common config success")
//Cfg.Special.Color, err = LoadGameConfig[game.ColorConfig](rdb, game.ColorKey)
}
type Value[T any] struct {
Val T
}
func LoadGameConfig[T any](rd *redis.Client, gameKey string) (*T, error) {
s, err := rd.Get(context.Background(), gameKey).Result()
if err != nil && !errors.Is(err, redis.Nil) {
log.ErrorF("init config:%v", err)
return nil, err
}
if s == "" {
return nil, fmt.Errorf("config:%s not found from redis", gameKey)
}
var val Value[T]
if err = json.Unmarshal([]byte(s), &val.Val); err != nil {
log.ErrorF("init %v config:%v", gameKey, err)
return nil, err
}
return &val.Val, nil
}
//func GetColorConfig() *game.ColorConfig {
// return Cfg.Special.Color
//}

24
server/match/main.go Normal file
View File

@ -0,0 +1,24 @@
package main
import (
"game/server/match/cmd"
"github.com/fox/fox/ksync"
"github.com/fox/fox/log"
"time"
)
var (
GitCommit = "unknown"
GitBranch = "unknown"
BuildDate = "unknown"
)
func main() {
tm, err := time.Parse("20060102150405", BuildDate)
if err == nil {
BuildDate = tm.Format("2006-01-02 15:04:05")
}
ksync.RunSafe(func() {
cmd.Run(GitBranch, GitCommit, BuildDate)
}, func() { log.ErrorF("reset run") })
}

View File

@ -0,0 +1,58 @@
package match
import (
"game/common/model/user"
"game/common/proto/pb"
)
type MatchPlayer struct {
user.User
user.UserResources
}
type RoomTypeMatch struct {
RoomMatch map[int32][]MatchPlayer
}
type MatchMgr struct {
users map[pb.ServiceTypeId]*RoomTypeMatch
}
func NewMatchMgr() *MatchMgr {
return &MatchMgr{
users: make(map[pb.ServiceTypeId]*RoomTypeMatch),
}
}
//func (mgr *MatchMgr) Add(us *MatchPlayer, sid pb.ServiceTypeId, roomType int32) {
// users, _ := mgr.users[sid]
// for _, u := range users {
// if u.ID == us.ID {
// return
// }
// }
// users = append(users, *us)
// mgr.users[sid] = users
//}
//
//func (mgr *MatchMgr) Remove(us *MatchPlayer, sid pb.ServiceTypeId, roomType int32) {
// users, _ := mgr.users[sid]
// for pos, u := range users {
// if u.ID == us.ID {
// users = append(users[:pos], users[pos+1:]...)
// mgr.users[sid] = users
// return
// }
// }
//}
//
//func (mgr *MatchMgr) Match(sid pb.ServiceTypeId, num int) []MatchPlayer {
// users, _ := mgr.users[sid]
// if len(users) <= num {
// return nil
// }
// ret := users[:num]
// users = users[num:]
// mgr.users[sid] = users
// return ret
//}

70
server/match/model/db.go Normal file
View File

@ -0,0 +1,70 @@
package model
import (
"game/common/constant"
"game/common/utils"
"game/server/match/config"
"github.com/fox/fox/db"
"github.com/fox/fox/log"
"github.com/go-redis/redis/v8"
)
var (
UserBindServiceRedis *redis.Client
ConfigRedis *redis.Client
// UserDB *gorm.DB
// LogDB *gorm.DB
)
func InitRedis() {
log.Debug("init redis")
var err error
cfg := &config.Cfg.Redis
UserBindServiceRedis, err = db.InitRedis(cfg.Password, cfg.Host, cfg.Port, constant.Redis3UserBindService)
if err != nil {
log.Fatal(err.Error())
return
}
utils.AutoSetRedisPool(UserBindServiceRedis)
ConfigRedis, err = db.InitRedis(cfg.Password, cfg.Host, cfg.Port, constant.Redis0Config)
if err != nil {
log.Fatal(err.Error())
return
}
utils.AutoSetRedisPool(UserBindServiceRedis)
}
//
// func InitDb() {
// log.Debug("init db")
// var err error
// cfg := &config.Cfg.Mysql
// UserDB, err = db.InitMysql(cfg.Username, cfg.Password, cfg.Host, cfg.Port, cfg.DbName)
// if err != nil {
// log.Fatal(err.Error())
// return
// }
// cfg = &config.Cfg.MysqlLog
// LogDB, err = db.InitMysql(cfg.Username, cfg.Password, cfg.Host, cfg.Port, cfg.DbName)
// if err != nil {
// log.Fatal(err.Error())
// return
// }
// // 自动迁移game库表结构
// err = UserDB.AutoMigrate(
// &UserAccount{},
// )
// if err != nil {
// log.Fatal(err.Error())
// return
// }
// // 自动迁移game_log库表结构
// err = LogDB.AutoMigrate(
// &UserLoginLog{},
// )
// if err != nil {
// log.Fatal(err.Error())
// return
// }
// }

View File

@ -0,0 +1,76 @@
package server
import (
"encoding/json"
"game/common/model/user"
"game/common/proto/pb"
"game/common/rpcName"
"game/common/utils"
"github.com/fox/fox/ipb"
"github.com/fox/fox/ksync"
"github.com/fox/fox/log"
"github.com/fox/fox/service"
)
func (s *MatchService) getGameUser(uid int64) (*user.GameUser, pb.ErrCode) {
node, err := s.bindService.HashServiceNode(pb.ServiceTypeId_STI_DB, uid)
if err != nil {
log.ErrorF("db service node error:%v", err)
return nil, pb.ErrCode_SystemErr
}
us := &user.GameUser{
User: user.User{
ID: uid,
},
UserResources: user.UserResources{
UID: uid,
},
}
rpcMsg := ipb.MakeRpcMsg[user.GameUser](rpcName.GetGameUser, uid, us)
rspMsg, err := s.Call(service.RpcTopicEx(node.Name), timeout, rpcMsg)
if err != nil {
log.ErrorF(s.Log("call rpc:%v err:%s ", rpcMsg.RpcMsgId, err.Error()))
return nil, pb.ErrCode_SystemErr
}
_ = json.Unmarshal(rspMsg.Msg, us)
return us, pb.ErrCode_OK
}
// 匹配房间
func (s *MatchService) onMatchRoom(iMsg *ipb.InternalMsg, req *pb.C2SMatchRoom) {
ksync.GoSafe(func() {
// color game无需进入匹配队列
switch pb.ServiceTypeId(req.PlayType) {
case pb.ServiceTypeId_STI_ColorGame:
s.SendServiceMsg(service.TopicEx(iMsg.ServiceName), iMsg.ConnId, iMsg.UserId, int32(pb.MsgId_S2CMatchRoomId), req)
return
}
var us *user.GameUser
rsp := &pb.S2CMatchRoom{}
us, rsp.Code = s.getGameUser(iMsg.UserId)
if rsp.Code != pb.ErrCode_OK {
s.SendServiceMsg(service.TopicEx(iMsg.ServiceName), iMsg.ConnId, iMsg.UserId, int32(pb.MsgId_S2CMatchRoomId), rsp)
return
}
vipLv, vipExp := utils.VipLevel(us.VipExp)
rsp.User = &pb.GameUser{
UserId: us.ID,
Nickname: us.Nickname,
Avatar: us.AvatarUrl,
AvatarFrame: us.AvatarFrame,
VipLevel: vipLv,
VipExp: vipExp,
Seat: 0,
Gold: us.Gold,
}
rsp.PlayType = req.PlayType
rsp.RoomType = req.RoomType
switch pb.ServiceTypeId(rsp.PlayType) {
case pb.ServiceTypeId_STI_ColorGame:
rsp.ColorInfo = &pb.S2CMatchRoom_ColorInfo{}
}
s.SendServiceMsg(service.TopicEx(iMsg.ServiceName), iMsg.ConnId, iMsg.UserId, int32(pb.MsgId_S2CMatchRoomId), rsp)
}, nil)
}

View File

@ -0,0 +1,17 @@
package server
import (
"game/common/proto/pb"
"github.com/fox/fox/processor"
"time"
)
const (
timeout = time.Second * 30
)
func (s *MatchService) initProcessor() {
s.processor.RegisterMessages(processor.RegisterMetas{
pb.MsgId_C2SMatchRoomId: {pb.C2SMatchRoom{}, s.onMatchRoom},
})
}

View File

@ -0,0 +1,119 @@
package server
import (
"fmt"
"game/common/proto/pb"
"game/common/serviceName"
"game/common/userBindService"
"game/server/match/config"
"game/server/match/match"
"game/server/match/model"
"github.com/fox/fox/ipb"
"github.com/fox/fox/log"
"github.com/fox/fox/processor"
"github.com/fox/fox/service"
"github.com/golang/protobuf/proto"
)
var Services []*MatchService
type MatchService struct {
*service.NatsService
processor *processor.Processor
bindService *userBindService.UserBindService
matchMgr *match.MatchMgr
}
func Init() {
log.DebugF("init service begin id:%v, num:%v", config.Command.ServiceId, config.Command.ServiceNum)
for i := 0; i < config.Command.ServiceNum; i++ {
sid := config.Command.ServiceId + i
if srv := newService(sid); srv != nil {
Services = append(Services, srv)
}
}
}
func Stop() {
for _, srv := range Services {
log.DebugF("notify stop service %v", srv.Name())
srv.NotifyStop()
}
for _, srv := range Services {
srv.WaitStop()
}
}
func newService(serviceId int) *MatchService {
var err error
s := new(MatchService)
s.matchMgr = match.NewMatchMgr()
sName := fmt.Sprintf("%v-%d", serviceName.Match, serviceId)
if s.NatsService, err = service.NewNatsService(&service.InitNatsServiceParams{
EtcdAddress: config.Cfg.Etcd.Address,
EtcdUsername: "",
EtcdPassword: "",
NatsAddress: config.Cfg.Nats.Address,
ServiceType: serviceName.Match,
ServiceName: sName,
OnFunc: s,
TypeId: int(pb.ServiceTypeId_STI_Match),
Version: config.Cfg.BuildDate,
}); err != nil {
log.Fatal(err.Error())
return nil
}
s.bindService = userBindService.NewUserBindService(model.UserBindServiceRedis, s.ServiceEtcd())
s.processor = processor.NewProcessor()
s.initProcessor()
s.OnInit()
return s
}
func (s *MatchService) OnInit() {
// if err := s.NatsService.QueueSubscribe(service.GroupTopic(s), service.GroupQueue(s)); err != nil {
// log.Error(err.Error())
// }
s.NatsService.Run()
log.Debug("onInit")
}
func (s *MatchService) CanStop() bool {
return true
}
func (s *MatchService) OnStop() {
s.NatsService.OnStop()
log.Debug("OnStop")
}
// 处理其它服发送过来的消息
func (s *MatchService) OnMessage(data []byte) error {
var iMsg = &ipb.InternalMsg{}
var err error
if err = proto.Unmarshal(data, iMsg); err != nil {
log.Error(err.Error())
return err
}
if req, err := s.processor.Unmarshal(iMsg.MsgId, iMsg.Msg); err == nil {
err = s.processor.Dispatch(iMsg.MsgId, iMsg, req)
} else {
log.Error(err.Error())
}
log.Debug(s.Log("received message:%v", iMsg.MsgId))
return nil
}
// 向内部服务发送消息
func (s *MatchService) SendServiceData(topic string, connId uint32, userId int64, msgId int32, data []byte) {
iMsg := ipb.MakeMsg(s.Name(), connId, userId, msgId, data)
_ = s.Send(topic, iMsg)
}
// 向内部服务发送消息
func (s *MatchService) SendServiceMsg(topic string, connId uint32, userId int64, msgId int32, msg proto.Message) {
data, _ := proto.Marshal(msg)
s.SendServiceData(topic, connId, userId, msgId, data)
}