diff --git a/common/baseroom/interface.go b/common/baseroom/interface.go index 47d9754..cb48baa 100644 --- a/common/baseroom/interface.go +++ b/common/baseroom/interface.go @@ -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 { diff --git a/common/config/game/color.go b/common/config/game/color.go new file mode 100644 index 0000000..12e6dfb --- /dev/null +++ b/common/config/game/color.go @@ -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"` +} diff --git a/common/config/loadConfig.go b/common/config/loadConfig.go index 90b7352..d7291e8 100644 --- a/common/config/loadConfig.go +++ b/common/config/loadConfig.go @@ -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 == "" { diff --git a/common/model/user/user.go b/common/model/user/user.go index 428732b..f80ed5e 100644 --- a/common/model/user/user.go +++ b/common/model/user/user.go @@ -17,3 +17,8 @@ func (u User) GetId() int64 { func (u User) TableName() string { return "user" } + +type GameUser struct { + User + UserResources +} diff --git a/common/pb/colorgame.proto b/common/pb/colorgame.proto new file mode 100644 index 0000000..29dc94c --- /dev/null +++ b/common/pb/colorgame.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; +package pb; +option go_package = "common/proto/pb"; + + + + + + diff --git a/common/pb/match.proto b/common/pb/match.proto new file mode 100644 index 0000000..53d8e46 --- /dev/null +++ b/common/pb/match.proto @@ -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; // 玩家进房间广播 +} + diff --git a/common/pb/msgId.proto b/common/pb/msgId.proto index 5376b3f..547a3e3 100644 --- a/common/pb/msgId.proto +++ b/common/pb/msgId.proto @@ -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; // 玩家进入房间 所有玩法共用此消息 } diff --git a/common/pb/service.proto b/common/pb/service.proto index 9f800e6..03d082f 100644 --- a/common/pb/service.proto +++ b/common/pb/service.proto @@ -9,6 +9,7 @@ enum ServiceTypeId STI_Login = 101; // 登陆服 STI_Chat = 102; // 聊天服 STI_DB = 103; // db服 + STI_Match = 104; // 匹配服 STI_ColorGame = 120; // color game } diff --git a/common/pb/user.proto b/common/pb/user.proto index aa8afef..ae7d8ce 100644 --- a/common/pb/user.proto +++ b/common/pb/user.proto @@ -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; // 金币 +} + diff --git a/common/proto/pb/colorgame.pb.go b/common/proto/pb/colorgame.pb.go new file mode 100644 index 0000000..43b76ee --- /dev/null +++ b/common/proto/pb/colorgame.pb.go @@ -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 +} diff --git a/common/proto/pb/match.pb.go b/common/proto/pb/match.pb.go new file mode 100644 index 0000000..0c4f4a2 --- /dev/null +++ b/common/proto/pb/match.pb.go @@ -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 +} diff --git a/common/proto/pb/msgId.pb.go b/common/proto/pb/msgId.pb.go index 6ee9e52..bf23e4a 100644 --- a/common/proto/pb/msgId.pb.go +++ b/common/proto/pb/msgId.pb.go @@ -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 diff --git a/common/proto/pb/service.pb.go b/common/proto/pb/service.pb.go index d6dbd0a..3ddf1e6 100644 --- a/common/proto/pb/service.pb.go +++ b/common/proto/pb/service.pb.go @@ -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 ( diff --git a/common/proto/pb/user.pb.go b/common/proto/pb/user.pb.go index beadb3a..d7322cd 100644 --- a/common/proto/pb/user.pb.go +++ b/common/proto/pb/user.pb.go @@ -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, }, diff --git a/common/rpcName/rpcName.go b/common/rpcName/rpcName.go index 9f8f649..09b285b 100644 --- a/common/rpcName/rpcName.go +++ b/common/rpcName/rpcName.go @@ -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" ) diff --git a/common/serviceName/serviceName.go b/common/serviceName/serviceName.go index cc14af1..6c52499 100644 --- a/common/serviceName/serviceName.go +++ b/common/serviceName/serviceName.go @@ -5,6 +5,7 @@ const ( Chat = "chat" Login = "login" Db = "db" + Match = "match" // 下面是具体玩法服 ColorGame = "color_game" diff --git a/common/userBindService/userService.go b/common/userBindService/userService.go index b0c1950..9aec8dd 100644 --- a/common/userBindService/userService.go +++ b/common/userBindService/userService.go @@ -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 +} diff --git a/common/utils/util.go b/common/utils/util.go index 9ff663a..4fd435c 100644 --- a/common/utils/util.go +++ b/common/utils/util.go @@ -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)) + } +} diff --git a/common/utils/util_test.go b/common/utils/util_test.go new file mode 100644 index 0000000..c8d39b0 --- /dev/null +++ b/common/utils/util_test.go @@ -0,0 +1,7 @@ +package utils + +import "testing" + +func TestCleanWarn(t *testing.T) { + _ = Tie[int](true, 1, 2) +} diff --git a/server/colorgame/config/config.go b/server/colorgame/config/config.go index b9375f8..f49d9b8 100644 --- a/server/colorgame/config/config.go +++ b/server/colorgame/config/config.go @@ -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() + } } diff --git a/server/colorgame/room/colorPlayer.go b/server/colorgame/room/colorPlayer.go index ee4f6ba..67eb20e 100644 --- a/server/colorgame/room/colorPlayer.go +++ b/server/colorgame/room/colorPlayer.go @@ -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 +} diff --git a/server/colorgame/room/factory.go b/server/colorgame/room/factory.go index 39d7671..2193f84 100644 --- a/server/colorgame/room/factory.go +++ b/server/colorgame/room/factory.go @@ -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 { diff --git a/server/db/server/handlerUser.go b/server/db/server/handlerUser.go index a13511f..2babf95 100644 --- a/server/db/server/handlerUser.go +++ b/server/db/server/handlerUser.go @@ -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 +} diff --git a/server/db/server/processor.go b/server/db/server/processor.go index cecb539..5d2201b 100644 --- a/server/db/server/processor.go +++ b/server/db/server/processor.go @@ -14,5 +14,6 @@ func (s *DbService) initRpcProcessor() { rpcName.GetUserByUid: s.onGetUserByUid, rpcName.GetUserByAccountId: s.onGetUserByAccountId, rpcName.GetUserResources: s.onGetUserResources, + rpcName.GetGameUser: s.onGetGameUser, }) } diff --git a/server/match/cmd/cmd.go b/server/match/cmd/cmd.go new file mode 100644 index 0000000..d173ce7 --- /dev/null +++ b/server/match/cmd/cmd.go @@ -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)) +} diff --git a/server/match/config/config.go b/server/match/config/config.go new file mode 100644 index 0000000..042fb3c --- /dev/null +++ b/server/match/config/config.go @@ -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 +//} diff --git a/server/match/main.go b/server/match/main.go new file mode 100644 index 0000000..d5dcc75 --- /dev/null +++ b/server/match/main.go @@ -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") }) +} diff --git a/server/match/match/match.go b/server/match/match/match.go new file mode 100644 index 0000000..9915dea --- /dev/null +++ b/server/match/match/match.go @@ -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 +//} diff --git a/server/match/model/db.go b/server/match/model/db.go new file mode 100644 index 0000000..f466366 --- /dev/null +++ b/server/match/model/db.go @@ -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 +// } +// } diff --git a/server/match/server/match.go b/server/match/server/match.go new file mode 100644 index 0000000..00999e6 --- /dev/null +++ b/server/match/server/match.go @@ -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) +} diff --git a/server/match/server/processor.go b/server/match/server/processor.go new file mode 100644 index 0000000..cdb4ee4 --- /dev/null +++ b/server/match/server/processor.go @@ -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}, + }) +} diff --git a/server/match/server/service.go b/server/match/server/service.go new file mode 100644 index 0000000..26f1885 --- /dev/null +++ b/server/match/server/service.go @@ -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) +}