From f2133bfb8018ab3f0ab5bfa27625e84ececdfcef Mon Sep 17 00:00:00 2001 From: liuxiaobo <1224730913@qq.com> Date: Sun, 15 Jun 2025 00:00:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=95=E6=B3=A8=E6=89=A3=E9=92=B1=E5=8F=8A?= =?UTF-8?q?=E7=BB=93=E7=AE=97=E6=89=A3=E9=92=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/baseroom/baseRoom.go | 4 + common/baseroom/hundredRoom.go | 4 + common/model/tableOperation.go | 123 ++++++++++++++++----- common/pb/code.proto | 2 +- common/pb/colorgame.proto | 2 +- common/pb/common.proto | 6 + common/pb/msgId.proto | 1 + common/proto/pb/common.pb.go | 82 ++++++++++++-- common/proto/pb/msgId.pb.go | 8 +- common/userBindService/userService.go | 38 +++---- server/colorgame/room/c2s.go | 6 +- server/colorgame/room/colorRoom.go | 56 ++++++++-- server/colorgame/room/factory.go | 5 +- server/colorgame/room/helper.go | 75 ++++++++++--- server/colorgame/room/process.go | 1 + server/colorgame/room/s2c.go | 5 + server/colorgame/server/processor.go | 2 +- server/colorgame/server/service.go | 2 +- server/db/server/handlerUser.go | 25 ++++- server/lobby/server/processor.go | 9 +- server/login/model/userAccount.go | 152 -------------------------- server/match/match/match.go | 2 +- server/match/server/match.go | 2 +- 23 files changed, 356 insertions(+), 256 deletions(-) delete mode 100644 server/login/model/userAccount.go diff --git a/common/baseroom/baseRoom.go b/common/baseroom/baseRoom.go index d896fe8..3884c80 100644 --- a/common/baseroom/baseRoom.go +++ b/common/baseroom/baseRoom.go @@ -63,6 +63,10 @@ func (r *BaseRoom[Seat]) GameId() int { return r.gameId } +func (r *BaseRoom[Seat]) GetService() service.IService { + return r.srv +} + // 入座玩家数量 func (r *BaseRoom[Seat]) SeatPlayerNum(playerType PlayerType) int { num := 0 diff --git a/common/baseroom/hundredRoom.go b/common/baseroom/hundredRoom.go index 1bd8efe..4fb688e 100644 --- a/common/baseroom/hundredRoom.go +++ b/common/baseroom/hundredRoom.go @@ -41,6 +41,10 @@ func (r *HundredRoom) GameId() int { return r.room.GameId() } +func (r *HundredRoom) GetService() service.IService { + return r.room.GetService() +} + // 房间玩家数量 func (r *HundredRoom) GetPlayerNum(playerType PlayerType) int { if playerType == PT_All { diff --git a/common/model/tableOperation.go b/common/model/tableOperation.go index 2edb429..3c6cd6b 100644 --- a/common/model/tableOperation.go +++ b/common/model/tableOperation.go @@ -9,6 +9,8 @@ import ( "github.com/fox/fox/log" "github.com/go-redis/redis/v8" "gorm.io/gorm" + "reflect" + "strconv" "time" ) @@ -42,7 +44,23 @@ func (s *TableOp[T]) redisKey(id int64) string { return fmt.Sprintf("%s:%d", s.tableName(), id) } +// 查找并返回结构体 func (s *TableOp[T]) findByRedis(id int64) *T { + maps := s.findByRedisMaps(id) + if len(maps) == 0 { + return nil + } + us, err := serialization.MapToStruct[T](maps) + if err != nil { + log.ErrorF("serialization map to struct err: %v", err) + return nil + } + //log.DebugF("findByRedis redis-key:%v result:%v", s.redisKey(id), us) + return us +} + +// 查找并返回map +func (s *TableOp[T]) findByRedisMaps(id int64) map[string]string { if s.rds == nil { return nil } @@ -54,13 +72,7 @@ func (s *TableOp[T]) findByRedis(id int64) *T { if len(maps) == 0 { return nil } - us, err := serialization.MapToStruct[T](maps) - if err != nil { - log.ErrorF("serialization map to struct err: %v", err) - return nil - } - //log.DebugF("findByRedis redis-key:%v result:%v", s.redisKey(id), us) - return us + return maps } func (s *TableOp[T]) writeRedis(id int64, t *T) { @@ -94,22 +106,6 @@ func (s *TableOp[T]) updateRedis(id int64, maps map[string]any) { _ = s.rds.Expire(context.Background(), s.redisKey(id), TableExpire).Err() } -func (s *TableOp[T]) addRedis(id int64, maps map[string]int64) map[string]int64 { - if s.rds == nil { - return nil - } - rets := map[string]int64{} - for k, v := range maps { - if ret, err := s.rds.HIncrBy(context.Background(), s.redisKey(id), k, v).Result(); err != nil { - log.ErrorF("redis-key:%v HIncrBy field err: %v", s.redisKey(id), k, err) - } else { - rets[k] = ret - } - } - _ = s.rds.Expire(context.Background(), s.redisKey(id), TableExpire).Err() - return rets -} - func (s *TableOp[T]) deleteRedis(id int64) { if s.rds == nil { return @@ -166,7 +162,64 @@ func (s *TableOp[T]) Update(id int64, updates map[string]any) (*T, pb.ErrCode) { return &result.V, pb.ErrCode_OK } -func (s *TableOp[T]) Add(id int64, res map[string]int64) (map[string]int64, pb.ErrCode) { +// 辅助函数:将map的keys转为字符串slice +func keysToStringSlice(m map[string]int64) []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + return keys +} + +// 获取资源 +func (s *TableOp[T]) GetInt(id int64, resName []string) (map[string]int64, pb.ErrCode) { + mapFields := s.findByRedisMaps(id) + // 查询更新后的值到map + updatedValues := make(map[string]int64) + if len(mapFields) != 0 { + for _, name := range resName { + v, _ := strconv.ParseInt(mapFields[name], 10, 64) + updatedValues[name] = v + } + return updatedValues, pb.ErrCode_OK + } + + // redis中没有值,从表中加载并写入redis + mapAny := make(map[string]any) + err := s.db.Model(new(T)). + Where("id = ?", id). + Take(&mapAny). // 扫描到map + Error + + if err != nil { + log.ErrorF("query updated values table:%v id:%v err:%v", s.tableName(), id, err) + return nil, pb.ErrCode_SystemErr + } + s.updateRedis(id, mapAny) + + // 查询更新后的值到map + updatedValues = make(map[string]int64) + for _, name := range resName { + if val, ok := mapAny[name]; ok { + var v64 int64 + switch v := val.(type) { + case int64: + v64 = v + case int, int32, uint, uint32, uint64: + v64 = reflect.ValueOf(v).Int() + case float32, float64: + v64 = int64(reflect.ValueOf(v).Float()) + default: + // 处理无法转换的情况 + } + updatedValues[name] = v64 + } + } + return updatedValues, pb.ErrCode_OK +} + +// 增加或减少资源 +func (s *TableOp[T]) AddInt(id int64, res map[string]int64) (map[string]int64, pb.ErrCode) { addRes := map[string]any{} for k, v := range res { addRes[k] = gorm.Expr(fmt.Sprintf("%v + ?", k), v) @@ -177,8 +230,26 @@ func (s *TableOp[T]) Add(id int64, res map[string]int64) (map[string]int64, pb.E log.ErrorF("add table:%v id:%v err:%v", s.tableName(), id, err) return nil, pb.ErrCode_SystemErr } - rets := s.addRedis(id, res) - return rets, pb.ErrCode_OK + + // 查询更新后的值到map + updatedValues := make(map[string]int64) + err = s.db.Model(new(T)). + Select(keysToStringSlice(res)). // 只选择需要返回的字段 + Where("id = ?", id). + Take(&updatedValues). // 扫描到map + Error + + if err != nil { + log.ErrorF("query updated values table:%v id:%v err:%v", s.tableName(), id, err) + return nil, pb.ErrCode_SystemErr + } + + mapAny := make(map[string]any) + for k, v := range updatedValues { + mapAny[k] = v + } + s.updateRedis(id, mapAny) + return updatedValues, pb.ErrCode_OK } func (s *TableOp[T]) Delete(id int64) (*T, pb.ErrCode) { diff --git a/common/pb/code.proto b/common/pb/code.proto index c8724d6..f0e072e 100644 --- a/common/pb/code.proto +++ b/common/pb/code.proto @@ -15,7 +15,7 @@ enum ErrCode VersionTooLow = 115; // 版本太低,无法登陆 Maintain = 120; // 系统维护 - GoldNotEnough = 125; // 金币不足 + GoldNotEnough = 125; // 金币或其它资源不足 NotBetCount = 126; // 数场不投注被踢出 NotLeaveRoom = 127; // 无法离开房间(有下注) // color game diff --git a/common/pb/colorgame.proto b/common/pb/colorgame.proto index 96312d8..3562ba1 100644 --- a/common/pb/colorgame.proto +++ b/common/pb/colorgame.proto @@ -215,7 +215,7 @@ message NtfColorSettle repeated UserBetAreaMul userAreaWin = 1; // 每个投注区域的下注及输赢 repeated ColorType threeDice = 2; // 骰子开出颜色 - int64 totalWin = 3; // 总输赢 + int64 totalWin = 3; // 总输赢,税后包含本金 int64 totalBet = 4; // 总投注 int64 totalWinBaseBet = 6; // 赢钱区域的总投注 int64 tax = 5; // 税 diff --git a/common/pb/common.proto b/common/pb/common.proto index 56ef1cf..fec80a7 100644 --- a/common/pb/common.proto +++ b/common/pb/common.proto @@ -37,3 +37,9 @@ message RspLeaveRoom ErrCode code = 1; } + +// 加钱或扣钱失败通知 +message NtfPayoutFail +{ + ErrCode code = 1; +} diff --git a/common/pb/msgId.proto b/common/pb/msgId.proto index da12c88..810923c 100644 --- a/common/pb/msgId.proto +++ b/common/pb/msgId.proto @@ -18,6 +18,7 @@ enum MsgId RspEnterRoomId = 1003; ReqLeaveRoomId = 1004; // 离开房间 RspLeaveRoomId = 1005; + NtfPayoutFailId = 1010; // 加减钱失败 // 聊天服 2000-2099 ReqChatId = 2000; // 玩家聊天消息 diff --git a/common/proto/pb/common.pb.go b/common/proto/pb/common.pb.go index 92a2ec1..00c0e93 100644 --- a/common/proto/pb/common.pb.go +++ b/common/proto/pb/common.pb.go @@ -247,6 +247,59 @@ func (x *RspLeaveRoom) GetCode() ErrCode { return ErrCode_OK } +// 加钱或扣钱失败通知 +type NtfPayoutFail struct { + state protoimpl.MessageState `protogen:"open.v1"` + Code ErrCode `protobuf:"varint,1,opt,name=code,proto3,enum=pb.ErrCode" json:"code,omitempty"` + UserId int64 `protobuf:"varint,2,opt,name=userId,proto3" json:"userId,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *NtfPayoutFail) Reset() { + *x = NtfPayoutFail{} + mi := &file_common_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *NtfPayoutFail) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NtfPayoutFail) ProtoMessage() {} + +func (x *NtfPayoutFail) ProtoReflect() protoreflect.Message { + mi := &file_common_proto_msgTypes[5] + 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 NtfPayoutFail.ProtoReflect.Descriptor instead. +func (*NtfPayoutFail) Descriptor() ([]byte, []int) { + return file_common_proto_rawDescGZIP(), []int{5} +} + +func (x *NtfPayoutFail) GetCode() ErrCode { + if x != nil { + return x.Code + } + return ErrCode_OK +} + +func (x *NtfPayoutFail) GetUserId() int64 { + if x != nil { + return x.UserId + } + return 0 +} + var File_common_proto protoreflect.FileDescriptor const file_common_proto_rawDesc = "" + @@ -262,7 +315,10 @@ const file_common_proto_rawDesc = "" + "\broomType\x18\x03 \x01(\x05R\broomType\"\x0e\n" + "\fReqLeaveRoom\"/\n" + "\fRspLeaveRoom\x12\x1f\n" + - "\x04code\x18\x01 \x01(\x0e2\v.pb.ErrCodeR\x04codeB\x11Z\x0fcommon/proto/pbb\x06proto3" + "\x04code\x18\x01 \x01(\x0e2\v.pb.ErrCodeR\x04code\"H\n" + + "\rNtfPayoutFail\x12\x1f\n" + + "\x04code\x18\x01 \x01(\x0e2\v.pb.ErrCodeR\x04code\x12\x16\n" + + "\x06userId\x18\x02 \x01(\x03R\x06userIdB\x11Z\x0fcommon/proto/pbb\x06proto3" var ( file_common_proto_rawDescOnce sync.Once @@ -276,24 +332,26 @@ func file_common_proto_rawDescGZIP() []byte { return file_common_proto_rawDescData } -var file_common_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_common_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_common_proto_goTypes = []any{ (*NtfKickOutUser)(nil), // 0: pb.NtfKickOutUser (*ReqEnterRoom)(nil), // 1: pb.ReqEnterRoom (*RspEnterRoom)(nil), // 2: pb.RspEnterRoom (*ReqLeaveRoom)(nil), // 3: pb.ReqLeaveRoom (*RspLeaveRoom)(nil), // 4: pb.RspLeaveRoom - (ErrCode)(0), // 5: pb.ErrCode + (*NtfPayoutFail)(nil), // 5: pb.NtfPayoutFail + (ErrCode)(0), // 6: pb.ErrCode } var file_common_proto_depIdxs = []int32{ - 5, // 0: pb.NtfKickOutUser.code:type_name -> pb.ErrCode - 5, // 1: pb.RspEnterRoom.code:type_name -> pb.ErrCode - 5, // 2: pb.RspLeaveRoom.code:type_name -> pb.ErrCode - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 6, // 0: pb.NtfKickOutUser.code:type_name -> pb.ErrCode + 6, // 1: pb.RspEnterRoom.code:type_name -> pb.ErrCode + 6, // 2: pb.RspLeaveRoom.code:type_name -> pb.ErrCode + 6, // 3: pb.NtfPayoutFail.code:type_name -> pb.ErrCode + 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_common_proto_init() } @@ -308,7 +366,7 @@ func file_common_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_proto_rawDesc), len(file_common_proto_rawDesc)), NumEnums: 0, - NumMessages: 5, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/common/proto/pb/msgId.pb.go b/common/proto/pb/msgId.pb.go index 2232b81..e064625 100644 --- a/common/proto/pb/msgId.pb.go +++ b/common/proto/pb/msgId.pb.go @@ -35,6 +35,7 @@ const ( MsgId_RspEnterRoomId MsgId = 1003 MsgId_ReqLeaveRoomId MsgId = 1004 // 离开房间 MsgId_RspLeaveRoomId MsgId = 1005 + MsgId_NtfPayoutFailId MsgId = 1010 // 加减钱失败 // 聊天服 2000-2099 MsgId_ReqChatId MsgId = 2000 // 玩家聊天消息 MsgId_RspChatId MsgId = 2001 // 复用C2SChatMsg @@ -74,6 +75,7 @@ var ( 1003: "RspEnterRoomId", 1004: "ReqLeaveRoomId", 1005: "RspLeaveRoomId", + 1010: "NtfPayoutFailId", 2000: "ReqChatId", 2001: "RspChatId", 2100: "ReqUserLoginId", @@ -106,6 +108,7 @@ var ( "RspEnterRoomId": 1003, "ReqLeaveRoomId": 1004, "RspLeaveRoomId": 1005, + "NtfPayoutFailId": 1010, "ReqChatId": 2000, "RspChatId": 2001, "ReqUserLoginId": 2100, @@ -163,7 +166,7 @@ var File_msgId_proto protoreflect.FileDescriptor const file_msgId_proto_rawDesc = "" + "\n" + - "\vmsgId.proto\x12\x02pb*\xaa\x05\n" + + "\vmsgId.proto\x12\x02pb*\xc0\x05\n" + "\x05MsgId\x12\x0e\n" + "\n" + "MI_Unknown\x10\x00\x12\x12\n" + @@ -172,7 +175,8 @@ const file_msgId_proto_rawDesc = "" + "\x0eReqEnterRoomId\x10\xea\a\x12\x13\n" + "\x0eRspEnterRoomId\x10\xeb\a\x12\x13\n" + "\x0eReqLeaveRoomId\x10\xec\a\x12\x13\n" + - "\x0eRspLeaveRoomId\x10\xed\a\x12\x0e\n" + + "\x0eRspLeaveRoomId\x10\xed\a\x12\x14\n" + + "\x0fNtfPayoutFailId\x10\xf2\a\x12\x0e\n" + "\tReqChatId\x10\xd0\x0f\x12\x0e\n" + "\tRspChatId\x10\xd1\x0f\x12\x13\n" + "\x0eReqUserLoginId\x10\xb4\x10\x12\x13\n" + diff --git a/common/userBindService/userService.go b/common/userBindService/userService.go index 31f16aa..92b7637 100644 --- a/common/userBindService/userService.go +++ b/common/userBindService/userService.go @@ -165,7 +165,7 @@ func (m *UserBindService) HashServiceNode(typeId pb.ServiceTypeId, uid int64) (* // 要查找的服务必须是状态服,无状态服不需要查找指定服务。 // 如果玩家是首次使用该服务,则随机一个服务并保存玩家该服务节点。下次查找时返回该节点。 -func (m *UserBindService) FindServiceNode(userId int64, typeId pb.ServiceTypeId) (*etcd.ServiceNode, error) { +func (m *UserBindService) FindServiceNode(typeId pb.ServiceTypeId, userId int64) (*etcd.ServiceNode, error) { if userId > 0 { // 向redis中查询。redis中保留的服务节点不一定是可用的,还需要向etcd中验证 sName := m.LoadFromRedis(userId, typeId) @@ -187,26 +187,24 @@ func (m *UserBindService) FindServiceNode(userId int64, typeId pb.ServiceTypeId) return node, nil } -// 查找玩家所有的玩法节点 -func (m *UserBindService) GetAllUserInGameServiceNode(userId int64) ([]*etcd.ServiceNode, error) { - if userId > 0 { - maps, err := m.rdb.HGetAll(context.Background(), m.makeRedisKey(userId)).Result() - if err != nil && err != redis.Nil { - log.ErrorF("user:%v get all service error:%v", err.Error()) - return nil, err - } - if len(maps) > 0 { - inGames := make([]*etcd.ServiceNode, 0) - m.etcdRegistry.GetNodes().Range(func(k, v interface{}) bool { - if node, ok := v.(etcd.ServiceNode); ok { - if _, ok = maps[node.Name]; ok && node.TypeId != int(pb.ServiceTypeId_STI_Gate) { - inGames = append(inGames, &node) +// 对玩家所在有所有节点做操作 +func (m *UserBindService) RangeUserAllServiceNode(userId int64, proc func(node *etcd.ServiceNode) bool) { + maps, err := m.rdb.HGetAll(context.Background(), m.makeRedisKey(userId)).Result() + if err != nil && err != redis.Nil { + log.ErrorF("user:%v get all service error:%v", userId, err.Error()) + return + } + if len(maps) > 0 { + m.etcdRegistry.GetNodes().Range(func(k, v interface{}) bool { + if node, ok := v.(etcd.ServiceNode); ok { + if _, ok = maps[node.Name]; ok { + if !proc(&node) { + return false } } - return true - }) - return inGames, nil - } + } + return true + }) } - return nil, nil + return } diff --git a/server/colorgame/room/c2s.go b/server/colorgame/room/c2s.go index 561c58a..78cd6d4 100644 --- a/server/colorgame/room/c2s.go +++ b/server/colorgame/room/c2s.go @@ -2,8 +2,6 @@ package room import ( "game/common/proto/pb" - "game/common/userBindService" - "game/server/colorgame/model" "github.com/fox/fox/ipb" ) @@ -55,9 +53,7 @@ func (rm *ColorRoom) checkLeaveRoom(user *ColorPlayer, iMsg *ipb.InternalMsg, re func (rm *ColorRoom) leaveRoom(user *ColorPlayer) { rm.DelPlayer(user.Id()) rm.userMgr.Del(user.ID) - // 移除redis中玩家与本服务绑定关系 - userService := userBindService.NewUserBindService(model.UserBindServiceRedis, nil) - userService.DelUserService(user.ID, pb.ServiceTypeId_STI_ColorGame) + rm.bindService.DelUserService(user.ID, pb.ServiceTypeId_STI_ColorGame) } func (rm *ColorRoom) onLeaveRoom(user *ColorPlayer, iMsg *ipb.InternalMsg, req *pb.ReqLeaveRoom) { diff --git a/server/colorgame/room/colorRoom.go b/server/colorgame/room/colorRoom.go index 12dfc6c..5ab4fdf 100644 --- a/server/colorgame/room/colorRoom.go +++ b/server/colorgame/room/colorRoom.go @@ -4,7 +4,10 @@ import ( "game/common/baseroom" "game/common/config/game" "game/common/jackpot" + modelUser "game/common/model/user" "game/common/proto/pb" + "game/common/rpc" + "game/common/userBindService" "game/server/chat/model" "github.com/fox/fox/log" "github.com/fox/fox/processor" @@ -13,7 +16,9 @@ import ( type ColorRoom struct { *baseroom.HundredRoom - userMgr *baseroom.PlayerMgr + userMgr *baseroom.PlayerMgr + bindService *userBindService.UserBindService + gameNo string roomCfg *game.ColorRoomConfig timingCfg *game.ColorGameTiming @@ -32,11 +37,12 @@ type ColorRoom struct { betAreaInfo []*pb.ColorBetAreaInfo // 本局每个投注区域信息 } -func newColorRoom(id, roomType int, srv service.IService, userMgr *baseroom.PlayerMgr) (baseroom.IRoom, pb.ErrCode) { +func newColorRoom(id, roomType int, srv service.IService, userMgr *baseroom.PlayerMgr, bindService *userBindService.UserBindService) (baseroom.IRoom, pb.ErrCode) { gameId := int(pb.ServiceTypeId_STI_ColorGame) rm := &ColorRoom{ HundredRoom: nil, userMgr: userMgr, + bindService: bindService, roomCfg: &game.ColorRoomConfig{}, timingCfg: &game.ColorGameTiming{}, status: 0, @@ -108,16 +114,42 @@ func (rm *ColorRoom) resetGameData() { rm.kickoutUsers() } -// 当前拥有金币 -func (rm *ColorRoom) GetGold(user *ColorPlayer) int64 { - return user.Gold - user.totalBet +func (rm *ColorRoom) GameNo() string { + return rm.gameNo } -// 加减金币 -func (rm *ColorRoom) AddGold(user *ColorPlayer, add int64) (int64, bool) { - if user.Gold-user.totalBet+add < 0 { - return user.Gold - user.totalBet, false - } - user.totalBet += add - return user.Gold - user.totalBet, true +// 当前拥有金币 +func (rm *ColorRoom) GetGold(user *ColorPlayer) int64 { + return user.Gold +} + +// 加减金币 预加减,没有真实向db服请求 +func (rm *ColorRoom) AddGold(user *ColorPlayer, add int64) (int64, bool) { + if user.Gold+add < 0 { + return user.Gold, false + } + user.Gold += add + return user.Gold, true +} + +// 从db服拿到最新数据,重置玩家数据 +func (rm *ColorRoom) setGold(user *ColorPlayer, gold int64) { + user.Gold = gold +} + +// 加减金币 真实抠钱 +func (rm *ColorRoom) rpcPayoutGold(user *ColorPlayer, add int64, reason string) (int64, bool) { + resName := "gold" + res, code := rpc.RpcAddUserRes(rm.bindService, rm.GetService(), user.Id(), &modelUser.AddUserRes{ + GameId: rm.GameId(), + GameNo: rm.GameNo(), + Reason: reason, + AddRes: map[string]int64{ + resName: add, + }, + }) + if code == pb.ErrCode_OK { + return res[resName], true + } + return 0, false } diff --git a/server/colorgame/room/factory.go b/server/colorgame/room/factory.go index 8124443..388b537 100644 --- a/server/colorgame/room/factory.go +++ b/server/colorgame/room/factory.go @@ -3,14 +3,15 @@ package room import ( "game/common/baseroom" "game/common/proto/pb" + "game/common/userBindService" "github.com/fox/fox/service" ) type RoomFactory struct { } -func (r *RoomFactory) CreateRoom(id, roomType int, srv service.IService, userMgr *baseroom.PlayerMgr) (baseroom.IRoom, pb.ErrCode) { - return newColorRoom(id, roomType, srv, userMgr) +func (r *RoomFactory) CreateRoom(id, roomType int, srv service.IService, userMgr *baseroom.PlayerMgr, bindService *userBindService.UserBindService) (baseroom.IRoom, pb.ErrCode) { + return newColorRoom(id, roomType, srv, userMgr, bindService) } func GetPlayer(p baseroom.IPlayer) *ColorPlayer { diff --git a/server/colorgame/room/helper.go b/server/colorgame/room/helper.go index 78c26da..ae0f597 100644 --- a/server/colorgame/room/helper.go +++ b/server/colorgame/room/helper.go @@ -14,6 +14,11 @@ import ( "sync" ) +const ( + ReasonBet = "bet" // 扣钱原因:投注 + ReasonSettle = "settle" // 扣钱或加钱原因:结算 +) + func (rm *ColorRoom) setStatus(status pb.ColorGameStatus) { rm.status = status rm.statusTime = xtime.Now().TimestampMilli() @@ -300,8 +305,8 @@ func (rm *ColorRoom) calculateUserScore(user *ColorPlayer, jpArea pb.ColorBetAre gold = win2 * (100 - rm.roomCfg.Rate) / 100 // 算税() msg.Tax += win2 - gold - // 加回投注本金 - gold += user.totalBets[winArea.Area] + //// 加回投注本金 + //gold += user.totalBets[winArea.Area] msg.TotalWin += gold // 统计赢区的下注总额 msg.TotalWinBaseBet += user.totalBets[winArea.Area] @@ -313,8 +318,8 @@ func (rm *ColorRoom) calculateUserScore(user *ColorPlayer, jpArea pb.ColorBetAre gold = jpScore * (100 - rm.roomCfg.Rate) / 100 // 算税() msg.Tax += win2 - gold - // 加回投注本金 - gold += user.totalBets[winArea.Area] + //// 加回投注本金 + //gold += user.totalBets[winArea.Area] msg.TotalWin += gold // 统计赢区的下注总额 msg.TotalWinBaseBet += user.totalBets[winArea.Area] @@ -337,22 +342,24 @@ func (rm *ColorRoom) calculateUserScore(user *ColorPlayer, jpArea pb.ColorBetAre // 和平台做结算 func (rm *ColorRoom) settle() { - //var allWinner []*pb.ColorPinoyLiveBigWinner rm.calculateAllUserScore() wg := new(sync.WaitGroup) rm.RangePlayer(func(u baseroom.IPlayer) bool { - wg.Add(1) user := GetPlayer(u) - ksync.GoSafe(func() { - defer wg.Done() - if user.totalBet > 0 { - // 和平台做赢钱结算 - //_, err := rm.TransInoutGameEnd(u, 0, u.SettleMsg.TotalWin, u.SettleMsg.Tax) - //if err != nil { - // log.Error(rm.Log(err.Error())) - //} + // 没有赢分 + if user.settleMsg.TotalWin < 1 { + return true + } + ksync.GroupGo(wg, func() { + afterGold, ok := rm.rpcPayoutGold(user, user.settleMsg.TotalWin, ReasonSettle) + if !ok { + log.Error(rm.UserLog(user.ID, "add gold:%v fail", user.settleMsg.TotalWin)) + } else { + rm.GetService().RunOnce(func() { + rm.setGold(user, afterGold) + }) } - }, nil) + }) return true }) wg.Wait() @@ -476,3 +483,41 @@ func (rm *ColorRoom) getProtoUser(user *ColorPlayer) *pb.ColorUser { } return u } + +// 结束下注,扣除投注玩家的投注金额 +func (rm *ColorRoom) endBetPayoutUser() { + var failer []*ColorPlayer + var mt sync.Mutex + wg := &sync.WaitGroup{} + rm.RangePlayer(func(u baseroom.IPlayer) bool { + user := GetPlayer(u) + // 没有下注 + if user.totalBet < 1 { + return true + } + ksync.GroupGo(wg, func() { + afterGold, ok := rm.rpcPayoutGold(user, -user.totalBet, ReasonBet) + if !ok { + mt.Lock() + failer = append(failer, user) + mt.Unlock() + } else { + rm.GetService().RunOnce(func() { + rm.setGold(user, afterGold) + }) + } + }) + + return true + }) + wg.Wait() + + // 移除扣钱失败玩家的投注信息 + for _, u := range failer { + u.totalBet = 0 + for pos := range u.totalBets { + u.totalBets[pos] = 0 + } + rm.notifyPayoutFail(u, pb.ErrCode_GoldNotEnough) + } +} diff --git a/server/colorgame/room/process.go b/server/colorgame/room/process.go index cf3f564..f7e892f 100644 --- a/server/colorgame/room/process.go +++ b/server/colorgame/room/process.go @@ -22,6 +22,7 @@ func (rm *ColorRoom) gameStartBetting() { func (rm *ColorRoom) gameEndBetting() { rm.setStatus(pb.ColorGameStatus_CGS_BetEnd) + rm.endBetPayoutUser() rm.updateEndBetAreaMul() rm.notifyEndBetting() rm.NewTimer(TtOpenThreeDices, time.Duration(rm.timingCfg.EndBetting)*time.Millisecond) diff --git a/server/colorgame/room/s2c.go b/server/colorgame/room/s2c.go index ed6aace..5f048e7 100644 --- a/server/colorgame/room/s2c.go +++ b/server/colorgame/room/s2c.go @@ -68,6 +68,11 @@ func (rm *ColorRoom) notifyKickoutUser(user *ColorPlayer, code pb.ErrCode) { rm.SendMsg(user, pb.MsgId_NtfKickOutUserId, &pb.NtfKickOutUser{Code: code}) } +// 每秒更新各投注区域投注信息 +func (rm *ColorRoom) notifyPayoutFail(user *ColorPlayer, code pb.ErrCode) { + rm.SendMsg(user, pb.MsgId_NtfPayoutFailId, &pb.NtfPayoutFail{Code: code}) +} + // 推送房间信息给玩家 func (rm *ColorRoom) notifyColorRoomInfo(user *ColorPlayer) { ntf := &pb.NtfColorRoomInfo{ diff --git a/server/colorgame/server/processor.go b/server/colorgame/server/processor.go index 0d34728..6b18183 100644 --- a/server/colorgame/server/processor.go +++ b/server/colorgame/server/processor.go @@ -20,7 +20,7 @@ func (s *ColorService) initProcessor() { }) } -// 登录或注册 +// 进房间 func (s *ColorService) onEnterRoom(iMsg *ipb.InternalMsg, req *pb.ReqMatchRoom) { ksync.GoSafe(func() { us, code := rpc.RpcGetGameUser(s.bindService, s, iMsg.UserId) diff --git a/server/colorgame/server/service.go b/server/colorgame/server/service.go index e1c2128..e6df651 100644 --- a/server/colorgame/server/service.go +++ b/server/colorgame/server/service.go @@ -54,7 +54,7 @@ func newColorService(serviceId int) service.IService { s := new(ColorService) s.playerMgr = baseroom.NewPlayerMgr(nil) factory := &room.RoomFactory{} - s.room, _ = factory.CreateRoom(int(pb.ServiceTypeId_STI_ColorGame), 0, s, s.playerMgr) + s.room, _ = factory.CreateRoom(int(pb.ServiceTypeId_STI_ColorGame), 0, s, s.playerMgr, s.bindService) s.status = baseroom.SsWorking //s.roomMgr = baseroom.NewRoomMgr(&room.RoomFactory{}) diff --git a/server/db/server/handlerUser.go b/server/db/server/handlerUser.go index 66db88e..e2a23fb 100644 --- a/server/db/server/handlerUser.go +++ b/server/db/server/handlerUser.go @@ -84,7 +84,7 @@ func (s *DbService) onGetUserByUid(iMsg *ipb.InternalMsg) *ipb.InternalMsg { return iMsg } -// 获取用户数据,没有则创建 +// 获取用户资源数据,没有则创建 func (s *DbService) onGetUserResources(iMsg *ipb.InternalMsg) *ipb.InternalMsg { operationDb[user.UserResources](iMsg, func(res *user.UserResources) (*user.UserResources, pb.ErrCode) { if us1, code := operation.NewUserResourcesOp().Find(res.UID); code != pb.ErrCode_OK { @@ -96,7 +96,7 @@ func (s *DbService) onGetUserResources(iMsg *ipb.InternalMsg) *ipb.InternalMsg { return iMsg } -// 添加用户资源,负数为减少 +// 添加用户资源,负数为减少。资源不能为负数 func (s *DbService) onAddUserResources(iMsg *ipb.InternalMsg) *ipb.InternalMsg { addUserRes := &user.AddUserRes{} err := json.Unmarshal(iMsg.Msg, addUserRes) @@ -105,7 +105,26 @@ func (s *DbService) onAddUserResources(iMsg *ipb.InternalMsg) *ipb.InternalMsg { iMsg.RpcCode = int32(pb.ErrCode_SystemErr) return iMsg } - rets, code := operation.NewUserResourcesOp().Add(iMsg.UserId, addUserRes.AddRes) + // 获取资源的值并检查够不够操作 + resOp := operation.NewUserResourcesOp() + var resNames []string + for name := range addUserRes.AddRes { + resNames = append(resNames, name) + } + userRes, code := resOp.GetInt(iMsg.UserId, resNames) + if code != pb.ErrCode_OK { + iMsg.RpcCode = int32(code) + return iMsg + } + for resName, resValue := range userRes { + // 如果add为负数则为扣除, + if addUserRes.AddRes[resName]+resValue < 1 { + iMsg.RpcCode = int32(pb.ErrCode_GoldNotEnough) + return iMsg + } + } + + rets, code := resOp.AddInt(iMsg.UserId, addUserRes.AddRes) if code != pb.ErrCode_OK { iMsg.RpcCode = int32(code) return iMsg diff --git a/server/lobby/server/processor.go b/server/lobby/server/processor.go index 8987cb6..9ed2d80 100644 --- a/server/lobby/server/processor.go +++ b/server/lobby/server/processor.go @@ -2,6 +2,7 @@ package server import ( "game/common/proto/pb" + "github.com/fox/fox/etcd" "github.com/fox/fox/ipb" "github.com/fox/fox/processor" "github.com/fox/fox/service" @@ -15,7 +16,13 @@ func (s *LobbyService) initProcessor() { // 玩家上线,告诉玩家在哪个玩法中 func (s *LobbyService) onUserOnline(iMsg *ipb.InternalMsg, msg *pb.NtfUserOnline) { - nodes, _ := s.bindService.GetAllUserInGameServiceNode(msg.UserId) + var nodes []*etcd.ServiceNode + s.bindService.RangeUserAllServiceNode(msg.UserId, func(node *etcd.ServiceNode) bool { + if node.TypeId != int(pb.ServiceTypeId_STI_Gate) { + nodes = append(nodes, node) + } + return true + }) ntf := &pb.NtfUserInService{} for _, node := range nodes { ntf.ServiceNames = append(ntf.ServiceNames, node.Name) diff --git a/server/login/model/userAccount.go b/server/login/model/userAccount.go deleted file mode 100644 index 0030e2a..0000000 --- a/server/login/model/userAccount.go +++ /dev/null @@ -1,152 +0,0 @@ -package model - -//import ( -// "errors" -// "github.com/fox/fox/log" -// "golang.org/x/crypto/bcrypt" -// "gorm.io/gorm" -// "time" -//) -// -//const ( -// AccountNormal = 1 // 正常 -// AccountFrozen = 2 // 冻结 -// AccountBanned = 3 // 封禁 -//) -// -//// 玩家账户表 -//type UserAccount struct { -// gorm.Model -// Username string `gorm:"type:varchar(32);uniqueIndex;not null"` // 用户名 -// Password string `gorm:"type:varchar(255);not null"` // 密码哈希 -// Email string `gorm:"type:varchar(100)"` // 邮箱(可选) -// Phone string `gorm:"type:varchar(20)"` // 手机号(可选) -// DeviceID string `gorm:"type:varchar(64);index"` // 设备ID -// LastLoginIP string `gorm:"type:varchar(45)"` // 最后登录IP(支持IPv6) -// LastLoginTime time.Time // 最后登录时间 -// Status int `gorm:"type:tinyint;default:1"` // 账号状态 1-正常 2-冻结 3-封禁 -// RegisterIP string `gorm:"type:varchar(45)"` // 注册IP -// RegisterTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP"` // 注册时间 -//} -// -//// 玩家登录记录表 -//type UserLoginLog struct { -// gorm.Model -// PlayerID uint `gorm:"index"` // 关联玩家ID -// LoginIP string `gorm:"type:varchar(45);not null"` // 登录IP -// LoginTime time.Time `gorm:"type:TIMESTAMP;default:CURRENT_TIMESTAMP"` // 登录时间 -// DeviceInfo string `gorm:"type:varchar(255)"` // 设备信息(JSON格式) -// LoginResult bool // 登录结果 true-成功 false-失败 -// FailReason string `gorm:"type:varchar(100)"` // 失败原因 -//} -// -//type UserLoginOp struct { -// db *gorm.DB -// logDb *gorm.DB -//} -// -//func NewUserLoginOp() *UserLoginOp { -// return &UserLoginOp{db: UserDB, logDb: LogDB} -//} -// -//var ( -// ErrUserOrPassword = errors.New("user or password was error") -// ErrAccountFrozen = errors.New("account frozen") -// ErrAccountBanned = errors.New("account banned") -//) -// -//func (s *UserLoginOp) Login(username, password, ip, deviceID string) (*UserAccount, error) { -// var user UserAccount -// err := s.db.Where("username = ?", username).First(&user).Error -// if err != nil { -// return nil, err -// } -// // 验证密码 -// if err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil { -// s.recordLoginLog(user.ID, ip, deviceID, false, ErrUserOrPassword.Error()) -// return nil, ErrUserOrPassword -// } -// // 检查账号状态 -// switch user.Status { -// case AccountNormal: -// -// case AccountFrozen: -// s.recordLoginLog(user.ID, ip, deviceID, false, ErrAccountFrozen.Error()) -// return nil, ErrAccountFrozen -// case AccountBanned: -// s.recordLoginLog(user.ID, ip, deviceID, false, ErrAccountBanned.Error()) -// return nil, ErrAccountBanned -// } -// // 更新最后登录信息 -// user.LastLoginIP = ip -// user.LastLoginTime = time.Now() -// _ = s.db.Save(&user).Error -// -// // 记录成功登录日志 -// s.recordLoginLog(user.ID, ip, deviceID, true, "") -// -// // 6. 生成访问令牌 -// token, err := generateToken(user.ID, user.Username) -// if err != nil { -// return nil, err -// } -// user.Password = token -// return &user, err -//} -// -//// 注册新用户 -//func (s *UserLoginOp) RegisterNewUser(username, password, ip, deviceID string) (*UserAccount, error) { -// // 密码加密 -// hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) -// if err != nil { -// return nil, err -// } -// user := UserAccount{ -// Username: username, -// Password: string(hashedPassword), -// DeviceID: deviceID, -// RegisterIP: ip, -// Status: 1, -// LastLoginIP: ip, -// LastLoginTime: time.Now(), -// } -// -// if err := s.db.Create(&user).Error; err != nil { -// return nil, err -// } -// -// s.recordLoginLog(user.ID, ip, deviceID, true, "") -// -// // 生成访问令牌 -// token, err := generateToken(user.ID, user.Username) -// if err != nil { -// return nil, err -// } -// user.Password = token -// -// return &user, nil -//} -// -//// 记录登录日志 -//func (s *UserLoginOp) recordLoginLog(userID uint, ip, deviceID string, success bool, failReason string) { -// logEntry := UserLoginLog{ -// PlayerID: userID, -// LoginIP: ip, -// DeviceInfo: deviceID, -// LoginResult: success, -// FailReason: failReason, -// } -// -// if err := s.logDb.Create(&logEntry).Error; err != nil { -// log.ErrorF("记录登录日志失败: %v", err) -// } -//} -// -//// 生成JWT令牌(简化版) -//func generateToken(userID uint, username string) (string, error) { -// _ = userID -// _ = username -// // 这里应该使用JWT库生成实际令牌 -// // 简化实现,实际项目中请使用安全的JWT实现 -// return "generated-token-placeholder", nil -//} diff --git a/server/match/match/match.go b/server/match/match/match.go index 9915dea..1f298fc 100644 --- a/server/match/match/match.go +++ b/server/match/match/match.go @@ -24,7 +24,7 @@ func NewMatchMgr() *MatchMgr { } } -//func (mgr *MatchMgr) Add(us *MatchPlayer, sid pb.ServiceTypeId, roomType int32) { +//func (mgr *MatchMgr) AddInt(us *MatchPlayer, sid pb.ServiceTypeId, roomType int32) { // users, _ := mgr.users[sid] // for _, u := range users { // if u.ID == us.ID { diff --git a/server/match/server/match.go b/server/match/server/match.go index 210b1fd..418ca74 100644 --- a/server/match/server/match.go +++ b/server/match/server/match.go @@ -18,7 +18,7 @@ func (s *MatchService) onMatchRoom(iMsg *ipb.InternalMsg, req *pb.ReqMatchRoom) gameId := pb.ServiceTypeId(req.GameId) switch gameId { case pb.ServiceTypeId_STI_ColorGame: - node, err := s.bindService.FindServiceNode(iMsg.UserId, gameId) + node, err := s.bindService.FindServiceNode(gameId, iMsg.UserId) if err != nil { log.ErrorF("db service node error:%v", err) s.SendServiceMsg(service.TopicEx(iMsg.ServiceName), iMsg.ConnId, iMsg.UserId,