投注扣钱及结算扣钱
This commit is contained in:
parent
fc13e7625c
commit
f2133bfb80
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -15,7 +15,7 @@ enum ErrCode
|
||||
VersionTooLow = 115; // 版本太低,无法登陆
|
||||
|
||||
Maintain = 120; // 系统维护
|
||||
GoldNotEnough = 125; // 金币不足
|
||||
GoldNotEnough = 125; // 金币或其它资源不足
|
||||
NotBetCount = 126; // 数场不投注被踢出
|
||||
NotLeaveRoom = 127; // 无法离开房间(有下注)
|
||||
// color game
|
||||
|
@ -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; // 税
|
||||
|
@ -37,3 +37,9 @@ message RspLeaveRoom
|
||||
ErrCode code = 1;
|
||||
}
|
||||
|
||||
|
||||
// 加钱或扣钱失败通知
|
||||
message NtfPayoutFail
|
||||
{
|
||||
ErrCode code = 1;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ enum MsgId
|
||||
RspEnterRoomId = 1003;
|
||||
ReqLeaveRoomId = 1004; // 离开房间
|
||||
RspLeaveRoomId = 1005;
|
||||
NtfPayoutFailId = 1010; // 加减钱失败
|
||||
|
||||
// 聊天服 2000-2099
|
||||
ReqChatId = 2000; // 玩家聊天消息
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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" +
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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{
|
||||
|
@ -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)
|
||||
|
@ -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{})
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
//}
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user